mpd-generator
Version:
MPD manifest generator for video streaming using Dashjs
174 lines (155 loc) • 6.44 kB
JavaScript
const {spawn} = require('child_process');
const ffprobe = require('ffprobe'),
ffprobeStatic = require('ffprobe-static');
/**
* Get the necessary information from a video
* @param {String} filePath
* @return {Object} { width: String, height: String, duration: String,
* bit_rate: String, size: String, videoPositions: String,
* audios: String, subs: String, display_aspect_ratio: String,
* sample_aspect_ratio: String }
*/
async function shInfo(filePath) {
try {
const data = await ffprobe(filePath, {path: ffprobeStatic.path});
const videoStreams = data.streams.filter(stream => stream.codec_type === 'video');
const audioStreams = data.streams.filter(stream => stream.codec_type === 'audio');
const subtitleStreams = data.streams.filter(stream => stream.codec_type === 'subtitle');
const videoPositions = videoStreams.map(stream => stream.index);
const audios = audioStreams.map(stream => ({
index: stream.index,
language: stream.tags?.language,
}));
const subs = subtitleStreams.map(stream => ({
index: stream.index,
language: stream.tags?.language,
}));
return {
width: videoStreams[0]?.width,
height: videoStreams[0]?.height,
duration: data.format?.duration,
bit_rate: data.format?.bit_rate,
size: data.format?.size,
videoPositions,
audios,
subs,
display_aspect_ratio: videoStreams[0]?.display_aspect_ratio,
sample_aspect_ratio: videoStreams[0]?.sample_aspect_ratio,
videoLanguage: videoStreams[0]?.tags?.language,
};
} catch (error) {
console.error(`Error in shInfo: ${error}`);
return null;
}
}
/**
* Ejecuta un comando en una terminal y devuelve una promesa que se resuelve
* cuando el proceso de terminal ha terminado.
*
* @param {string} cmd - El comando que se va a ejecutar.
* @param {string[]} [options] - Las opciones que se pasan al comando.
* @returns {Promise<number>} - Una promesa que se resuelve con el código de salida del proceso.
*/
function shSpawn(cmd, options) {
return new Promise((resolve, reject) => {
const process = spawn(cmd, options);
// Maneja la salida estándar del proceso.
process.stdout.on('data', data => {
console.log(`${data}`);
});
// Maneja la salida de error del proceso.
process.stderr.on('data', data => {
console.log(`${data}`);
});
// Maneja los errores del proceso.
process.on('error', err => {
reject(err);
});
// Maneja el cierre del proceso.
process.on('close', code => {
console.log(`Process exited with code ${code}`);
resolve(code);
});
});
};
/**
* Calcula la altura de una imagen en función de su anchura y su relación de aspecto.
* Si la altura resultante es impar o la anchura dada es impar, se llama recursivamente
* con una anchura incrementada en 1 hasta que se obtenga una altura par y una anchura par.
* @param {number} widthO - Anchura original de la imagen
* @param {number} heightO - Altura original de la imagen
* @param {number} width - Anchura deseada de la imagen
* @returns {Object} - Objeto con las propiedades de anchura y altura calculadas
*/
function calculateAspectRatioHeight(widthO, heightO, width) {
// Calcula la relación de aspecto de la imagen original
let aspectRatio = widthO / heightO;
// Calcula la altura de la imagen en función de la anchura y la relación de aspecto
let height = Math.floor(width / aspectRatio);
// Si la altura calculada es impar o la anchura dada es impar, llama a la función recursivamente
// con una anchura incrementada en 1 hasta que se obtenga una altura par y una anchura par
if (height % 2 !== 0 || width % 2 !== 0) {
return calculateAspectRatioHeight(widthO, heightO, width + 1);
}
// Devuelve un objeto con las propiedades de anchura y altura calculadas
return {
width: width,
height: height
};
}
/**
* Calcula la resolución y las tasas de bits de un video dado su ancho, alto, resolución y calidad
* @param {number} width - Ancho del video
* @param {number} height - Alto del video
* @param {string} resolution - Resolución del video ('144p', '240p', '360p', '480p', '720p' o '1080p')
* @param {Array<string>} qualities - Calidades del video ('low', 'medium' o 'high')
* @returns {Object} - Objeto con las propiedades de la resolución y las tasas de bits
*/
function getResolution(width, height, resolution, qualities) {
/**
* Un objeto que contiene las resoluciones y tasas de bits correspondientes
* para cada resolución
*/
const resolutions = {
"144p": {width: 256, height: 144, bitrates: {low: 80, medium: 90, high: 100}},
"240p": {width: 426, height: 240, bitrates: {low: 300, medium: 400, high: 700}},
"360p": {width: 640, height: 360, bitrates: {low: 400, medium: 750, high: 1000}},
"480p": {width: 854, height: 480, bitrates: {low: 500, medium: 1000, high: 2000}},
"720p": {width: 1280, height: 720, bitrates: {low: 1500, medium: 2500, high: 4000}},
"1080p": {width: 1920, height: 1080, bitrates: {low: 3000, medium: 4500, high: 6000}}
};
/**
* El objeto de resultado que se devolverá al finalizar el cálculo
*/
const result = {
width: 0,
height: 0,
bitrate: [],
fps: 30
};
/**
* Si la resolución dada está en el objeto de resoluciones,
* extrae la información relevante y guarda la resolución y las tasas de bits
*/
if (resolutions.hasOwnProperty(resolution)) {
const {width: resWidth, height: resHeight, bitrates} = resolutions[resolution];
const res = calculateAspectRatioHeight(width, height, resWidth);
result.width = res.width;
result.height = res.height;
qualities.forEach(q => {
if (bitrates.hasOwnProperty(q)) {
result.bitrate.push({
value: bitrates[q],
type: q
});
}
});
}
return result;
}
module.exports = {
shInfo,
shSpawn,
calculateAspectRatioHeight,
getResolution
};