Conversation
…s there Matroska block timestamps are presentation timestamps by specification, and the format has no composition-offset table to lose. A writer that fills them packet by packet while the bitstream reorders pictures therefore loses nothing: every presentation slot is still in the file, each one just arrived attached to the picture DECODED at that position rather than the one DISPLAYED there. The reporting asset's first slots are 0, 40, 73, 107, 140 and the decoder emitted them as 0, 73, 107, 140, 40, which is one stepped-back presentation clock per mini-GOP for the length of the file. Measured here through the engine's own software decoder on a generated twin: 15 of 30 frame times stepped backwards before, 0 after. The repair permutes rather than reconstructs. A picture carries the slot its own display rank owns, and that slot is READ from the file instead of being computed, which buys three things a rank-arithmetic rewrite cannot have. The slots keep their own rounding, so a ladder quantized from a fractional frame rate is exact rather than within a tick, and the one slot the reporting asset's writer clamped onto its cluster origin (7 ticks below the 1001/30 lattice its other 59 slots sit on) survives as written; fitting that lattice would have needed 21 slots at 30000/1001 and 61 at 60000/1001 before its phase is unique at all. The decode timestamps are already right, because libavformat derives them from the rising slot ladder, so nothing moves them and nothing moves the container index either, which holds keyframe slots and a keyframe is the first picture of its own sequence. And the slot a picture needs is a packet away, not a plan away: a picture coded ahead of its slot waits for the packet carrying it, three video packets on the reporting asset, never the end of a sequence. Detection is fail-closed and costs a healthy file almost nothing: one stepped-back slot is the container doing what the format says, and it ends the sample on what is normally the third packet. PTS != DTS is not an eligibility test here, because libavformat synthesizes a decode ladder from a rising presentation ladder just as readily as from a reordered one, which is why the ISO-BMFF policy cannot be reused. A stream that later stops being that shape, or a wait no mini-GOP explains, hands its packets back exactly as they arrived rather than permuting half a sequence. Diagnosed by @orut34iop on PR #511, whose numeric ladder is the regression fixture and whose session protocol this keeps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014tQ8MerNbZQdWBpDStitoD
… the picture that displays there (#511) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014tQ8MerNbZQdWBpDStitoD
|
Thank you for the diagnosis and for the numeric ladder. Both went into the fix, which is on Why not the branchThe fail-closed path turns a file that plays today into one that does not play at all. Three fixtures, all built the way your own control builds one (
The trigger is The second half is the hold. What is mergedYour reading that the slots must not be regenerated is the part I kept, and the way to keep it without the hold is to read the slot rather than compute it. A picture carries the slot its own display rank owns, and that slot is a packet away rather than a sequence away: a picture coded ahead of its slot waits for the packet carrying it, which on your ladder is three video packets in a 60-picture sequence. Everything your permutation was protecting falls out of that. Nothing fits a cadence, so the rounding survives exactly and so does the first slot the writer clamped onto its cluster origin, 7 ticks below the 1001/30 lattice its other 59 slots sit on. That clamp is also why a lattice was the wrong tool here: on your ladder one phase only reproduces the window after 21 slots. Nothing moves the decode timestamps, because libavformat derives them from the rising ladder and they are already the decode order the stream has, and the container index is untouched for the same reason, since it holds keyframe slots and a keyframe is the first picture of its own sequence. A stream that later stops being that shape hands its packets back exactly as they arrived instead of failing the session. Measured through the engine's own software decoder on a generated twin: 15 of 30 frame times stepped backwards before, 0 after. The healthy twin is 0 both ways and is never sampled past its third packet, because one stepped-back slot is the container doing what the format says and ends the sample there. Your Two smaller notes
Your ladder is the regression fixture in |
|
Closing in favour of 1e1db64 on main, which keeps the diagnosis, the numeric ladder and the session protocol from here. |
|
Released in 6.74.0 ( |
Summary
Companion submissions, independently reviewable against main:
Repair positively identified Matroska H.264 streams whose packet timestamps were assigned in coding order rather than presentation order. Assign the existing presentation slots to parsed POC; keep hardware/native playback, original compressed bytes, audio timing and the seek-index axis.
This is distinct from missing MP4
cttsin #409 / #413 / #415 / #419: FFmpeg can synthesize unequal DTS/PTS for this MKV defect, so the existing ISO-BMFF equality guard correctly does not repair it.What changed
1af43f017ffca973e35f8fec1e700852f2953d55. The accepted downstream algorithm is unchanged; downstream-only public diagnostic types were replaced with an internal numeric summary. No host UI, recovery-point route, software-cache implementation or dependency-pin changes.Reproduction and measured result
Reported MKV: H.264 Main level 4.0, progressive 1920×1080, 8-bit 4:2:0, nominal 30000/1001 fps, time base 1/1000, video_delay=1; AAC-LC 48 kHz stereo. Duration 7148.085 s. No HDR/DV signaling reported.
The measured first timestamp slots begin
0, 40, 73, 107, 140, ...; original decoder output began0, 73, 107, 140, 40, .... Complete sequences have 60 pictures. The repair preserves every slot while using a 34 ms decode lead.Pinned FFmpegBuild
421e13be7061de67d91b85ac34a6b22a002b164f, actual parser/session and two decoders:Each run checks unchanged packet payload, side data, flags, duration, position, audio and frame count, exact preservation of the sorted presentation-time multiset, constant seek decode offset and zero outstanding tracked packets. Lifecycle tests cover seek with both pending/ready packets, active failure and the all-stream packet ceiling.
Test plan
8b3001c85d38512c14ed7a06e5db441372c62220, candidate 20260907140332; hardware H.264/native route remained active. User passed startup, forward/backward and repeated seeks, pause/resume and extended playback.bash Scripts/test-h264-matroska-timestamps.shpassed.bash Scripts/test-h264-timestamp-controls.shpassed. Generates healthy MP4/MKV, missing-ctts MP4, and coding-order MKV from solid colour + AAC silence; no private fixture required. Generated broken MKV has 87 → 0 regressions at head and after seek.AETHER_FFMPEG_CHECKOUTfor an existing exact pinned checkout, defaulting to.build/checkouts/FFmpegBuild.Scope / limitations
This intentionally does not guess for fields, open-GOP leading pictures, incomplete/duplicate POC, VFR ladders or unknown timestamps. It adds one bounded sequence of startup hold to positively matching candidates. Later violations fail closed. It does not rewrite the original source file. Numeric measurements only are public: no private media, filenames/paths, network identifiers, raw logs or screenshots are distributed.
Checklist
CHANGELOG.mdand format documentation updated