Still lane: size the looped picture to the audio's real length

This commit is contained in:
Jonathan Sykes
2026-10-16 18:33:09 +08:00
parent fcbc9344ba
commit 2e6c79e360

View File

@@ -523,11 +523,15 @@ export function createMediaCache({
const niced = (args) => (nice ? run(nice, ['-n', '19', ffmpeg, ...args]) : run(ffmpeg, args));
try {
const at = String(Math.min(30, Math.floor(row.duration / 2)));
// The picture must last as long as the audio; -shortest cuts a 1 fps loop
// a few seconds early, so read the audio stream's own length.
const pa = await run(ffprobe, ['-v', 'error', '-select_streams', 'a:0', '-show_entries', 'stream=duration', '-of', 'csv=p=0', src]);
const seconds = Number(String(pa.stdout).trim()) > 0 ? Number(String(pa.stdout).trim()) : row.duration;
const f = await niced(['-v', 'error', '-nostdin', '-y', '-ss', at, '-i', src, '-frames:v', '1',
'-vf', 'scale=-2:min(720\\,ih)', frame]);
if (f.code !== 0) throw new Error('still frame grab failed: ' + tail(f.stderr));
const r = await niced(['-v', 'error', '-nostdin', '-y', '-loop', '1', '-framerate', '1', '-i', frame, '-i', src,
'-map', '0:v', '-map', '1:a:0', '-t', String(row.duration), '-shortest',
'-map', '0:v', '-map', '1:a:0', '-t', String(seconds),
'-c:v', 'libx264', '-preset', 'medium', '-crf', String(transcode.crf), '-tune', 'stillimage',
'-profile:v', 'high', '-level:v', '4.0', '-pix_fmt', 'yuv420p', '-g', '600', '-threads', threads,
'-c:a', 'copy', '-movflags', '+faststart', '-f', 'mp4', out]);