mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-03 20:37:30 +00:00
sync/hyperframes-codegen-ec4d7109
1
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9460c983e0 |
fix(engine): recover audio duration by decoding the final frame (#3986)
* fix(engine): recover audio duration from packet timestamps before clamping An authored `data-duration` on an <audio> element could be silently shortened when a source file's container-summary duration (mvhd/tkhd/mdhd) undercounts the real audio length while the packet stream itself is intact. Add `probeAudioDurationFromPackets()`, which re-derives the true duration from the last packet's own pts_time + duration_time — figures ffprobe computes from the stream's own timestamps, not the container's summary header — and wire it into the compiler's existing clamp check as a rescue attempt before accepting a shorter value. Works regardless of codec/profile; verified against real HE-AAC/SBR fixtures and a binary-patched "lying container" MP4 built for the regression test. The rescue only fires for candidates the existing clamp predicate already flagged (no added cost on the common path), runs in parallel across candidates rather than serially, and requires the recovered duration to exceed the existing clamp epsilon (absorbing the few milliseconds of priming-delay drift raw packet timestamps carry relative to an edit-list-corrected container duration) before overriding the shorter value. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> * fix(engine): move audio duration correction into extractAudioMetadata The previous approach compared the compiler's own packet-scan probe against the already-corrected duration returned by extractAudioMetadata, which meant the correction only applied to one caller and the regression fixture happened to exercise a pre-existing AAC-LC-specific refinement instead of the new code path. Move the fix to the single canonical place duration is derived: replace the old AAC-LC-only packet-count refinement (a profile allowlist assuming a fixed 1024-samples-per-frame constant) with a codec-agnostic mechanism that decodes the final audio frame and reads its own timestamp plus real sample count. This has no per-codec assumption, so it applies uniformly regardless of codec or profile, and every caller of extractAudioMetadata benefits automatically. The compiler's own clamp logic is now unchanged from its pre-existing form. Verified against a real, previously-untouched case: a FLAC-in-MP4 file (a codec the old AAC-LC-only path never corrected) with its mvhd/tkhd/mdhd duration atoms binary-patched to half their true value while every real frame stays intact. Confirmed this reproduces on the prior code (by literally substituting its actual content) and is fixed by the new code. The decode probe reads CSV output (each line stands alone) rather than JSON (a single top-level array), and retains only the output tail under a small cap — a lying container's true duration can be arbitrarily larger than its declared one, and JSON truncated from the front isn't valid JSON, so an unbounded scan of a long file silently fell back to the wrong duration once ffprobe's stdout cap was hit. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> * test(producer): update style-7-prod golden for corrected AAC duration probe The deleted AAC-LC packet-count refinement assumed every demuxed packet decodes to exactly 1024 samples. For this suite's real audio fixture (a 48kHz stereo AAC-LC MP4), that assumption overcounts: ffprobe reports 753 demuxed packets, but only 752 of them actually decode into a real audio frame (verified via raw PCM sample count: 770048 samples / 48000Hz = 16.0426666...s). The old mechanism's 753 * 1024 / 48000 = 16.064s was therefore wrong; the new decode-based probe correctly derives ~16.043s from the real final frame. Regression-shards caught this as a compilation mismatch on the previous commit. Verified the corrected golden against the real source: full compilation, visual (0 failed frames), and audio (correlation 0.966) checks all pass locally with the actual LFS-hosted golden video, and a standalone compileForRender() check confirms all other audio-bearing fixture suites in the repo are unaffected. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> * perf(engine): seek the audio decode probe instead of scanning the whole file probeDecodedAudioDuration ran a full, single-threaded decode of the entire audio stream on every extractAudioMetadata call (once per audio element in the HTML compiler, plus audio mixing/padding/encoding steps). On real 30-minute files this took over a second, and could approach ffprobe's 30s deadline on multi-hour files while holding a shared probe slot the whole time. Add -read_intervals so the probe seeks to ~1 second before the container's already-known claimed duration and decodes only that tail. This is safe because the correction only ever raises the duration: an honest or over-claiming container's seek lands at or past the real end and finds nothing extra, while an under-claiming container (the only case this exists to fix) still has real frames past the seek point. If the tail read finds nothing at all, fall back to a full unseeked decode before giving up, protecting files whose seek index is unreliable (an index-less or non-seekably-sourced container) from silently missing a real correction — mirroring the existing seek-then-fallback pattern already used by the sibling final-video-frame probe in this file. Also replaces the real-file regression test's runtime ffmpeg-spawning fixture generation with two small, committed binary fixtures (an honest FLAC-in-MP4 file and a copy with its container duration atoms halved) plus the ffprobe-static package, so this test's real-file proof actually runs in CI instead of being silently skipped when no system ffmpeg is on PATH. Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com> * refactor(engine): trim the audio duration probe comments to the invariant * test(engine): cache tests queue the decode probe and count metadata probes only * test(engine): probe for ffprobe with a literal argv so the argv contract classifies it * test(producer): regenerate the speed-ramp golden for the single-encode mix The audio probe now reads the mixed AAC as exactly 4.000 s instead of the packet-count 4.032 s (which counted the priming frame), so the final mix is stream-copied instead of re-encoded once more. Same waveform (sample correlation 0.9996 vs the old golden), 3.1 dB less true-peak attenuation; video frames are byte-identical. * fix(engine): measure decoded audio from the stream start and trust it for raw ADTS The decoded last-frame end is an absolute timestamp, so subtract the audio stream's start time (format start as fallback) before comparing it with the start-relative container duration, and start the tail read at start + duration - 1 s. MPEG-TS audio (1.4 s default start) and offset MP4s were over-extended by their start time. Raw ADTS (format aac) has no duration field; ffprobe estimates it from the opening bitrate and overclaims after a quiet opening. Its decoded timestamps are sample-counted, so for raw ADTS the decoded end now wins in both directions beyond the margin. Other containers stay upward-only so an MP4 edit list is never extended. --------- Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com> |