The plan boundary carries its container's axis, and two things that followed from it (AE#561) - #578
Merged
Merged
Conversation
…stamped (AE#561) The keyframe-aligned plan's boundaries ARE the container's index entries, and containers disagree about what an entry's timestamp means: a mov/mp4 sample table holds decode times, a Matroska Cue holds a presentation time. Since #358 the VOD cutter gate compared decode times against both, which is right for mov/mp4 and wrong for every Matroska whose video carries composition offsets, i.e. every MKV with B-frames. There, no IRAP ever reached its own boundary: its decode time sits a composition offset below it. The gate never opened on the planned keyframe, so audio, which is routed by boundary and not gated, opened the segment instead and the IRAP stayed in the segment before. Every segment then began mid-GOP, roughly one IRAP below its own first random-access point, which is what #412 reported on every segment of the field report. Nothing in such a segment can start a decode run, so playback survived only while AVPlayer decoded THROUGH the boundaries and stopped with CoreMediaErrorDomain -19602 the first time it had to decode FROM one, at a position that depends on the encode rather than on elapsed time. The automatic item reload re-fetched the same segment and died on it again. The gate now compares a packet on the plan's own axis (PlanBoundaryAxis, read from the demuxer's format name, never from the URL or host metadata), so a keyframe hits its own boundary exactly on either container. The AE#412 reach is recorded on that same axis, since it is measured against a boundary. mov/mp4 sessions are unchanged byte for byte. Measured on one HEVC stream muxed into both containers, the stamping being the only difference: before, the MKV opened seg1 and up on a dependent picture with its first IRAP 19 frames in and the isolated decode logged 184 reference errors; after, every segment opens on a sync sample and the decode is clean, while the MP4 control keeps its existing timestamps. H.264 in Matroska was affected identically. PlanBoundaryAxisTests pins both arms and fails on the Matroska arm alone when the gate is put back on the wrong axis. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F
…priming (AE#561) `baseMediaDecodeTime` is `unsigned int(64)`, so a negative published timestamp is not unusual, it is unrepresentable. The bridge's PTS counter stamps the FRAME handed to the encoder. An encoder that declares `initial_padding` stamps its first PACKET a padding below that frame, so that a consumer which discards the priming lands back on the source position: 256 samples on the AC-3 family, which surround-compat mode reaches for above two channels, and none on FLAC. Nothing discards it here, because the muxer writes no edit list on purpose and the init segment has to stay restart-invariant, so the priming plays as the silence it is. Without the offset the published timeline therefore started a padding BELOW the source, and at source position 0 that is negative: -256 went out as 2^64 - 256, and the whole first audio fragment was placed 584 thousand years out, losing its ~190 ms of audio at the start of every bridged multichannel session. Measured in the AE#561 reporter's own captures, where the TrueHD track (bridged, EAC3 encoder) carries tfdt 0xFFFFFFFFFFFFFF00 while the same file's DD+5.1 track (stream copy, no bridge) starts cleanly at 0, and reproduced here on 5.1 PCM in Matroska. The counter now carries the padding, which puts the first packet exactly on the source position and costs the content the padding's 5.3 ms, two orders below the lip-sync threshold and what an unsignalled priming is worth. Applied on every rebase rather than only near zero, so a restart mid-file inherits the same relationship instead of stepping by a padding. The FLAC path is unchanged, measured: it declares no padding. BridgedAudioOriginTests fails without the offset with exactly the reported value. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F
…s does not have (AE#561) The AE#408 tolerance decides when a segment opened so far past its boundary that going back for an earlier sync sample is worth a second seek, and it carried the stream's declared reorder depth. That term pays for an AXIS MISMATCH, not for anything the stream does: the gate judges presentation time (AE#169 round 3), so a boundary stamped in DECODE time puts a correctly indexed keyframe a reorder delay above it. A Matroska Cue is already a presentation time. There a correctly indexed keyframe presents exactly at its boundary, and the reorder term only widened the window in which a genuinely late open escaped its re-aim, on the very container AE#408 was reported against. On that axis the tolerance is now the floor, which still absorbs a Cue that is approximate rather than skewed. mov/mp4 keeps the term, because there the skew is real. The gate's own comparison is left lenient on purpose, and the reason is now written down rather than assumed: it answers a different question than the cutter gate does. Being wrong in the strict direction costs the whole restart, and the reported AE#169 geometry (target 2878501 with the anchor IRAP at dts 2878495 and pts 2878620) has the boundary falling BETWEEN that keyframe's two timestamps, so it matches neither axis and only the permissive reading admits it. Since pts >= dts holds for any conforming stream, judging presentation time cannot starve on a skew in either direction. 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.
Three commits, all from the AE#561 investigation. The first is the reported defect, the other two are what the same question turned up next to it.
1. A plan boundary is compared on the axis its container stamped
A keyframe-aligned plan's boundaries ARE the container's index entries, and containers disagree about what an entry's timestamp means: a mov/mp4 sample table holds decode times, a Matroska Cue holds a presentation time. Since #358 the VOD cutter gate compared decode times against both. Correct for mov/mp4, wrong for every Matroska whose video carries composition offsets, which is every MKV with B-frames.
On those sources no IRAP ever reaches its own boundary, because its decode time sits a composition offset below it. The gate never opens on the planned keyframe, so audio, which is routed by boundary and not gated, opens the segment instead and the IRAP stays in the segment before. Every segment then begins mid-GOP, roughly one IRAP below its own first random-access point, which is what
#412 seg-N opens 0.751s below its first random-access pointreported on every single segment of the field report.Nothing in such a segment can start a decode run. Playback survives while AVPlayer decodes THROUGH the boundaries and stops with
CoreMediaErrorDomain -19602the first time it has to decode FROM one, which is why the reporter measured it at specific places in the film rather than after a fixed time, and why the automatic item reload died on the same segment again.The gate now compares a packet on the plan's own axis (
PlanBoundaryAxis, read from the container the demuxer actually opened, never from the URL or host metadata). mov/mp4 sessions are unchanged byte for byte.One HEVC stream muxed into both containers by stream copy, so the packets and timestamps are identical and the index stamping is the only difference:
H.264 in Matroska is affected identically, so this is a property of the container rather than of HEVC.
2. The bridge's published origin accounts for the encoder's priming
baseMediaDecodeTimeisunsigned int(64), so a negative published timestamp is unrepresentable rather than merely unusual.The audio bridge stamps the FRAME it hands the encoder, and an encoder that declares
initial_paddingstamps its first PACKET a padding below that frame, so a consumer that discards the priming lands back on the source position: 256 samples on the AC-3 family, which surround-compat mode reaches for above two channels, and none on FLAC. Nothing discards it here, because the muxer writes no edit list on purpose. At source position 0 the published timeline therefore started negative: -256 went out as 2^64 - 256 and the whole first audio fragment was placed 584 thousand years out, losing its ~190 ms of audio at the start of every bridged multichannel session.Visible in the reporter's own captures, where the TrueHD track (bridged, EAC3 encoder) carries
tfdt 0xFFFFFFFFFFFFFF00while the same file's DD+5.1 track (stream copy, no bridge) starts cleanly at 0. Reproduced here on 5.1 PCM in Matroska, down to the same value.The counter now carries the padding, which costs the content 5.3 ms, two orders below the lip-sync threshold and what an unsignalled priming is worth. The FLAC path is unchanged, measured.
3. The boundary tolerance stops paying for a skew its axis does not have
The AE#408 tolerance, which decides when a segment opened so far past its boundary that a second seek is worth it, carried the stream's reorder depth. That term pays for the axis mismatch above, not for anything the stream does, and a Matroska Cue has no such skew. On that axis the tolerance is now the floor, which sharpens the decision on the container AE#408 was reported against; mov/mp4 keeps the term.
The gate's own comparison is deliberately left lenient, and the reasoning is now written down instead of assumed. It answers a different question than the cutter does, being wrong in the strict direction costs the whole restart, and the reported AE#169 geometry has the boundary falling BETWEEN the anchor keyframe's two timestamps, matching neither axis.
Verification
Each fix has a witness that fails without it:
PlanBoundaryAxisTestsfails on the Matroska arm alone,BridgedAudioOriginTestsfails with exactly the reported18446744073709551360, and the tolerance arms are pinned inIssue408BoundaryRandomAccessTests. Full suite green over both runners, exit 0. Fixtures come fromScripts/fetch-fixtures.sh; the tests skip when they are absent.🤖 Generated with Claude Code
https://claude.ai/code/session_015PM3xUJB6ZQyqnmGK1fp6F