fix(encode): make every output format render the frames it claims - #151
Merged
LeadcodeDev merged 1 commit intoAug 8, 2026
Merged
Conversation
Seven confirmed audit findings, all in the same failure family: an output path that quietly disagreed with the frame stream, the sample rate, or the duration it advertised. - Audio muxed at the wrong rate. `mix_audio_tracks` resamples every track to one constant; two muxer call sites hardcoded a *different* rate when declaring the track. The PCM was correct, the declaration was not, so playback drifted. Both sites now read `audio::OUTPUT_SAMPLE_RATE`, and hardcoding a rate next to it is no longer possible without noticing. - `still --time` picked its frame by walking scenes directly, while the encoder walks `build_frame_tasks`. Any scenario with a transition made the two diverge: the still showed a frame the video never contains. It now goes through the same task stream. - `--format raw` emitted 120 frames for a 2s+2s scenario with a 1s fade where the MP4 emits 90, and skipped post-effects entirely. - GIF playback ran short: `100.0 / fps` was rounded once and reused as a flat per-frame delay, so the rounding error accumulated (1.8s for a 2.0s scenario). Delays are now derived from cumulative rounded boundaries, with the 2cs floor preserved. - JPEG stills failed unconditionally and left a zero-byte file behind: the encoder was handed RGBA, which it cannot take. Now flattened onto an opaque background first. `--format` also no longer loses to the output file's extension. - Incremental encoding rendered every dirty frame into one `all_yuv` buffer before starting the encoder, holding the whole video in memory. Rendering and encoding now interleave. - `ffmpeg` audio extraction wrote straight to the destination path, so a failed run left a truncated WAV that later runs treated as a valid cache. It now writes to a sibling scratch file and promotes on success. Tests: 124 + 126 pass on this branch alone, without the other round-3 lots.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Round 3 du chantier d'audit — lot encodage, 7 constats vérifiés.
Tous partagent la même famille de défaut : une sortie qui contredit silencieusement le flux de frames, la fréquence d'échantillonnage ou la durée qu'elle annonce.
mix_audio_tracksrééchantillonne vers une constante, deux sites de mux en déclaraient une autre en dur. Le PCM était juste, la déclaration non.still --timechoisissait sa frame en parcourant les scènes, l'encodeur parcourtbuild_frame_tasks. Dès qu'il y a une transition, le still montre une image absente de la vidéo.--format rawsortait 120 frames là où le MP4 en sort 90 (scénario 2s+2s, fondu 1s) et n'appliquait aucun post-effet.100.0 / fpsarrondi une fois puis réutilisé comme délai plat — 1,8 s pour un scénario de 2,0 s.--formatperdait aussi face à l'extension du fichier.all_yuvavant de démarrer l'encodeur — la vidéo entière en mémoire.Notes d'implémentation
audio::OUTPUT_SAMPLE_RATE) et lue par les deux muxers ; on ne peut plus en réintroduire une en dur sans le voir.still --timeet--format rawpassent tous deux parbuild_frame_tasks+render_frame_task— le même chemin que l'encodeur, donc plus de divergence possible par construction.Vérification
cargo test -p rustmotion -p rustmotion-clisur cette branche seule (sans les trois autres lots du round 3) : 124 + 126 tests, 0 échec.