From 5168ccd7484f62c494149042fbb17a426317e33c Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 1 Mar 2025 20:59:14 -0600 Subject: [PATCH] Write uploads to disk so ffmpeg can seek properly --- packages/ditto/utils/upload.ts | 10 ++++++++-- packages/transcode/analyze.ts | 2 +- packages/transcode/ffmpeg.test.ts | 12 +++++++----- packages/transcode/ffprobe.test.ts | 16 ++++++++-------- packages/transcode/frame.test.ts | 6 ++++-- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/ditto/utils/upload.ts b/packages/ditto/utils/upload.ts index c8161649..94d627e3 100644 --- a/packages/ditto/utils/upload.ts +++ b/packages/ditto/utils/upload.ts @@ -64,9 +64,12 @@ export async function uploadFile( } if (needsTranscode) { - const stream = transcodeVideo(file.stream(), { ffmpegPath }); + const tmp = new URL('file://' + await Deno.makeTempFile()); + await Deno.writeFile(tmp, file.stream()); + const stream = transcodeVideo(tmp, { ffmpegPath }); const transcoded = await new Response(stream).bytes(); file = new File([transcoded], file.name, { type: 'video/mp4' }); + await Deno.remove(tmp); } } perf.mark('transcode-end'); @@ -108,7 +111,10 @@ export async function uploadFile( const { width, height } = video; try { - const bytes = await extractVideoFrame(file.stream(), '00:00:01', { ffmpegPath }); + const tmp = new URL('file://' + await Deno.makeTempFile()); + await Deno.writeFile(tmp, file.stream()); + const bytes = await extractVideoFrame(tmp, '00:00:01', { ffmpegPath }); + await Deno.remove(tmp); const [[, url]] = await uploader.upload(new File([bytes], 'thumb.jpg', { type: 'image/jpeg' }), { signal }); if (!image) { diff --git a/packages/transcode/analyze.ts b/packages/transcode/analyze.ts index 1b137428..06f866f4 100644 --- a/packages/transcode/analyze.ts +++ b/packages/transcode/analyze.ts @@ -92,7 +92,7 @@ export function analyzeFile( opts?: { ffprobePath?: string | URL }, ): Promise { const stream = ffprobe(input, { - 'v': 'error', + 'loglevel': 'fatal', 'show_streams': '', 'show_format': '', 'of': 'json', diff --git a/packages/transcode/ffmpeg.test.ts b/packages/transcode/ffmpeg.test.ts index 06191d07..d93be547 100644 --- a/packages/transcode/ffmpeg.test.ts +++ b/packages/transcode/ffmpeg.test.ts @@ -1,7 +1,9 @@ import { ffmpeg } from './ffmpeg.ts'; +const uri = new URL('./buckbunny.mp4', import.meta.url); + Deno.test('ffmpeg', async () => { - await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url)); + await using file = await Deno.open(uri); const output = ffmpeg(file.readable, { 'c:v': 'libx264', @@ -12,11 +14,11 @@ Deno.test('ffmpeg', async () => { }); await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true }); - await Deno.writeFile(new URL('./tmp/buckbunny-transcoded.mp4', import.meta.url), output); + await Deno.writeFile(new URL('./tmp/transcoded-1.mp4', import.meta.url), output); }); -Deno.test('ffmpeg from file', async () => { - const output = ffmpeg(new URL('./buckbunny.mp4', import.meta.url), { +Deno.test('ffmpeg from file URI', async () => { + const output = ffmpeg(uri, { 'c:v': 'libx264', 'preset': 'veryfast', 'loglevel': 'fatal', @@ -25,5 +27,5 @@ Deno.test('ffmpeg from file', async () => { }); await Deno.mkdir(new URL('./tmp', import.meta.url), { recursive: true }); - await Deno.writeFile(new URL('./tmp/buckbunny-transcoded-fromfile.mp4', import.meta.url), output); + await Deno.writeFile(new URL('./tmp/transcoded-2.mp4', import.meta.url), output); }); diff --git a/packages/transcode/ffprobe.test.ts b/packages/transcode/ffprobe.test.ts index 6dbab12c..953c6271 100644 --- a/packages/transcode/ffprobe.test.ts +++ b/packages/transcode/ffprobe.test.ts @@ -1,9 +1,11 @@ -import { assertEquals } from '@std/assert'; +import { assertObjectMatch } 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 uri = new URL('./buckbunny.mp4', import.meta.url); + +Deno.test('ffprobe from ReadableStream', async () => { + await using file = await Deno.open(uri); const stream = ffprobe(file.readable, { 'v': 'error', @@ -14,12 +16,10 @@ Deno.test('ffprobe', async () => { const { streams: [dimensions] } = await new Response(stream).json(); - assertEquals(dimensions, { width: 1920, height: 1080 }); + assertObjectMatch(dimensions, { width: 1920, height: 1080 }); }); -Deno.test('ffprobe from file', async () => { - const uri = new URL('./buckbunny.mp4', import.meta.url); - +Deno.test('ffprobe from file URI', async () => { const stream = ffprobe(uri, { 'v': 'error', 'select_streams': 'v:0', @@ -29,5 +29,5 @@ Deno.test('ffprobe from file', async () => { const { streams: [dimensions] } = await new Response(stream).json(); - assertEquals(dimensions, { width: 1920, height: 1080 }); + assertObjectMatch(dimensions, { width: 1920, height: 1080 }); }); diff --git a/packages/transcode/frame.test.ts b/packages/transcode/frame.test.ts index 8fba6e94..c0710cfc 100644 --- a/packages/transcode/frame.test.ts +++ b/packages/transcode/frame.test.ts @@ -1,10 +1,12 @@ import { extractVideoFrame } from './frame.ts'; +const uri = new URL('./buckbunny.mp4', import.meta.url); + Deno.test('extractVideoFrame', async () => { - await using file = await Deno.open(new URL('./buckbunny.mp4', import.meta.url)); + await using file = await Deno.open(uri); 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); + await Deno.writeFile(new URL('./tmp/poster.jpg', import.meta.url), result); });