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:
@@ -0,0 +1,38 @@
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from sync_logic import compute_offset
|
||||
|
||||
|
||||
class TestComputeOffset(unittest.TestCase):
|
||||
def test_audio_ahead_needs_delay(self):
|
||||
# Flash seen at 5.000s, beep heard at 4.500s -> audio is running
|
||||
# ahead of video, so audio needs to be delayed to catch up.
|
||||
offset = compute_offset(video_mark_sec=5.000, audio_mark_sec=4.500)
|
||||
self.assertAlmostEqual(offset.delay_ms, 500.0)
|
||||
self.assertEqual(offset.target, "audio")
|
||||
|
||||
def test_audio_behind_needs_video_delay(self):
|
||||
# This is our synthetic test fixture: audio was shifted +350ms
|
||||
# later than video, so the fix is to delay video to catch up to
|
||||
# the already-late audio -- NOT to delay audio further.
|
||||
offset = compute_offset(video_mark_sec=5.000, audio_mark_sec=5.350)
|
||||
self.assertAlmostEqual(offset.delay_ms, -350.0)
|
||||
self.assertEqual(offset.target, "video")
|
||||
|
||||
def test_already_in_sync(self):
|
||||
offset = compute_offset(video_mark_sec=5.000, audio_mark_sec=5.000)
|
||||
self.assertEqual(offset.delay_ms, 0.0)
|
||||
self.assertEqual(offset.target, "none")
|
||||
|
||||
def test_describe_formatting(self):
|
||||
self.assertIn("audio will be delayed", compute_offset(5.5, 5.0).describe())
|
||||
self.assertIn("video will be delayed", compute_offset(5.0, 5.5).describe())
|
||||
self.assertIn("+500", compute_offset(5.5, 5.0).describe())
|
||||
self.assertIn("-500", compute_offset(5.0, 5.5).describe())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user