fluent-ffmpeg
Advanced tools
Comparing version 2.0.1 to 2.1.0
@@ -302,3 +302,3 @@ /*jshint node:true*/ | ||
this._spawnFfmpeg(['-filters'], { captureStdout: true }, function (err, stdout) { | ||
this._spawnFfmpeg(['-filters'], { captureStdout: true, stdoutLines: 0 }, function (err, stdoutRing) { | ||
if (err) { | ||
@@ -308,2 +308,3 @@ return callback(err); | ||
var stdout = stdoutRing.get(); | ||
var lines = stdout.split('\n'); | ||
@@ -359,3 +360,3 @@ var data = {}; | ||
this._spawnFfmpeg(['-codecs'], { captureStdout: true }, function(err, stdout) { | ||
this._spawnFfmpeg(['-codecs'], { captureStdout: true, stdoutLines: 0 }, function(err, stdoutRing) { | ||
if (err) { | ||
@@ -365,2 +366,3 @@ return callback(err); | ||
var stdout = stdoutRing.get(); | ||
var lines = stdout.split(lineBreakRegexp); | ||
@@ -462,3 +464,3 @@ var data = {}; | ||
this._spawnFfmpeg(['-encoders'], { captureStdout: true }, function(err, stdout) { | ||
this._spawnFfmpeg(['-encoders'], { captureStdout: true, stdoutLines: 0 }, function(err, stdoutRing) { | ||
if (err) { | ||
@@ -468,2 +470,3 @@ return callback(err); | ||
var stdout = stdoutRing.get(); | ||
var lines = stdout.split(lineBreakRegexp); | ||
@@ -520,3 +523,3 @@ var data = {}; | ||
// Run ffmpeg -formats | ||
this._spawnFfmpeg(['-formats'], { captureStdout: true }, function (err, stdout) { | ||
this._spawnFfmpeg(['-formats'], { captureStdout: true, stdoutLines: 0 }, function (err, stdoutRing) { | ||
if (err) { | ||
@@ -527,2 +530,3 @@ return callback(err); | ||
// Parse output | ||
var stdout = stdoutRing.get(); | ||
var lines = stdout.split(lineBreakRegexp); | ||
@@ -529,0 +533,0 @@ var data = {}; |
@@ -12,16 +12,23 @@ /*jshint node:true, laxcomma:true*/ | ||
var lines = out.split(/\r\n|\r|\n/); | ||
lines = lines.filter(function (line) { | ||
return line.length > 0; | ||
}); | ||
var data = { | ||
streams: [] | ||
streams: [], | ||
format: {}, | ||
chapters: [] | ||
}; | ||
function parseBlock() { | ||
function parseBlock(name) { | ||
var data = {}; | ||
var line = lines.shift(); | ||
while (line) { | ||
if (line.match(/^\[\//)) { | ||
while (typeof line !== 'undefined') { | ||
if (line.toLowerCase() == '[/'+name+']') { | ||
return data; | ||
} else if (line.match(/^\[/)) { | ||
lines.unshift(line); | ||
return data; | ||
line = lines.shift(); | ||
continue; | ||
} | ||
@@ -45,8 +52,11 @@ | ||
var line = lines.shift(); | ||
while (line) { | ||
while (typeof line !== 'undefined') { | ||
if (line.match(/^\[stream/i)) { | ||
var stream = parseBlock(); | ||
var stream = parseBlock('stream'); | ||
data.streams.push(stream); | ||
} else if (line.match(/^\[chapter/i)) { | ||
var chapter = parseBlock('chapter'); | ||
data.chapters.push(chapter); | ||
} else if (line.toLowerCase() === '[format]') { | ||
data.format = parseBlock(); | ||
data.format = parseBlock('format'); | ||
} | ||
@@ -83,14 +93,40 @@ | ||
* | ||
* @param {Number} [index] 0-based index of input to probe (defaults to last input) | ||
* @param {?Number} [index] 0-based index of input to probe (defaults to last input) | ||
* @param {?String[]} [options] array of output options to return | ||
* @param {FfmpegCommand~ffprobeCallback} callback callback function | ||
* | ||
*/ | ||
proto.ffprobe = function(index, callback) { | ||
var input; | ||
proto.ffprobe = function() { | ||
var input, index = null, options = [], callback; | ||
if (typeof callback === 'undefined') { | ||
callback = index; | ||
// the last argument should be the callback | ||
var callback = arguments[arguments.length - 1]; | ||
var ended = false | ||
function handleCallback(err, data) { | ||
if (!ended) { | ||
ended = true; | ||
callback(err, data); | ||
} | ||
}; | ||
// map the arguments to the correct variable names | ||
switch (arguments.length) { | ||
case 3: | ||
index = arguments[0]; | ||
options = arguments[1]; | ||
break; | ||
case 2: | ||
if (typeof arguments[0] === 'number') { | ||
index = arguments[0]; | ||
} else if (Array.isArray(arguments[0])) { | ||
options = arguments[0]; | ||
} | ||
break; | ||
} | ||
if (index === null) { | ||
if (!this._currentInput) { | ||
return callback(new Error('No input specified')); | ||
return handleCallback(new Error('No input specified')); | ||
} | ||
@@ -103,16 +139,12 @@ | ||
if (!input) { | ||
return callback(new Error('Invalid input index')); | ||
return handleCallback(new Error('Invalid input index')); | ||
} | ||
} | ||
if (input.isStream) { | ||
return callback(new Error('Cannot run ffprobe on stream input')); | ||
} | ||
// Find ffprobe | ||
this._getFfprobePath(function(err, path) { | ||
if (err) { | ||
return callback(err); | ||
return handleCallback(err); | ||
} else if (!path) { | ||
return callback(new Error('Cannot find ffprobe')); | ||
return handleCallback(new Error('Cannot find ffprobe')); | ||
} | ||
@@ -126,12 +158,25 @@ | ||
// Spawn ffprobe | ||
var ffprobe = spawn(path, [ | ||
'-show_streams', | ||
'-show_format', | ||
input.source | ||
]); | ||
var src = input.isStream ? 'pipe:0' : input.source; | ||
var ffprobe = spawn(path, ['-show_streams', '-show_format'].concat(options, src)); | ||
ffprobe.on('error', function(err) { | ||
callback(err); | ||
}); | ||
if (input.isStream) { | ||
// Skip errors on stdin. These get thrown when ffprobe is complete and | ||
// there seems to be no way hook in and close stdin before it throws. | ||
ffprobe.stdin.on('error', function(err) { | ||
if (['ECONNRESET', 'EPIPE'].indexOf(err.code) >= 0) { return; } | ||
handleCallback(err); | ||
}); | ||
// Once ffprobe's input stream closes, we need no more data from the | ||
// input | ||
ffprobe.stdin.on('close', function() { | ||
input.source.pause(); | ||
input.source.unpipe(ffprobe.stdin); | ||
}); | ||
input.source.pipe(ffprobe.stdin); | ||
} | ||
ffprobe.on('error', callback); | ||
// Ensure we wait for captured streams to end before calling callback | ||
@@ -150,3 +195,3 @@ var exitError = null; | ||
return callback(exitError); | ||
return handleCallback(exitError); | ||
} | ||
@@ -159,26 +204,28 @@ | ||
[data.format].concat(data.streams).forEach(function(target) { | ||
var legacyTagKeys = Object.keys(target).filter(legacyTag); | ||
if (target) { | ||
var legacyTagKeys = Object.keys(target).filter(legacyTag); | ||
if (legacyTagKeys.length) { | ||
target.tags = target.tags || {}; | ||
if (legacyTagKeys.length) { | ||
target.tags = target.tags || {}; | ||
legacyTagKeys.forEach(function(tagKey) { | ||
target.tags[tagKey.substr(4)] = target[tagKey]; | ||
delete target[tagKey]; | ||
}); | ||
} | ||
legacyTagKeys.forEach(function(tagKey) { | ||
target.tags[tagKey.substr(4)] = target[tagKey]; | ||
delete target[tagKey]; | ||
}); | ||
} | ||
var legacyDispositionKeys = Object.keys(target).filter(legacyDisposition); | ||
var legacyDispositionKeys = Object.keys(target).filter(legacyDisposition); | ||
if (legacyDispositionKeys.length) { | ||
target.disposition = target.disposition || {}; | ||
if (legacyDispositionKeys.length) { | ||
target.disposition = target.disposition || {}; | ||
legacyDispositionKeys.forEach(function(dispositionKey) { | ||
target.disposition[dispositionKey.substr(12)] = target[dispositionKey]; | ||
delete target[dispositionKey]; | ||
}); | ||
legacyDispositionKeys.forEach(function(dispositionKey) { | ||
target.disposition[dispositionKey.substr(12)] = target[dispositionKey]; | ||
delete target[dispositionKey]; | ||
}); | ||
} | ||
} | ||
}); | ||
callback(null, data); | ||
handleCallback(null, data); | ||
} | ||
@@ -222,2 +269,1 @@ } | ||
}; | ||
@@ -27,2 +27,3 @@ /*jshint node:true*/ | ||
* @param {String} [options.preset="fluent-ffmpeg/lib/presets"] alias for `presets` | ||
* @param {String} [options.stdoutLines=100] maximum lines of ffmpeg output to keep in memory, use 0 for unlimited | ||
* @param {Number} [options.timeout=<no timeout>] ffmpeg processing timeout in seconds | ||
@@ -65,2 +66,3 @@ * @param {String|ReadableStream} [options.source=<no input>] alias for the `input` parameter | ||
// Set default option values | ||
options.stdoutLines = 'stdoutLines' in options ? options.stdoutLines : 100; | ||
options.presets = options.presets || options.preset || path.join(__dirname, 'presets'); | ||
@@ -214,4 +216,5 @@ options.niceness = options.niceness || options.priority || 0; | ||
FfmpegCommand.ffprobe = function(file, callback) { | ||
(new FfmpegCommand(file)).ffprobe(callback); | ||
FfmpegCommand.ffprobe = function(file) { | ||
var instance = new FfmpegCommand(file); | ||
instance.ffprobe.apply(instance, Array.prototype.slice.call(arguments, 1)); | ||
}; | ||
@@ -218,0 +221,0 @@ |
@@ -10,2 +10,3 @@ /*jshint node:true*/ | ||
var nlRegexp = /\r\n|\r|\n/g; | ||
@@ -52,2 +53,9 @@ /* | ||
/** | ||
* Emitted when ffmpeg outputs to stderr | ||
* | ||
* @event FfmpegCommand#stderr | ||
* @param {String} line stderr output line | ||
*/ | ||
/** | ||
* Emitted when ffmpeg reports input codec data | ||
@@ -77,3 +85,4 @@ * | ||
* @event FfmpegCommand#end | ||
* @param {Array|null} [filenames] generated filenames when taking screenshots, null otherwise | ||
* @param {Array|String|null} [filenames|stdout] generated filenames when taking screenshots, ffmpeg stdout when not outputting to a stream, null otherwise | ||
* @param {String|null} stderr ffmpeg stderr | ||
*/ | ||
@@ -87,4 +96,5 @@ | ||
* - 'niceness': specify process niceness, ignored on Windows (default: 0) | ||
* - `cwd`: change working directory | ||
* - 'captureStdout': capture stdout and pass it to 'endCB' as its 2nd argument (default: false) | ||
* - 'captureStderr': capture stderr and pass it to 'endCB' as its 3rd argument (default: false) | ||
* - 'stdoutLines': override command limit (default: use command limit) | ||
* | ||
@@ -100,4 +110,4 @@ * The 'processCB' callback, if present, is called as soon as the process is created and | ||
* @param {Object} [options] spawn options (see above) | ||
* @param {Function} [processCB] callback called with process object when it has been created | ||
* @param {Function} endCB callback with signature (err, stdout, stderr) | ||
* @param {Function} [processCB] callback called with process object and stdout/stderr ring buffers when process has been created | ||
* @param {Function} endCB callback called with error (if applicable) and stdout/stderr ring buffers when process finished | ||
* @private | ||
@@ -119,2 +129,4 @@ */ | ||
var maxLines = 'stdoutLines' in options ? options.stdoutLines : this.options.stdoutLines; | ||
// Find ffmpeg | ||
@@ -134,6 +146,6 @@ this._getFfmpegPath(function(err, command) { | ||
var stdout = null; | ||
var stdoutRing = utils.linesRing(maxLines); | ||
var stdoutClosed = false; | ||
var stderr = null; | ||
var stderrRing = utils.linesRing(maxLines); | ||
var stderrClosed = false; | ||
@@ -144,3 +156,3 @@ | ||
if (ffmpegProc.stderr && options.captureStderr) { | ||
if (ffmpegProc.stderr) { | ||
ffmpegProc.stderr.setEncoding('utf8'); | ||
@@ -160,6 +172,4 @@ } | ||
if (processExited && | ||
(stdoutClosed || !options.captureStdout) && | ||
(stderrClosed || !options.captureStderr)) { | ||
endCB(exitError, stdout, stderr); | ||
if (processExited && (stdoutClosed || !options.captureStdout) && stderrClosed) { | ||
endCB(exitError, stdoutRing, stderrRing); | ||
} | ||
@@ -184,9 +194,8 @@ } | ||
if (options.captureStdout) { | ||
stdout = ''; | ||
ffmpegProc.stdout.on('data', function(data) { | ||
stdout += data; | ||
stdoutRing.append(data); | ||
}); | ||
ffmpegProc.stdout.on('close', function() { | ||
stdoutRing.close(); | ||
stdoutClosed = true; | ||
@@ -198,17 +207,14 @@ handleExit(); | ||
// Capture stderr if specified | ||
if (options.captureStderr) { | ||
stderr = ''; | ||
ffmpegProc.stderr.on('data', function(data) { | ||
stderrRing.append(data); | ||
}); | ||
ffmpegProc.stderr.on('data', function(data) { | ||
stderr += data; | ||
}); | ||
ffmpegProc.stderr.on('close', function() { | ||
stderrRing.close(); | ||
stderrClosed = true; | ||
handleExit(); | ||
}); | ||
ffmpegProc.stderr.on('close', function() { | ||
stderrClosed = true; | ||
handleExit(); | ||
}); | ||
} | ||
// Call process callback | ||
processCB(ffmpegProc); | ||
processCB(ffmpegProc, stdoutRing, stderrRing); | ||
}); | ||
@@ -437,10 +443,11 @@ }; | ||
// Run ffmpeg | ||
var stdout = null; | ||
var stderr = ''; | ||
self._spawnFfmpeg( | ||
args, | ||
{ | ||
captureStdout: !outputStream, | ||
niceness: self.options.niceness, | ||
cwd: self.options.cwd | ||
}, | ||
{ niceness: self.options.niceness }, | ||
function processCB(ffmpegProc) { | ||
function processCB(ffmpegProc, stdoutRing, stderrRing) { | ||
self.ffmpegProc = ffmpegProc; | ||
@@ -470,3 +477,3 @@ self.emit('start', 'ffmpeg ' + args.join(' ')); | ||
emitEnd(new Error(msg), stdout, stderr); | ||
emitEnd(new Error(msg), stdoutRing.get(), stderrRing.get()); | ||
ffmpegProc.kill(); | ||
@@ -476,2 +483,3 @@ }, self.options.timeout * 1000); | ||
if (outputStream) { | ||
@@ -497,22 +505,29 @@ // Pipe ffmpeg stdout to output stream | ||
self.logger.debug('Output stream error, killing ffmpgeg process'); | ||
emitEnd(new Error('Output stream error: ' + err.message)); | ||
emitEnd(new Error('Output stream error: ' + err.message), stdoutRing.get(), stderrRing.get()); | ||
ffmpegProc.kill(); | ||
}); | ||
} else { | ||
// Gather ffmpeg stdout | ||
stdout = ''; | ||
ffmpegProc.stdout.on('data', function (data) { | ||
stdout += data; | ||
}); | ||
} | ||
// Process ffmpeg stderr data | ||
self._codecDataSent = false; | ||
ffmpegProc.stderr.on('data', function (data) { | ||
stderr += data; | ||
// Setup stderr handling | ||
if (stderrRing) { | ||
if (!self._codecDataSent && self.listeners('codecData').length) { | ||
utils.extractCodecData(self, stderr); | ||
// 'stderr' event | ||
if (self.listeners('stderr').length) { | ||
stderrRing.callback(function(line) { | ||
self.emit('stderr', line); | ||
}); | ||
} | ||
// 'codecData' event | ||
if (self.listeners('codecData').length) { | ||
var codecDataSent = false; | ||
var codecObject = {}; | ||
stderrRing.callback(function(line) { | ||
if (!codecDataSent) | ||
codecDataSent = utils.extractCodecData(self, line, codecObject); | ||
}); | ||
} | ||
// 'progress' event | ||
if (self.listeners('progress').length) { | ||
@@ -525,8 +540,10 @@ var duration = 0; | ||
utils.extractProgress(self, stderr, duration); | ||
stderrRing.callback(function(line) { | ||
utils.extractProgress(self, line, duration); | ||
}); | ||
} | ||
}); | ||
} | ||
}, | ||
function endCB(err) { | ||
function endCB(err, stdoutRing, stderrRing) { | ||
delete self.ffmpegProc; | ||
@@ -537,6 +554,6 @@ | ||
// Add ffmpeg error message | ||
err.message += ': ' + utils.extractError(stderr); | ||
err.message += ': ' + utils.extractError(stderrRing.get()); | ||
} | ||
emitEnd(err, stdout, stderr); | ||
emitEnd(err, stdoutRing.get(), stderrRing.get()); | ||
} else { | ||
@@ -550,3 +567,6 @@ // Find out which outputs need flv metadata | ||
self._getFlvtoolPath(function(err, flvtool) { | ||
// No possible error here, getFlvtoolPath was already called by _prepare | ||
if (err) { | ||
return emitEnd(err); | ||
} | ||
async.each( | ||
@@ -576,3 +596,3 @@ flvmeta, | ||
} else { | ||
emitEnd(null, stdout, stderr); | ||
emitEnd(null, stdoutRing.get(), stderrRing.get()); | ||
} | ||
@@ -583,3 +603,3 @@ } | ||
} else { | ||
emitEnd(null, stdout, stderr); | ||
emitEnd(null, stdoutRing.get(), stderrRing.get()); | ||
} | ||
@@ -586,0 +606,0 @@ } |
@@ -193,2 +193,5 @@ /*jshint node:true*/ | ||
var duration = Number(vstream.duration); | ||
if (isNaN(duration)) { | ||
duration = Number(meta.format.duration); | ||
} | ||
@@ -195,0 +198,0 @@ if (isNaN(duration)) { |
175
lib/utils.js
@@ -6,2 +6,3 @@ /*jshint node:true*/ | ||
var isWindows = require('os').platform().match(/win(32|64)/); | ||
var which = require('which'); | ||
@@ -220,14 +221,8 @@ var nlRegexp = /\r\n|\r|\n/g; | ||
var cmd = 'which ' + name; | ||
if (isWindows) { | ||
cmd = 'where ' + name + '.exe'; | ||
} | ||
exec(cmd, function(err, stdout) { | ||
which(name, function(err, result){ | ||
if (err) { | ||
// Treat errors as not found | ||
callback(null, whichCache[name] = ''); | ||
} else { | ||
callback(null, whichCache[name] = stdout.trim()); | ||
return callback(null, whichCache[name] = ''); | ||
} | ||
callback(null, whichCache[name] = result); | ||
}); | ||
@@ -274,38 +269,51 @@ }, | ||
* Extract codec data from ffmpeg stderr and emit 'codecData' event if appropriate | ||
* Call it with an initially empty codec object once with each line of stderr output until it returns true | ||
* | ||
* @param {FfmpegCommand} command event emitter | ||
* @param {String} stderr ffmpeg stderr output | ||
* @param {String} stderrLine ffmpeg stderr output line | ||
* @param {Object} codecObject object used to accumulate codec data between calls | ||
* @return {Boolean} true if codec data is complete (and event was emitted), false otherwise | ||
* @private | ||
*/ | ||
extractCodecData: function(command, stderr) { | ||
var format= /Input #[0-9]+, ([^ ]+),/.exec(stderr); | ||
var dur = /Duration\: ([^,]+)/.exec(stderr); | ||
var audio = /Audio\: (.*)/.exec(stderr); | ||
var video = /Video\: (.*)/.exec(stderr); | ||
var codecObject = { format: '', audio: '', video: '', duration: '' }; | ||
extractCodecData: function(command, stderrLine, codecsObject) { | ||
var inputPattern = /Input #[0-9]+, ([^ ]+),/; | ||
var durPattern = /Duration\: ([^,]+)/; | ||
var audioPattern = /Audio\: (.*)/; | ||
var videoPattern = /Video\: (.*)/; | ||
if (format && format.length > 1) { | ||
codecObject.format = format[1]; | ||
if (!('inputStack' in codecsObject)) { | ||
codecsObject.inputStack = []; | ||
codecsObject.inputIndex = -1; | ||
codecsObject.inInput = false; | ||
} | ||
if (dur && dur.length > 1) { | ||
codecObject.duration = dur[1]; | ||
} | ||
var inputStack = codecsObject.inputStack; | ||
var inputIndex = codecsObject.inputIndex; | ||
var inInput = codecsObject.inInput; | ||
if (audio && audio.length > 1) { | ||
var format, dur, audio, video; | ||
if (format = stderrLine.match(inputPattern)) { | ||
inInput = codecsObject.inInput = true; | ||
inputIndex = codecsObject.inputIndex = codecsObject.inputIndex + 1; | ||
inputStack[inputIndex] = { format: format[1], audio: '', video: '', duration: '' }; | ||
} else if (inInput && (dur = stderrLine.match(durPattern))) { | ||
inputStack[inputIndex].duration = dur[1]; | ||
} else if (inInput && (audio = stderrLine.match(audioPattern))) { | ||
audio = audio[1].split(', '); | ||
codecObject.audio = audio[0]; | ||
codecObject.audio_details = audio; | ||
} | ||
if (video && video.length > 1) { | ||
inputStack[inputIndex].audio = audio[0]; | ||
inputStack[inputIndex].audio_details = audio; | ||
} else if (inInput && (video = stderrLine.match(videoPattern))) { | ||
video = video[1].split(', '); | ||
codecObject.video = video[0]; | ||
codecObject.video_details = video; | ||
inputStack[inputIndex].video = video[0]; | ||
inputStack[inputIndex].video_details = video; | ||
} else if (/Output #\d+/.test(stderrLine)) { | ||
inInput = codecsObject.inInput = false; | ||
} else if (/Stream mapping:|Press (\[q\]|ctrl-c) to stop/.test(stderrLine)) { | ||
command.emit.apply(command, ['codecData'].concat(inputStack)); | ||
return true; | ||
} | ||
var codecInfoPassed = /Press (\[q\]|ctrl-c) to stop/.test(stderr); | ||
if (codecInfoPassed) { | ||
command.emit('codecData', codecObject); | ||
command._codecDataSent = true; | ||
} | ||
return false; | ||
}, | ||
@@ -318,15 +326,9 @@ | ||
* @param {FfmpegCommand} command event emitter | ||
* @param {String} stderr ffmpeg stderr data | ||
* @param {String} stderrLine ffmpeg stderr data | ||
* @param {Number} [duration=0] expected output duration in seconds | ||
* @private | ||
*/ | ||
extractProgress: function(command, stderr, duration) { | ||
var lines = stderr.split(nlRegexp); | ||
var lastline = lines[lines.length - 2]; | ||
var progress; | ||
extractProgress: function(command, stderrLine, duration) { | ||
var progress = parseProgressLine(stderrLine); | ||
if (lastline) { | ||
progress = parseProgressLine(lastline); | ||
} | ||
if (progress) { | ||
@@ -337,3 +339,3 @@ // build progress report object | ||
currentFps: parseInt(progress.fps, 10), | ||
currentKbps: parseFloat(progress.bitrate.replace('kbits/s', '')), | ||
currentKbps: progress.bitrate ? parseFloat(progress.bitrate.replace('kbits/s', '')) : 0, | ||
targetSize: parseInt(progress.size, 10), | ||
@@ -370,3 +372,90 @@ timemark: progress.time | ||
}, []).join('\n'); | ||
}, | ||
/** | ||
* Creates a line ring buffer object with the following methods: | ||
* - append(str) : appends a string or buffer | ||
* - get() : returns the whole string | ||
* - close() : prevents further append() calls and does a last call to callbacks | ||
* - callback(cb) : calls cb for each line (incl. those already in the ring) | ||
* | ||
* @param {Numebr} maxLines maximum number of lines to store (<= 0 for unlimited) | ||
*/ | ||
linesRing: function(maxLines) { | ||
var cbs = []; | ||
var lines = []; | ||
var current = null; | ||
var closed = false | ||
var max = maxLines - 1; | ||
function emit(line) { | ||
cbs.forEach(function(cb) { cb(line); }); | ||
} | ||
return { | ||
callback: function(cb) { | ||
lines.forEach(function(l) { cb(l); }); | ||
cbs.push(cb); | ||
}, | ||
append: function(str) { | ||
if (closed) return; | ||
if (str instanceof Buffer) str = '' + str; | ||
if (!str || str.length === 0) return; | ||
var newLines = str.split(nlRegexp); | ||
if (newLines.length === 1) { | ||
if (current !== null) { | ||
current = current + newLines.shift(); | ||
} else { | ||
current = newLines.shift(); | ||
} | ||
} else { | ||
if (current !== null) { | ||
current = current + newLines.shift(); | ||
emit(current); | ||
lines.push(current); | ||
} | ||
current = newLines.pop(); | ||
newLines.forEach(function(l) { | ||
emit(l); | ||
lines.push(l); | ||
}); | ||
if (max > -1 && lines.length > max) { | ||
lines.splice(0, lines.length - max); | ||
} | ||
} | ||
}, | ||
get: function() { | ||
if (current !== null) { | ||
return lines.concat([current]).join('\n'); | ||
} else { | ||
return lines.join('\n'); | ||
} | ||
}, | ||
close: function() { | ||
if (closed) return; | ||
if (current !== null) { | ||
emit(current); | ||
lines.push(current); | ||
if (max > -1 && lines.length > max) { | ||
lines.shift(); | ||
} | ||
current = null; | ||
} | ||
closed = true; | ||
} | ||
}; | ||
} | ||
}; |
{ | ||
"name": "fluent-ffmpeg", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "A fluent API to FFMPEG (http://www.ffmpeg.org)", | ||
@@ -15,2 +15,3 @@ "keywords": [ | ||
], | ||
"license": "MIT", | ||
"bugs": { | ||
@@ -24,9 +25,7 @@ "mail": "schaermu@gmail.com", | ||
"should": "latest", | ||
"grunt-contrib-watch": "~0.4.4", | ||
"grunt-shell": "~0.3.0", | ||
"grunt": "~0.4.1", | ||
"jsdoc": "latest" | ||
}, | ||
"dependencies": { | ||
"async": ">=0.2.9" | ||
"async": ">=0.2.9", | ||
"which": "^1.1.1" | ||
}, | ||
@@ -33,0 +32,0 @@ "engines": { |
@@ -100,2 +100,3 @@ # Fluent ffmpeg-API for node.js [![Build Status](https://secure.travis-ci.org/fluent-ffmpeg/node-fluent-ffmpeg.svg?branch=master)](http://travis-ci.org/fluent-ffmpeg/node-fluent-ffmpeg) | ||
* `logger`: logger object with `debug()`, `info()`, `warn()` and `error()` methods (defaults to no logging) | ||
* `stdoutLines`: maximum number of lines from ffmpeg stdout/stderr to keep in memory (defaults to 100, use 0 for unlimited storage) | ||
@@ -850,2 +851,13 @@ | ||
#### 'stderr': FFmpeg output | ||
The `stderr` event is emitted every time FFmpeg outputs a line to `stderr`. It is emitted with a string containing the line of stderr (minus trailing new line characters). | ||
```js | ||
ffmpeg('/path/to/file.avi') | ||
.on('stderr', function(stderrLine) { | ||
console.log('Stderr output: ' + stderrLine); | ||
}); | ||
``` | ||
#### 'error': transcoding error | ||
@@ -866,7 +878,7 @@ | ||
The `end` event is emitted when processing has finished. Listeners receive no arguments, except when generating thumbnails (see below), in which case they receive an array of the generated filenames. | ||
The `end` event is emitted when processing has finished. Listeners receive ffmpeg standard output and standard error as arguments, except when generating thumbnails (see below), in which case they receive an array of the generated filenames. | ||
```js | ||
ffmpeg('/path/to/file.avi') | ||
.on('end', function() { | ||
.on('end', function(stdout, stderr) { | ||
console.log('Transcoding succeeded !'); | ||
@@ -876,3 +888,5 @@ }); | ||
`stdout` is empty when the command outputs to a stream. Both `stdout` and `stderr` are limited by the `stdoutLines` option (defaults to 100 lines). | ||
### Starting FFmpeg processing | ||
@@ -1129,2 +1143,4 @@ | ||
**Warning:** ffprobe may be called with an input stream, but in this case *it will consume data from the stream*, and this data will no longer be available for ffmpeg. Using both ffprobe and a transcoding command on the same input stream will most likely fail unless the stream is a live stream. Only do this if you know what you're doing. | ||
The returned object is the same that is returned by running the following command from your shell (depending on your ffmpeg version you may have to replace `-of` with `-print_format`) : | ||
@@ -1131,0 +1147,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
3
1499
19
3
706304
2
74
5173
+ Addedwhich@^1.1.1
+ Addedisexe@2.0.0(transitive)
+ Addedwhich@1.3.1(transitive)