A video sample whose NAL chain overruns it is cut, not handed to Apple's parser (AE#561) - #581
Merged
Merged
Conversation
…n (AE#561) A sample in an mp4 video track is a run of NAL units introduced by a big-endian length, and a parser walks that run by addition. A damaged source can carry a length that reaches past the end of its own sample (a reporter's Blu-ray remux declared 384137139 bytes with 350873 left in the packet), and the two consumers answer that differently: libavcodec logs "Invalid NAL unit size", skips the frame and plays on, while Apple's fMP4 parser answers the whole SEGMENT with CoreMediaErrorDomain -19602, which ends the session and every reload onto that segment. Such a file plays in mpv, plays after an MKVToolNix remux (which drops the unparsable tail), and died here at whichever position AVPlayer first had to decode across the damaged sample. The session muxer now walks each video sample's chain before the write and cuts it at its last complete NAL, which is the same bytes the remux would have written. A healthy sample is left alone, an Annex B payload is refused rather than walked as lengths, and a sample with no complete unit at all is dropped instead of written empty. The prefix width comes out of the avcC / hvcC record rather than being assumed to be four. Measured four ways on an 8 s HEVC fixture pair that differs in four bytes (Scripts/nal-overrun-fixture.py rewrites one length field, so the healthy source is a true control arm): healthy before VERDICT: OK healthy after VERDICT: OK, no cut logged damaged before -19602 at once damaged after VERDICT: OK, one cut logged On the reporter's own 0.667 s clip, which carries one damaged sample and neither audio nor subtitles: -19602 at once before, plays to the end after, cutting 505491 bytes to 154614, which is byte for byte what MKVToolNix wrote. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F
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.
The report
7.8.1 (the segment cut on the container's own axis, #578) did not fix 1096bimu's Blu-ray remux. The reporter then did the decisive work: clipped the failing region, found that an MKVToolNix remux of that clip plays although no track changed, and produced a minimal 0.667 s MKV with one damaged packet, no audio and no subtitles.
What it is
A sample in an mp4 video track is a run of NAL units, each introduced by a big-endian length of the width the
avcC/hvcCrecord declares, and a parser walks that run by addition. That packet's sixth length field declares 384137139 bytes with 350873 left in the packet.The two consumers answer that differently, which is the whole shape of the report:
Invalid NAL unit size (384137139 > 350873), skips the frame and plays on, so the file plays in mpv.CoreMediaErrorDomain -19602, which ends the session, and the stage-2 reload dies on the same segment.That also explains "specific places all through the film, not evenly spaced": each is a damaged sample, and it kills the session at whichever position AVPlayer first has to decode across one.
The fix
The session muxer walks each video sample's chain before the write and cuts it at its last complete NAL, which is the same bytes the remux would have written. A healthy sample is left alone, an Annex B payload is refused rather than walked as lengths, and a sample with no complete unit at all is dropped instead of written empty. The prefix width comes out of the configuration record rather than being assumed to be four, so 1, 2 and 4 byte framing and H.264 are covered along with HEVC.
Measured
Scripts/nal-overrun-fixture.pyrewrites ONE length field of an 8 s HEVC encode, so the twin differs in four bytes and the healthy source is a true control arm:VERDICT: OK-19602at onceVERDICT: OK, nothing cutVERDICT: OK, one cut loggedOn the reporter's own clip:
-19602at once before, plays to the end after, cutting 505491 bytes to 154614, which is byte for byte what MKVToolNix wrote.Full suite: 3209 tests in 438 suites, green. Nine new unit tests pin the walk itself (healthy run untouched, overrun, trailing stub, zero length, nothing complete, Annex B in both start-code widths, prefix width, and the width read out of
hvcCbyte 21 /avcCbyte 4).Not covered here
The session dying outright on one unparsable segment, with the reload landing on the same segment, is a second weakness this defect exposed. It does not arise once the sample is cut, so it is left alone here.
🤖 Generated with Claude Code
https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F