dragonflight/services/worker/src/ffmpeg/executor.js

85 lines
1.9 KiB
JavaScript
Raw Normal View History

import { execFile } from 'child_process';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);
export const runFFmpeg = async (args, options = {}) => {
const { stdio = 'pipe', ...otherOptions } = options;
try {
const { stdout, stderr } = await execFileAsync('ffmpeg', args, {
stdio,
...otherOptions,
});
return { stdout, stderr };
} catch (error) {
throw new Error(`FFmpeg error: ${error.message}\nStderr: ${error.stderr}`);
}
};
export const getMediaDuration = async (inputPath) => {
const args = [
'-v', 'error',
'-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1:noinvalidchars=1',
inputPath,
];
const { stdout } = await runFFmpeg(args);
return parseFloat(stdout.trim());
};
export const extractFrameAtTime = async (inputPath, outputPath, timeCode) => {
const args = [
'-i', inputPath,
'-ss', timeCode,
'-vframes', '1',
'-q:v', '2',
outputPath,
];
await runFFmpeg(args);
};
export const transcodeVideo = async (inputPath, outputPath, options = {}) => {
const {
videoCodec = 'libx264',
videoPreset = 'fast',
videoBitrate = '10M',
audioCodec = 'aac',
audioBitrate = '192k',
} = options;
const args = [
'-i', inputPath,
'-c:v', videoCodec,
'-preset', videoPreset,
'-b:v', videoBitrate,
'-c:a', audioCodec,
'-b:a', audioBitrate,
'-movflags', '+faststart',
outputPath,
];
await runFFmpeg(args);
};
export const trimSegment = async (inputPath, outputPath, inPoint, outPoint) => {
const args = [
'-i', inputPath,
'-ss', inPoint,
'-to', outPoint,
'-c', 'copy',
outputPath,
];
await runFFmpeg(args);
};
export const concatSegments = async (segmentListFile, outputPath) => {
const args = [
'-f', 'concat',
'-safe', '0',
'-i', segmentListFile,
'-c', 'copy',
outputPath,
];
await runFFmpeg(args);
};