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
1 change: 0 additions & 1 deletion quest/m1/test-flakes-2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Public API: none. Wire: none.

## Required

- [TS decoded content](/quest/m1/test-flakes-2/ts-video-content.md) - CI's TS compliance decoded-content checks fail only on real loss, and the exporter names every frame it drops
- [Interop close code](/quest/m1/test-flakes-2/interop-close-code.md) - the browser reliably sees `unauthorized` for a refused session
- [Auth refresh mocks time](/quest/m1/test-flakes-2/auth-refresh-mock-time.md) - moq-relay's auth refresh and fixed-lease tests measure a grant's life on one mocked clock
- [uring teardown close](/quest/m1/test-flakes-2/uring-teardown-close.md) - moq-uring's published-close teardown test holds under load, fixed at its cause
Expand Down
153 changes: 0 additions & 153 deletions quest/m1/test-flakes-2/ts-video-content.md

This file was deleted.

2 changes: 2 additions & 0 deletions quest/m1/ts-reliability/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ remain with their current quests.
- [Whole-PES import](/quest/m1/ts-reliability/pes-import.md) - private PES travels whole at its source placement, a missing PTS stays missing, and the `pes` framing is deleted
- [Section placement](/quest/m1/ts-reliability/section-placement.md) - sections share the PCR placement clock instead of the video PTS
- [Loss and recovery tests](/quest/m1/ts-reliability/recovery.md) - actual MoQ omissions exercise reacquisition and unaffected tracks
- [Data-track breaks](/quest/m1/ts-reliability/data-break.md) - a data track's declared break reads as a break, not as media lost upstream
- [Audio behind the keyframe](/quest/m1/ts-reliability/audio-lead.md) - an export whose video start group is dropped still begins on a keyframe
- [T-STD TS export](/quest/m1/tstd/README.md) - the semantic mux schedules the content it admits within its supported buffer models

## Related
Expand Down
27 changes: 27 additions & 0 deletions quest/m1/ts-reliability/audio-lead.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# [S] A trimmed video start keeps audio behind the keyframe

## Goal

`moq export ts` begins on a video keyframe even when the video's start group
is dropped at acquisition and its next keyframe has not arrived yet. No audio
or data frame goes out ahead of the first video frame.

## Plan

Found reading [#5713](https://github.com/moq-dev/moq/pull/5713), not
reproduced. Until video goes out, `Export::leads_video`
(`rs/moq-mux/src/container/ts/export.rs`) drops non-video frames that present
before the earliest queued video frame. With no video frame queued it falls
back to `video_start`. When the acquisition drops the video's start group as
stale, the video queue is empty until the next keyframe arrives. Audio after
`video_start` then goes out first, by up to the remaining GOP less the delay.
The upstream-cut path that #5565 hit is not affected, because the next
group's keyframe arrives with the gap.

Recommended fix, settled in #5713: while no video frame has gone out and none
is queued, drop non-video frames as trimmed. Land it only with a regression
that fails without it. Three paused-clock attempts on the `Interleave` rig did
not drop the start group, because a frame's `arrived` is when its source was
last found empty. A fixture likely needs the tables to wait on something
other than the video (a late catalog entry), so the held keyframe is read
well before the rest of its group.
29 changes: 29 additions & 0 deletions quest/m1/ts-reliability/data-break.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# [M] A data track's declared break is not a loss

## Goal

A consumer tells a data track's declared discontinuity from media lost
upstream. `moq export ts` stops counting a declared break on a verbatim PES
track (DVB AC-3, teletext, SCTE-35, ID3) in `stats::Export::gaps`, and stops
warning "media skipped upstream" for it.

## Plan

Found in [#5713](https://github.com/moq-dev/moq/pull/5713). Audio and video
mark a break with one empty frame, which the consumer reads as `Read::End`.
An empty payload is data on a data track, so `Producer::discontinuity`
(`rs/moq-mux/src/container/producer.rs`) skips a sequence with no object
instead. The consumer reads that skip as `Read::Gap`, the same as a lost
group, so the export's gap count and warning treat it as loss. #5713
documents the overlap. Its start trim already follows only gaps that cut a
group short, and the break comes after its group ended, so nothing is trimmed.

Settle the carriage first: the marker must be visible on the wire, and an
empty group (zero objects) already means nothing. Any wire or format change
updates `drafts/` and `js/hang` in the same PR. Extend
`export_drops_an_ac3_frame_cut_by_a_marker` (`rs/moq-mux/src/container/ts/export_test.rs`)
to assert `gaps` stays 0 across the break.

## Related

- [Loss and recovery tests](/quest/m1/ts-reliability/recovery.md) - the loss reporting this keeps honest
11 changes: 7 additions & 4 deletions rs/moq-cli/src/subscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,13 @@ impl Subscribe {
// the way `publish` reports one that stops arriving.
let mut log = moq_mux::container::ts::stats::Log::default();
let mut sampled = tokio::time::Instant::now();
// The jitter buffer's late drops and out-of-tolerance count, as last logged.
let mut reported = (0, 0);
// The export's drop, gap and out-of-tolerance counts, as last logged.
let mut reported = (0, 0, 0, 0);
let release = |stats: &moq_mux::container::ts::stats::Export| {
tracing::info!(
dropped = stats.dropped,
trimmed = stats.trimmed,
gaps = stats.gaps,
drift_ppm = ?stats.drift,
out_of_tolerance = stats.out_of_tolerance,
"TS export release clock"
Expand All @@ -421,8 +423,9 @@ impl Subscribe {
if sampled.elapsed() >= moq_mux::container::ts::stats::Log::INTERVAL {
sampled = tokio::time::Instant::now();
let stats = ts.export().stats();
if (stats.dropped, stats.out_of_tolerance) != reported {
reported = (stats.dropped, stats.out_of_tolerance);
let counts = (stats.dropped, stats.trimmed, stats.gaps, stats.out_of_tolerance);
if counts != reported {
reported = counts;
release(&stats);
}
log.sample(stats.into());
Expand Down
1 change: 1 addition & 0 deletions rs/moq-mux/benches/jitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ async fn steer(tracks: u16, floors: u64) -> u64 {
decode: Timestamp::from_millis(second * 1_000).unwrap(),
restart: 0,
skip: 0,
cut: 0,
sync: true,
leading: false,
item: (),
Expand Down
5 changes: 5 additions & 0 deletions rs/moq-mux/src/container/consumer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ impl<F: Container> Consumer<F> {
self.group
}

/// The track's name.
pub(crate) fn name(&self) -> &str {
self.track.name()
}

/// Read the next frame from the track.
///
/// This method handles timestamp decoding, group ordering, and age management
Expand Down
3 changes: 3 additions & 0 deletions rs/moq-mux/src/container/flv/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ impl<S: Stream> Export<S> {
decode: frame.timestamp,
restart: track.source.markers(),
skip: track.source.skips(),
cut: track.source.cuts(),
sync: kind == 1 || frame.keyframe,
leading: false,
item: frame,
Expand All @@ -380,6 +381,7 @@ impl<S: Stream> Export<S> {
read,
restart,
skip,
cut,
sync,
leading,
item,
Expand All @@ -393,6 +395,7 @@ impl<S: Stream> Export<S> {
decode: moq_net::Timestamp::from_millis(u64::from(timestamp))?,
restart,
skip,
cut,
sync,
leading,
item: (item, timestamp),
Expand Down
1 change: 1 addition & 0 deletions rs/moq-mux/src/container/mkv/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ impl<S: Stream> Export<S> {
decode: frame.timestamp,
restart: track.source.markers(),
skip: track.source.skips(),
cut: track.source.cuts(),
sync: track.kind == TrackKind::Audio || frame.keyframe,
leading: false,
item: frame,
Expand Down
Loading
Loading