aboutsummaryrefslogtreecommitdiff
path: root/src/api/utils/videoPreview/FrameIntervalPreview.js
blob: 96c6e3abaab1d0b58509f309b7997dbaef26a4bb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* 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;
};