Split transcode and ffmpeg into separate modules

This commit is contained in:
Alex Gleason 2025-02-27 21:59:19 -06:00
parent bb13a8dc71
commit 6ce64822e1
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
3 changed files with 25 additions and 43 deletions

View file

@ -4,13 +4,9 @@ Deno.test('ffmpeg', async () => {
await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url)); await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url));
const output = ffmpeg(file.readable, { const output = ffmpeg(file.readable, {
'i': 'pipe:0',
'c:v': 'libx264', 'c:v': 'libx264',
'preset': 'veryfast', 'preset': 'veryfast',
'loglevel': 'fatal', 'loglevel': 'fatal',
'crf': '23',
'c:a': 'aac',
'b:a': '128k',
'movflags': 'frag_keyframe+empty_moov', 'movflags': 'frag_keyframe+empty_moov',
'f': 'mp4', 'f': 'mp4',
}); });

View file

@ -1,31 +1,33 @@
export type FFmpegFlags = { export type FFmpegFlags = {
'i': string; 'c:v'?: string;
'c:v': string; 'preset'?: string;
'preset': string; 'loglevel'?: string;
'loglevel': string; 'crf'?: string;
'crf': string; 'c:a'?: string;
'c:a': string; 'b:a'?: string;
'b:a': string; 'movflags'?: string;
'movflags': string; 'f'?: string;
'f': string; [key: string]: string | undefined;
[key: string]: string;
}; };
export function ffmpeg(input: BodyInit, flags: FFmpegFlags): ReadableStream<Uint8Array> { export function ffmpeg(input: ReadableStream<Uint8Array>, flags: FFmpegFlags): ReadableStream<Uint8Array> {
const command = new Deno.Command('ffmpeg', { const args = ['-i', 'pipe:0']; // Input from stdin
args: [
...Object.entries(flags).flatMap(([k, v]) => [`-${k}`, v]), for (const [key, value] of Object.entries(flags)) {
'pipe:1', // Output to stdout if (typeof value === 'string') {
], args.push(`-${key}`, value);
stdin: 'piped', }
stdout: 'piped', }
});
args.push('pipe:1'); // Output to stdout
// Spawn the FFmpeg process // Spawn the FFmpeg process
const command = new Deno.Command('ffmpeg', { args, stdin: 'piped', stdout: 'piped' });
const child = command.spawn(); const child = command.spawn();
// Pipe the input stream into FFmpeg stdin and ensure completion // Pipe the input stream into FFmpeg stdin and ensure completion
new Response(input).body!.pipeTo(child.stdin); input.pipeTo(child.stdin);
// Return the FFmpeg stdout stream
return child.stdout; return child.stdout;
} }

View file

@ -1,6 +1,7 @@
import { ffmpeg } from './ffmpeg.ts';
export function transcodeVideo(input: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> { export function transcodeVideo(input: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {
const opts = { return ffmpeg(input, {
'i': 'pipe:0', // Read input from stdin
'c:v': 'libx264', // Convert to H.264 'c:v': 'libx264', // Convert to H.264
'preset': 'veryfast', // Encoding speed 'preset': 'veryfast', // Encoding speed
'loglevel': 'fatal', // Suppress logs 'loglevel': 'fatal', // Suppress logs
@ -9,22 +10,5 @@ export function transcodeVideo(input: ReadableStream<Uint8Array>): ReadableStrea
'b:a': '128k', // Audio bitrate 'b:a': '128k', // Audio bitrate
'movflags': 'frag_keyframe+empty_moov', // Ensures MP4 streaming compatibility 'movflags': 'frag_keyframe+empty_moov', // Ensures MP4 streaming compatibility
'f': 'mp4', // Force MP4 format 'f': 'mp4', // Force MP4 format
};
const command = new Deno.Command('ffmpeg', {
args: [
...Object.entries(opts).flatMap(([k, v]) => [`-${k}`, v]),
'pipe:1', // Output to stdout
],
stdin: 'piped',
stdout: 'piped',
}); });
// Spawn the FFmpeg process
const child = command.spawn();
// Pipe the input stream into FFmpeg stdin and ensure completion
input.pipeTo(child.stdin);
return child.stdout;
} }