Skip to content

A segment inside a keyframe drought is cut by audio and starts mid-GOP #412

Description

@superuser404notfound

Split out of AE#408, where it was found while measuring the fix at the serving layer.

What happens

Video packets are routed into segments by the keyframe-gated cutter (VODSegmentCutter.index, #92): a segment opens at the IRAP that reaches its plan boundary, so a boundary no IRAP reaches is stepped over. Audio packets are routed by segmentIndex(forSourcePts:), which is a plain boundary lookup with no such gate.

Where every boundary has an IRAP the two agree and nothing shows. Where they do not, the audio still crosses the boundary, ensureMuxer(forSegmentIndex:) cuts there and opens the folded index, and the video packets that follow land in a segment whose first video sample is not a sync sample.

Measured on a fixture built with Scripts/mkv-cue-fixture.py (cue points at non-sync positions, a deliberate 12 s keyframe drought at 43 s), fetched from the loopback server and read back as init.mp4 + segN:

segment advertised first video packet sync samples inside
seg10 40.0-44.0 38.417000,K__ 38.417
seg11 44.0-48.0 44.000000,___ none
seg12 48.0-52.0 48.042000,___ none
seg13 52.0-56.0 52.000000,___ 55.000

The cuts sit exactly on the plan boundaries, which is the audio crossing them; the #65 ledger lines for the same run show the video cutter folding 10 straight to 13.

What it costs today

Less than it looks, on this client:

  • A consumer that arrives sequentially is already decoding, so a mid-GOP segment costs it nothing.
  • A cold arrival on a folded index (a seek) takes the out-of-range path, which restarts the producer there. Since AE#408 that restart opens on a sync sample, so what it gets back is decodable and covers its advertised start.
  • A cold arrival on an index that is already cached from a sequential pass gets the mid-GOP segment, and AVPlayer resolves it by fetching the PREVIOUS segment first: GET /seg10.mp4 immediately after seek#2 programmatic began target=46.00, then landed rendered=46.00 target=46.00.

So the visible cost is one extra segment fetch on that last path. Not free: on the AE#408 reporting asset the segments run about 17 MB.

The playlist does not carry EXT-X-INDEPENDENT-SEGMENTS, so nothing in the manifest claims otherwise. Measured on macOS only; a tvOS reading is worth having before deciding this is benign, since the backward walk is the client's choice, not a guarantee.

Direction

The producer knows, at adopt time, whether the first video sample it routed into a segment was a sync sample. Recording that with the cache entry would let a COLD arrival treat a non-decodable cached segment the way the #358 fold path already treats a missing one, and re-produce it with a restart at that index. Sequential arrivals keep what they have, since it costs them nothing.

Not proposing to let the video fold win over audio: that would leave the folded indices with no media at all, which turns a benign extra fetch into a hole.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions