Coverage

77%
788
614
174

calculate.js

87%
86
75
11
LineHitsSource
1
21exports = module.exports = function Calculate(command) {
31 this._aspectIsEqual = function(ar1, ar2) {
45 var p1 = this.toAspectRatio(ar1);
55 var p2 = this.toAspectRatio(ar2);
65 if (p1 === undefined || p2 === undefined) {
70 return false;
8 } else {
95 return (p1.x === p2.x && p1.y === p2.y);
10 }
11 };
12
131 this._calculatePadding = function(data) {
145 if (data.video.aspect) {
155 var newaspect, padAmount;
16 // check if the aspect ratio has changed
175 if (this.options.video.aspect && !this.options.video.size) {
182 newaspect = this.options.video.aspect;
193 } else if (!this.options.video.aspect) {
20 // check aspect ratio change by calculating new aspect ratio from size (using greatest common divider, GCD)
211 var ratio = this.gcd(this.options.video.width, this.options.video.height);
221 newaspect = this.options.video.width / ratio + ':' + this.options.video.height / ratio;
23 } else {
24 // we have both aspect ratio and size set, all calculations are fine
252 newaspect = this.options.video.aspect;
26 }
27
28 // if there are still no sizes for our output video, assume input size
295 if (!this.options.video.width && !this.options.video.height) {
302 this.options.video.width = data.video.resolution.w;
312 this.options.video.height = data.video.resolution.h;
32 }
33
345 if (!this._aspectIsEqual(data.video.aspectString, newaspect)) {
355 var ardata = this.toAspectRatio(newaspect);
36
375 if (newaspect === '16:9') {
38 // assume conversion from 4:3 to 16:9, pad output video stream left- / right-sided
391 var newWidth = parseInt(this.options.video.width / (4 / 3), 10);
401 newWidth += (newWidth % 2);
411 var wdiff = this.options.video.width - newWidth;
421 padAmount = parseInt(wdiff / 2, 10);
431 padAmount += (padAmount % 2);
44
45 // set pad filter options
461 this.options.video.pad = {
47 x: padAmount,
48 y: 0,
49 w: this.options.video.width,
50 h: this.options.video.height
51 };
521 this.options.video.size = newWidth + 'x' + this.options.video.height;
534 } else if (newaspect === '4:3') {
54 // assume conversion from 16:9 to 4:3, add padding to top and bottom
554 var newHeight = parseInt(this.options.video.height / (4 / 3), 10);
564 newHeight -= (newHeight % 2);
574 var hdiff = this.options.video.height - newHeight;
584 padAmount = parseInt(hdiff / 2, 10);
594 padAmount += (padAmount % 2);
60
61 // set pad filter options
624 this.options.video.pad = {
63 x: 0,
64 y: padAmount,
65 w: this.options.video.width,
66 h: this.options.video.height
67 };
684 this.options.video.size = this.options.video.pad.w + 'x' + newHeight;
69 }
70 }
71 } else {
72 // aspect ratio could not be read from source file
730 return;
74 }
75 };
76
771 this._calculateDimensions = function(data) {
78 // load metadata and prepare size calculations
7918 var fixedWidth = /([0-9]+)x\?/.exec(this.options.video.size);
8018 var fixedHeight = /\?x([0-9]+)/.exec(this.options.video.size);
8118 var percentRatio = /\b([0-9]{1,2})%/.exec(this.options.video.size);
82
8318 var resolution = this.options.keepPixelAspect ? data.video.resolution : data.video.resolutionSquare;
8418 var w, h;
85
8618 if (!resolution) {
870 return new Error('could not determine video resolution, check your ffmpeg setup');
88 }
89
9018 var ratio, ardata;
9118 if (fixedWidth && fixedWidth.length > 0) {
92 // calculate height of output
9315 if (!resolution.w) {
940 return new Error('could not determine width of source video, aborting execution');
95 }
96
9715 ratio = resolution.w / parseInt(fixedWidth[1], 10);
98 // if we have an aspect ratio target set, calculate new size using AR
9915 if (this.options.video.aspect !== undefined) {
1001 ardata = this.toAspectRatio(this.options.video.aspect);
1011 if (ardata) {
1021 w = parseInt(fixedWidth[1], 10);
1031 h = Math.round((w / ardata.x) * ardata.y);
104 } else {
105 // aspect ratio could not be parsed, return error
1060 return new Error('could not parse aspect ratio set using withAspect(), aborting execution');
107 }
108 } else {
10914 w = parseInt(fixedWidth[1], 10);
11014 h = Math.round(resolution.h / ratio);
111 }
1123 } else if (fixedHeight && fixedHeight.length > 0) {
113 // calculate width of output
1142 if (!resolution.h) {
1150 return new Error('could not determine height of source video, aborting execution');
116 }
117
1182 ratio = resolution.h / parseInt(fixedHeight[1], 10);
119
120 // if we have an aspect ratio target set, calculate new size using AR
1212 if (this.options.video.aspect !== undefined) {
1221 ardata = this.toAspectRatio(this.options.video.aspect);
1231 if (ardata) {
1241 h = parseInt(fixedHeight[1], 10);
1251 w = Math.round((h / ardata.y) * ardata.x);
126 } else {
127 // aspect ratio could not be parsed, return error
1280 return new Error('could not parse aspect ratio set using withAspect(), aborting execution');
129 }
130 } else {
1311 w = Math.round(resolution.w / ratio);
1321 h = parseInt(fixedHeight[1], 10);
133 }
1341 } else if (percentRatio && percentRatio.length > 0) {
135 // calculate both height and width of output
1361 if (!resolution.w || !resolution.h) {
1370 return new Error('could not determine resolution of source video, aborting execution');
138 }
139
1401 ratio = parseInt(percentRatio[1], 10) / 100;
1411 w = Math.round(resolution.w * ratio);
1421 h = Math.round(resolution.h * ratio);
143 } else {
1440 return new Error('could not determine type of size string, aborting execution');
145 }
146
147 // for video resizing, width and height have to be a multiple of 2
14818 if (w % 2 === 1) {
1490 w -= 1;
150 }
15118 if (h % 2 === 1) {
1520 h -= 1;
153 }
154
15518 this.options.video.size = w + 'x' + h;
156
15718 this.options.video.width = w;
15818 this.options.video.height = h;
159
160 };
1611 exports.calculateDimensions = this._calculateDimensions;
162};

debug.js

80%
26
21
5
LineHitsSource
11exports = module.exports = function Debug(command) {
21 this.getCommand = function(outputmethod, callback) {
31 var self = this;
41 this._prepare(function(err, meta) {
51 if (err) {
60 callback(null, err);
7 } else {
81 var args = self.buildFfmpegArgs(true, meta);
9 // kinda hacky, have to make sure the returned object is no array
101 if (args.length === undefined) {
110 callback(null, args);
12 } else {
131 var cmd = '';
141 cmd += 'ffmpeg';
151 args.forEach(function(el) {
1618 cmd += ' ' + el;
17 });
181 callback(cmd, null);
19 }
20 }
21 });
221 return this;
23 };
24
251 this.getArgs = function(callback) {
2635 if (callback) {
2735 var self = this;
2835 this._prepare(function(err, meta) {
2935 if (err) {
300 callback(null, err);
31 } else {
3235 var args = self.buildFfmpegArgs(true, meta);
33 // kinda hacky, have to make sure the returned object is no array
3435 if (args.length === undefined) {
350 callback(null, args);
36 } else {
3735 callback(args, null);
38 }
39 }
40 });
41 } else {
420 return this.buildFfmpegArgs(true, null);
43 }
44 };
45};

extensions.js

89%
49
44
5
LineHitsSource
1
21exports = module.exports = function Extensions() {
3
42 this.ffmpegPath = process.env.FFMPEG_PATH || 'ffmpeg';
5
62 this.setFfmpegPath = function(path) {
70 this.ffmpegPath = path;
8 };
9
102 this.determineFfmpegPath = function() {
110 if (process.env.FFMPEG_PATH) {
120 return process.env.FFMPEG_PATH;
13 }
140 return 'ffmpeg';
15 };
16
172 this.gcd = function(a, b) {
181 if (!a && !b) {
190 return 0;
20 }
211 while (a !== 0) {
223 var z = b % a;
233 b = a;
243 a = z;
25 }
261 return b;
27 };
28
292 this.toAspectRatio = function(ar) {
3019 var p = ar.split(':');
3119 if (p.length !== 2) {
321 return undefined;
33 } else {
3418 return {
35 x: parseInt(p[0], 10),
36 y: parseInt(p[1], 10)
37 };
38 }
39 };
40
412 this.ffmpegTimemarkToSeconds = function(timemark) {
4247 var parts = timemark.split(':');
4347 var secs = 0;
44
45 // add hours
4647 secs += parseInt(parts[0], 10) * 3600;
47 // add minutes
4847 secs += parseInt(parts[1], 10) * 60;
49
50 // split sec/msec part
5147 var secParts = parts[2].split('.');
52
53 // add seconds
5447 secs += parseInt(secParts[0], 10);
55
5647 return secs;
57 };
58
592 this.parseVersionString = function(versionstr) {
6019 if (typeof versionstr != 'string' || versionstr.indexOf('.') == -1) {
611 return false;
62 }
6318 var x = versionstr.split('.');
64 // parse from string or default to 0 if can't parse
6518 var maj = parseInt(x[0], 10) || 0;
6618 var min = parseInt(x[1], 10) || 0;
6718 var pat = parseInt(x[2], 10) || 0;
6818 return {
69 major: maj,
70 minor: min,
71 patch: pat
72 };
73 };
74
752 this.atLeastVersion = function(actualVersion, minVersion) {
769 var minimum = this.parseVersionString(minVersion);
779 var running = this.parseVersionString(actualVersion);
78
79 // if we can't even parse the version string (affects git builds for windows),
80 // we simply return true and assume a current build
819 if (!running)
821 return true;
83
848 if (running.major !== minimum.major) {
855 return (running.major > minimum.major);
86 } else {
873 if (running.minor !== minimum.minor) {
881 return (running.minor > minimum.minor);
89 } else {
902 if (running.patch !== minimum.patch) {
911 return (running.patch > minimum.patch);
92 } else {
931 return true;
94 }
95 }
96 }
97 };
98};

fluent-ffmpeg.js

92%
177
164
13
LineHitsSource
11var path = require('path'),
2 async = require('../support/async.min.js'),
3 exec = require('child_process').exec,
4 spawn = require('child_process').spawn;
5
6/* options object consists of the following keys:
7 * - source: either a ReadableStream or the path to a file (required)
8 * - timeout: timeout in seconds for all ffmpeg sub-processes (optional, defaults to 30)
9 * - priority: default-priority for all ffmpeg sub-processes (optional, defaults to 0)
10 * - logger: add a winston logging instance (optional, default is clumsy console logging)
11 * - nolog: completely disables any logging
12 */
131function FfmpegCommand(args) {
1449 var source = args.source,
15 timeout = args.timeout != null ? args.timeout : 30,
16 priority = args.priority || 0,
17 logger = args.logger || null,
18 nologging = args.nolog || false,
19 inputlive = args.inputlive || false;
20
2149 if (!logger && !nologging) {
22 // create new winston instance
232 logger = require('winston');
2447 } else if (!logger && nologging) {
25 // create fake object to route log calls
2647 logger = {
27 debug: function() {},
28 info: function() {},
29 warn: function() {},
30 error: function() {}
31 };
32 }
33
34 // make sure execution is not killed on error
3549 logger.exitOnError = false;
36
37 // check if argument is a stream
3849 var srcstream, srcfile;
3949 if (typeof source === 'object') {
402 if (source.readable) {
41 // streaming mode
422 source.pause();
432 srcstream = source;
442 srcfile = source.path;
45 } else {
460 logger.error('Source is not a ReadableStream instance');
470 throw new Error('Source is not a ReadableStream instance');
48 }
49 } else {
50 // file mode
5147 srcfile = source;
52 }
53
5449 this.options = {
55 _isStreamable: true,
56 _updateFlvMetadata: false,
57 _useConstantVideoBitrate: false,
58 _nice: { level: priority },
59 keepPixelAspect: false,
60 inputfile: srcfile,
61 inputstream: srcstream,
62 inputlive: inputlive,
63 timeout: timeout,
64 mergeList:[],
65 video: {},
66 audio: {},
67 additional: [],
68 otherInputs: [],
69 informInputAudioCodec: null,
70 informInputVideoCodec: null,
71 logger: logger
72 };
73
74 // public chaining methods
7549 FfmpegCommand.prototype.usingPreset = function(preset) {
76 // require preset (since require() works like a singleton, multiple calls generate no overhead)
7712 try {
7812 var module = require('./presets/' + preset);
7911 if (typeof module.load === 'function') {
8011 module.load(this);
81 }
8211 return this;
83 } catch (err) {
841 throw new Error('preset ' + preset + ' could not be loaded');
85 }
860 return this;
87 };
8849 FfmpegCommand.prototype.withNoVideo = function() {
892 this.options.video.skip = true;
902 return this;
91 };
9249 FfmpegCommand.prototype.withNoAudio = function() {
932 this.options.audio.skip = true;
942 return this;
95 };
9649 FfmpegCommand.prototype.withVideoBitrate = function(vbitrate, type) {
9713 if (typeof vbitrate === 'string' && vbitrate.indexOf('k') > 0) {
9813 vbitrate = vbitrate.replace('k', '');
99 }
10013 if (type && type === exports.CONSTANT_BITRATE) {
1011 this.options._useConstantVideoBitrate = true;
102 }
10313 this.options.video.bitrate = parseInt(vbitrate, 10);
10413 return this;
105 };
10649 FfmpegCommand.prototype.withSize = function(sizeString) {
10722 this.options.video.size = sizeString;
10822 return this;
109 };
11049 FfmpegCommand.prototype.applyAutopadding = function(autopad, color) {
1115 this.options._applyAutopad = autopad;
1125 if (!color) {
1134 this.options.video.padcolor = 'black';
114 } else {
1151 this.options.video.padcolor = color;
116 }
1175 return this;
118 };
11949 FfmpegCommand.prototype.withFps = function(fps) {
1209 this.options.video.fps = fps;
1219 return this;
122 };
12349 FfmpegCommand.prototype.withFpsInput = function(fps) {
1240 this.options.video.fpsInput = fps;
1250 return this;
126 };
12749 FfmpegCommand.prototype.withFpsOutput = function(fps) {
1280 this.options.video.fpsOutput = fps;
1290 return this;
130 };
13149 FfmpegCommand.prototype.withAspect = function(aspectRatio) {
1325 this.options.video.aspect = aspectRatio;
1335 return this;
134 };
13549 FfmpegCommand.prototype.keepPixelAspect = function(bool) {
1360 this.options.keepPixelAspect = bool ? true : false;
1370 return this;
138 };
13949 FfmpegCommand.prototype.withVideoCodec = function(codec) {
14012 this.options.video.codec = codec;
14112 return this;
142 };
14349 FfmpegCommand.prototype.loop = function(duration) {
1443 this.options.video.loop = true;
1453 if (duration) {
1462 this.options.duration = duration;
147 }
1483 return this;
149 };
15049 FfmpegCommand.prototype.takeFrames = function(frameCount) {
1511 this.options.video.framecount = frameCount;
1521 return this;
153 };
15449 FfmpegCommand.prototype.withAudioBitrate = function(abitrate) {
15513 if (typeof abitrate === 'string' && abitrate.indexOf('k') > 0) {
15612 abitrate = abitrate.replace('k', '');
157 }
15813 this.options.audio.bitrate = parseInt(abitrate, 10);
15913 return this;
160 };
16149 FfmpegCommand.prototype.withAudioCodec = function(audiocodec){
16212 this.options.audio.codec = audiocodec;
16312 return this;
164 };
16549 FfmpegCommand.prototype.withAudioChannels = function(audiochannels) {
16613 this.options.audio.channels = audiochannels;
16713 return this;
168 };
16949 FfmpegCommand.prototype.withAudioFrequency = function(frequency) {
1709 this.options.audio.frequency = frequency;
1719 return this;
172 };
17349 FfmpegCommand.prototype.withAudioQuality = function(quality) {
1741 this.options.audio.quality = parseInt(quality, 10);
1751 return this;
176 };
17749 FfmpegCommand.prototype.setStartTime = function(timestamp) {
1781 this.options.starttime = timestamp;
1791 return this;
180 };
18149 FfmpegCommand.prototype.setDuration = function(duration) {
1821 this.options.duration = duration;
1831 return this;
184 };
18549 FfmpegCommand.prototype.addInput = function(inputFile) {
1861 this.options.otherInputs.push(inputFile);
1871 return this;
188 };
18949 FfmpegCommand.prototype.addOptions = function(optionArray) {
1904 if (typeof optionArray.length !== undefined) {
1914 var self = this;
1924 optionArray.forEach(function(el) {
19341 if (el.indexOf(' ') > 0) {
19419 var values = el.split(' ');
19519 self.options.additional.push(values[0], values[1]);
196 } else {
19722 self.options.additional.push(el);
198 }
199 });
200 }
2014 return this;
202 };
20349 FfmpegCommand.prototype.addOption = function(option, value) {
2041 this.options.additional.push(option, value);
2051 return this;
206 };
20749 FfmpegCommand.prototype.mergeAdd = function(path){
2080 this.options.mergeList.push(path)
2090 return this;
210 };
21149 FfmpegCommand.prototype.toFormat = function(format) {
21212 this.options.format = format;
213
214 // some muxers require the output stream to be seekable, disable streaming for those formats
21512 if (this.options.format === 'mp4') {
2161 this.options._isStreamable = false;
217 }
21812 return this;
219 };
22049 FfmpegCommand.prototype.updateFlvMetadata = function() {
2218 this.options._updateFlvMetadata = true;
2228 return this;
223 };
22449 FfmpegCommand.prototype.renice = function(level) {
2251 if (!level) {
226 // use 0 as default nice level (os default)
2270 level = 0;
228 }
229
230 // make sure niceness is within allowed boundaries
2311 if (level > 20 || level < -20) {
2321 this.options.logger.warn('niceness ' + level + ' is not valid, consider a value between -20 and +20 (whereas -20 is the highest priority)');
2331 level = 0;
234 }
2351 this.options._nice.level = level;
2361 return this;
237 };
23849 FfmpegCommand.prototype.onCodecData = function(callback) {
2391 this.options.onCodecData = callback;
2401 return this;
241 };
24249 FfmpegCommand.prototype.onProgress = function(callback) {
2431 this.options.onProgress = callback;
2441 return this;
245 };
246
247 // private methods
24849 FfmpegCommand.prototype._prepare = function(callback) {
24947 var calcDimensions = false, calcPadding = false;
250
251 // check for allowed sizestring formats and handle them accordingly
25247 var fixedWidth = /([0-9]+)x\?/.exec(this.options.video.size);
25347 var fixedHeight = /\?x([0-9]+)/.exec(this.options.video.size);
25447 var percentRatio = /\b([0-9]{1,2})%/.exec(this.options.video.size);
255
25647 if (!fixedWidth && !fixedHeight && !percentRatio) {
257 // check for invalid size string
25829 var defaultSizestring = /([0-9]+)x([0-9]+)/.exec(this.options.video.size);
25929 if (this.options.video.size && !defaultSizestring) {
2601 callback(new Error('could not parse size string, aborting execution'));
2611 return;
262 } else {
263 // get width and height as integers (used for padding calculation)
26428 if (defaultSizestring) {
2653 this.options.video.width = parseInt(defaultSizestring[1], 10);
2663 this.options.video.height = parseInt(defaultSizestring[2], 10);
267 }
26828 calcDimensions = false;
269 }
270 } else {
27118 calcDimensions = true;
272 }
273
274 // check if we have to check aspect ratio for changes and auto-pad the output
27546 if (this.options._applyAutopad) {
2765 calcPadding = true;
277 }
278
27946 var self = this;
28046 this.getMetadata(this.options.inputfile, function(meta, err) {
28146 if (calcDimensions || calcPadding) {
28221 var dimErr, padErr;
283 // calculate dimensions
28421 if (calcDimensions) {
28518 dimErr = self._calculateDimensions(meta);
286 }
287
288 // calculate padding
28921 if (calcPadding) {
2905 padErr = self._calculatePadding(meta);
291 }
292
29321 if (dimErr || padErr) {
2940 callback(new Error('error while preparing: dimension -> ' + dimErr + ' padding -> ' + padErr));
295 } else {
29621 callback(undefined, meta);
297 }
298 } else {
29925 callback(undefined, meta);
300 }
301 });
302 };
303}
304
305// add module methods
3061require('./extensions').apply(FfmpegCommand.prototype);
3071require('./metadata').apply(FfmpegCommand.prototype);
3081require('./processor').apply(FfmpegCommand.prototype);
3091require('./calculate').apply(FfmpegCommand.prototype);
3101require('./debug').apply(FfmpegCommand.prototype);
311
312// module exports
3131exports = module.exports = function(args) {
31449 return new FfmpegCommand(args);
315};
316
317// export meta data discovery
3181exports.Metadata = require('./metadata');
3191exports.Calculate = require('./calculate');
320
3211exports.CONSTANT_BITRATE = 1;
3221exports.VARIABLE_BITRATE = 2;

metadata.js

68%
60
41
19
LineHitsSource
11var exec = require('child_process').exec
2 , os = require('os').platform();
3
41exports = module.exports = function Metadata(inputfile) {
51 this.escapedPath = function(path, enclose) {
660 if(/http/.exec(path)) {
70 path = path.replace(' ', '%20');
8 } else {
960 if (os.match(/win(32|64)/)) {
10 // on windows, we have to fix up the filename
110 var parts = path.split(/\\/gi);
120 var fName = parts[parts.length - 1];
130 parts[parts.length - 1] = fName.replace(/[\s\\:"'*?<>|\/]+/mig, '-');
140 path = parts.join('\\');
150 if (enclose && path[0] != '"' && path[path.length-1] != '"')
160 path = '"' + path + '"'
17 } else {
1860 if (enclose && path[0] != '"' && path[path.length-1] != '"')
198 path = '"' + path + '"';
20 }
21 }
2260 return path;
23 };
24
251 this.inputfile = inputfile;
26
271 this.setFfmpegPath = function(path) {
280 this.ffmpegPath = path;
29 };
30
31 // for internal use
321 this.getMetadata = function(inputfile, callback) {
3346 this.inputfile = inputfile;
3446 this._loadDataInternal(callback);
35 };
36
37 // for external use
381 this.get = function(callback) {
39 // import extensions for external call
400 require('./extensions').apply(Metadata.prototype);
410 this._loadDataInternal(callback);
42 };
43
441 this._loadDataInternal = function(callback) {
4546 var inputfile = this.escapedPath(this.inputfile);
4646 var self = this;
4746 exec(this.ffmpegPath + ' -i ' + inputfile, function(err, stdout, stderr) {
48 // parse data from stderr
49
5046 var none = []
51 , aspect = /DAR ([0-9\:]+)/.exec(stderr) || none
52 , pixel = /[SP]AR ([0-9\:]+)/.exec(stderr) || none
53 , video_bitrate = /bitrate: ([0-9]+) kb\/s/.exec(stderr) || none
54 , fps = /([0-9\.]+) (fps|tb\(r\))/.exec(stderr) || none
55 , container = /Input #0, ([a-zA-Z0-9]+),/.exec(stderr) || none
56 , title = /(INAM|title)\s+:\s(.+)/i.exec(stderr) || none
57 , artist = /artist\s+:\s(.+)/i.exec(stderr) || none
58 , album = /album\s+:\s(.+)/i.exec(stderr) || none
59 , track = /track\s+:\s(.+)/i.exec(stderr) || none
60 , date = /date\s+:\s(.+)/i.exec(stderr) || none
61 , video_stream = /Stream #([0-9\.]+)([a-z0-9\(\)\[\]]*)[:] Video/.exec(stderr) || none
62 , video_codec = /Video: ([\w]+)/.exec(stderr) || none
63 , duration = /Duration: (([0-9]+):([0-9]{2}):([0-9]{2}).([0-9]+))/.exec(stderr) || none
64 , resolution = /(([0-9]{2,5})x([0-9]{2,5}))/.exec(stderr) || none
65 , audio_bitrate = /Audio:(.)*, ([0-9]+) kb\/s/.exec(stderr) || none
66 , sample_rate = /([0-9]+) Hz/i.exec(stderr) || none
67 , audio_codec = /Audio: ([\w]+)/.exec(stderr) || none
68 , channels = /Audio: [\w]+, [0-9]+ Hz, ([a-z0-9:]+)[a-z0-9\/,]*/.exec(stderr) || none
69 , audio_stream = /Stream #([0-9\.]+)([a-z0-9\(\)\[\]]*)[:] Audio/.exec(stderr) || none
70 , is_synched = (/start: 0.000000/.exec(stderr) !== null)
71 , rotate = /rotate[\s]+:[\s]([\d]{2,3})/.exec(stderr) || none
72 , getVersion = /ffmpeg version (?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)/i.exec(stderr)
73 , major_brand = /major_brand\s+:\s([^\s]+)/.exec(stderr) || none
74 , ffmpegVersion = 0;
75
7646 if (getVersion) {
7746 ffmpegVersion = [
78 getVersion[1]>=0 ? getVersion[1] : null,
79 getVersion[2]>=0 ? getVersion[2] : null,
80 getVersion[3]>=0 ? getVersion[3] : null
81 ].filter(function(val) {
82138 return val !== null;
83 }).join('.');
84 }
85
86 // build return object
8746 var _ref
88 , ret = {
89 ffmpegversion: ffmpegVersion
90 , title: title[2] || ''
91 , artist: artist[1] || ''
92 , album: album[1] || ''
93 , track: track[1] || ''
94 , date: date[1] || ''
95 , durationraw: duration[1] || ''
96 , durationsec: duration[1] ? self.ffmpegTimemarkToSeconds(duration[1]) : 0
97 , synched: is_synched
98 , major_brand: major_brand[1]
99 , video: {
100 container: container[1] || ''
101 , bitrate: (video_bitrate.length > 1) ? parseInt(video_bitrate[1], 10) : 0
102 , codec: video_codec[1] || ''
103 , resolution: {
104 w: resolution.length > 2 ? parseInt(resolution[2], 10) : 0
105 , h: resolution.length > 3 ? parseInt(resolution[3], 10) : 0
106 }
107 , resolutionSquare: {}
108 , rotate: rotate.length > 1 ? parseInt(rotate[1], 10) : 0
109 , fps: fps.length > 1 ? parseFloat(fps[1]) : 0.0
110 , stream: video_stream.length > 1 ? parseFloat(video_stream[1]) : 0.0
111 }
112 , audio: {
113 codec: audio_codec[1] || ''
114 , bitrate: parseInt((_ref = audio_bitrate[audio_bitrate.length - 1]) != null ? _ref : 0, 10)
115 , sample_rate: sample_rate.length > 1 ? parseInt(sample_rate[1], 10) : 0
116 , stream: audio_stream.length > 1 ? parseFloat(audio_stream[1]) : 0.0
117 }
118 };
119
12046 if (channels.length > 0) {
1210 ret.audio.channels = {stereo:2, mono:1}[channels[1]] || 0;
122 }
123
124 // save aspect ratio for auto-padding
12546 if (aspect.length > 0) {
12645 ret.video.aspectString = aspect[1];
12745 var n = aspect[1].split(":");
12845 ret.video.aspect = parseFloat((parseInt(n[0], 10) / parseInt(n[1], 10)));
129 } else {
1301 if(ret.video.resolution.w !== 0) {
1310 var f = self.gcd(ret.video.resolution.w, ret.video.resolution.h);
1320 ret.video.aspectString = ret.video.resolution.w/f + ':' + ret.video.resolution.h/f;
1330 ret.video.aspect = parseFloat((ret.video.resolution.w / ret.video.resolution.h));
134 } else {
1351 ret.video.aspect = 0.0;
136 }
137 }
138
139 // save pixel ratio for output size calculation
14046 if (pixel.length > 0) {
14145 ret.video.pixelString = pixel[1];
14245 var n = pixel[1].split(":");
14345 ret.video.pixel = parseFloat((parseInt(n[0], 10) / parseInt(n[1], 10)));
144 } else {
1451 if (ret.video.resolution.w !== 0) {
1460 var f = self.gcd(ret.video.resolution.w, ret.video.resolution.h);
1470 ret.video.pixelString = '1:1';
1480 ret.video.pixel = 1;
149 } else {
1501 ret.video.pixel = 0.0;
151 }
152 }
153
154 // correct video.resolution when pixel aspectratio is not 1
15546 if (ret.video.pixel !== 1 || ret.video.pixel !== 0) {
15646 if( ret.video.pixel > 1 ) {
1570 ret.video.resolutionSquare.w = parseInt(ret.video.resolution.w * ret.video.pixel, 10);
1580 ret.video.resolutionSquare.h = ret.video.resolution.h;
159 } else {
16046 ret.video.resolutionSquare.w = ret.video.resolution.w;
16146 ret.video.resolutionSquare.h = parseInt(ret.video.resolution.h / ret.video.pixel, 10);
162 }
163 }
164
16546 callback(ret);
166 });
167 };
168};

presets/divx.js

100%
3
3
0
LineHitsSource
11exports.load = function(ffmpeg) {
21 ffmpeg
3 .toFormat('avi')
4 .withVideoBitrate('1024k')
5 .withVideoCodec('mpeg4')
6 .withSize('720x?')
7 .withAudioBitrate('128k')
8 .withAudioChannels(2)
9 .withAudioCodec('libmp3lame')
10 .addOptions([ '-vtag DIVX' ]);
111 return ffmpeg;
12};

presets/flashvideo.js

100%
3
3
0
LineHitsSource
11exports.load = function(ffmpeg) {
28 ffmpeg
3 .toFormat('flv')
4 .updateFlvMetadata()
5 .withSize('320x?')
6 .withVideoBitrate('512k')
7 .withVideoCodec('libx264')
8 .withFps(24)
9 .withAudioBitrate('96k')
10 .withAudioCodec('libfaac')
11 .withAudioFrequency(22050)
12 .withAudioChannels(2);
138 return ffmpeg;
14};

presets/podcast.js

100%
3
3
0
LineHitsSource
11exports.load = function(ffmpeg) {
22 ffmpeg
3 .toFormat('m4v')
4 .withVideoBitrate('512k')
5 .withVideoCodec('libx264')
6 .withSize('320x176')
7 .withAudioBitrate('128k')
8 .withAudioCodec('libfaac')
9 .withAudioChannels(1)
10 .addOptions(['-flags', '+loop', '-cmp', '+chroma', '-partitions','+parti4x4+partp8x8+partb8x8', '-flags2',
11 '+mixed_refs', '-me_method umh', '-subq 5', '-bufsize 2M', '-rc_eq \'blurCplx^(1-qComp)\'',
12 '-qcomp 0.6', '-qmin 10', '-qmax 51', '-qdiff 4', '-level 13' ]);
132 return ffmpeg;
14};

processor.js

67%
369
248
121
LineHitsSource
11var fs = require('fs'),
2 path = require('path'),
3 async = require('../support/async.min.js'),
4 os = require('os').platform(),
5 exec = require('child_process').exec,
6 spawn = require('child_process').spawn,
7 Registry = require('./registry'),
8
9exports = module.exports = function Processor(command) {
10 // constant for timeout checks
111 this.E_PROCESSTIMEOUT = -99;
121 this._codecDataAlreadySent = false;
13
141 this.saveToFile = function(targetfile, callback) {
15
166 callback = callback || function() {};
17
186 this.options.outputfile = targetfile;
19
206 var self = this;
216 var options = this.options;
22
23 // parse options to command
246 this._prepare(function(err, meta) {
25
266 if (err) {
270 return callback(null, null, err);
28 }
29
306 var args = self.buildFfmpegArgs(false, meta);
31
326 if (!args instanceof Array) {
330 return callback (null, null, args);
34 }
35
36 // start conversion of file using spawn
376 var ffmpegProc = self._spawnProcess(args);
386 if (options.inputstream) {
39 // pump input stream to stdin
401 options.inputstream.resume();
411 options.inputstream.pipe(ffmpegProc.stdin);
42 }
43
44 //handle timeout if set
456 var processTimer;
466 if (options.timeout) {
476 processTimer = setTimeout(function() {
481 ffmpegProc.removeAllListeners('exit');
491 ffmpegProc.kill('SIGKILL');
501 options.logger.warn('process ran into a timeout (' + self.options.timeout + 's)');
511 callback(self.E_PROCESSTIMEOUT, 'timeout');
52 }, options.timeout * 1000);
53 }
54
556 var stdout = '';
566 var stderr = '';
576 ffmpegProc.on('exit', function(code) {
585 if (processTimer) {
595 clearTimeout(processTimer);
60 }
61 // check if we have to run flvtool2 to update flash video meta data
625 if (self.options._updateFlvMetadata === true) {
63 // make sure we didn't try to determine this capability before
645 if (!Registry.instance.get('capabilityFlvTool2')) {
65 // check if flvtool2 is installed
665 exec('which flvtool2', function(whichErr, whichStdOut, whichStdErr) {
675 if (whichStdOut !== '') {
680 Registry.instance.set('capabilityFlvTool2', true);
69 // update metadata in flash video
700 exec('flvtool2 -U ' + self.options.outputfile, function(flvtoolErr, flvtoolStdout, flvtoolStderr) {
710 callback(stdout, stderr, null);
72 });
73 } else {
74 // flvtool2 is not installed, skip further checks
755 Registry.instance.set('capabilityFlvTool2', false);
765 callback(stdout, stderr, null);
77 }
78 });
790 } else if (!Registry.instance.get('capabilityFlvTool2')) {
80 // flvtool2 capability was checked before, execute update
810 exec('flvtool2 -U ' + self.options.outputfile, function(flvtoolErr, flvtoolStdout, flvtoolStderr) {
820 callback(stdout, stderr, null);
83 });
84 } else {
85 // flvtool2 not installed, skip update
860 callback(stdout, stderr, null);
87 }
88 } else {
890 callback(stdout, stderr, null);
90 }
91 });
926 ffmpegProc.stdout.on('data', function (data) {
930 stdout += data;
94 });
95
966 ffmpegProc.stderr.on('data', function (data) {
97119 stderr += data;
98119 if (options.onCodecData) {
9911 self._checkStdErrForCodec(stderr);
100 }
101119 if (options.onProgress) {
10217 self._getProgressFromStdErr(stderr, meta.durationsec);
103 }
104 });
105 });
106 };
107
1081 this.mergeToFile = function(targetfile,callback){
1090 this.options.outputfile = targetfile;
1100 var self = this;
1110 var options = this.options;
112
1130 var getExtension = function(filename) {
1140 var ext = path.extname(filename||'').split('.');
1150 return ext[ext.length - 1];
116 };
117
118 // creates intermediate copies of each video.
1190 var makeIntermediateFile = function(_mergeSource,_callback){
1200 var fname = _mergeSource+".temp.mpg";
1210 var command = [
122 self.ffmpegPath,
123 [
124 '-i', _mergeSource,
125 '-qscale:v',1,
126 fname
127 ].join(' ')
128 ];
1290 exec(command.join(' '),function(err, stdout, stderr) {
1300 if(err)throw err;
1310 _callback(fname);
132 });
133 };
134
135 // concat all created intermediate copies
1360 var concatIntermediates = function(target,intermediatesList,_callback){
1370 var fname = target+".temp.merged.mpg";
138
139 // unescape paths
1400 for(var i=0; i<intermediatesList.length; i++){
1410 intermediatesList[i] = unescapePath(intermediatesList[i]);
142 }
143
1440 var command = [
145 self.ffmpegPath,
146 [
147 '-loglevel','panic', //Generetes too much muxing warnings and fills default buffer of exec. This is to ignore them.
148 '-i', 'concat:"'+intermediatesList.join("|")+'"',
149 '-c',"copy",
150 fname
151 ].join(' ')
152 ];
1530 exec(command.join(' '), function(err, stdout, stderr) {
1540 if(err)throw err;
1550 _callback(fname);
156 });
157 };
158
1590 var quantizeConcat = function(concatResult,numFiles,_callback){
1600 var command = [
161 self.ffmpegPath,
162 [
163 '-i', concatResult,
164 '-qscale:v',numFiles,
165 targetfile
166 ].join(' ')
167 ];
1680 exec(command.join(' '), function(err, stdout, stderr) {
1690 if(err)throw err;
1700 _callback();
171 });
172 }
173
1740 var deleteIntermediateFiles = function(intermediates){
1750 for(var i=0 ; i<intermediates.length ; i++){
1760 fs.unlinkSync( unescapePath(intermediates[i]));
177 }
178 }
179
1800 var unescapePath = function(path){
1810 var f = path+"";
1820 if(f.indexOf('"')==0)f = f.substring(1);
1830 if(f.lastIndexOf('"')== f.length-1)f = f.substring(0, f.length-1);
1840 return f;
185 }
186
1870 if(options.mergeList.length<=0)throw new Error("No file added to be merged");
1880 var mergeList = options.mergeList;
1890 mergeList.unshift(options.inputfile)
190
1910 var intermediateFiles = [];
192
1930 async.whilst(function(){
1940 return (mergeList.length != 0);
195 },function(callback){
1960 makeIntermediateFile(mergeList.shift(),function(createdIntermediateFile){
1970 if(!createdIntermediateFile)throw new Error("Invalid intermediate file");
1980 intermediateFiles.push(createdIntermediateFile);
1990 callback();
200 })
201 },function(err){
2020 if(err)throw err;
2030 concatIntermediates(targetfile,intermediateFiles,function(concatResult){
2040 if(!concatResult)throw new Error("Invalid concat result file");
2050 quantizeConcat(concatResult,intermediateFiles.length,function(){
2060 intermediateFiles.push(concatResult); // add concatResult to intermediates list so it can be deleted too.
2070 deleteIntermediateFiles(intermediateFiles);
2080 callback(); // completed;
209 });
210 });
211 });
212
213 }
214
2151 this.writeToStream = function(stream, callback) {
216
2172 callback = callback || function(){};
218
2192 if (!this.options._isStreamable) {
2200 this.options.logger.error('selected output format is not streamable');
2210 return callback(null, new Error('selected output format is not streamable'));
222 }
223
2242 var self = this;
2252 var options = this.options;
226
227 // parse options to command
2282 this._prepare(function(err, meta) {
2292 if (err) {
2300 return callback(null, err);
231 }
232
2332 var args = self.buildFfmpegArgs(true, meta);
234
2352 if (!args instanceof Array) {
2360 return callback(null, args);
237 }
238 // write data to stdout
2392 args.push('pipe:1');
240
241 // start conversion of file using spawn
2422 var ffmpegProc = self._spawnProcess(args);
243
2442 if (options.inputstream) {
245 // pump input stream to stdin
2461 options.inputstream.resume();
2471 options.inputstream.pipe(ffmpegProc.stdin);
248 }
249
250 //handle timeout if set
2512 var processTimer;
2522 if (options.timeout) {
2532 processTimer = setTimeout(function() {
2540 ffmpegProc.removeAllListeners('exit');
2550 ffmpegProc.kill('SIGKILL');
2560 options.logger.warn('process ran into a timeout (' + options.timeout + 's)');
2570 callback(self.E_PROCESSTIMEOUT, 'timeout');
258 }, options.timeout * 1000);
259 }
260
2612 var stderr = '';
262
2632 ffmpegProc.stderr.on('data', function(data) {
26443 stderr += data;
26543 if (options.onCodecData) {
2660 self._checkStdErrForCodec(stderr);
267 }
26843 if (options.onProgress) {
2690 self._getProgressFromStdErr(stderr, meta.durationsec);
270 }
271 });
272
2732 ffmpegProc.stdout.on('data', function(chunk) {
27423 stream.write(chunk);
275 });
276
2772 ffmpegProc.on('exit', function(code, signal) {
2782 if (processTimer) {
2792 clearTimeout(processTimer);
280 }
281 // close file descriptor on outstream
2822 if(/^[a-z]+:\/\//.test(options.inputfile)) {
2830 return callback(code, stderr);
284 }
285
2862 var cb_ = function() {
2872 if (!options.inputstream || !options.inputstream.fd) {
2881 return callback(code, stderr);
289 }
2901 if (!options.inputstream.fd) {
2910 options.inputstream.destroy();
2920 return callback(code, stderr);
293 }
2941 fs.close(options.inputstream.fd, function() {
2951 callback(code, stderr);
296 });
297 };
298
2992 if (stream.fd) {
3002 return fs.close(stream.fd, cb_);
301 }
3020 if (stream.end) {
3030 stream.end();
304 } else {
3050 callback(code, "stream will not be closed");
306 }
3070 cb_();
308 });
309
3102 stream.on("close", function()
311 {
3120 options.logger.debug("Output stream closed, killing ffmpgeg process");
3130 ffmpegProc.kill();
314 });
315 });
316 };
317
3181 this.takeScreenshots = function(config, folder, callback) {
319
3203 callback = callback || function(){};
321
3223 function _zeroPad(number, len) {
3234 len = len-String(number).length+2;
3244 return new Array(len<0?0:len).join('0')+number;
325 }
326
3273 function _renderOutputName(j, offset) {
3284 var result = filename;
3294 if(/%0*i/.test(result)) {
3304 var numlen = String(result.match(/%(0*)i/)[1]).length;
3314 result = result.replace(/%0*i/, _zeroPad(j, numlen));
332 }
3334 result = result.replace('%s', offset);
3344 result = result.replace('%w', self.options.video.width);
3354 result = result.replace('%h', self.options.video.height);
3364 result = result.replace('%r', self.options.video.width+'x'+self.options.video.height);
3374 result = result.replace('%f', self.options.inputfile);
3384 result = result.replace('%b', self.options.inputfile.substr(0,self.options.inputfile.lastIndexOf('.')));
3394 return result;
340 }
341
3423 function _screenShotInternal(callback) {
343
344 // get correct dimensions
3453 self._prepare(function(err, meta) {
3463 if(err) {
3471 return callback(err);
348 }
3492 if (!meta.durationsec) {
3500 var errString = 'meta data contains no duration, aborting screenshot creation';
3510 self.options.logger.warn(errString);
3520 return callback(new Error(errString));
353 }
354
355 // check if all timemarks are inside duration
3562 if (Array.isArray(timemarks)) {
3572 for (var i = 0; i < timemarks.length; i++) {
358 /* convert percentage to seconds */
3594 if( timemarks[i].indexOf('%') > 0 ) {
3600 timemarks[i] = (parseInt(timemarks[i], 10) / 100) * meta.durationsec;
361 }
3624 if (parseInt(timemarks[i], 10) > meta.durationsec) {
363 // remove timemark from array
3640 timemarks.splice(i, 1);
3650 --i;
366 }
367 }
368 // if there are no more timemarks around, add one at end of the file
3692 if (timemarks.length === 0) {
3700 timemarks[0] = (meta.durationsec * 0.9);
371 }
372 }
373 // get positions for screenshots (using duration of file minus 10% to remove fade-in/fade-out)
3742 var secondOffset = (meta.durationsec * 0.9) / screenshotcount;
3752 var donecount = 0;
3762 var series = [];
377
378 // reset iterator
3792 var j = 1;
380
3812 var filenames = [];
382
383 // use async helper function to generate all screenshots and
384 // fire callback just once after work is done
3852 async.until(
386 function() {
3876 return j > screenshotcount;
388 },
389 function(taskcallback) {
3904 var offset;
3914 if (Array.isArray(timemarks)) {
392 // get timemark for current iteration
3934 offset = timemarks[(j - 1)];
394 } else {
3950 offset = secondOffset * j;
396 }
3974 var fname = _renderOutputName(j, offset) + '.jpg';
3984 var target = self.escapedPath(path.join(folder, fname), true);
3994 var input = self.escapedPath(self.options.inputfile, true);
400
401 // build screenshot command
4024 var command = [
403 self.ffmpegPath,
404 [
405 '-ss', Math.floor(offset * 100) / 100,
406 '-i', input,
407 '-vcodec', 'mjpeg',
408 '-vframes', '1',
409 '-an',
410 '-f', 'rawvideo',
411 '-s', self.options.video.size,
412 '-y', target
413 ].join(' ')
414 ];
415
4164 j++;
417
418 // only set niceness if running on a non-windows platform
4194 if (self.options.hasOwnProperty('_nice.level') && !os.match(/win(32|64)/)) {
420 // execute ffmpeg through nice
4210 command.unshift('nice -n', self.options._nice.level||0);
422 }
423
4244 exec(command.join(' '), taskcallback);
4254 filenames.push(fname);
426 },
427 function(err) {
4282 callback(err, filenames);
429 }
430 );
431 });
432 }
433
4343 var timemarks, screenshotcount, filename;
4353 if (typeof config === 'object') {
436 // use json object as config
4372 if (config.count) {
4382 screenshotcount = config.count;
439 }
4402 if (config.timemarks) {
4412 timemarks = config.timemarks;
442 }
443 } else {
444 // assume screenshot count as parameter
4451 screenshotcount = config;
4461 timemarks = null;
447 }
4483 if (!this.options.video.size) {
4490 this.options.logger.warn("set size of thumbnails using 'withSize' method");
4500 callback(new Error("set size of thumbnails using 'withSize' method"));
451 }
452
4533 filename = config.filename || 'tn_%ss';
4543 if(!/%0*i/.test(filename) && Array.isArray(timemarks) && timemarks.length > 1 ) {
455 // if there are multiple timemarks but no %i in filename add one
456 // so we won't overwrite the same thumbnail with each timemark
4571 filename += '_%i';
458 }
4593 folder = folder || '.';
460
4613 var self = this;
462
463 // WORKAROUND: exists will be moved from path to fs with node v0.7
4643 var check = fs.exists;
4653 if (!check) {
4660 check = path.exists;
467 }
468
469 // check target folder
4703 check(folder, function(exists) {
4713 if (!exists) {
4722 fs.mkdir(folder, '0755', function(err) {
4732 if (err !== null) {
4740 callback(err);
475 } else {
4762 _screenShotInternal(callback);
477 }
478 });
479 } else {
4801 _screenShotInternal(callback);
481 }
482 });
483 };
484
4851 this._getProgressFromStdErr = function(stderrString, totalDurationSec) {
486 // get last stderr line
48717 var lastLine = stderrString.split(/\r\n|\r|\n/g);
48817 var ll = lastLine[lastLine.length - 2];
48917 var progress;
49017 if (ll) {
49117 progress = ll.split(/frame=([0-9\s]+)fps=([0-9\.\s]+)q=([0-9\.\s]+)(L?)size=([0-9\s]+)kB time=(([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2})) bitrate=([0-9\.\s]+)kbits/ig);
492 }
49317 if (progress && progress.length > 10) {
494 // build progress report object
4950 var ret = {
496 frames: parseInt(progress[1], 10),
497 currentFps: parseInt(progress[2], 10),
498 currentKbps: parseFloat(progress[10]),
499 targetSize: parseInt(progress[5], 10),
500 timemark: progress[6]
501 };
502
503 // calculate percent progress using duration
5040 if (totalDurationSec && totalDurationSec > 0) {
5050 ret.percent = (this.ffmpegTimemarkToSeconds(ret.timemark) / totalDurationSec) * 100;
506 }
507
5080 this.options.onProgress(ret);
509 }
510 };
511
5121 this._checkStdErrForCodec = function(stderrString) {
51311 var format= /Input #[0-9]+, ([^ ]+),/.exec(stderrString);
51411 var dur = /Duration\: ([^,]+)/.exec(stderrString);
51511 var audio = /Audio\: (.*)/.exec(stderrString);
51611 var video = /Video\: (.*)/.exec(stderrString);
51711 var codecObject = { format: '', audio: '', video: '', duration: '' };
518
51911 if (format && format.length > 1) {
5209 codecObject.format = format[1];
521 }
522
52311 if (dur && dur.length > 1) {
5249 codecObject.duration = dur[1];
525 }
526
52711 if (audio && audio.length > 1) {
5280 audio = audio[1].split(', ');
5290 codecObject.audio = audio[0];
5300 codecObject.audio_details = audio;
531 }
53211 if (video && video.length > 1) {
5338 video = video[1].split(', ');
5348 codecObject.video = video[0];
5358 codecObject.video_details = video;
536 }
537
53811 var codecInfoPassed = /Press (\[q\]|ctrl-c) to stop/.test(stderrString);
53911 if (codecInfoPassed) {
5401 this.options.onCodecData(codecObject);
5411 this.options.onCodecData = null;
542 }
543 };
544
5451 this._spawnProcess = function(args, options) {
5468 var retProc = spawn(this.ffmpegPath, args, options);
547 // only re-nice if running on a non-windows platform
5488 if (this.options.hasOwnProperty('_nice.level') && !os.match(/win(32|64)/)) {
5490 var niceLevel = this.options._nice.level || 0;
5500 if (niceLevel > 0) {
5510 niceLevel = '+' + niceLevel;
552 }
553 // renice the spawned process without waiting for callback
5540 var self = this;
5550 var command = [
556 'renice -n', niceLevel,
557 '-p', retProc.pid
558 ].join(' ');
559
5600 exec(command, function(err, stderr, stdout) {
5610 if (!err) {
5620 self.options.logger.info('successfully reniced process ' + retProc.pid + ' to ' + niceLevel + ' niceness!');
563 }
564 });
565 }
5668 if (retProc.stderr) {
5678 retProc.stderr.setEncoding('utf8');
568 }
5698 return retProc;
570 };
571
5721 this.buildFfmpegArgs = function(overrideOutputCheck, meta) {
57344 var args = [];
574
575 // add startoffset and duration
57644 if (this.options.starttime) {
5771 args.push('-ss', this.options.starttime);
578 }
579
58044 if (this.options.video.loop) {
5813 args.push('-loop', 1);
582 }
583
584
585 // add input file (if using fs mode)
58644 if (this.options.inputfile && !this.options.inputstream && !this.options.inputlive) {
587 // add input file fps
58842 if (this.options.video.fpsInput) {
5890 args.push('-r', this.options.video.fpsInput);
590 }
59142 if (/^[a-z]+:\/\//.test(this.options.inputfile)) {
5920 args.push('-i', this.options.inputfile.replace(' ', '%20'));
59342 } else if (/%\d*d/.test(this.options.inputfile)) { // multi-file format - http://ffmpeg.org/ffmpeg.html#image2-1
5941 args.push('-i', this.options.inputfile.replace(' ', '\ '));
595 } else {
59641 var fstats = fs.statSync(this.options.inputfile);
59741 if (fstats.isFile()) {
598 // fix for spawn call with path containing spaces and quotes
59941 args.push('-i', this.options.inputfile.replace(/ /g, "\ ")
600 .replace(/'/g, "\'")
601 .replace(/"/g, "\""));
602 } else {
6030 this.options.logger.error('input file is not readable');
6040 throw new Error('input file is not readable');
605 }
606 }
607 // check for input stream
6082 } else if (this.options.inputstream) {
609 // push args to make ffmpeg read from stdin
6102 args.push('-i', '-');
6110 } else if (this.options.inputlive){
612 //Check if input URI
6130 if(/^[a-z]+:\/\//.test(this.options.inputfile)) {
614 // add input with live flag
6150 args.push('-i', this.options.inputfile.replace(' ', '%20')+' live=1');
616 }else {
6170 this.options.logger.error('live input URI is not valid');
6180 throw new Error('live input URI is not valid');
619 }
620 }
621
62244 if (this.options.otherInputs) {
62344 if (this.options.otherInputs.length > 0) {
6241 this.options.otherInputs.forEach(function(el) {
6251 args.push('-i', el);
626 });
627 }
628 }
629
63044 if (this.options.duration) {
6313 args.push('-t', this.options.duration);
632 }
633
63444 if (this.options.video.framecount) {
6351 args.push('-vframes', this.options.video.framecount);
636 }
637
638 // add format
63944 if (this.options.format) {
64012 args.push('-f', this.options.format);
641 }
642
643 // add video options
64444 if (this.options.video.skip) {
645 // skip video stream completely (#45)
6462 args.push('-vn');
647 } else {
64842 if (this.options.video.bitrate) {
64913 args.push('-b', this.options.video.bitrate + 'k');
65013 if (this.options._useConstantVideoBitrate) {
651 // add parameters to ensure constant bitrate encoding
6521 args.push('-maxrate', this.options.video.bitrate + 'k');
6531 args.push('-minrate', this.options.video.bitrate + 'k');
6541 args.push('-bufsize', '3M');
655 }
656 }
65742 if (this.options.video.codec) {
65812 args.push('-vcodec', this.options.video.codec);
659 }
66042 if (this.options.video.fps) {
6619 args.push('-r', this.options.video.fps);
662 }
66342 if (this.options.video.aspect) {
6645 args.push('-aspect', this.options.video.aspect);
665 }
666 }
667
668 // add video options
66944 if (this.options.audio.skip) {
670 // skip audio stream completely (#45)
6712 args.push('-an');
672 } else {
67342 if (this.options.audio.bitrate) {
67413 args.push('-ab', this.options.audio.bitrate + 'k');
675 }
67642 if (this.options.audio.channels) {
67712 args.push('-ac', this.options.audio.channels);
678 }
67942 if (this.options.audio.codec) {
68012 args.push('-acodec', this.options.audio.codec);
681 }
68242 if (this.options.audio.frequency) {
6839 args.push('-ar', this.options.audio.frequency);
684 }
68542 if (this.options.audio.quality || this.options.audio.quality === 0) {
6861 args.push('-aq', this.options.audio.quality);
687 }
688 }
689
690 // add additional options
69144 if (this.options.additional) {
69244 if (this.options.additional.length > 0) {
6935 this.options.additional.forEach(function(el) {
69462 args.push(el);
695 });
696 }
697 }
698
69944 if (this.options.video.pad && !this.options.video.skip) {
700 // we have padding arguments, push
7015 if (this.atLeastVersion(meta.ffmpegversion, '0.7')) {
702 // padding is not supported ffmpeg < 0.7 (only using legacy commands which were replaced by vfilter calls)
7035 args.push('-vf');
7045 args.push('pad=' + this.options.video.pad.w +
705 ':' + this.options.video.pad.h +
706 ':' + this.options.video.pad.x +
707 ':' + this.options.video.pad.y +
708 ':' + this.options.video.padcolor);
709 } else {
7100 return new Error("Your ffmpeg version " + meta.ffmpegversion + " does not support padding");
711 }
712 }
713
714 // add size and output file
71544 if (this.options.video.size && !this.options.video.skip) {
71620 args.push('-s', this.options.video.size);
717 }
718
719 // add output file fps
72044 if (this.options.video.fpsOutput) {
7210 args.push('-r', this.options.video.fpsOutput);
722 }
723
72444 if (this.options.outputfile) {
7256 var target = this.escapedPath(this.options.outputfile, false);
7266 if (!os.match(/win(32|64)/)) {
7276 args.push('-y', target.replace(' ', '\\ '));
728 } else {
7290 args.push('-y', target);
730 }
731 } else {
73238 if (!overrideOutputCheck) {
7330 this.options.logger.error('no outputfile specified');
734 }
735 }
73644 return args;
737 };
738};

registry.js

100%
12
12
0
LineHitsSource
11exports = module.exports = {
2 instance: {
3 values: [],
4 getIndex: function(name) {
52 for (var i = 0; i < this.values.length; i++) {
62 if (this.values[i].name === name) {
72 return i;
8 }
9 }
10 },
11 set : function(name, value) {
128 if (this.get(name) === false) {
136 this.values.push({ name: name, value: value });
14 } else {
152 this.values[this.getIndex(name)].value = value;
16 }
17 },
18 get: function(name) {
1916 for (var i = 0; i < this.values.length; i++) {
2013 if (this.values[i].name === name) {
2112 return this.values[i].value;
22 }
23 }
244 return false;
25 },
26 reset: function() {
271 this.values = [];
28 }
29 }
30};