Repository navigation
fix(rendering): read again from a fresh AVAssetReader when one fails instead of answering with a stale frame - #2872
Open
richiemcilroy wants to merge 2 commits into
Open
richiemcilroy wants to merge 2 commits into
richiemcilroy wants to merge 2 commits into
Conversation
…instead of answering with a stale frame
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This comment has been minimized.
This comment has been minimized.
richiemcilroy
marked this pull request as ready for review
October 22, 2026 05:26
Member
Author
|
@greptileai review |
This branch was successfully deployed
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.
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Why
On macOS, the cap-verify AVF seek cases that fail on loaded Blacksmith runners (
seek/seek-gop/scrub30/avf,seek/vfr-holds/into-holds60/avf,seek/seek-gop/home-then-play30/avf,seek/seek-gop/keyframes60/avf) are a real decoder bug, not a harness timing problem. The stale answers come from the decoder itself.A traced run of main's decoder (scratch run 39709617728, seek suite under parallel nextest on
blacksmith-6vcpu-macos-15) shows the native reader failing in the middle of the stream:-11821isAVErrorDecodeFailed. The status isAVAssetReaderStatusFailed, so the reader never vends again. The decoder did not check the status. It treated the end ofFramesIteras "no frames yet", and the fallback after the decode loop answered every waiting request with the newest cached frame at or before it. That is howseek-gop/scrub30/avfgot frame 106 for 111 and 105 for 118. The reader was only recreated afterDecoderHealth::MAX_CONSECUTIVE_EMPTY(5) empty batches, so playback kept showing a frozen frame for several frames, as ininto-holds60(235 for 236, 237 and 238). When nothing was cached at or before the request, the request went unanswered and the handle switched to FFmpeg ("AVAssetReader returned no frame"), which is thedecoder-fallbackvariant of the same failure.In the same run, serial rounds (
--test-threads 1) never hit-11821. Six CPU burners with serial tests did not hit it either; they only caused 2 s request timeouts. So the trigger is many decode sessions at once on the paravirtualised VideoToolbox, not CPU starvation. In the app the same thing can happen with many segments or readers open, or under GPU or media-server pressure: after a scrub the editor shows an older frame.What
cap-video-decode:AVAssetReaderDecoder::failure()returns the reader's error when its status isFailed, andNonewhen it reached the end normally.crates/rendering/src/decoder/avassetreader.rs: when the decode loop ends with requests still waiting and the reader has failed, those requests are re-queued as retries instead of falling through to the cached-frame fallback. A retry always resets the reader it runs on through the existingretriedpath, so the requests are read again from a fresh reader. If that reader fails too, the requests are left unanswered, and the handle's existing software fallback serves the right frame instead of a stale one.Registration::fail_avf_read_at(index)makes the next scripted AVAssetReader that reaches a sample fail there, the way a native reader fails on a decode error.Tests
decoder::model::avassetreader_answers_stay_exact_when_its_reader_fails. It runs playback (frames 0 to 89) and a forward scrub (every 7 frames), with the reader failing once or twice at sample 40. Every answer must be the exact frame. One failure must stay on AVAssetReader; two must end on software decoding. On main's decoder it fails with the stale frame 39 for request 40 (scratch run 39715399800).avassetreader_answers_match_the_reference_modelcalls it, because that is the only test of the module that Engine verify runs on macOS.decoder::tests (63) and thecap-video-decodelib tests pass on macOS.Measured
Seek suite under parallel nextest on
blacksmith-6vcpu-macos-15. Base (main's decoder) and head were interleaved in one job for 5 rounds (run 39715399800), then head ran 5 more rounds with tracing (run 39717985986).scrub303,home-then-play302,into-holds601)-11821) read again from a fresh readerdecoder-fallbackcase failuresThe remaining
decoder-fallbackfailures are not stale answers. They happen on base too. The trace shows the nativecopyNextSampleBufferblocking past the request deadline (2 s, or 10 s for an initial seek) before it fails with-11821, so the handle switches to FFmpeg as designed and serves the right frames. In the same rounds, base and head also hitFFmpeg decoder timed out after 30s initializingon ffmpeg cases, so this is the runner's VideoToolbox stalling. The higher head count only looks worse: on base, the same case often failed asstalefirst and was counted there. A run that copied cached frames out of the reader pool instead of holding pool buffers (run 39719242686) still hit-11821with no pool buffers held. So the trigger is the VM's VideoToolbox, not the decoder's cache.Coordination with #2868
This branch merges cleanly with #2868 (
git merge-treeon both heads). #2868's first version saw the reader "stop vending after about 40 outstanding buffers" inkeyframes60/avf. That has the same signature as this failure, and with this change it would be read again instead of answered stale.Risk
Low. The change only applies when AVFoundation reports
Failed, which previously led to wrong frames. A reader failing twice in a row now falls back to software decoding for that track, as a reader that returns nothing already did.

Confidence Score: 5/5
The PR appears safe to merge; no actionable issues were found.Summary
The decoder retries waiting requests from a fresh reader when AVFoundation reports a failure, rather than answering with an older cached frame.
Reviews (1) · Last reviewed commit: "test(rendering): run the reader failure ..." · Reviewed by Greptile