1
0
mirror of https://github.com/musix-org/musix-oss synced 2025-06-17 01:16:00 +00:00
This commit is contained in:
MatteZ02
2019-05-30 12:06:47 +03:00
parent cbdffcf19c
commit 5eb0264906
2502 changed files with 360854 additions and 0 deletions

View File

@ -0,0 +1,31 @@
const Ffmpeg = require('./ffmpeg/Ffmpeg');
const transcoders = [
'ffmpeg',
];
class MediaTranscoder {
constructor(prism) {
this.prism = prism;
this.ffmpeg = new Ffmpeg(this);
}
static verifyOptions(options) {
if (!options) throw new Error('Options must be passed to MediaTranscoder.transcode()');
if (!options.type) throw new Error('Options.type must be passed to MediaTranscoder.transcode()');
if (!transcoders.includes(options.type)) throw new Error(`Options.type must be: ${transcoders.join(' ')}`);
return options;
}
/**
* Transcodes a media stream based on specified options
* @param {Object} options the options to use when transcoding
* @returns {ReadableStream} the transcodeed stream
*/
transcode(options) {
options = MediaTranscoder.verifyOptions(options);
return this[options.type].transcode(options);
}
}
module.exports = MediaTranscoder;

View File

@ -0,0 +1,51 @@
const ChildProcess = require('child_process');
const FfmpegProcess = require('./FfmpegProcess');
class FfmpegTranscoder {
constructor(mediaTranscoder) {
this.mediaTranscoder = mediaTranscoder;
this.command = FfmpegTranscoder.selectFfmpegCommand();
this.processes = [];
}
static verifyOptions(options) {
if (!options) throw new Error('Options not provided!');
if (!options.media) throw new Error('Media must be provided');
if (!options.ffmpegArguments || !(options.ffmpegArguments instanceof Array)) {
throw new Error('FFMPEG Arguments must be an array');
}
if (options.ffmpegArguments.includes('-i')) return options;
if (typeof options.media === 'string') {
options.ffmpegArguments = ['-i', `${options.media}`].concat(options.ffmpegArguments).concat(['pipe:1']);
} else {
options.ffmpegArguments = ['-i', '-'].concat(options.ffmpegArguments).concat(['pipe:1']);
}
return options;
}
/**
* Transcodes an input using FFMPEG
* @param {FfmpegTranscoderOptions} options the options to use
* @returns {FfmpegProcess} the created FFMPEG process
* @throws {FFMPEGOptionsError}
*/
transcode(options) {
if (!this.command) this.command = FfmpegTranscoder.selectFfmpegCommand();
const proc = new FfmpegProcess(this, FfmpegTranscoder.verifyOptions(options));
this.processes.push(proc);
return proc;
}
static selectFfmpegCommand() {
try {
return require('ffmpeg-binaries');
} catch (err) {
for (const command of ['ffmpeg', 'avconv', './ffmpeg', './avconv']) {
if (!ChildProcess.spawnSync(command, ['-h']).error) return command;
}
throw new Error('FFMPEG not found');
}
}
}
module.exports = FfmpegTranscoder;

View File

@ -0,0 +1,95 @@
const EventEmitter = require('events').EventEmitter;
const ChildProcess = require('child_process');
/**
* A spawned FFMPEG process
*/
class FfmpegProcess extends EventEmitter {
constructor(ffmpegTranscoder, options) {
super();
/**
* The ffmpeg process
* @type {ChildProcess}
*/
this.process = ChildProcess.spawn(ffmpegTranscoder.command, options.ffmpegArguments);
/**
* The FFMPEG transcoder that created this process
* @type {FfmpegTranscoder}
*/
this.transcoder = ffmpegTranscoder;
/**
* The input media
* @type {?ReadableStream|string}
*/
this.inputMedia = options.media;
if (typeof this.inputMedia !== 'string') {
try {
this.connectStream(this.inputMedia);
} catch (e) {
this.emit('error', e, 'instantiation');
}
} else {
this.attachErrorHandlers();
}
this.on('error', this.kill.bind(this));
this.once('end', this.kill.bind(this));
}
/**
* The ffmpeg output stream
* @type {?ReadableStream}
*/
get output() {
return this.process ? this.process.stdout : null;
}
attachErrorHandlers() {
this.process.stdin.on('error', e => {
// if not killed
if (this.process) {
this.emit('error', e, 'ffmpegProcess.stdin');
}
});
this.process.stdout.on('error', e => {
// if not killed
if (this.process) {
this.emit('error', e, 'ffmpegProcess.stdout');
}
});
this.process.on('error', e => this.emit('error', e, 'ffmpegProcess'));
this.process.stdout.on('end', () => this.emit('end'));
}
/**
* Connects an input stream to the ffmpeg process
* @param {ReadableStream} inputMedia the stream to pass to ffmpeg
* @returns {ReadableStream} the ffmpeg output stream
*/
connectStream(inputMedia) {
if (!this.process) throw new Error('No FFMPEG process available');
this.inputMedia = inputMedia;
this.inputMedia.pipe(this.process.stdin, { end: false });
inputMedia.on('error', e => this.emit('error', e, 'inputstream', inputMedia));
this.attachErrorHandlers();
return this.process.stdout;
}
/**
* Kills the ffmpeg process
*/
kill() {
if (!this.process) return;
if (this.inputMedia && this.inputMedia.unpipe) {
this.inputMedia.unpipe(this.process.stdin);
}
this.process.kill('SIGKILL');
this.process = null;
}
}
module.exports = FfmpegProcess;