Skip to content

Commit

Permalink
Add a clone() method
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicolas Joyard committed May 22, 2014
1 parent 9333e33 commit 4f4e29d
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 15 deletions.
28 changes: 28 additions & 0 deletions README.md
Expand Up @@ -1061,6 +1061,34 @@ The returned object for filters looks like:
* `output` tells the output type this filter generates, one of "audio", "video" or "none". When "none", the filter has no output (sink only)
* `multipleInputs` tells whether the filter can generate multiple outputs

### Cloning an FfmpegCommand

You can create clones of an FfmpegCommand instance by calling the `clone()` method. The clone will be an exact copy of the original at the time it has been called (same inputs, same options, same event handlers, etc.). This is mainly useful when you want to apply different processing options on the same input.

Setting options, adding inputs or event handlers on a clone will not affect the original command.

```js
// Create a command to convert source.avi to MP4
var command = ffmpeg('/path/to/source.avi')
.audioCodec('libfaac')
.videoCodec('libx264')
.format('mp4');

// Create a clone to save a small resized version
command.clone()
.size('320x200')
.save('/path/to/output-small.mp4');

// Create a clone to save a medium resized version
command.clone()
.size('640x400')
.save('/path/to/output-medium.mp4');

// Save a converted version with the original size
command.save('/path/to/output-original-size.mp4');
```


## Migrating from fluent-ffmpeg 1.x

fluent-ffmpeg 2.0 is mostly compatible with previous versions, as all previous method names have been kept as aliases. The paragraphs below explain how to get around the few remaining incompatibilities.
Expand Down
12 changes: 3 additions & 9 deletions lib/capabilities.js
Expand Up @@ -20,12 +20,6 @@ var filterRegexp = /^(?: [T\.][S\.][C\.] )?([^ ]+) +(AA?|VV?|\|)->(AA?|VV?|\|) +

var cache = {};

function copy(src, dest) {
Object.keys(src).forEach(function(k) {
dest[k] = src[k];
});
}

module.exports = function(proto) {
/**
* Forget executable paths
Expand Down Expand Up @@ -353,13 +347,13 @@ module.exports = function(proto) {

if (encoders.length || decoders.length) {
var coderData = {};
copy(codecData, coderData);
utils.copy(codecData, coderData);
delete coderData.canEncode;
delete coderData.canDecode;

encoders.forEach(function(name) {
data[name] = {};
copy(coderData, data[name]);
utils.copy(coderData, data[name]);
data[name].canEncode = true;
});

Expand All @@ -368,7 +362,7 @@ module.exports = function(proto) {
data[name].canDecode = true;
} else {
data[name] = {};
copy(coderData, data[name]);
utils.copy(coderData, data[name]);
data[name].canDecode = true;
}
});
Expand Down
71 changes: 65 additions & 6 deletions lib/fluent-ffmpeg.js
Expand Up @@ -6,6 +6,7 @@ var util = require('util');
var EventEmitter = require('events').EventEmitter;

var utils = require('./utils');
var ARGLISTS = ['_audio', '_audioFilters', '_video', '_videoFilters', '_sizeFilters', '_output'];


/**
Expand Down Expand Up @@ -50,12 +51,10 @@ function FfmpegCommand(input, options) {
}

// Create argument lists
this._audio = utils.args();
this._audioFilters = utils.args();
this._video = utils.args();
this._videoFilters = utils.args();
this._sizeFilters = utils.args();
this._output = utils.args();
var self = this;
ARGLISTS.forEach(function(prop) {
self[prop] = utils.args();
});

// Set default option values
options.presets = options.presets || options.preset || path.join(__dirname, 'presets');
Expand All @@ -76,6 +75,66 @@ util.inherits(FfmpegCommand, EventEmitter);
module.exports = FfmpegCommand;


/**
* Clone an ffmpeg command
*
* This method is useful when you want to process the same input multiple times.
* It returns a new FfmpegCommand instance with the exact same options.
*
* All options set _after_ the clone() call will only be applied to the instance
* it has been called on.
*
* @example
* var command = ffmpeg('/path/to/source.avi')
* .audioCodec('libfaac')
* .videoCodec('libx264')
* .format('mp4');
*
* command.clone()
* .size('320x200')
* .save('/path/to/output-small.mp4');
*
* command.clone()
* .size('640x400')
* .save('/path/to/output-medium.mp4');
*
* command.save('/path/to/output-original-size.mp4');
*
* @method FfmpegCommand#clone
* @return FfmpegCommand
*/
FfmpegCommand.prototype.clone = function() {
var clone = new FfmpegCommand();
var self = this;

// Clone options and logger
clone.options = this.options;
clone.logger = this.logger;

// Clone inputs
clone._inputs = this._inputs.map(function(input) {
return {
source: input.source,
before: input.before.clone(),
after: input.after.clone()
};
});

// Clone argument lists
ARGLISTS.forEach(function(prop) {
clone[prop] = self[prop].clone();
});

// Clone size data
if (this._sizeData) {
clone._sizeData = {};
utils.copy(this._sizeData, clone._sizeData);
}

return clone;
};


/* Add methods from options submodules */

require('./options/inputs')(FfmpegCommand.prototype);
Expand Down
21 changes: 21 additions & 0 deletions lib/utils.js
Expand Up @@ -40,6 +40,21 @@ function parseProgressLine(line) {
var utils = module.exports = {
isWindows: isWindows,


/**
* Copy an object keys into another one
*
* @param {Object} source source object
* @param {Object} dest destination object
* @private
*/
copy: function(source, dest) {
Object.keys(source).forEach(function(key) {
dest[key] = source[key];
});
},


/**
* Create an argument list
*
Expand Down Expand Up @@ -84,6 +99,12 @@ var utils = module.exports = {
}
};

argfunc.clone = function() {
var cloned = utils.args();
cloned(list);
return cloned;
};

return argfunc;
},

Expand Down
41 changes: 41 additions & 0 deletions test/args.test.js
Expand Up @@ -866,4 +866,45 @@ describe('Command', function() {
filters[1].should.equal('pad=\'w=100:h=200:x=if(gt(a,0.5),0,(100-iw)/2):y=if(lt(a,0.5),0,(200-ih)/2):color=black\'');
});
});

describe('clone', function() {
it('should return a new FfmpegCommand instance', function() {
var command = new Ffmpeg({ source: this.testfile, logger: testhelper.logger });
var clone = command.clone();

clone.should.instanceof(Ffmpeg);
clone.should.not.equal(command);
});

it('should duplicate FfmpegCommand options at the time of the call', function(done) {
var command = new Ffmpeg({ source: this.testfile, logger: testhelper.logger })
.preset('flashvideo');

var clone = command.clone();

command._test_getArgs(function(originalArgs, err) {
clone._test_getArgs(function(cloneArgs, err) {
cloneArgs.length.should.equal(originalArgs.length);
originalArgs.forEach(function(arg, index) {
cloneArgs[index].should.equal(arg);
});
done();
});
});
});

it('should have separate argument lists', function(done) {
var command = new Ffmpeg({ source: this.testfile, logger: testhelper.logger })
.preset('flashvideo');

var clone = command.clone().audioFrequency(22050);

command._test_getArgs(function(originalArgs, err) {
clone._test_getArgs(function(cloneArgs, err) {
cloneArgs.length.should.equal(originalArgs.length + 2);
done();
});
});
});
});
});

0 comments on commit 4f4e29d

Please sign in to comment.