From 2533d2f4697e678a5d34898e72603256c16de739 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Fri, 28 Feb 2025 20:52:11 -0600 Subject: [PATCH] Support readable in extractVideoFrame --- packages/transcode/ffmpeg.ts | 8 +++++++- packages/transcode/frame.test.ts | 5 +++-- packages/transcode/frame.ts | 7 +++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/transcode/ffmpeg.ts b/packages/transcode/ffmpeg.ts index d52b0c9d..4a53f760 100644 --- a/packages/transcode/ffmpeg.ts +++ b/packages/transcode/ffmpeg.ts @@ -38,7 +38,13 @@ export function ffmpeg(input: URL | ReadableStream, flags: FFmpegFla // Pipe the input stream into FFmpeg stdin and ensure completion if (input instanceof ReadableStream) { - input.pipeTo(child.stdin); + input.pipeTo(child.stdin).catch((e: unknown) => { + if (e instanceof Error && e.name === 'BrokenPipe') { + // Ignore. ffprobe closes the pipe once it has read the metadata. + } else { + throw e; + } + }); } // Return the FFmpeg stdout stream diff --git a/packages/transcode/frame.test.ts b/packages/transcode/frame.test.ts index 93894df2..8fba6e94 100644 --- a/packages/transcode/frame.test.ts +++ b/packages/transcode/frame.test.ts @@ -1,8 +1,9 @@ import { extractVideoFrame } from './frame.ts'; Deno.test('extractVideoFrame', async () => { - const uri = new URL('./buckbunny.mp4', import.meta.url); - const result = await extractVideoFrame(uri); + await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url)); + + const result = await extractVideoFrame(file.readable); await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true }); await Deno.writeFile(new URL('./tmp/buckbunny-poster.jpg', import.meta.url), result); diff --git a/packages/transcode/frame.ts b/packages/transcode/frame.ts index 68e30e5e..689677ff 100644 --- a/packages/transcode/frame.ts +++ b/packages/transcode/frame.ts @@ -1,7 +1,10 @@ import { ffmpeg } from './ffmpeg.ts'; -export function extractVideoFrame(file: URL, ss: string = '00:00:01'): Promise { - const output = ffmpeg(file, { +export function extractVideoFrame( + input: URL | ReadableStream, + ss: string = '00:00:01', +): Promise { + const output = ffmpeg(input, { 'ss': ss, // Seek to timestamp 'frames:v': '1', // Extract only 1 frame 'q:v': '2', // High-quality JPEG (lower = better quality)