Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/interop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ jobs:
env:
MOQ_TEST_RUNS: ${{ runner.temp }}/moq-test-runs

# The T-STD model must pass a broadcast capture and fail the same capture
# delivered too slowly, too early, or in a burst.
- name: T-STD controls
run: nix develop --command just test ts-tstd
shell: bash -leo pipefail {0}

# A failing harness run keeps its directory, holding the process logs, the
# relay config, and a Playwright trace of the failing page. Upload it for a
# short while so a red cell is diagnosable; a passing run deletes its own.
Expand Down
9 changes: 8 additions & 1 deletion quest/m1/tstd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@ Padding, pacing, and muxing all assume a fixed delay, so the export gets one
first. t0ms is testing whether T-STD compliance is feasible at all; record
the result here. If it isn't, re-plan this line.

The full-model check (`tstd` in `test/ts/compliance.py`) fails today's export
for two reasons, both delivery timing: 7-10 % of video access units finish
arriving after their DTS, and audio runs ~230 ms ahead in bursts that overflow
its TB and B. The media itself fits its buffers, so nothing yet says the bar
is out of reach.

This README owns the end-to-end proof: the #4613 netem rig (10% loss, a real
~10 Mb/s broadcast TS) passes the strict T-STD check, and the recipe runs
nightly.

## Required

- [Fixed-delay release](/quest/m1/tstd/delay.md) - frames go out at media time plus a fixed `--delay`, in one order under loss
- [T-STD check](/quest/m1/tstd/check.md) - the harness grades the full buffer model instead of the transport buffer alone
- [TS byte schedule](/quest/m1/tstd/byte-schedule.md) - PCRs sit on the byte grid the mux rate implies, paced against the fixed delay
- [Send-ahead within the delay](/quest/m1/tstd/send-ahead.md) - total lag is `--delay`, send-ahead included, with a 1 s default
- [Mux-rate hold](/quest/m1/tstd/mux-rate-hold.md) - import publishes its catalog once the mux rate is measured, so export is constant-rate from the start

## Related

Expand Down
25 changes: 0 additions & 25 deletions quest/m1/tstd/check.md

This file was deleted.

3 changes: 3 additions & 0 deletions quest/m1/tstd/delay.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ to `main` once the line (#4640) lands, and merge `main` in.

Update `doc/bin/cli.md` and the `moq export ts` examples.

Promote `tstd` in `test/ts/compliance.py` from shape to hard, so `just test
ts` fails a round-trip the T-STD model rejects; it reports only until then.

Public API: `ts::Export` takes the delay in place of its max age and loses the
hold; breaking. `Export::stats` returns `ts::stats::Export` (decided in the
2026-10-05 audit, matching [TS stats module](/quest/m1/ts-stats-module.md)),
Expand Down
39 changes: 39 additions & 0 deletions quest/m1/tstd/mux-rate-hold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# [S] TS import publishes its catalog once the mux rate is measured

## Goal

A TS→MoQ→TS export is constant-rate from its first packet. `moq import ts`
holds the catalog until its mux-rate meter settles, so `mpegts.muxRate` is
there from the start rather than appearing about 2 s in. A VBR source that
never settles is published without it once the window closes.

## Plan

Decided (2026-10-01). Found in [fixed-delay release](/quest/m1/tstd/delay.md)
(#4645): the importer's meter (`rs/moq-mux/src/container/ts/mux_rate.rs`)
publishes `mpegts.muxRate` only after a 2 s window agrees. So the export's
first ~2 s go out unpadded, and the harness skips them (`pcr-timing.py`
grades from the first null packet).

- Hold the whole catalog until the meter settles, or until one `WINDOW` of
PCR time has passed with no rate published, whichever is first: a
one-time ~2 s start-up cost at ingest. The meter itself keeps rolling and
never times out, so the hold needs that explicit bound. Media written in
the meantime is still published.
- A source whose rate never settles (VBR) publishes after that window
without `muxRate`, and the export stays unpadded, as today.
- End of input releases a held catalog without `muxRate` before
`catalog.finish` closes it: a TS shorter than one window never completes
a measurement, and finishing would otherwise close the catalog track with
the reservation unpublished, leaving media with no catalog.
- Remove the harness's "grade from the first null packet" skip, and the
export's grace for a rate that arrives mid-stream, if nothing else needs
them.
- Test: an imported CBR source's first catalog carries `muxRate`, a VBR
source's catalog arrives after the window without it, and a CBR import
shorter than 2 s still publishes its catalog. A TS→TS export passes
`pcr-schedule` from its first PCR.

## Required

- [Fixed-delay release](/quest/m1/tstd/delay.md) - its export is what reads the rate from the start
37 changes: 37 additions & 0 deletions quest/m1/tstd/send-ahead.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# [M] TS export lag stays within the delay, send-ahead included

## Goal

`moq export ts` output trails the source by `--delay`, not twice it. The
send-ahead window that pre-loads the receiver's buffers comes out of the same
budget, capped at what the decoder buffer can hold. The default becomes 1 s,
so broadcast-sized CPBs fit out of the box.

## Plan

Decided (2026-10-01). Found in [fixed-delay release](/quest/m1/tstd/delay.md)
(#4645): frames are held until anchor + DTS + delay, and the schedule then
sends each unit up to another delay ahead of its DTS, so a mid-group joiner
trails the source by two delays. t0ms measured about 1.7 s of delivery at
500 ms: the two delays plus the join and the source's own send-ahead, so
carving alone should cut it by about one delay, not to 0.5 s.

- One budget. A frame's release deadline is anchor + DTS + delay − its
send-ahead. The send-ahead is at most what the PID's decoder buffer can hold
ahead of its DTS (EB from the SPS HRD or the level, B per 13818-1); sending
further ahead only adds latency (0.93 s of reach on `hrd9m` against a 2 s
delay).
- The default `--delay` rises from 500 ms to 1 s. Broadcast CPBs need
0.6-1 s of send-ahead (t0ms's CNN capture, `hrd9m`). A source that needs
more than the delay allows fails loud and names the `--delay` it needs.
Update `doc/bin/cli.md`, moq-srt egress (which passes its SRT latency as
the delay), and every example invocation.
- Test: a mid-group joiner's every slot goes out `delay` after the source sent
it (the existing `a_joiner_runs_at_the_delay_from_its_first_output`, tightened
from two delays to one). `hrd9m` passes strict `tstd` at the 1 s default,
and a delay below its buffer delay fails loud.
- Ask t0ms to re-grade the CNN capture: latency should drop by about a delay.

## Required

- [Fixed-delay release](/quest/m1/tstd/delay.md) - the hold and schedule this merges into one budget
9 changes: 7 additions & 2 deletions test/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ drill-sensitivity *args:
./drill/sensitivity.sh "$@"

# Round-trips a PCR-paced TS through a relay and checks the subscriber's `export
# ts` output with TSDuck plus a custom analyzer (PCR jitter/repetition,
# burstiness, instantaneous bitrate, T-STD). Flags: `--analyze-only file.ts`
# ts` output with TSDuck plus a custom analyzer (the T-STD buffer model, PCR
# spacing and byte schedule). Flags: `--analyze-only file.ts`
# (skip the round-trip), `--strict` (fail on shape warnings), `--source file.ts`
# (use a real capture), `--live` (grade PCR release timing and byte position off
# the pipe instead, which a capture cannot carry; nightly runs this arm),
Expand All @@ -117,6 +117,11 @@ ts *args:
ts-eit *args:
./ts/eit-roundtrip.sh "$@"

# The T-STD model's controls: a broadcast capture must pass, and the same capture
# delivered too slowly, too early, or in a burst must fail. Needs only TSDuck.
ts-tstd:
./ts/tstd-controls.py

# A JS viewer watching a live track through a relay that drains (SIGTERM, after
# a stand-in for DNS stops pointing at it) migrates to a second relay behind the
# same name without a dropped group. See drain/README.md.
Expand Down
Loading
Loading