Implement sync_logic.py and ffmpeg_export.py (build order steps 2-3)
sync_logic.py: pure delay_ms computation with the documented sign convention. Caught and fixed a sign error in my own test-fixture ground-truth doc while writing the unit tests: the audio+350ms fixture actually requires delay_ms=-350 (delay video), not +350, since the audio is already the late one. ffmpeg_export.py: ports ffdelay's two-branch command structure (offset video input vs audio input depending on delay_ms sign) plus its documented quirks (leading-zero float formatting, -ss/-t applied to both inputs for clip mode, non-fatal stream-copy warnings surfaced via stderr rather than swallowed). Added an end-to-end integration test (scripts/test_export_roundtrip.py) that independently re-detects event timing in the exported output via ffmpeg scene-change/silence detection (not the same code path used to build the export) and confirms both the positive- and negative-delay branches bring drift down from ~350ms to under 1ms.
This commit is contained in:
+14
-4
@@ -10,14 +10,24 @@ validating marker capture, offset math, and lossless export.
|
||||
- `sync_test_OUTOFSYNC_audio+350ms.mp4` — same content, but the audio track has
|
||||
been shifted +350ms later relative to video (via `-itsoffset 0.350` on the audio
|
||||
input, stream-copied). The flash event stays at video t=5.000s; the beep event
|
||||
now appears at t=5.350s.
|
||||
now appears at t=5.350s. Exercises the negative-delay_ms / "delay video" branch
|
||||
in ffmpeg_export.py.
|
||||
|
||||
- `sync_test_OUTOFSYNC_video+350ms.mp4` — same content, but the *video* track has
|
||||
been shifted +350ms later instead (via `-itsoffset 0.350` on the video input).
|
||||
The beep stays at audio t=5.000s; the flash now appears at t=5.350s. Exercises
|
||||
the positive-delay_ms / "delay audio" branch in ffmpeg_export.py.
|
||||
|
||||
**Ground truth for the app's marker/offset workflow:**
|
||||
- Mark Video at the flash → ~5.000s
|
||||
- Mark Audio at the beep → ~5.350s
|
||||
- Expected computed `delay_ms` = +350 (audio needs to be delayed to match video)
|
||||
- Exporting `sync_test_OUTOFSYNC_audio+350ms.mp4` with delay_ms = +350 should
|
||||
reproduce (bit-for-bit, modulo container overhead) `sync_test_INSYNC.mp4`'s sync.
|
||||
- Expected computed `delay_ms` = (video_mark - audio_mark) * 1000 = **-350**
|
||||
(audio is already the *late* one here, so the fix is to delay the video by
|
||||
350ms to catch up to it -- not to delay the audio further. Negative
|
||||
delay_ms → offset the video input, per sync_logic.py's sign convention.)
|
||||
- Exporting `sync_test_OUTOFSYNC_audio+350ms.mp4` with delay_ms = -350 should
|
||||
land both events at t=5.350s in the output (not back at 5.000s -- the
|
||||
absolute timeline shifts, only the *relative* sync matters).
|
||||
|
||||
Frame at exactly t=5.000s is frame index 150 (30fps) — useful for checking
|
||||
frame-step precision lands exactly on it.
|
||||
|
||||
Reference in New Issue
Block a user