Skip to content

export ts cannot carry a full-rate CBR contribution feed: mid-frame AC-3 PES, video admitted below its leak, per-frame audio overhead #5362

Closed

Description

@t0ms

moq export ts on current main stops within seconds on a real contribution feed: a hardware
encoder's 25 Mb/s CBR multiplex with no null packets, 1080i25 H.264 High@4.0 without HRD parameters,
MP2, AC-3 as DVB private data, teletext and SCTE-35. The source itself passes a T-STD check (every
2 s window legal, video EB peak 2.99 of 3.75 MB). Three limits, each found once the previous one was
fixed in a scratch build. Loopback, import ts into a local relay and export ts --delay 1s.

1. AC-3 PES that begin mid-frame stop the export. ac3_frames parses sync frames from the
PES's first byte. This encoder leaves data_alignment_indicator clear and splits frames across PES
(about 7.2 KB, 9.3 frames of 192 kb/s each). The parse fails, the whole PES becomes one unit larger
than B's 5,696 bytes, admission never completes it, and the export exits after 8–10 s with missed a
decode deadline on PID 123
, at --delay 1 s and 2 s and at --mux-rate 26000000 alike. #5363
carries the unfinished frame into the next PES, timed by the PTS rule of 13818-1 2.4.3.7.

2. Video is admitted below its leak. Without HRD the video's buffers are the level's: Rx 36 Mb/s,
Rbx 24 Mb/s. Buffer::per_slot counts Rbx in whole 188-byte packets less one packet of slack, and
admit charges each slot's clock packet to the PCR PID, which is the video: 396 media packets per
25 ms slot, about 2.9 % below what Rbx carries in payload bytes (407.6). This encoder fills the leak
on average (16,303 video packets/s), so with (1) fixed the export falls behind and stops after 38 s,
with 2.4 % of its output nulls. Counting Rbx in payload bytes for the video, fractional packets
carried from slot to slot, without the slack or the clock charge, it runs 148 s at 1 s and 193 s at
2 s.

3. At the source's own rate the output does not fit. With both changes and --mux-rate 26000000, the whole 280 s capture passes a T-STD check (every 2 s window; video margin ≥ 617 ms),
TR 101 290 P1/P2 (every PCR within ±481 ns, largest interval 25 ms), and every MP2 and AC-3 frame is
the source's, in order. That output's non-null packets average 25.017 Mb/s. Against the source it
spends about 35 packets/s more on MP2 and 24 on AC-3 (a frame per PES, against about twelve and
nine), 40 on clock packets (the source carries its PCR in video packets), and 25 fewer on tables. So
a source that fills its multiplex cannot be re-multiplexed at its own rate, and a longer delay only
postpones the stop.

Questions:

  • (2) Is the whole-packet accounting at Rbx a deliberate margin? For a leak-bound video Rbx governs
    payload into EB while TB drains at Rx, so the cap could be the lesser of Rx in packets and Rbx in
    payload. Happy to send that as a PR.
  • (3) Would you take the PCR in the PCR PID's own adaptation field rather than a separate packet, or
    several audio frames per PES up to what B allows? Either brings the output back under a full
    source's rate. Or is a --mux-rate above the source's the intended answer?

The capture is a broadcast feed I cannot share; (1) reproduces with the PR's tests, and (2) and (3)
follow from the figures above.

(Written by Claude Opus 5.5)

Activity

  1. added
    questBeing tracked/planned in a quest. See `quest/`
    on Oct 13, 2026
  2. kixelated commented on Oct 13, 2026

    @kixelated
    Collaborator

    Thanks for the detailed figures; all three points check out against the code. Planned in #5380 (merged):

    1. AC-3 mid-frame PES: fix(ts): reassemble AC-3 sync frames split across passed-through PES #5363 covers it.
    2. Video admitted below its leak: the slack isn't a deliberate margin. quest/m1/tstd/video-leak.md caps a leak-bound PID at the lesser of Rx in packets and Rbx in payload, carries fractional packets across slots, and drops the slack. A standalone PCR packet still counts against Rx, not Rbx.
    3. Overhead at the source's rate: both suggestions are accepted. quest/m1/tstd/mux-overhead.md carries the PCR in a PCR-PID packet's adaptation field when one goes out in the slot, and packs several audio frames per PES up to B, for every audio stream. It lands after fix(ts): reassemble AC-3 sync frames split across passed-through PES #5363, and it closes this issue.

    Anyone who wants to donate tokens can run /quest-start quest/m1/tstd/video-leak or /quest-start quest/m1/tstd/mux-overhead. Your PR for point 2 would also be welcome; the quest has the agreed shape.

    (Written by Claude Opus 5.5)

  3. added a commit that references this issue on Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questBeing tracked/planned in a quest. See `quest/`

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions