FFmpeg.js 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. const ChildProcess = require('child_process');
  2. const { Duplex } = require('stream');
  3. let FFMPEG_COMMAND = null;
  4. class FFmpegTransform extends Duplex {
  5. constructor(options) {
  6. super();
  7. this.process = createFFmpeg(options);
  8. const EVENTS = {
  9. readable: this._reader,
  10. data: this._reader,
  11. end: this._reader,
  12. unpipe: this._reader,
  13. finish: this._writer,
  14. drain: this._writer,
  15. };
  16. this._readableState = this._reader._readableState;
  17. this._writableState = this._writer._writableState;
  18. this._copy(['write', 'end'], this._writer);
  19. this._copy(['read', 'setEncoding', 'pipe', 'unpipe'], this._reader);
  20. for (const method of ['on', 'once', 'removeListener', 'removeListeners', 'listeners']) {
  21. this[method] = (ev, fn) => EVENTS[ev] ? EVENTS[ev][method](ev, fn) : Duplex.prototype[method].call(this, ev, fn);
  22. }
  23. const processError = error => this.emit('error', error);
  24. this._reader.on('error', processError);
  25. this._writer.on('error', processError);
  26. }
  27. get _reader() { return this.process.stdout; }
  28. get _writer() { return this.process.stdin; }
  29. _copy(methods, target) {
  30. for (const method of methods) {
  31. this[method] = target[method].bind(target);
  32. }
  33. }
  34. _destroy(err, cb) {
  35. super._destroy(err, cb);
  36. this.process.kill('SIGKILL');
  37. }
  38. }
  39. module.exports = FFmpegTransform;
  40. function createFFmpeg(options) {
  41. let args = options.args || [];
  42. if (!options.args.includes('-i')) args = ['-i', '-'].concat(args);
  43. return ChildProcess.spawn(selectFFmpegCommand(), args.concat(['pipe:1']));
  44. }
  45. function selectFFmpegCommand() {
  46. if (FFMPEG_COMMAND) return FFMPEG_COMMAND;
  47. try {
  48. FFMPEG_COMMAND = require('ffmpeg-binaries').ffmpegPath();
  49. return FFMPEG_COMMAND;
  50. } catch (err) {
  51. for (const command of ['ffmpeg', 'avconv', './ffmpeg', './avconv']) {
  52. if (!ChildProcess.spawnSync(command, ['-h']).error) {
  53. FFMPEG_COMMAND = command;
  54. return FFMPEG_COMMAND;
  55. }
  56. }
  57. throw new Error('FFMPEG not found');
  58. }
  59. }