1
0
mirror of https://github.com/musix-org/musix-oss synced 2024-12-23 16:13:18 +00:00
musix-oss/node_modules/ffmpeg/lib/ffmpeg.js
2020-03-03 22:30:50 +02:00

251 lines
9.1 KiB
JavaScript

var when = require('when')
, fs = require('fs');
var errors = require('./errors')
, utils = require('./utils')
, configs = require('./configs')
, video = require('./video');
var ffmpeg = function (/* inputFilepath, settings, callback */) {
/**
* Retrieve the list of the codec supported by the ffmpeg software
*/
var _ffmpegInfoConfiguration = function (settings) {
// New 'promise' instance
var deferred = when.defer();
// Instance the new arrays for the format
var format = { modules : new Array(), encode : new Array(), decode : new Array() };
// Make the call to retrieve information about the ffmpeg
utils.exec(['ffmpeg','-formats','2>&1'], settings, function (error, stdout, stderr) {
// Get the list of modules
var configuration = /configuration:(.*)/.exec(stdout);
// Check if exists the configuration
if (configuration) {
// Get the list of modules
var modules = configuration[1].match(/--enable-([a-zA-Z0-9\-]+)/g);
// Scan all modules
for (var indexModule in modules) {
// Add module to the list
format.modules.push(/--enable-([a-zA-Z0-9\-]+)/.exec(modules[indexModule])[1]);
}
}
// Get the codec list
var codecList = stdout.match(/ (DE|D|E) (.*) {1,} (.*)/g);
// Scan all codec
for (var i in codecList) {
// Get the match value
var match = / (DE|D|E) (.*) {1,} (.*)/.exec(codecList[i]);
// Check if match is valid
if (match) {
// Get the value from the match
var scope = match[1].replace(/\s/g,'')
, extension = match[2].replace(/\s/g,'');
// Check which scope is best suited
if (scope == 'D' || scope == 'DE')
format.decode.push(extension);
if (scope == 'E' || scope == 'DE')
format.encode.push(extension);
}
}
// Returns the list of supported formats
deferred.resolve(format);
});
// Return 'promise' instance
return deferred.promise;
}
/**
* Get the video info
*/
var _videoInfo = function (fileInput, settings) {
// New 'promise' instance
var deferred = when.defer();
// Make the call to retrieve information about the ffmpeg
utils.exec(['ffmpeg','-i',fileInput,'2>&1'], settings, function (error, stdout, stderr) {
// Perse output for retrieve the file info
var filename = /from \'(.*)\'/.exec(stdout) || []
, title = /(INAM|title)\s+:\s(.+)/.exec(stdout) || []
, artist = /artist\s+:\s(.+)/.exec(stdout) || []
, album = /album\s+:\s(.+)/.exec(stdout) || []
, track = /track\s+:\s(.+)/.exec(stdout) || []
, date = /date\s+:\s(.+)/.exec(stdout) || []
, is_synched = (/start: 0.000000/.exec(stdout) !== null)
, duration = /Duration: (([0-9]+):([0-9]{2}):([0-9]{2}).([0-9]+))/.exec(stdout) || []
, container = /Input #0, ([a-zA-Z0-9]+),/.exec(stdout) || []
, video_bitrate = /bitrate: ([0-9]+) kb\/s/.exec(stdout) || []
, video_stream = /Stream #([0-9\.]+)([a-z0-9\(\)\[\]]*)[:] Video/.exec(stdout) || []
, video_codec = /Video: ([\w]+)/.exec(stdout) || []
, resolution = /(([0-9]{2,5})x([0-9]{2,5}))/.exec(stdout) || []
, pixel = /[SP]AR ([0-9\:]+)/.exec(stdout) || []
, aspect = /DAR ([0-9\:]+)/.exec(stdout) || []
, fps = /([0-9\.]+) (fps|tb\(r\))/.exec(stdout) || []
, audio_stream = /Stream #([0-9\.]+)([a-z0-9\(\)\[\]]*)[:] Audio/.exec(stdout) || []
, audio_codec = /Audio: ([\w]+)/.exec(stdout) || []
, sample_rate = /([0-9]+) Hz/i.exec(stdout) || []
, channels = /Audio:.* (stereo|mono)/.exec(stdout) || []
, audio_bitrate = /Audio:.* ([0-9]+) kb\/s/.exec(stdout) || []
, rotate = /rotate[\s]+:[\s]([\d]{2,3})/.exec(stdout) || [];
// Build return object
var ret = {
filename : filename[1] || ''
, title : title[2] || ''
, artist : artist[1] || ''
, album : album[1] || ''
, track : track[1] || ''
, date : date[1] || ''
, synched : is_synched
, duration : {
raw : duration[1] || ''
, seconds : duration[1] ? utils.durationToSeconds(duration[1]) : 0
}
, video : {
container : container[1] || ''
, bitrate : (video_bitrate.length > 1) ? parseInt(video_bitrate[1], 10) : 0
, stream : video_stream.length > 1 ? parseFloat(video_stream[1]) : 0.0
, codec : video_codec[1] || ''
, resolution : {
w : resolution.length > 2 ? parseInt(resolution[2], 10) : 0
, h : resolution.length > 3 ? parseInt(resolution[3], 10) : 0
}
, resolutionSquare : {}
, aspect : {}
, rotate : rotate.length > 1 ? parseInt(rotate[1], 10) : 0
, fps : fps.length > 1 ? parseFloat(fps[1]) : 0.0
}
, audio : {
codec : audio_codec[1] || ''
, bitrate : audio_bitrate[1] || ''
, sample_rate : sample_rate.length > 1 ? parseInt(sample_rate[1], 10) : 0
, stream : audio_stream.length > 1 ? parseFloat(audio_stream[1]) : 0.0
, channels : {
raw : channels[1] || ''
, value : (channels.length > 0) ? ({ stereo : 2, mono : 1 }[channels[1]] || 0) : ''
}
}
};
// Check if exist aspect ratio
if (aspect.length > 0) {
var aspectValue = aspect[1].split(":");
ret.video.aspect.x = parseInt(aspectValue[0], 10);
ret.video.aspect.y = parseInt(aspectValue[1], 10);
ret.video.aspect.string = aspect[1];
ret.video.aspect.value = parseFloat((ret.video.aspect.x / ret.video.aspect.y));
} else {
// If exists horizontal resolution then calculate aspect ratio
if(ret.video.resolution.w > 0) {
var gcdValue = utils.gcd(ret.video.resolution.w, ret.video.resolution.h);
// Calculate aspect ratio
ret.video.aspect.x = ret.video.resolution.w / gcdValue;
ret.video.aspect.y = ret.video.resolution.h / gcdValue;
ret.video.aspect.string = ret.video.aspect.x + ':' + ret.video.aspect.y;
ret.video.aspect.value = parseFloat((ret.video.aspect.x / ret.video.aspect.y));
}
}
// Save pixel ratio for output size calculation
if (pixel.length > 0) {
ret.video.pixelString = pixel[1];
var pixelValue = pixel[1].split(":");
ret.video.pixel = parseFloat((parseInt(pixelValue[0], 10) / parseInt(pixelValue[1], 10)));
} else {
if (ret.video.resolution.w !== 0) {
ret.video.pixelString = '1:1';
ret.video.pixel = 1;
} else {
ret.video.pixelString = '';
ret.video.pixel = 0.0;
}
}
// Correct video.resolution when pixel aspectratio is not 1
if (ret.video.pixel !== 1 || ret.video.pixel !== 0) {
if( ret.video.pixel > 1 ) {
ret.video.resolutionSquare.w = parseInt(ret.video.resolution.w * ret.video.pixel, 10);
ret.video.resolutionSquare.h = ret.video.resolution.h;
} else {
ret.video.resolutionSquare.w = ret.video.resolution.w;
ret.video.resolutionSquare.h = parseInt(ret.video.resolution.h / ret.video.pixel, 10);
}
}
// Returns the list of supported formats
deferred.resolve(ret);
});
// Return 'promise' instance
return deferred.promise;
}
/**
* Get the info about ffmpeg's codec and about file
*/
var _getInformation = function (fileInput, settings) {
var deferreds = [];
// Add promise
deferreds.push(_ffmpegInfoConfiguration(settings));
deferreds.push(_videoInfo(fileInput, settings));
// Return defer
return when.all(deferreds);
}
var __constructor = function (args) {
// Check if exist at least one option
if (args.length == 0 || args[0] == undefined)
throw errors.renderError('empty_input_filepath');
// Check if first argument is a string
if (typeof args[0] != 'string')
throw errors.renderError('input_filepath_must_be_string');
// Get the input filepath
var inputFilepath = args[0];
// Check if file exist
if (!fs.existsSync(inputFilepath))
throw errors.renderError('fileinput_not_exist');
// New instance of the base configuration
var settings = new configs();
// Callback to call
var callback = null;
// Scan all arguments
for (var i = 1; i < args.length; i++) {
// Check the type of variable
switch (typeof args[i]) {
case 'object' :
utils.mergeObject(settings, args[i]);
break;
case 'function' :
callback = args[i];
break;
}
}
// Building the value for return value. Check if the callback is not a function. In this case will created a new instance of the deferred class
var deferred = typeof callback != 'function' ? when.defer() : { promise : null };
when(_getInformation(inputFilepath, settings), function (data) {
// Check if the callback is a function
if (typeof callback == 'function') {
// Call the callback function e return the new instance of 'video' class
callback(null, new video(inputFilepath, settings, data[0], data[1]));
} else {
// Positive response
deferred.resolve(new video(inputFilepath, settings, data[0], data[1]));
}
}, function (error) {
// Check if the callback is a function
if (typeof callback == 'function') {
// Call the callback function e return the error found
callback(error, null);
} else {
// Negative response
deferred.reject(error);
}
});
// Return a possible promise instance
return deferred.promise;
}
return __constructor.call(this, arguments);
};
module.exports = ffmpeg;