mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-27 19:48:38 -06:00
Problem (#73): a 30 fps WebM played and exported with every third frame missing and the frame before it shown twice (20 unique frames a second). The same happens at 29.97, 60, 59.94, 24 and 23.976 fps, and with MP4 files whose video track has a 1 ms timescale. 25 and 50 fps were fine. Cause: Matroska stores block timestamps in TimestampScale units (usually 1 ms), rounded to the nearest unit, so at 30 fps frame 2 (66.67 ms) is stored as 67. MkvSource::video_frame converted the requested time to track units with a floor (66), and the lookup (last sample with pts <= target) returned frame 1. Every frame whose timestamp was rounded up was skipped. Mp4Source::video_at, and the web build's WebCodecs MP4 path, floored the same way against the track timescale; normal MP4 timescales hold frame times exactly, so only coarse (1000) timescales were hit. Fix: new Tick::to_rational_round (nearest unit, halves up, total for zero and negative timebases), used for the video lookup target and the catch-up threshold in mkv.rs, mp4.rs and webcodecs.rs. A requested time now matches a frame whose stored timestamp is within half a unit of it, the same precision the timestamps have. Truncating muxers (stamps rounded down) still work. Times on exact timebases (90 kHz, 15360, 30000/1001…) convert exactly as before. Test: crates/codecs/src/rounded_pts_tests.rs builds synthetic Motion-JPEG streams in the test (each picture shows its index as black/white blocks), muxed as WebM with 1 ms timestamps and as MOV with a 1000 timescale, at 30, 29.97, 60, 59.94, 24, 23.976 and 25 fps with nearest and truncated stamps. Requesting every frame's start over 3 s must show every frame exactly once, and every frame's midpoint must show that frame. Before the fix both tests failed ("30 fps, Nearest ms stamps: 30 of 90 frames never shown [2, 5, 8, 11]…, 30 shown twice [1, 4, 7, 10]…"). The time crate tests cover the rounding, ties and hostile timebases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>