v3.0.0/src/api/utils/videoPreview/FrameIntervalPreview.js

74 lines
1.8 KiB
JavaScript

/* eslint-disable no-bitwise */
const ffmpeg = require('fluent-ffmpeg');
const probe = require('ffmpeg-probe');
const noop = () => {};
module.exports = async opts => {
const {
log = noop,
// general output options
quality = 2,
width,
height,
input,
output,
numFrames,
numFramesPercent = 0.05
} = opts;
const info = await probe(input);
// const numFramesTotal = parseInt(info.streams[0].nb_frames, 10);
const { avg_frame_rate: avgFrameRate, duration } = info.streams[0];
const [frames, time] = avgFrameRate.split('/').map(e => parseInt(e, 10));
const numFramesTotal = (frames / time) * duration;
let numFramesToCapture = numFrames || numFramesPercent * numFramesTotal;
numFramesToCapture = Math.max(1, Math.min(numFramesTotal, numFramesToCapture)) | 0;
const nthFrame = (numFramesTotal / numFramesToCapture) | 0;
const result = {
output,
numFrames: numFramesToCapture
};
await new Promise((resolve, reject) => {
let scale = null;
if (width && height) {
result.width = width | 0;
result.height = height | 0;
scale = `scale=${width}:${height}`;
} else if (width) {
result.width = width | 0;
result.height = ((info.height * width) / info.width) | 0;
scale = `scale=${width}:-1`;
} else if (height) {
result.height = height | 0;
result.width = ((info.width * height) / info.height) | 0;
scale = `scale=-1:${height}`;
} else {
result.width = info.width;
result.height = info.height;
}
const filter = [`select=not(mod(n\\,${nthFrame}))`, scale].filter(Boolean).join(',');
ffmpeg(input)
.outputOptions(['-vsync', 'vfr'])
.outputOptions(['-q:v', quality, '-vf', filter])
.noAudio()
.outputFormat('webm')
.output(output)
.on('start', cmd => log && log({ cmd }))
.on('end', () => resolve())
.on('error', err => reject(err))
.run();
});
return result;
};