mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-07-02 21:44:56 +02:00
Remove fluent-ffmpeg dependency
This commit is contained in:
parent
8562b8d1b3
commit
b61ecefce4
35 changed files with 4405 additions and 50 deletions
665
server/libs/fluentFfmpeg/capabilities.js
Normal file
665
server/libs/fluentFfmpeg/capabilities.js
Normal file
|
@ -0,0 +1,665 @@
|
|||
/*jshint node:true*/
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var async = require('../async');
|
||||
var utils = require('./utils');
|
||||
|
||||
/*
|
||||
*! Capability helpers
|
||||
*/
|
||||
|
||||
var avCodecRegexp = /^\s*([D ])([E ])([VAS])([S ])([D ])([T ]) ([^ ]+) +(.*)$/;
|
||||
var ffCodecRegexp = /^\s*([D\.])([E\.])([VAS])([I\.])([L\.])([S\.]) ([^ ]+) +(.*)$/;
|
||||
var ffEncodersRegexp = /\(encoders:([^\)]+)\)/;
|
||||
var ffDecodersRegexp = /\(decoders:([^\)]+)\)/;
|
||||
var encodersRegexp = /^\s*([VAS\.])([F\.])([S\.])([X\.])([B\.])([D\.]) ([^ ]+) +(.*)$/;
|
||||
var formatRegexp = /^\s*([D ])([E ]) ([^ ]+) +(.*)$/;
|
||||
var lineBreakRegexp = /\r\n|\r|\n/;
|
||||
var filterRegexp = /^(?: [T\.][S\.][C\.] )?([^ ]+) +(AA?|VV?|\|)->(AA?|VV?|\|) +(.*)$/;
|
||||
|
||||
var cache = {};
|
||||
|
||||
module.exports = function (proto) {
|
||||
/**
|
||||
* Manually define the ffmpeg binary full path.
|
||||
*
|
||||
* @method FfmpegCommand#setFfmpegPath
|
||||
*
|
||||
* @param {String} ffmpegPath The full path to the ffmpeg binary.
|
||||
* @return FfmpegCommand
|
||||
*/
|
||||
proto.setFfmpegPath = function (ffmpegPath) {
|
||||
cache.ffmpegPath = ffmpegPath;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Manually define the ffprobe binary full path.
|
||||
*
|
||||
* @method FfmpegCommand#setFfprobePath
|
||||
*
|
||||
* @param {String} ffprobePath The full path to the ffprobe binary.
|
||||
* @return FfmpegCommand
|
||||
*/
|
||||
proto.setFfprobePath = function (ffprobePath) {
|
||||
cache.ffprobePath = ffprobePath;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Manually define the flvtool2/flvmeta binary full path.
|
||||
*
|
||||
* @method FfmpegCommand#setFlvtoolPath
|
||||
*
|
||||
* @param {String} flvtool The full path to the flvtool2 or flvmeta binary.
|
||||
* @return FfmpegCommand
|
||||
*/
|
||||
proto.setFlvtoolPath = function (flvtool) {
|
||||
cache.flvtoolPath = flvtool;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Forget executable paths
|
||||
*
|
||||
* (only used for testing purposes)
|
||||
*
|
||||
* @method FfmpegCommand#_forgetPaths
|
||||
* @private
|
||||
*/
|
||||
proto._forgetPaths = function () {
|
||||
delete cache.ffmpegPath;
|
||||
delete cache.ffprobePath;
|
||||
delete cache.flvtoolPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check for ffmpeg availability
|
||||
*
|
||||
* If the FFMPEG_PATH environment variable is set, try to use it.
|
||||
* If it is unset or incorrect, try to find ffmpeg in the PATH instead.
|
||||
*
|
||||
* @method FfmpegCommand#_getFfmpegPath
|
||||
* @param {Function} callback callback with signature (err, path)
|
||||
* @private
|
||||
*/
|
||||
proto._getFfmpegPath = function (callback) {
|
||||
if ('ffmpegPath' in cache) {
|
||||
return callback(null, cache.ffmpegPath);
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
// Try FFMPEG_PATH
|
||||
function (cb) {
|
||||
if (process.env.FFMPEG_PATH) {
|
||||
fs.exists(process.env.FFMPEG_PATH, function (exists) {
|
||||
if (exists) {
|
||||
cb(null, process.env.FFMPEG_PATH);
|
||||
} else {
|
||||
cb(null, '');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cb(null, '');
|
||||
}
|
||||
},
|
||||
|
||||
// Search in the PATH
|
||||
function (ffmpeg, cb) {
|
||||
if (ffmpeg.length) {
|
||||
return cb(null, ffmpeg);
|
||||
}
|
||||
|
||||
utils.which('ffmpeg', function (err, ffmpeg) {
|
||||
cb(err, ffmpeg);
|
||||
});
|
||||
}
|
||||
], function (err, ffmpeg) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, cache.ffmpegPath = (ffmpeg || ''));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check for ffprobe availability
|
||||
*
|
||||
* If the FFPROBE_PATH environment variable is set, try to use it.
|
||||
* If it is unset or incorrect, try to find ffprobe in the PATH instead.
|
||||
* If this still fails, try to find ffprobe in the same directory as ffmpeg.
|
||||
*
|
||||
* @method FfmpegCommand#_getFfprobePath
|
||||
* @param {Function} callback callback with signature (err, path)
|
||||
* @private
|
||||
*/
|
||||
proto._getFfprobePath = function (callback) {
|
||||
var self = this;
|
||||
|
||||
if ('ffprobePath' in cache) {
|
||||
return callback(null, cache.ffprobePath);
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
// Try FFPROBE_PATH
|
||||
function (cb) {
|
||||
if (process.env.FFPROBE_PATH) {
|
||||
fs.exists(process.env.FFPROBE_PATH, function (exists) {
|
||||
cb(null, exists ? process.env.FFPROBE_PATH : '');
|
||||
});
|
||||
} else {
|
||||
cb(null, '');
|
||||
}
|
||||
},
|
||||
|
||||
// Search in the PATH
|
||||
function (ffprobe, cb) {
|
||||
if (ffprobe.length) {
|
||||
return cb(null, ffprobe);
|
||||
}
|
||||
|
||||
utils.which('ffprobe', function (err, ffprobe) {
|
||||
cb(err, ffprobe);
|
||||
});
|
||||
},
|
||||
|
||||
// Search in the same directory as ffmpeg
|
||||
function (ffprobe, cb) {
|
||||
if (ffprobe.length) {
|
||||
return cb(null, ffprobe);
|
||||
}
|
||||
|
||||
self._getFfmpegPath(function (err, ffmpeg) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
} else if (ffmpeg.length) {
|
||||
var name = utils.isWindows ? 'ffprobe.exe' : 'ffprobe';
|
||||
var ffprobe = path.join(path.dirname(ffmpeg), name);
|
||||
fs.exists(ffprobe, function (exists) {
|
||||
cb(null, exists ? ffprobe : '');
|
||||
});
|
||||
} else {
|
||||
cb(null, '');
|
||||
}
|
||||
});
|
||||
}
|
||||
], function (err, ffprobe) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, cache.ffprobePath = (ffprobe || ''));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check for flvtool2/flvmeta availability
|
||||
*
|
||||
* If the FLVTOOL2_PATH or FLVMETA_PATH environment variable are set, try to use them.
|
||||
* If both are either unset or incorrect, try to find flvtool2 or flvmeta in the PATH instead.
|
||||
*
|
||||
* @method FfmpegCommand#_getFlvtoolPath
|
||||
* @param {Function} callback callback with signature (err, path)
|
||||
* @private
|
||||
*/
|
||||
proto._getFlvtoolPath = function (callback) {
|
||||
if ('flvtoolPath' in cache) {
|
||||
return callback(null, cache.flvtoolPath);
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
// Try FLVMETA_PATH
|
||||
function (cb) {
|
||||
if (process.env.FLVMETA_PATH) {
|
||||
fs.exists(process.env.FLVMETA_PATH, function (exists) {
|
||||
cb(null, exists ? process.env.FLVMETA_PATH : '');
|
||||
});
|
||||
} else {
|
||||
cb(null, '');
|
||||
}
|
||||
},
|
||||
|
||||
// Try FLVTOOL2_PATH
|
||||
function (flvtool, cb) {
|
||||
if (flvtool.length) {
|
||||
return cb(null, flvtool);
|
||||
}
|
||||
|
||||
if (process.env.FLVTOOL2_PATH) {
|
||||
fs.exists(process.env.FLVTOOL2_PATH, function (exists) {
|
||||
cb(null, exists ? process.env.FLVTOOL2_PATH : '');
|
||||
});
|
||||
} else {
|
||||
cb(null, '');
|
||||
}
|
||||
},
|
||||
|
||||
// Search for flvmeta in the PATH
|
||||
function (flvtool, cb) {
|
||||
if (flvtool.length) {
|
||||
return cb(null, flvtool);
|
||||
}
|
||||
|
||||
utils.which('flvmeta', function (err, flvmeta) {
|
||||
cb(err, flvmeta);
|
||||
});
|
||||
},
|
||||
|
||||
// Search for flvtool2 in the PATH
|
||||
function (flvtool, cb) {
|
||||
if (flvtool.length) {
|
||||
return cb(null, flvtool);
|
||||
}
|
||||
|
||||
utils.which('flvtool2', function (err, flvtool2) {
|
||||
cb(err, flvtool2);
|
||||
});
|
||||
},
|
||||
], function (err, flvtool) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, cache.flvtoolPath = (flvtool || ''));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A callback passed to {@link FfmpegCommand#availableFilters}.
|
||||
*
|
||||
* @callback FfmpegCommand~filterCallback
|
||||
* @param {Error|null} err error object or null if no error happened
|
||||
* @param {Object} filters filter object with filter names as keys and the following
|
||||
* properties for each filter:
|
||||
* @param {String} filters.description filter description
|
||||
* @param {String} filters.input input type, one of 'audio', 'video' and 'none'
|
||||
* @param {Boolean} filters.multipleInputs whether the filter supports multiple inputs
|
||||
* @param {String} filters.output output type, one of 'audio', 'video' and 'none'
|
||||
* @param {Boolean} filters.multipleOutputs whether the filter supports multiple outputs
|
||||
*/
|
||||
|
||||
/**
|
||||
* Query ffmpeg for available filters
|
||||
*
|
||||
* @method FfmpegCommand#availableFilters
|
||||
* @category Capabilities
|
||||
* @aliases getAvailableFilters
|
||||
*
|
||||
* @param {FfmpegCommand~filterCallback} callback callback function
|
||||
*/
|
||||
proto.availableFilters =
|
||||
proto.getAvailableFilters = function (callback) {
|
||||
if ('filters' in cache) {
|
||||
return callback(null, cache.filters);
|
||||
}
|
||||
|
||||
this._spawnFfmpeg(['-filters'], { captureStdout: true, stdoutLines: 0 }, function (err, stdoutRing) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var stdout = stdoutRing.get();
|
||||
var lines = stdout.split('\n');
|
||||
var data = {};
|
||||
var types = { A: 'audio', V: 'video', '|': 'none' };
|
||||
|
||||
lines.forEach(function (line) {
|
||||
var match = line.match(filterRegexp);
|
||||
if (match) {
|
||||
data[match[1]] = {
|
||||
description: match[4],
|
||||
input: types[match[2].charAt(0)],
|
||||
multipleInputs: match[2].length > 1,
|
||||
output: types[match[3].charAt(0)],
|
||||
multipleOutputs: match[3].length > 1
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, cache.filters = data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A callback passed to {@link FfmpegCommand#availableCodecs}.
|
||||
*
|
||||
* @callback FfmpegCommand~codecCallback
|
||||
* @param {Error|null} err error object or null if no error happened
|
||||
* @param {Object} codecs codec object with codec names as keys and the following
|
||||
* properties for each codec (more properties may be available depending on the
|
||||
* ffmpeg version used):
|
||||
* @param {String} codecs.description codec description
|
||||
* @param {Boolean} codecs.canDecode whether the codec is able to decode streams
|
||||
* @param {Boolean} codecs.canEncode whether the codec is able to encode streams
|
||||
*/
|
||||
|
||||
/**
|
||||
* Query ffmpeg for available codecs
|
||||
*
|
||||
* @method FfmpegCommand#availableCodecs
|
||||
* @category Capabilities
|
||||
* @aliases getAvailableCodecs
|
||||
*
|
||||
* @param {FfmpegCommand~codecCallback} callback callback function
|
||||
*/
|
||||
proto.availableCodecs =
|
||||
proto.getAvailableCodecs = function (callback) {
|
||||
if ('codecs' in cache) {
|
||||
return callback(null, cache.codecs);
|
||||
}
|
||||
|
||||
this._spawnFfmpeg(['-codecs'], { captureStdout: true, stdoutLines: 0 }, function (err, stdoutRing) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var stdout = stdoutRing.get();
|
||||
var lines = stdout.split(lineBreakRegexp);
|
||||
var data = {};
|
||||
|
||||
lines.forEach(function (line) {
|
||||
var match = line.match(avCodecRegexp);
|
||||
if (match && match[7] !== '=') {
|
||||
data[match[7]] = {
|
||||
type: { 'V': 'video', 'A': 'audio', 'S': 'subtitle' }[match[3]],
|
||||
description: match[8],
|
||||
canDecode: match[1] === 'D',
|
||||
canEncode: match[2] === 'E',
|
||||
drawHorizBand: match[4] === 'S',
|
||||
directRendering: match[5] === 'D',
|
||||
weirdFrameTruncation: match[6] === 'T'
|
||||
};
|
||||
}
|
||||
|
||||
match = line.match(ffCodecRegexp);
|
||||
if (match && match[7] !== '=') {
|
||||
var codecData = data[match[7]] = {
|
||||
type: { 'V': 'video', 'A': 'audio', 'S': 'subtitle' }[match[3]],
|
||||
description: match[8],
|
||||
canDecode: match[1] === 'D',
|
||||
canEncode: match[2] === 'E',
|
||||
intraFrameOnly: match[4] === 'I',
|
||||
isLossy: match[5] === 'L',
|
||||
isLossless: match[6] === 'S'
|
||||
};
|
||||
|
||||
var encoders = codecData.description.match(ffEncodersRegexp);
|
||||
encoders = encoders ? encoders[1].trim().split(' ') : [];
|
||||
|
||||
var decoders = codecData.description.match(ffDecodersRegexp);
|
||||
decoders = decoders ? decoders[1].trim().split(' ') : [];
|
||||
|
||||
if (encoders.length || decoders.length) {
|
||||
var coderData = {};
|
||||
utils.copy(codecData, coderData);
|
||||
delete coderData.canEncode;
|
||||
delete coderData.canDecode;
|
||||
|
||||
encoders.forEach(function (name) {
|
||||
data[name] = {};
|
||||
utils.copy(coderData, data[name]);
|
||||
data[name].canEncode = true;
|
||||
});
|
||||
|
||||
decoders.forEach(function (name) {
|
||||
if (name in data) {
|
||||
data[name].canDecode = true;
|
||||
} else {
|
||||
data[name] = {};
|
||||
utils.copy(coderData, data[name]);
|
||||
data[name].canDecode = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, cache.codecs = data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A callback passed to {@link FfmpegCommand#availableEncoders}.
|
||||
*
|
||||
* @callback FfmpegCommand~encodersCallback
|
||||
* @param {Error|null} err error object or null if no error happened
|
||||
* @param {Object} encoders encoders object with encoder names as keys and the following
|
||||
* properties for each encoder:
|
||||
* @param {String} encoders.description codec description
|
||||
* @param {Boolean} encoders.type "audio", "video" or "subtitle"
|
||||
* @param {Boolean} encoders.frameMT whether the encoder is able to do frame-level multithreading
|
||||
* @param {Boolean} encoders.sliceMT whether the encoder is able to do slice-level multithreading
|
||||
* @param {Boolean} encoders.experimental whether the encoder is experimental
|
||||
* @param {Boolean} encoders.drawHorizBand whether the encoder supports draw_horiz_band
|
||||
* @param {Boolean} encoders.directRendering whether the encoder supports direct encoding method 1
|
||||
*/
|
||||
|
||||
/**
|
||||
* Query ffmpeg for available encoders
|
||||
*
|
||||
* @method FfmpegCommand#availableEncoders
|
||||
* @category Capabilities
|
||||
* @aliases getAvailableEncoders
|
||||
*
|
||||
* @param {FfmpegCommand~encodersCallback} callback callback function
|
||||
*/
|
||||
proto.availableEncoders =
|
||||
proto.getAvailableEncoders = function (callback) {
|
||||
if ('encoders' in cache) {
|
||||
return callback(null, cache.encoders);
|
||||
}
|
||||
|
||||
this._spawnFfmpeg(['-encoders'], { captureStdout: true, stdoutLines: 0 }, function (err, stdoutRing) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
var stdout = stdoutRing.get();
|
||||
var lines = stdout.split(lineBreakRegexp);
|
||||
var data = {};
|
||||
|
||||
lines.forEach(function (line) {
|
||||
var match = line.match(encodersRegexp);
|
||||
if (match && match[7] !== '=') {
|
||||
data[match[7]] = {
|
||||
type: { 'V': 'video', 'A': 'audio', 'S': 'subtitle' }[match[1]],
|
||||
description: match[8],
|
||||
frameMT: match[2] === 'F',
|
||||
sliceMT: match[3] === 'S',
|
||||
experimental: match[4] === 'X',
|
||||
drawHorizBand: match[5] === 'B',
|
||||
directRendering: match[6] === 'D'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, cache.encoders = data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A callback passed to {@link FfmpegCommand#availableFormats}.
|
||||
*
|
||||
* @callback FfmpegCommand~formatCallback
|
||||
* @param {Error|null} err error object or null if no error happened
|
||||
* @param {Object} formats format object with format names as keys and the following
|
||||
* properties for each format:
|
||||
* @param {String} formats.description format description
|
||||
* @param {Boolean} formats.canDemux whether the format is able to demux streams from an input file
|
||||
* @param {Boolean} formats.canMux whether the format is able to mux streams into an output file
|
||||
*/
|
||||
|
||||
/**
|
||||
* Query ffmpeg for available formats
|
||||
*
|
||||
* @method FfmpegCommand#availableFormats
|
||||
* @category Capabilities
|
||||
* @aliases getAvailableFormats
|
||||
*
|
||||
* @param {FfmpegCommand~formatCallback} callback callback function
|
||||
*/
|
||||
proto.availableFormats =
|
||||
proto.getAvailableFormats = function (callback) {
|
||||
if ('formats' in cache) {
|
||||
return callback(null, cache.formats);
|
||||
}
|
||||
|
||||
// Run ffmpeg -formats
|
||||
this._spawnFfmpeg(['-formats'], { captureStdout: true, stdoutLines: 0 }, function (err, stdoutRing) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Parse output
|
||||
var stdout = stdoutRing.get();
|
||||
var lines = stdout.split(lineBreakRegexp);
|
||||
var data = {};
|
||||
|
||||
lines.forEach(function (line) {
|
||||
var match = line.match(formatRegexp);
|
||||
if (match) {
|
||||
match[3].split(',').forEach(function (format) {
|
||||
if (!(format in data)) {
|
||||
data[format] = {
|
||||
description: match[4],
|
||||
canDemux: false,
|
||||
canMux: false
|
||||
};
|
||||
}
|
||||
|
||||
if (match[1] === 'D') {
|
||||
data[format].canDemux = true;
|
||||
}
|
||||
if (match[2] === 'E') {
|
||||
data[format].canMux = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
callback(null, cache.formats = data);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check capabilities before executing a command
|
||||
*
|
||||
* Checks whether all used codecs and formats are indeed available
|
||||
*
|
||||
* @method FfmpegCommand#_checkCapabilities
|
||||
* @param {Function} callback callback with signature (err)
|
||||
* @private
|
||||
*/
|
||||
proto._checkCapabilities = function (callback) {
|
||||
var self = this;
|
||||
async.waterfall([
|
||||
// Get available formats
|
||||
function (cb) {
|
||||
self.availableFormats(cb);
|
||||
},
|
||||
|
||||
// Check whether specified formats are available
|
||||
function (formats, cb) {
|
||||
var unavailable;
|
||||
|
||||
// Output format(s)
|
||||
unavailable = self._outputs
|
||||
.reduce(function (fmts, output) {
|
||||
var format = output.options.find('-f', 1);
|
||||
if (format) {
|
||||
if (!(format[0] in formats) || !(formats[format[0]].canMux)) {
|
||||
fmts.push(format);
|
||||
}
|
||||
}
|
||||
|
||||
return fmts;
|
||||
}, []);
|
||||
|
||||
if (unavailable.length === 1) {
|
||||
return cb(new Error('Output format ' + unavailable[0] + ' is not available'));
|
||||
} else if (unavailable.length > 1) {
|
||||
return cb(new Error('Output formats ' + unavailable.join(', ') + ' are not available'));
|
||||
}
|
||||
|
||||
// Input format(s)
|
||||
unavailable = self._inputs
|
||||
.reduce(function (fmts, input) {
|
||||
var format = input.options.find('-f', 1);
|
||||
if (format) {
|
||||
if (!(format[0] in formats) || !(formats[format[0]].canDemux)) {
|
||||
fmts.push(format[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return fmts;
|
||||
}, []);
|
||||
|
||||
if (unavailable.length === 1) {
|
||||
return cb(new Error('Input format ' + unavailable[0] + ' is not available'));
|
||||
} else if (unavailable.length > 1) {
|
||||
return cb(new Error('Input formats ' + unavailable.join(', ') + ' are not available'));
|
||||
}
|
||||
|
||||
cb();
|
||||
},
|
||||
|
||||
// Get available codecs
|
||||
function (cb) {
|
||||
self.availableEncoders(cb);
|
||||
},
|
||||
|
||||
// Check whether specified codecs are available and add strict experimental options if needed
|
||||
function (encoders, cb) {
|
||||
var unavailable;
|
||||
|
||||
// Audio codec(s)
|
||||
unavailable = self._outputs.reduce(function (cdcs, output) {
|
||||
var acodec = output.audio.find('-acodec', 1);
|
||||
if (acodec && acodec[0] !== 'copy') {
|
||||
if (!(acodec[0] in encoders) || encoders[acodec[0]].type !== 'audio') {
|
||||
cdcs.push(acodec[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return cdcs;
|
||||
}, []);
|
||||
|
||||
if (unavailable.length === 1) {
|
||||
return cb(new Error('Audio codec ' + unavailable[0] + ' is not available'));
|
||||
} else if (unavailable.length > 1) {
|
||||
return cb(new Error('Audio codecs ' + unavailable.join(', ') + ' are not available'));
|
||||
}
|
||||
|
||||
// Video codec(s)
|
||||
unavailable = self._outputs.reduce(function (cdcs, output) {
|
||||
var vcodec = output.video.find('-vcodec', 1);
|
||||
if (vcodec && vcodec[0] !== 'copy') {
|
||||
if (!(vcodec[0] in encoders) || encoders[vcodec[0]].type !== 'video') {
|
||||
cdcs.push(vcodec[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return cdcs;
|
||||
}, []);
|
||||
|
||||
if (unavailable.length === 1) {
|
||||
return cb(new Error('Video codec ' + unavailable[0] + ' is not available'));
|
||||
} else if (unavailable.length > 1) {
|
||||
return cb(new Error('Video codecs ' + unavailable.join(', ') + ' are not available'));
|
||||
}
|
||||
|
||||
cb();
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue