From 793c1bfe4b9cd1f1d4d579890c746dfee2c29948 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 9 Nov 2026 21:59:06 -0800 Subject: [PATCH] quest: apply the studio audit's decisions Decisions the maintainer made on 2026-11-09 in a /quest-plan interview during moq-dev/studio's /quest-audit: - Move the AudioToolbox and Media Foundation AAC encoders and the moq-audio filter chain from m5 to m3: the studio's m0 quests wait on them through pin quests. Media Foundation decode moves with its encoder, which requires it. - A layer layout's audio layer carries pan and delay as well as gain and mute. - `moq play` toggles layers and maps their priorities, as `@moq/watch` does. - Plan moq-rtmp's native rtmps:// dial with a tcUrl, and a caller's dial policy for moq-webrtc and moq-rtmp. - The browser moq-video and moq-audio quests stay deferred: the studio's web build moved to its m2. Co-Authored-By: Claude --- quest/m3/README.md | 6 ++ quest/m3/audio-decode-audiotoolbox.md | 2 +- .../audio-decode-mediafoundation.md | 6 ++ quest/{m5 => m3}/audio-encode-audiotoolbox.md | 10 ++- .../audio-encode-mediafoundation.md | 10 ++- quest/{m5 => m3}/audio-filters.md | 5 ++ quest/m3/layers/README.md | 5 +- quest/m3/layers/play.md | 21 +++++-- quest/m3/layers/spec.md | 12 +++- quest/m3/layers/watch.md | 6 +- quest/m3/obs-moq-video/README.md | 2 +- quest/m3/rtmp-rtmps-tcurl.md | 51 ++++++++++++++++ quest/m3/webcodecs-priming.md | 2 +- quest/m3/webrtc-dial-policy.md | 61 +++++++++++++++++++ quest/m5/README.md | 4 -- quest/m5/audio-web.md | 8 ++- quest/m5/video-web.md | 10 ++- 17 files changed, 196 insertions(+), 25 deletions(-) rename quest/{m5 => m3}/audio-decode-mediafoundation.md (83%) rename quest/{m5 => m3}/audio-encode-audiotoolbox.md (82%) rename quest/{m5 => m3}/audio-encode-mediafoundation.md (71%) rename quest/{m5 => m3}/audio-filters.md (84%) create mode 100644 quest/m3/rtmp-rtmps-tcurl.md create mode 100644 quest/m3/webrtc-dial-policy.md diff --git a/quest/m3/README.md b/quest/m3/README.md index e1ba55da519..24c5fbff0bb 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -67,6 +67,9 @@ federation) land. - [OBS native codecs](/quest/m3/obs-moq-video/README.md) - replace FFmpeg video and audio decoding with moq-video and moq-audio, deliver GPU frames, and use native audio/video encoders - [MoQ in obs-studio](/quest/m3/obs-studio/README.md) - a native MoQ output and service merged into obs-studio, once its maintainers agree; the plugin carries it until then - [AudioToolbox decode](/quest/m3/audio-decode-audiotoolbox.md) - macOS and iOS decode HE-AAC, multichannel AAC, and what else the framework offers +- [AudioToolbox encode](/quest/m3/audio-encode-audiotoolbox.md) - macOS and iOS encode AAC-LC, for moq-dev/studio's RTMP destinations +- [Media Foundation decode](/quest/m3/audio-decode-mediafoundation.md) - Windows decodes HE-AAC, multichannel AAC, and what else the MFTs offer +- [Media Foundation encode](/quest/m3/audio-encode-mediafoundation.md) - Windows encodes AAC-LC, for moq-dev/studio's RTMP destinations - [GPU CI](/quest/m3/gpu-ci.md) - NVIDIA tests gate the merge queue on a self-hosted GPU runner VM, and `just rs nvidia` runs them locally instead of skipping - [Media stats](/quest/m3/stats/README.md) - publishers announce a stats track in the catalog, viewers answer a soliciting catalog through a per-catalog `.echo` broadcast, and one model turns both into a health verdict and a preflight report - [Wire stats](/quest/m3/wire-stats/README.md) - `Presence` counts each root's transport bytes on the wire, `wire_tx` and `wire_rx`, from QUIC, WebSocket, and embedder gateway sources @@ -136,6 +139,7 @@ federation) land. - [Lint override and deprecation audit](/quest/m3/audit-lints.md) - repeat the suppression and deprecation audit after the release ships, starting with clippy on the macOS and Windows targets - [Clean ALSA playback onset](/quest/m3/audio-alsa-onset.md) - no onset restarts or clicks over PulseAudio - [Read a bus source's own samples](/quest/m3/audio-strip-tap.md) - a tap reads one source's samples before its delay with their bus-clock positions, for the studio's clap sync +- [moq-audio filter chain](/quest/m3/audio-filters.md) - per-source suppression, gate, compressor, limiter, ducking, and plugins, which moq-dev/studio configures - [Interop audit](/quest/m3/audit-interop.md) - our interop runner entries match what we ship, and every red interop cell with a moq.dev side is fixed, reported, or noted as down or unsupported - [moq-hls exposes each rendition's bound source](/quest/m3/hls-rendition-source.md) - playlists and segments report the source path and instance they read, so a cache can key on the source - [The memory bench finishes at 1 MiB frames](/quest/m3/memory-bench-hang.md) - the allocation mode's park at `size=1048576` is root-caused and fixed @@ -189,6 +193,8 @@ federation) land. - [Padding probes from the browser](/quest/m3/probe-padding.md) - parked: the maintainer doubts padding is the right way for a browser to probe - [JS rung disable](/quest/m3/probe-rung-disable-js.md) - `@moq/publish` applies the native rung rule, recovering through padding; parked with it - [Layered broadcasts](/quest/m3/layers/README.md) - hang specifies a layer layout track, and `moq play` and `@moq/watch` composite layers over the main picture +- [moq-rtmp dials rtmps:// and always advertises a tcUrl](/quest/m3/rtmp-rtmps-tcurl.md) - the RTMP client dials an `rtmp://` or `rtmps://` URL and sends its `tcUrl` on both, for moq-dev/studio's destinations +- [moq-webrtc and moq-rtmp dial through a caller's policy](/quest/m3/webrtc-dial-policy.md) - a host that dials untrusted URLs refuses private, loopback, and link-local addresses on every WHIP, WHEP, and RTMP dial - [A room keeps a participant while one of its aliases is announced](/quest/m3/room-alias-withdraw.md) - withdrawing `camera` while `camera.hang` stays announced keeps the participant - [@moq/auth key derivation and key sets validate like single keys](/quest/m3/auth-key-validation.md) - `Key.public` shares no scope and never yields an invalid key, and key sets and Rust refuse mismatched keys at parse - [Binding server helpers observe session errors and describe close truthfully](/quest/m3/serve-session-errors.md) - Python retrieves session task errors, and the Kotlin, Go, and Dart docs say close ends every connection diff --git a/quest/m3/audio-decode-audiotoolbox.md b/quest/m3/audio-decode-audiotoolbox.md index d359e586bee..407bcc9e781 100644 --- a/quest/m3/audio-decode-audiotoolbox.md +++ b/quest/m3/audio-decode-audiotoolbox.md @@ -37,5 +37,5 @@ alongside the `objc2-core-audio-types` the crate already carries. ## Related -- [Media Foundation decode](/quest/m5/audio-decode-mediafoundation.md) - the same shape on Windows +- [Media Foundation decode](/quest/m3/audio-decode-mediafoundation.md) - the same shape on Windows - [MediaCodec decode](/quest/m5/audio-decode-mediacodec.md) - the same shape on Android diff --git a/quest/m5/audio-decode-mediafoundation.md b/quest/m3/audio-decode-mediafoundation.md similarity index 83% rename from quest/m5/audio-decode-mediafoundation.md rename to quest/m3/audio-decode-mediafoundation.md index cb53debe31d..2d711abe9ac 100644 --- a/quest/m5/audio-decode-mediafoundation.md +++ b/quest/m3/audio-decode-mediafoundation.md @@ -24,6 +24,12 @@ ones. Behind the decode seam as the first candidate on `target_os = Changed 2026-11-07 (maintainer, /quest-plan from the API sweep of open PRs and remaining quests): moq-audio's codecs return zero or more outputs with `flush` before the cut, so this backend adds no break. `decode::Consumer` still places each packet by the one output it returns and refuses any other count, so if this backend holds or splits output, it also teaches the consumer to place that output. +Changed 2026-11-09 (moq-dev/studio /quest-audit): this moved from m5 to m3 +with [Media Foundation encode](/quest/m3/audio-encode-mediafoundation.md), +which the maintainer moved for moq-dev/studio's destinations and which +requires this for its round-trip regression. A quest an m3 quest requires +joins m3. + ## Related - [Windows decoded frames](/quest/m5/obs-decode-windows.md) - the OBS Windows line this feeds diff --git a/quest/m5/audio-encode-audiotoolbox.md b/quest/m3/audio-encode-audiotoolbox.md similarity index 82% rename from quest/m5/audio-encode-audiotoolbox.md rename to quest/m3/audio-encode-audiotoolbox.md index b1218f6b2af..22d951c15a6 100644 --- a/quest/m5/audio-encode-audiotoolbox.md +++ b/quest/m3/audio-encode-audiotoolbox.md @@ -34,6 +34,14 @@ moq-audio's encoder took in m2 before the cut (decided 2026-11-07, maintainer, /quest-plan from the API sweep of open PRs and remaining quests), so it adds no break and waits on no other encoder backend; its round-trip test still needs its platform decoder. +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): this moved from +m5 to m3. The studio's m0 +[destinations](https://github.com/moq-dev/studio/blob/main/quest/m0/destinations.md) +wait on it through the +[AAC pin quest](https://github.com/moq-dev/studio/blob/main/quest/m0/pin-aac-encode.md) +for RTMP ingests that require AAC, and m3 is where work a named consumer +waits on belongs. + ## Required - [AudioToolbox decode](/quest/m3/audio-decode-audiotoolbox.md) - the round-trip regression decodes through it @@ -41,4 +49,4 @@ and remaining quests), so it adds no break and waits on no other encoder backend ## Related - [MediaCodec AAC encode](/quest/m5/audio-encode-mediacodec.md) - the same encode seam on Android -- [Media Foundation AAC encode](/quest/m5/audio-encode-mediafoundation.md) - the same encode seam on Windows +- [Media Foundation AAC encode](/quest/m3/audio-encode-mediafoundation.md) - the same encode seam on Windows diff --git a/quest/m5/audio-encode-mediafoundation.md b/quest/m3/audio-encode-mediafoundation.md similarity index 71% rename from quest/m5/audio-encode-mediafoundation.md rename to quest/m3/audio-encode-mediafoundation.md index 2ca85484b43..03f39ed9e50 100644 --- a/quest/m5/audio-encode-mediafoundation.md +++ b/quest/m3/audio-encode-mediafoundation.md @@ -23,7 +23,15 @@ moq-audio's encoder took in m2 before the cut (decided 2026-11-07, maintainer, /quest-plan from the API sweep of open PRs and remaining quests), so it adds no break and waits on no other encoder backend; its round-trip test still needs its platform decoder. +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): this moved from +m5 to m3. The studio's m0 +[destinations](https://github.com/moq-dev/studio/blob/main/quest/m0/destinations.md) +wait on it through the +[AAC pin quest](https://github.com/moq-dev/studio/blob/main/quest/m0/pin-aac-encode.md) +for RTMP ingests that require AAC, and m3 is where work a named consumer +waits on belongs. + ## Required -- [Media Foundation decode](/quest/m5/audio-decode-mediafoundation.md) - the round-trip regression decodes through it +- [Media Foundation decode](/quest/m3/audio-decode-mediafoundation.md) - the round-trip regression decodes through it diff --git a/quest/m5/audio-filters.md b/quest/m3/audio-filters.md similarity index 84% rename from quest/m5/audio-filters.md rename to quest/m3/audio-filters.md index 01c2dfc1aa1..f8a2a05e5d7 100644 --- a/quest/m5/audio-filters.md +++ b/quest/m3/audio-filters.md @@ -15,6 +15,11 @@ Changed 2026-11-02 (maintainer): the studio moved to moq-dev/studio. The chain lands here with its own tests and benchmark; the studio's panel and scene-file shape follow a pin bump there. +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): this moved from +m5 to m3. The studio's m0 audio filters wait on it through the +[filter chain pin quest](https://github.com/moq-dev/studio/blob/main/quest/m0/pin-audio-filters.md), +and m3 is where work a named consumer waits on belongs. + Decided 2026-11-02 (maintainer): the chain runs on the capture producer before `capture::Producer` fans out to its consumers, so the bus and a source's own track both carry filtered audio; gain and mute diff --git a/quest/m3/layers/README.md b/quest/m3/layers/README.md index 26358332db5..48371c383e5 100644 --- a/quest/m3/layers/README.md +++ b/quest/m3/layers/README.md @@ -29,7 +29,8 @@ Decided 2026-11-08 (maintainer, moq-dev/studio /quest-plan): - The studio prototypes the format first (`moq-studio --watch`, reading a provisional `dev.moq.studio.layers` track), and the spec starts from what that prototype settles (rejected: drafting the spec in parallel). -- Players mix layers' audio in, at the gain and mute the layout carries. +- Players mix layers' audio in, at the gain, mute, pan, and delay the layout + carries (pan and delay added 2026-11-09, as the spec quest records). - Each layer carries a priority and whether a viewer may toggle it. A player maps priority onto moq-lite's subscription priorities and unsubscribes a toggled-off layer. @@ -38,5 +39,5 @@ Decided 2026-11-08 (maintainer, moq-dev/studio /quest-plan): ## Required - [hang specifies a layer layout track](/quest/m3/layers/spec.md) - a versioned layout track in the hang draft, `rs/hang`, and `js/hang` that both players composite -- [moq play composites layers](/quest/m3/layers/play.md) - `moq play` composites visible layers and mixes their audio +- [moq play composites layers](/quest/m3/layers/play.md) - `moq play` composites visible layers, mixes their audio, and toggles them - [The watch player composites layers](/quest/m3/layers/watch.md) - `@moq/watch` composites visible layers, mixes their audio, and toggles them diff --git a/quest/m3/layers/play.md b/quest/m3/layers/play.md index 5761e0e0cbd..e40cb13ca48 100644 --- a/quest/m3/layers/play.md +++ b/quest/m3/layers/play.md @@ -3,16 +3,25 @@ ## Goal `moq play` plays a broadcast that carries a layer layout track: it composites -each visible layer over the main picture where and when the layout says, and -mixes each audio layer in at its gain and mute. A broadcast without one plays -as today. +each visible layer over the main picture where and when the layout says, +mixes each audio layer in at its gain, mute, pan, and delay, and lets a +viewer toggle each layer the layout marks toggleable. A broadcast without one +plays as today. ## Plan Fetch only visible layers, at the layout's priorities through moq-lite's -subscription priorities. Prove placement and timing against the epic's -fixture. moq-dev/studio's `hang-layers` quest deletes its watch prototype -once this lands. +subscription priorities, and unsubscribe a toggled-off layer. Prove placement +and timing against the epic's fixture. + +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): the viewer +toggle and the priority mapping are this quest's work, not only +`@moq/watch`'s. moq-dev/studio decided on 2026-11-08 (its +`quest/m2/compose/controls.md`) that viewer controls land in the native +player, and its `hang-layers` quest deletes its prototype player once this +lands, so this quest is their home. Subscriptions are per track, not per +layer, as that quest settled: a track several layers show stays subscribed +while any of them is on, at the highest priority among them. ## Required diff --git a/quest/m3/layers/spec.md b/quest/m3/layers/spec.md index da1074e7ae8..d59c2608e8c 100644 --- a/quest/m3/layers/spec.md +++ b/quest/m3/layers/spec.md @@ -5,8 +5,8 @@ `drafts/draft-lcurley-moq-hang.md`, `rs/hang`, and `js/hang` define a layer layout track: a versioned JSON track in snapshot mode on the media clock that names each layer's track and gives its transform, crop, z-order, opacity, -visibility, keyframes, gain and mute, priority, and whether a viewer may -toggle it. +visibility, keyframes, priority, and whether a viewer may toggle it. An audio +layer also carries its gain, mute, pan, and delay. ## Plan @@ -15,6 +15,14 @@ what every player can reproduce. A consumer refuses an unknown version loudly and plays the main picture alone. Layers name tracks and catalog references, never files. +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): an audio layer +carries pan and delay as well as gain and mute. The studio's strip has all +four (`rs/moq-scene/src/collection.rs` in moq-dev/studio; pan shipped in +[moq-dev/studio#116](https://github.com/moq-dev/studio/pull/116)), and a +source's own track is taken before the strip applies them. Without the delay +in the layout, a microphone the creator delayed for sync and published on its +own track would play out of sync for viewers. + ## Waiting - moq-dev/studio: its `moq-studio --watch` prototype plays program plus diff --git a/quest/m3/layers/watch.md b/quest/m3/layers/watch.md index f9fec74a15c..f2d1059db0c 100644 --- a/quest/m3/layers/watch.md +++ b/quest/m3/layers/watch.md @@ -4,9 +4,9 @@ `@moq/watch` plays a broadcast that carries a layer layout track: it composites each visible layer over the main picture where and when the layout -says, mixes each audio layer in at its gain and mute, and lets a viewer -toggle each layer the layout marks toggleable. A broadcast without one plays -as today. +says, mixes each audio layer in at its gain, mute, pan, and delay, and lets a +viewer toggle each layer the layout marks toggleable. A broadcast without one +plays as today. ## Plan diff --git a/quest/m3/obs-moq-video/README.md b/quest/m3/obs-moq-video/README.md index 88c28e60b47..c5e047edd24 100644 --- a/quest/m3/obs-moq-video/README.md +++ b/quest/m3/obs-moq-video/README.md @@ -33,5 +33,5 @@ Decided in the 2026-09-30 audit: the Windows and Linux GPU decode paths and the - [Windows GPU input](/quest/m5/obs-windows.md) - feed D3D11 compositor frames to the encoder without CPU staging; needs physical-hardware proof - [Video hardware validation](/quest/m5/video-hardware.md) - physical hardware evidence is required for each claimed GPU path - [AudioToolbox decode](/quest/m3/audio-decode-audiotoolbox.md) - HE-AAC and multichannel AAC reach the OBS source on macOS through moq-ffi -- [AudioToolbox encode](/quest/m5/audio-encode-audiotoolbox.md) - native AAC encode reaches the OBS encoder adapter on macOS through moq-ffi +- [AudioToolbox encode](/quest/m3/audio-encode-audiotoolbox.md) - native AAC encode reaches the OBS encoder adapter on macOS through moq-ffi - [Linux GPU input](/quest/m5/obs-linux-gpu.md) - allocation-export feasibility and its dependent implementation are deferred diff --git a/quest/m3/rtmp-rtmps-tcurl.md b/quest/m3/rtmp-rtmps-tcurl.md new file mode 100644 index 00000000000..f03cff13025 --- /dev/null +++ b/quest/m3/rtmp-rtmps-tcurl.md @@ -0,0 +1,51 @@ +# [S] moq-rtmp dials rtmps:// and always advertises a tcUrl + +## Goal + +moq-rtmp's client dials an `rtmp://` or `rtmps://` URL itself: it resolves +the host, connects, and for `rtmps://` completes the TLS handshake against +the host's name. Its `connect` command advertises a `tcUrl` derived from that +URL on both schemes: its scheme, host, port if it names one, and app, never +the stream key. +`moq import rtmp` and `moq export rtmp` take `rtmps://` in `--connect`. + +A test dials the crate's own TLS listener by URL and asserts the server saw +that `tcUrl`, and a server certificate the client does not trust fails the +dial. + +## Plan + +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): this is its own +quest in m3, since moq-dev/studio's +[destinations](https://github.com/moq-dev/studio/blob/main/quest/m0/destinations.md) +wait on it to push to RTMPS ingests. + +Today (`rs/moq-rtmp/src/dial.rs`): + +- `Client::connect(addr, app)` takes a resolved `SocketAddr`, dials plaintext + TCP, and advertises `rtmp:///`. That `tcUrl` carries the + resolved IP, not the name the URL gave, so a server that picks a virtual + host from it sees the wrong one. +- `Client::with_stream(stream, app)` is the only route to `rtmps://`: the + caller makes the TLS stream. It passes `tc_url: None`, and several ingest + servers reject a `connect` without one. +- moq-cli resolves the host itself and refuses every scheme but `rtmp` + (`parse_url` in `rs/moq-cli/src/rtmp.rs`), so `doc/bin/rtmp.md` says the + CLI is plaintext only. + +Guidance: + +- No route leaves the `tcUrl` out. `with_stream` either takes what it needs + to advertise one or goes away; propose the shape, changing `connect` + rather than adding a second entry point beside it. +- The caller supplies the TLS client configuration, as `Server::with_tls` + takes the server's, behind the same `tls` feature. +- `Request::tc_url()` on the server already reports what a client sent, + and `tls_fixture` in `rs/moq-rtmp/src/server.rs` builds a self-signed + listener, so the test needs no new harness. +- Update `doc/bin/rtmp.md`, and put the migration note in the PR's Impact + section if a published entry point changes. + +## Related + +- [moq-webrtc and moq-rtmp dial through a caller's policy](/quest/m3/webrtc-dial-policy.md) - applies a host's address policy to the URL dial this adds diff --git a/quest/m3/webcodecs-priming.md b/quest/m3/webcodecs-priming.md index 3c74ec04175..0480a65e150 100644 --- a/quest/m3/webcodecs-priming.md +++ b/quest/m3/webcodecs-priming.md @@ -68,5 +68,5 @@ Decided 2026-11-04 (maintainer, /quest-plan): ## Related - [moq-audio in the browser](/quest/m5/audio-web.md) - its WebCodecs backend's `delay()` reuses this measurement -- [AudioToolbox encode](/quest/m5/audio-encode-audiotoolbox.md) - the native AAC encoder's delay, folded the same way +- [AudioToolbox encode](/quest/m3/audio-encode-audiotoolbox.md) - the native AAC encoder's delay, folded the same way - [Chromium's Opus encoder stamps chunks from their input](/quest/m4/chromium-opus-timestamps.md) - another WebCodecs `AudioEncoder` timestamp bug diff --git a/quest/m3/webrtc-dial-policy.md b/quest/m3/webrtc-dial-policy.md new file mode 100644 index 00000000000..be5d093f839 --- /dev/null +++ b/quest/m3/webrtc-dial-policy.md @@ -0,0 +1,61 @@ +# [M] moq-webrtc and moq-rtmp dial through a caller's policy + +## Goal + +A host that dials URLs it does not trust, such as a service pushing a +creator's broadcast to destinations the creator typed in, gives moq-webrtc's +WHIP and WHEP client and moq-rtmp's client a dial policy. A policy that +refuses private, loopback, and link-local addresses stops every connection +those clients would make to one, and the dial fails with an error that says +the policy refused it. The default policy allows every address, so a desktop +app and the `moq` CLI still reach an ingest on the LAN or on loopback. + +The policy judges the address a socket is about to connect or send to, not +the URL's text, so it holds for each of: + +- a literal address in the URL, in any spelling, IPv4-mapped IPv6 included, +- a hostname, checked on the addresses it resolved to for this dial, so a + second lookup cannot swap in another, +- an HTTP redirect from the WHIP or WHEP endpoint, +- the remote ICE candidates in the SDP answer, which the session then sends + UDP to, +- moq-rtmp's `rtmp://` and `rtmps://` dial. + +Tests prove each case refuses without a listener on the refused address +seeing a connection or a datagram, and that the default still dials loopback. + +## Plan + +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): this is its own +quest in m3. moq-dev/studio's +[destinations](https://github.com/moq-dev/studio/blob/main/quest/m0/destinations.md) +build an egress library a hosted service reuses, and that service must not +be a way to reach its own network. + +Today: + +- `moq_webrtc::Client` (`rs/moq-webrtc/src/client/mod.rs`) builds its own + `reqwest::Client` with default settings and POSTs the offer to whatever + URL `publish` or `subscribe` is handed. reqwest resolves the host and + follows redirects, and nothing checks the answer's candidates. +- moq-rtmp has no gap yet, only because it never resolves a name: + `Client::connect` takes a `SocketAddr` and `Client::with_stream` a + finished stream, so the caller picks the address. The + [rtmps:// quest](/quest/m3/rtmp-rtmps-tcurl.md) makes it dial a URL, and + from then on it has the same gap, which is why this waits on it. + +Guidance: + +- One policy type serves both crates, so a host states its rule once for + every destination kind. Propose its shape and its home; a value the host + builds is preferred over a callback, per the root `AGENTS.md`. +- A refusal is terminal for that URL, and its error is distinct from a + failed connection, so a caller that redials on failure does not retry it. +- Check at connect time on the resolved address. Checking the URL and then + letting the HTTP client resolve again is the DNS rebinding hole. +- moq-srt's client takes a `SocketAddr` like moq-rtmp does today. It is out + of scope until it dials a URL. + +## Required + +- [moq-rtmp dials rtmps:// and always advertises a tcUrl](/quest/m3/rtmp-rtmps-tcurl.md) - the URL dial in moq-rtmp that the policy covers diff --git a/quest/m5/README.md b/quest/m5/README.md index 4fbdb4b505c..9e812299d09 100644 --- a/quest/m5/README.md +++ b/quest/m5/README.md @@ -85,7 +85,6 @@ lite-07; bitrate enforcement is not part of the next release. - [Safari WebTransport](/quest/m5/safari-webtransport.md) - WebKit browsers return to WebTransport once WebKit 319818 ships fixed - [moq-video in the browser](/quest/m5/video-web.md) - getUserMedia, getDisplayMedia, WebCodecs, and VideoFrame import into wgpu, for moq-dev/studio's web studio - [moq-audio in the browser](/quest/m5/audio-web.md) - AudioWorklet capture and playback and WebCodecs Opus and AAC, for the web studio's audio -- [moq-audio filter chain](/quest/m5/audio-filters.md) - per-source suppression, gate, compressor, limiter, ducking, and plugins, which moq-dev/studio configures - [Mesh keys](/quest/m5/mesh-keys.md) - parked: encrypted and HMAC-authenticated mesh broadcasts through a wrapped `.keys` track - [rust2-ts reaches moq-net's model](/quest/m5/rs2ts-model-ready.md) - rust2's translator is ready to translate the model - [Keep moq-net's model in rust2-ts's subset](/quest/m5/rs2ts-model-subset.md) - safe twins for the model's internal `unsafe`, `dyn Expiry` as an enum, once rust2-ts reaches the model @@ -99,7 +98,6 @@ lite-07; bitrate enforcement is not part of the next release. - [Native compression flush](/quest/m5/native-compression-flush.md) - report the per-frame nonfinal flush requirement in existing browser discussions - [Native synchronous codecs](/quest/m5/native-compression-sync.md) - report the stateful small-frame synchronous API requirement - [Native browser codecs](/quest/m5/native-compression-adopt.md) - adopt native operations when timing, frame and resource contracts hold -- [AudioToolbox encode](/quest/m5/audio-encode-audiotoolbox.md) - macOS and iOS encode AAC-LC - [Vendor-free CARLA bridge](/quest/m5/carla-bridge.md) - the CARLA demo's GPU bridge lets moq-video pick the encoder, and its stand-in test runs on AMD as well as NVIDIA - [Performance survey follow-ups](/quest/m5/performance-survey/README.md) - benchmark the source audit's transport, browser, media and serving risks before optimizing - [Receive pool](/quest/m5/receive-pool.md) - one byte budget per worker covers every connection's receive credit and the cache, billing each frame once @@ -137,8 +135,6 @@ lite-07; bitrate enforcement is not part of the next release. - [io_uring thread check](/quest/m5/uring-transport-machines.md) - the relay fanout bench measures `moq_uring::transport::Local`'s per-call thread check after the transport is boxed, removing it only if it shows - [Opus implementation](/quest/m5/audio-opus-backend.md) - compare Opus codec quality, CPU, build cost, and the loss recovery each backend offers - [Latency ledger](/quest/m5/latency-ledger.md) - a viewer reports its share of playback delay stage by stage: jitter buffer and device -- [Media Foundation decode](/quest/m5/audio-decode-mediafoundation.md) - Windows decodes HE-AAC, multichannel AAC, and what else the MFTs offer -- [Media Foundation encode](/quest/m5/audio-encode-mediafoundation.md) - Windows encodes AAC-LC - [MediaCodec decode](/quest/m5/audio-decode-mediacodec.md) - Android decodes HE-AAC, multichannel AAC, and what else the device offers - [MediaCodec encode](/quest/m5/audio-encode-mediacodec.md) - Android encodes AAC-LC - [#2848](/quest/m5/2848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md) - the Opus producer follows its bandwidth grant through the settled `moq_mux::rate::Control` diff --git a/quest/m5/audio-web.md b/quest/m5/audio-web.md index 915d7d547dd..78c412393e7 100644 --- a/quest/m5/audio-web.md +++ b/quest/m5/audio-web.md @@ -3,7 +3,7 @@ ## Goal moq-audio runs in the browser for the -[web studio](https://github.com/moq-dev/studio/blob/main/quest/m0/web.md) in moq-dev/studio: +[web studio](https://github.com/moq-dev/studio/blob/main/quest/m2/web.md) in moq-dev/studio: - **Capture:** getUserMedia into an AudioWorklet. - **Playback:** through an AudioWorklet. - **Codecs:** encode and decode Opus and AAC through WebCodecs @@ -15,6 +15,12 @@ mixing on the bus, and an encode and decode round trip. ## Plan +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit): the studio's +web build moved from its m0 to its m2, so this stays deferred in m5 with +[moq-video in the browser](/quest/m5/video-web.md). The studio waits on it +through its +[browser audio pin quest](https://github.com/moq-dev/studio/blob/main/quest/m2/pin-browser-audio.md). + Changed 2026-11-02 (maintainer): the studio moved to moq-dev/studio, so this lands here first with its own browser test rather than in the same PR as the studio page's audio slice. diff --git a/quest/m5/video-web.md b/quest/m5/video-web.md index edcd680a54c..b112ef15fc6 100644 --- a/quest/m5/video-web.md +++ b/quest/m5/video-web.md @@ -3,7 +3,7 @@ ## Goal moq-video runs in the browser for the -[web studio](https://github.com/moq-dev/studio/blob/main/quest/m0/web.md) in moq-dev/studio: +[web studio](https://github.com/moq-dev/studio/blob/main/quest/m2/web.md) in moq-dev/studio: - **Capture:** getUserMedia for cameras and getDisplayMedia for screens, producing `Frame`s backed by WebCodecs `VideoFrame`s. - **Codecs:** encode and decode through WebCodecs for whichever of H.264, @@ -14,13 +14,19 @@ moq-video runs in the browser for the `fs` feature and file IO sit behind a native-only cfg, the prerequisite for browser media muxing. A CI `cargo check` for wasm32 keeps them building. moq removed its WASM support on 2026-10-31, so this quest restores the wasm32 -target; confirm the web studio still wants it before starting. +target. A test in a headless browser proves capture from a fake device, an encode and decode round trip, and the import, so this lands without the studio page. ## Plan +Decided 2026-11-09 (maintainer, moq-dev/studio /quest-audit), answering +whether the web studio still wants this: the studio's web build moved from +its m0 to its m2, so it is still planned and this stays deferred in m5. The +studio waits on it through its +[browser video pin quest](https://github.com/moq-dev/studio/blob/main/quest/m2/pin-browser-video.md). + Changed 2026-11-02 (maintainer): the studio moved to moq-dev/studio. This was planned to land in the same PR as the first studio page; it now lands here first with its own browser test, and the studio's page follows a pin