Audio-clocked playback for judging sync
Lip sync cannot be judged silently. extract.sh now pulls the audio track and writes manifest.json alongside the frames; the page reads the true extraction rate from it rather than assuming one, since a guessed fps desynchronises picture from sound - the one thing this view exists to show. Audio is the clock: frame = floor(currentTime * fps). A slow render loop drops frames instead of drifting, and half/quarter speed work via playbackRate with the picture following for free. Scrubbing, stepping and clicking a thumbnail all seek the audio too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a082208ad5
commit
5ac919b9d6
7 changed files with 157 additions and 36 deletions
31
extract.sh
31
extract.sh
|
|
@ -1,17 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
# Extract a clip to a PNG sequence for the take builder.
|
||||
# Extract a clip to a PNG sequence + audio + manifest for the take builder.
|
||||
#
|
||||
# Frames are pre-extracted rather than decoded in the page on purpose: browser
|
||||
# video seeking by currentTime is approximate and requestVideoFrameCallback only
|
||||
# delivers frames at playback speed, so neither gives a deterministic per-frame
|
||||
# pass. A PNG sequence is exact, instantly seekable, and reproducible.
|
||||
#
|
||||
# Audio comes out alongside because the page uses it as the PLAYBACK CLOCK -
|
||||
# frame = floor(audio.currentTime * fps) - so picture and sound cannot drift
|
||||
# apart no matter how long the shot is or how slow the render loop runs.
|
||||
set -euo pipefail
|
||||
|
||||
src="${1:?usage: ./extract.sh CLIP [FPS] [OUTDIR]}"
|
||||
fps="${2:-24}"
|
||||
fps="${2:-12}"
|
||||
out="${3:-frames}"
|
||||
|
||||
rm -rf "$out"
|
||||
mkdir -p "$out"
|
||||
rm -rf "$out"; mkdir -p "$out"
|
||||
ffmpeg -hide_banner -loglevel warning -i "$src" -vf "fps=$fps" "$out/%04d.png"
|
||||
echo "$(ls -1 "$out" | wc -l) frames at ${fps}fps -> $out/"
|
||||
count=$(ls -1 "$out" | wc -l)
|
||||
|
||||
# Mono is enough for judging sync and halves the file. Absent audio is not fatal.
|
||||
if ffprobe -v error -select_streams a:0 -show_entries stream=codec_type \
|
||||
-of csv=p=0 "$src" 2>/dev/null | grep -q audio; then
|
||||
ffmpeg -hide_banner -loglevel warning -y -i "$src" -vn -ac 1 -ar 44100 audio.wav
|
||||
audio='"audio.wav"'
|
||||
echo "audio -> audio.wav"
|
||||
else
|
||||
audio='null'
|
||||
echo "no audio stream"
|
||||
fi
|
||||
|
||||
# The page must know the true extraction rate: if it guessed, audio and picture
|
||||
# would drift. Source of truth lives here, next to the frames it describes.
|
||||
printf '{"fps":%s,"frames":%s,"dir":"%s","audio":%s,"source":"%s"}\n' \
|
||||
"$fps" "$count" "$out" "$audio" "$(basename "$src")" > manifest.json
|
||||
|
||||
echo "$count frames at ${fps}fps -> $out/ (manifest.json written)"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue