Let ffprobe pipe data to stdin

This commit is contained in:
Alex Gleason 2025-02-28 18:08:11 -06:00
parent d70a00a8ca
commit 5f10f92d4e
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
4 changed files with 50 additions and 8 deletions

View file

@ -4,8 +4,7 @@ import { getVideoDimensions } from './analyze.ts';
Deno.test('getVideoDimensions', async () => {
const uri = new URL('./buckbunny.mp4', import.meta.url);
const dimensions = await getVideoDimensions(uri.href);
const dimensions = await getVideoDimensions(uri);
assertEquals(dimensions, { width: 1920, height: 1080 });
});

View file

@ -1,7 +1,9 @@
import { ffprobe } from './ffprobe.ts';
export async function getVideoDimensions(path: string): Promise<{ width: number; height: number } | null> {
const stream = ffprobe(path, {
export async function getVideoDimensions(
input: URL | ReadableStream<Uint8Array>,
): Promise<{ width: number; height: number } | null> {
const stream = ffprobe(input, {
'v': 'error',
'select_streams': 'v:0',
'show_entries': 'stream=width,height',

View file

@ -3,6 +3,21 @@ import { assertEquals } from '@std/assert';
import { ffprobe } from './ffprobe.ts';
Deno.test('ffprobe', async () => {
await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url));
const stream = ffprobe(file.readable, {
'v': 'error',
'select_streams': 'v:0',
'show_entries': 'stream=width,height',
'of': 'json',
});
const { streams: [dimensions] } = await new Response(stream).json();
assertEquals(dimensions, { width: 1920, height: 1080 });
});
Deno.test('ffprobe from file', async () => {
const uri = new URL('./buckbunny.mp4', import.meta.url);
const stream = ffprobe(uri, {

View file

@ -6,19 +6,45 @@ export interface FFprobeFlags {
[key: string]: string | undefined;
}
export function ffprobe(path: URL | string, flags: FFprobeFlags): ReadableStream<Uint8Array> {
export function ffprobe(input: URL | ReadableStream<Uint8Array>, flags: FFprobeFlags): ReadableStream<Uint8Array> {
const args = [];
for (const [key, value] of Object.entries(flags)) {
if (typeof value === 'string') {
if (value) {
args.push(`-${key}`, value);
} else {
args.push(`-${key}`);
}
}
}
args.push(path instanceof URL ? path.href : path);
if (input instanceof URL) {
args.push('-i', input.href);
} else {
args.push('-i', 'pipe:0');
}
// Spawn the FFprobe process
const command = new Deno.Command('ffprobe', {
args,
stdin: input instanceof ReadableStream ? 'piped' : 'null',
stdout: 'piped',
});
const command = new Deno.Command('ffprobe', { args, stdout: 'piped' });
const child = command.spawn();
// Pipe the input stream into FFmpeg stdin and ensure completion
if (input instanceof ReadableStream) {
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
return child.stdout;
}