fluent-ffmpeg.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*jshint node:true*/
  2. 'use strict';
  3. var path = require('path');
  4. var util = require('util');
  5. var EventEmitter = require('events').EventEmitter;
  6. var utils = require('./utils');
  7. var ARGLISTS = ['_global', '_audio', '_audioFilters', '_video', '_videoFilters', '_sizeFilters', '_complexFilters'];
  8. /**
  9. * Create an ffmpeg command
  10. *
  11. * Can be called with or without the 'new' operator, and the 'input' parameter
  12. * may be specified as 'options.source' instead (or passed later with the
  13. * addInput method).
  14. *
  15. * @constructor
  16. * @param {String|ReadableStream} [input] input file path or readable stream
  17. * @param {Object} [options] command options
  18. * @param {Object} [options.logger=<no logging>] logger object with 'error', 'warning', 'info' and 'debug' methods
  19. * @param {Number} [options.niceness=0] ffmpeg process niceness, ignored on Windows
  20. * @param {Number} [options.priority=0] alias for `niceness`
  21. * @param {String} [options.presets="fluent-ffmpeg/lib/presets"] directory to load presets from
  22. * @param {String} [options.preset="fluent-ffmpeg/lib/presets"] alias for `presets`
  23. * @param {String} [options.stdoutLines=100] maximum lines of ffmpeg output to keep in memory, use 0 for unlimited
  24. * @param {Number} [options.timeout=<no timeout>] ffmpeg processing timeout in seconds
  25. * @param {String|ReadableStream} [options.source=<no input>] alias for the `input` parameter
  26. */
  27. function FfmpegCommand(input, options) {
  28. // Make 'new' optional
  29. if (!(this instanceof FfmpegCommand)) {
  30. return new FfmpegCommand(input, options);
  31. }
  32. EventEmitter.call(this);
  33. if (typeof input === 'object' && !('readable' in input)) {
  34. // Options object passed directly
  35. options = input;
  36. } else {
  37. // Input passed first
  38. options = options || {};
  39. options.source = input;
  40. }
  41. // Add input if present
  42. this._inputs = [];
  43. if (options.source) {
  44. this.input(options.source);
  45. }
  46. // Add target-less output for backwards compatibility
  47. this._outputs = [];
  48. this.output();
  49. // Create argument lists
  50. var self = this;
  51. ['_global', '_complexFilters'].forEach(function(prop) {
  52. self[prop] = utils.args();
  53. });
  54. // Set default option values
  55. options.stdoutLines = 'stdoutLines' in options ? options.stdoutLines : 100;
  56. options.presets = options.presets || options.preset || path.join(__dirname, 'presets');
  57. options.niceness = options.niceness || options.priority || 0;
  58. // Save options
  59. this.options = options;
  60. // Setup logger
  61. this.logger = options.logger || {
  62. debug: function() {},
  63. info: function() {},
  64. warn: function() {},
  65. error: function() {}
  66. };
  67. }
  68. util.inherits(FfmpegCommand, EventEmitter);
  69. module.exports = FfmpegCommand;
  70. /**
  71. * Clone an ffmpeg command
  72. *
  73. * This method is useful when you want to process the same input multiple times.
  74. * It returns a new FfmpegCommand instance with the exact same options.
  75. *
  76. * All options set _after_ the clone() call will only be applied to the instance
  77. * it has been called on.
  78. *
  79. * @example
  80. * var command = ffmpeg('/path/to/source.avi')
  81. * .audioCodec('libfaac')
  82. * .videoCodec('libx264')
  83. * .format('mp4');
  84. *
  85. * command.clone()
  86. * .size('320x200')
  87. * .save('/path/to/output-small.mp4');
  88. *
  89. * command.clone()
  90. * .size('640x400')
  91. * .save('/path/to/output-medium.mp4');
  92. *
  93. * command.save('/path/to/output-original-size.mp4');
  94. *
  95. * @method FfmpegCommand#clone
  96. * @return FfmpegCommand
  97. */
  98. FfmpegCommand.prototype.clone = function() {
  99. var clone = new FfmpegCommand();
  100. var self = this;
  101. // Clone options and logger
  102. clone.options = this.options;
  103. clone.logger = this.logger;
  104. // Clone inputs
  105. clone._inputs = this._inputs.map(function(input) {
  106. return {
  107. source: input.source,
  108. options: input.options.clone()
  109. };
  110. });
  111. // Create first output
  112. if ('target' in this._outputs[0]) {
  113. // We have outputs set, don't clone them and create first output
  114. clone._outputs = [];
  115. clone.output();
  116. } else {
  117. // No outputs set, clone first output options
  118. clone._outputs = [
  119. clone._currentOutput = {
  120. flags: {}
  121. }
  122. ];
  123. ['audio', 'audioFilters', 'video', 'videoFilters', 'sizeFilters', 'options'].forEach(function(key) {
  124. clone._currentOutput[key] = self._currentOutput[key].clone();
  125. });
  126. if (this._currentOutput.sizeData) {
  127. clone._currentOutput.sizeData = {};
  128. utils.copy(this._currentOutput.sizeData, clone._currentOutput.sizeData);
  129. }
  130. utils.copy(this._currentOutput.flags, clone._currentOutput.flags);
  131. }
  132. // Clone argument lists
  133. ['_global', '_complexFilters'].forEach(function(prop) {
  134. clone[prop] = self[prop].clone();
  135. });
  136. return clone;
  137. };
  138. /* Add methods from options submodules */
  139. require('./options/inputs')(FfmpegCommand.prototype);
  140. require('./options/audio')(FfmpegCommand.prototype);
  141. require('./options/video')(FfmpegCommand.prototype);
  142. require('./options/videosize')(FfmpegCommand.prototype);
  143. require('./options/output')(FfmpegCommand.prototype);
  144. require('./options/custom')(FfmpegCommand.prototype);
  145. require('./options/misc')(FfmpegCommand.prototype);
  146. /* Add processor methods */
  147. require('./processor')(FfmpegCommand.prototype);
  148. /* Add capabilities methods */
  149. require('./capabilities')(FfmpegCommand.prototype);
  150. FfmpegCommand.setFfmpegPath = function(path) {
  151. (new FfmpegCommand()).setFfmpegPath(path);
  152. };
  153. FfmpegCommand.setFfprobePath = function(path) {
  154. (new FfmpegCommand()).setFfprobePath(path);
  155. };
  156. FfmpegCommand.setFlvtoolPath = function(path) {
  157. (new FfmpegCommand()).setFlvtoolPath(path);
  158. };
  159. FfmpegCommand.availableFilters =
  160. FfmpegCommand.getAvailableFilters = function(callback) {
  161. (new FfmpegCommand()).availableFilters(callback);
  162. };
  163. FfmpegCommand.availableCodecs =
  164. FfmpegCommand.getAvailableCodecs = function(callback) {
  165. (new FfmpegCommand()).availableCodecs(callback);
  166. };
  167. FfmpegCommand.availableFormats =
  168. FfmpegCommand.getAvailableFormats = function(callback) {
  169. (new FfmpegCommand()).availableFormats(callback);
  170. };
  171. FfmpegCommand.availableEncoders =
  172. FfmpegCommand.getAvailableEncoders = function(callback) {
  173. (new FfmpegCommand()).availableEncoders(callback);
  174. };
  175. /* Add ffprobe methods */
  176. require('./ffprobe')(FfmpegCommand.prototype);
  177. FfmpegCommand.ffprobe = function(file) {
  178. var instance = new FfmpegCommand(file);
  179. instance.ffprobe.apply(instance, Array.prototype.slice.call(arguments, 1));
  180. };
  181. /* Add processing recipes */
  182. require('./recipes')(FfmpegCommand.prototype);