diff --git a/quest/m1/README.md b/quest/m1/README.md index e64ee5a520..7bc126d696 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -50,17 +50,19 @@ transport, benchmark tooling); worktrees isolate commits, not semantics. - [TS PSI reassembly](/quest/m1/ts-psi-reassembly.md) - `import ts` reads a PAT or PMT that spans packets or follows a nonzero pointer_field instead of aborting, and one corrupted section costs a repetition and a counted `CRC_error`, not the import - [Same-hop importers](/quest/m1/hop-aligned-import.md) - importers sharing a `--hop` and fed one stream publish identical groups and timestamps, so failover survives - [Capture control](/quest/m1/capture-control.md) - on dev, `encode::Capture` replaces `CaptureOptions` without a `clock` field (it reads the catalog's), an unsupported `cut()` errors, and dropping the last `Control` cancels in-flight opens +- [Capture by default](/quest/m1/capture-default.md) - moq-video and moq-audio build `capture` by default, so pre-merge checks test it and the capture gate goes away - [Video surface](/quest/m1/video-surface.md) - on dev, moq-ffi's `native` becomes `surface`, refused on platforms with no surface - [HLS discontinuity sequence](/quest/m1/hls-discontinuity-sequence.md) - on dev, `Segment::discontinuity` is the absolute sequence, so every cursor agrees - [Auth client CA](/quest/m1/relay-auth-client-ca.md) - on dev, `auth::Config::validate` and `init` take the client-CA flag, so no caller can skip the check -- [HLS linger](/quest/m1/hls-linger.md) - `moq_hls::Server` serves an ended broadcast for its playlist window plus grace, so the moq.pro edge drops its own pool - [Remove live()](/quest/m1/remove-live.md) - on dev, importers publish stream timestamps verbatim, the catalog clock maps them to wall time, and an encoder restart becomes a new epoch +- [Data track clock](/quest/m1/data-track-clock.md) - JSON and binary data tracks stamp on the catalog's clock at write time, matching the media's anchored clock - [Go and Dart doc samples](/quest/m1/doc-samples-go-dart.md) - Go and Dart doc samples compile against their wrappers - [Data capture in bindings](/quest/m1/data-capture-bindings.md) - moq-ffi and every wrapper pass a data frame's capture time, and the JSON window producer takes one - [Moxygen compatibility](/quest/m1/moxygen/README.md) - one subgroup per group, whole-group FETCH, and one datagram per group, never a full moxygen pass - [JS IETF reprice](/quest/m1/js-ietf-reprice.md) - `@moq/net`'s IETF publisher reprices a held namespace in place, like Rust, instead of withdraw-then-advertise - [Cluster publisher in place](/quest/m1/cluster-publisher-in-place.md) - under the IETF cluster extension a publisher change updates an advertisement in place, like lite, and never splices across publishers - [JS IETF datagrams](/quest/m1/js-ietf-datagram.md) - `@moq/net` sends and receives datagram groups over moq-transport, like Rust +- [Datagrams are live-only](/quest/m1/datagram-unfetchable.md) - no FETCH, replay to a new subscriber, or cache fill ever returns a datagram group - [#2991](/quest/m1/2991-net-coalesce-dynamic-tracks-and-preserve-sequences-across.md) - one dynamic producer per track name in both languages, with the sequence namespace surviving a replacement - [JavaScript FETCH](/quest/m1/js-fetch.md) - generic on-demand group serving and IETF FETCH for browser publishers - [JS fetch cancel](/quest/m1/js-fetch-cancel.md) - `FetchGroupOptions.signal` abandons one pending fetch without closing the track diff --git a/quest/m1/capture-default.md b/quest/m1/capture-default.md new file mode 100644 index 0000000000..8e2264afc8 --- /dev/null +++ b/quest/m1/capture-default.md @@ -0,0 +1,38 @@ +# [S] Capture is a default feature + +## Goal + +`capture` is on by default in moq-video and moq-audio, so the pre-merge +`just check` compiles and tests device capture like any other default code, +and the separate capture CI gate goes away. The flag stays so a minimal build +can opt out. Workspace consumers that set `default-features = false` (the +relay, the language bindings) keep building without it. + +## Plan + +Decided in planning (09-29), after capture-control (#4545) found that +pre-merge `just check` never runs moq-video's `capture` tests: + +- **Default-on, keep the flag.** Removing the flag was rejected: consumers + that don't want device stacks would pay for them. +- moq-video's `capture` already costs nothing to build on any platform. Its + Cargo comment says it stays off only "until every distribution ships it", a + packaging concern this reverses. +- moq-audio's `capture` links libasound when it builds on Linux, which is why + it depends on [ALSA link](/quest/m1/capture-alsa-link.md). Once libasound + loads at runtime, the default costs no system library. +- Delete the special capture gate (`rs capture`, `capture-test`, and the + capture branch in `sh/rs/select.sh` on the tooling line) once default + `just check` covers it. Keep the platform jobs (`just rs macos`, + `just rs windows`). +- Update the Cargo feature comments, moq-cli's `capture` feature (it may + become redundant), and `doc/` wherever capture is described as opt-in. + +Verify: `just check` on a moq-video or moq-audio change runs the capture +tests, and a `default-features = false` consumer (for example +`cargo tree -p moq-ffi -e features`) pulls no capture dependency. + +## Required + +- [Tooling](/quest/m1/tooling/README.md) - rewrites the capture gate this deletes +- [ALSA link](/quest/m1/capture-alsa-link.md) - moq-audio capture can't be on by default while it links libasound at build time diff --git a/quest/m1/data-track-clock.md b/quest/m1/data-track-clock.md new file mode 100644 index 0000000000..170d34a09a --- /dev/null +++ b/quest/m1/data-track-clock.md @@ -0,0 +1,19 @@ +# [XS] Data tracks stamp on the catalog's current clock + +## Goal + +A JSON or binary data track publishes timestamps on the same clock mapping as +the media next to it, however early it was created. Today a data track copies +the catalog clock when it's created. An importer sets that clock from its +first frame (remove-live, #4543), so a data track created before that frame +maps its timestamps to a different clock than the media. + +## Plan + +Have data tracks read the catalog's clock when they stamp, not a copy made +when they were created. Keep it crate-private if possible. Test: a data track +created before an importer's first frame stamps on the anchored clock. + +## Required + +- [Remove live()](/quest/m1/remove-live.md) - introduces the first-frame clock anchor diff --git a/quest/m1/datagram-unfetchable.md b/quest/m1/datagram-unfetchable.md new file mode 100644 index 0000000000..264d256331 --- /dev/null +++ b/quest/m1/datagram-unfetchable.md @@ -0,0 +1,45 @@ +# [S] Datagrams are live-only + +## Goal + +A datagram group is delivered only to subscribers present when it is sent. +FETCH never returns one, a new subscription never replays buffered datagrams, +and a relay fills no cache miss with one. Anything that needs reliable or +late-joiner delivery should use a stream group instead. + +## Plan + +Decided in planning (09-29), after the moxygen fetch-fill agent found that the +relay's group-fetch decoder refuses a draft-16+ fetch object that carries the +datagram flag, and reports it as "unsupported": + +- Keep the existing 64-datagram send buffer per track + (`rs/moq-net/src/model/track.rs`, `MAX_DATAGRAMS`). An age bound was + considered and dropped. +- A new subscription starts at the next datagram sent, not at the buffered + backlog. This covers Rust and `@moq/net`, lite and IETF. +- FETCH never serves a datagram group. A publisher answers a FETCH for one the + way it answers any group it won't deliver (dropped or does not exist), + never with its payload. +- A received fetch object with the datagram flag is refused as not fetchable. + It fails only that fetch or fill, not the session, because draft-16+ allows + the flag (maintainer, 09-29, on Codex's review). That covers the relay's group + fill (`recv_group_fetch_objects`, on the moxygen line) and the joining-fetch + fill (`run_fill_objects`). +- Update `drafts/draft-lcurley-moq-lite.md` to say datagrams are neither + cached nor fetchable, and `doc/concept/`. Run `just drafts check` and + `just test interop --all`. + +Not planned: reliable or cached datagrams. If they are ever wanted, the way to +get them is back-pressure in the QUIC library and treating each datagram like +a one-shot stream, not a cache. + +Tests: a subscriber joining after datagrams were sent receives only later +ones. A FETCH covering a datagram group gets no payload. A fetch stream +carrying a datagram-flagged object fails that fetch and leaves the session up. + +## Related + +- [Moxygen compatibility](/quest/m1/moxygen/README.md) - says it carries one datagram per group live; this keeps them out of FETCH +- [JS IETF datagrams](/quest/m1/js-ietf-datagram.md) - JS datagram send and receive over moq-transport +- [SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md) - datagram groups stay best effort there too diff --git a/quest/m1/hls-linger.md b/quest/m1/hls-linger.md deleted file mode 100644 index d240cdf86c..0000000000 --- a/quest/m1/hls-linger.md +++ /dev/null @@ -1,37 +0,0 @@ -# [S] moq-hls keeps an ended broadcast for its playlist window - -## Goal - -A player polling `moq_hls::Server` can finish the last segments of a broadcast -that just ended, and a republish of the same name takes over cleanly. Today -`Server` evicts a broadcaster as soon as its broadcast closes, so the playlist -404s mid-window. The moq.pro edge works around it with its own pool on top of -`Broadcaster` that holds an ended broadcast for the playlist window plus a -grace period and checks for a republish -([#3964](https://github.com/moq-dev/moq/pull/3964)). Once moq-hls owns this, -the edge deletes its pool. - -## Plan - -Decided: moq-hls owns the retention through an `export::Config` field (the -playlist window plus a grace period), so every embedder gets it and the edge -has nothing to duplicate. `Config` is `#[non_exhaustive]`, so the field is -additive on `main`. - -Guidance: - -- The eviction lives in `server/mod.rs` (`evict_closed` and the - `is_closed` checks in `Server::broadcaster`). An ended broadcaster keeps - serving its final playlist, marked ended if the renditions know it, until - the linger expires. -- A republish during the linger replaces the ended broadcaster. Decide - whether a request for the name resolves the new broadcast first and falls - back to the ended one only when nothing is announced; match what the edge - pool does today. -- Segments are fetched from the relay cache on request, so the linger is only - useful within the cache's retention; say so on the field, as `window` does. -- A zero linger keeps today's behavior. Pick the default with the edge's - current value in mind. -- Tests: a request inside the linger after close still gets the playlist and - a cached segment; after it expires the name 404s; a republish inside the - linger is served. diff --git a/quest/m2/README.md b/quest/m2/README.md index d1a1a141c7..95f0845640 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -93,3 +93,4 @@ upstream release waits in [m4](/quest/m4/README.md). - [Audio capture time](/quest/m2/audio-capture-time.md) - native audio stamps a buffer's capture instant, not when the driver reads it - [X11 capture transport](/quest/m2/x11-capture-shm.md) - move X11 capture to shared memory and RandR events instead of a per-frame socket copy - [Capture frame buffers](/quest/m2/capture-frame-buffers.md) - stop rebuilding a full-frame buffer every tick in the X11 and Windows backends +- [Demo serve-hls renditions](/quest/m2/serve-hls-renditions.md) - `just pub serve-hls` serves 720p and 144p instead of two 256-wide copies diff --git a/quest/m2/serve-hls-renditions.md b/quest/m2/serve-hls-renditions.md new file mode 100644 index 0000000000..3e86c953bc --- /dev/null +++ b/quest/m2/serve-hls-renditions.md @@ -0,0 +1,16 @@ +# [XS] Demo serve-hls publishes distinct renditions + +## Goal + +`just pub serve-hls` serves two renditions at different resolutions, as +`just pub hls` does. Today both come out 256 wide: the `-vf:0` and `-vf:1` +options aren't per-stream in ffmpeg, so the last filter wins. + +## Plan + +Use per-stream filters (`-filter:v:0`, `-filter:v:1`), scaled to 720p and +144p to match `hls`. Scaling the 720p source up to 1080p couldn't encode in +real time on a dev machine. The recipe body moves to `sh/demo/serve-hls.sh` +when the [tooling line](/quest/m1/tooling/README.md) lands; fix it wherever it +lives. Check it by fetching `master.m3u8` and comparing the two renditions' +`RESOLUTION` attributes.