Repository navigation
quest: plan the studio's MoQ-native and OBS-polish backlog #6876
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8686b13
quest: plan the studio's MoQ-native and OBS-polish backlog
kixelated c8bcc62
Merge remote-tracking branch 'origin/main' into quest-plan/studio-bac…
kixelated 685ae7e
quest: arm replay as a local subscriber so unwatched tracks still replay
kixelated d6a9e56
quest: settle the review's gaps in the studio backlog plans
kixelated ef0f5f5
quest: failover links negotiate the same draft
kixelated File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # [S] Per-app audio on macOS | ||
|
|
||
| ## Goal | ||
|
|
||
| `moq_audio::capture` captures one application's audio on macOS, so a creator | ||
| can stream a game without their music. The Linux and Windows halves live in | ||
| their capture parity quests. | ||
|
|
||
| ## Plan | ||
|
|
||
| Decided 2026-10-26 (maintainer). ScreenCaptureKit already delivers system | ||
| audio here (`Source::System`), and an `SCContentFilter` that includes only one | ||
| application's windows limits the audio to that app. Apps are listed from | ||
| `SCShareableContent`, so only a running app can be chosen. It needs the | ||
| Screen Recording permission, like system audio. Per-app audio is its own | ||
| source variant naming the application; `Source::System` keeps capturing all | ||
| desktop audio through its display filter. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Linux capture parity](/quest/m2/capture-linux.md) - per-app audio through PipeWire nodes | ||
| - [Windows capture parity](/quest/m2/capture-windows.md) - per-app audio through WASAPI process loopback |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # [M] Animation and transitions | ||
|
|
||
| ## Goal | ||
|
|
||
| Scene changes in `moq-studio` use transitions beyond a cut and fade: move | ||
| (items glide between their positions in the two scenes), slide, wipe, and | ||
| stinger (a video clip with a cut point). Any item property can be keyframed | ||
| with easing, so an item can fly in or a camera can push in, and the same | ||
| animation plays in every composition host. | ||
|
|
||
| ## Plan | ||
|
|
||
| Move is OBS's most-installed plugin (Move Transition), and stingers are | ||
| standard in broadcast. Animate in moq-scene on the media clock and carry the | ||
| transition and keyframes in the layout track, so viewers compose them too. | ||
| Overlays animate through their own CSS. No Lottie runtime for now (decided | ||
| 2026-10-26): the open ones either lack text (velato) or add a C++ build | ||
| (ThorVG). A stinger clip is a media source like any other. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Undo and the scene model](/quest/m3/studio/scene-model.md) - keyframes are scene model state | ||
| - [Layout track](/quest/m3/studio/compose/layout.md) - where transitions and keyframes travel |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # Viewer-side composition | ||
|
|
||
| ## Goal | ||
|
|
||
| A `moq-studio` broadcast carries each source as its own track plus a layout | ||
| track describing the live scene. Viewers compose the picture themselves: | ||
| they prioritize sources and toggle elements on and off. A composited program | ||
| remains for protocols and devices that cannot compose (RTMP and WHIP | ||
| destinations, HLS, legacy players), produced by the studio or by a server. | ||
|
|
||
| This README's own work is the end-to-end test: a studio broadcast is composed | ||
| by the browser, by `moq compose`, and by the studio itself, and all three | ||
| pictures match. | ||
|
|
||
| ## Plan | ||
|
|
||
| Decided 2026-10-26 (maintainer), the studio's flagship and first in its epic: | ||
|
|
||
| - **Delivery:** MoQ's value is per-track delivery, so the studio embraces | ||
| viewer composition. The program is the fallback, not the default. | ||
| - **One compositor, three hosts:** moq-scene renders in the studio, headless | ||
| on a server (`moq compose` in moq-cli), and in the browser, so all three | ||
| pictures come from one implementation. | ||
| - **Parity rule:** every scene feature (transitions, filters, effects, | ||
| overlays) renders in all three hosts with the same layout, timing, and | ||
| colors, within a tolerance. Each renderer keeps its own text antialiasing | ||
| and blur, so the browser can draw overlays as real DOM. A feature that | ||
| cannot meet this, such as a CEF browser source or an ML effect, is baked | ||
| into its source's own track before publishing rather than living in the | ||
| layout. | ||
| - **Wire layout:** local sources are tracks in the studio's one broadcast. | ||
| Guests stay their own broadcasts, named through | ||
| [contribution references](/quest/m2/contribution-ref.md). | ||
| - **Layout:** the live scene state (items, transforms, transitions, and each | ||
| element's priority and toggleable flag) as a hang JSON track on the media | ||
| clock, so any host composites the same frame the studio shows. | ||
| - **Browser build:** whichever line [Rust-backed @moq/net](/quest/m2/rs2ts/README.md) | ||
| lands (WASM first, then JS built from Rust). Not fixed to WASM. | ||
| - **Cost:** every source and the program follow MoQ's pull model and are | ||
| encoded only while something subscribes, so a fallback nobody uses costs | ||
| nothing. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Source tracks](/quest/m3/studio/compose/sources.md) - each source publishes as its own track in the studio broadcast, encoded only while subscribed | ||
| - [Layout track](/quest/m3/studio/compose/layout.md) - the live scene state as a hang JSON track on the media clock, specified in the hang draft | ||
| - [Browser composition](/quest/m3/studio/compose/browser.md) - `@moq/watch` composes a broadcast with a layout track through moq-scene's browser build | ||
| - [Server composition](/quest/m3/studio/compose/server.md) - `moq compose` renders the program from sources and layout, headless | ||
| - [Viewer controls](/quest/m3/studio/compose/controls.md) - viewers toggle elements and the layout's priorities steer what they fetch | ||
| - [Hang contribution references](/quest/m2/contribution-ref.md) - guests composed by reference |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # [L] Browser composition | ||
|
|
||
| ## Goal | ||
|
|
||
| `@moq/watch` composes a broadcast that carries a layout track: it fetches the | ||
| tracks the layout shows and draws them through moq-scene's browser build, | ||
| matching the studio's picture. A broadcast without one plays its program as | ||
| today, so embedders change nothing. | ||
|
|
||
| ## Plan | ||
|
|
||
| The browser build comes from whichever line | ||
| [Rust-backed @moq/net](/quest/m2/rs2ts/README.md) lands (decided 2026-10-26), | ||
| WASM first and JS built from Rust later, so this quest does not fix the | ||
| mechanism. A thin package (e.g. `@moq/scene`) wraps it and `@moq/watch` uses | ||
| it. Frames reach it as WebCodecs `VideoFrame`s through the same browser import | ||
| path the [web studio](/quest/m3/studio/web/video-web.md) adds. | ||
|
|
||
| Prove parity with a fixture: the same broadcast composited here and by the | ||
| studio, compared within a stated tolerance. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Layout track](/quest/m3/studio/compose/layout.md) - what the browser composes | ||
| - [moq-video in the browser](/quest/m3/studio/web/video-web.md) - browser frames into wgpu |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # [M] Viewer controls | ||
|
|
||
| ## Goal | ||
|
|
||
| A viewer composing a studio broadcast can switch off any element the layout | ||
| marks toggleable (a facecam, a chat overlay), and stops fetching its track. | ||
| The layout's per-element priorities steer delivery, so under congestion the | ||
| least important sources degrade first. The creator sets both per element in | ||
| the studio. | ||
|
|
||
| ## Plan | ||
|
|
||
| Map element priority onto the subscription priorities moq-lite already has | ||
| (`doc/concept/moq-lite.md`), rather than a second mechanism. A toggled-off | ||
| element is unsubscribed, so it costs the viewer and the relay nothing, and | ||
| the pull model may stop its encode. Subscriptions are per track, not per | ||
| element: a linked source shown by several items stays subscribed while any | ||
| of them is on, at the highest priority among them. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Browser composition](/quest/m3/studio/compose/browser.md) - the viewer that exposes the toggles |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # [M] Layout track | ||
|
|
||
| ## Goal | ||
|
|
||
| A studio broadcast carries a layout track: moq-scene's live scene state | ||
| (items, the tracks they show, transforms, transitions, and each element's | ||
| priority and whether a viewer may toggle it) as a hang JSON track on the | ||
| media clock. Any host that reads it composites the same frame the studio | ||
| shows. The hang catalog and `drafts/draft-lcurley-moq-hang.md` specify it. | ||
|
|
||
| ## Plan | ||
|
|
||
| Serialize moq-scene's own model rather than a second layout schema, so the | ||
| studio, server, and browser cannot drift. Version it and refuse an unknown | ||
| version loudly. Keep it free of local paths and credentials: items name | ||
| tracks and contribution references, never files. A viewer that refuses a | ||
| layout version plays the program instead and says why. A late joiner needs the | ||
| current state, so use the JSON track's snapshot mode. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Source tracks](/quest/m3/studio/compose/sources.md) - the tracks a layout names | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # [M] Server composition | ||
|
|
||
| ## Goal | ||
|
|
||
| `moq compose` in moq-cli subscribes to a studio broadcast's sources and | ||
| layout track and publishes the composited program, headless (a GPU, or | ||
| lavapipe without one). It can push that program to RTMP and WHIP through the | ||
| studio's egress library, so a hosted service makes the fallback composite and | ||
| the creator saves the upload. | ||
|
|
||
| ## Plan | ||
|
|
||
| Decided 2026-10-26 (maintainer): a moq-cli subcommand, not a mode of | ||
| `moq transcode`. A hosted service embeds the same library. It publishes its | ||
| own broadcast, never the studio's names: each publisher produces its own | ||
| stream (AGENTS.md). Update `doc/bin/cli.md`. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Layout track](/quest/m3/studio/compose/layout.md) - what the server composes |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # [M] Source tracks | ||
|
|
||
| ## Goal | ||
|
|
||
| Each local source in a `moq-studio` scene (camera, display, window, image, | ||
| microphone) publishes as its own track in the studio's broadcast, listed in | ||
| its catalog, alongside the program. Each is encoded only while something | ||
| subscribes to it. | ||
|
|
||
| ## Plan | ||
|
|
||
| Follow the pull model the program already uses: an unwatched source costs | ||
| no encode and no upload. Static sources (images, colors) publish once rather | ||
| than as a video stream. Consumer GPUs cap concurrent hardware encode | ||
| sessions, so when the hardware refuses one, that source's track fails loudly | ||
| with the reason shown rather than silently degrading. | ||
| Track names stay stable for a source's life, and an edited source that | ||
| changes what it shows gets a new name. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # [M] Video effects | ||
|
|
||
| ## Goal | ||
|
|
||
| Sources and scene items in `moq-studio` take a curated set of video effects: | ||
| color correction, `.cube` LUTs, blur, crop and masks, chroma and luma key, | ||
| and blend modes. They render the same in every composition host. ML effects | ||
| such as background removal are offered too, applied before the source | ||
| publishes. | ||
|
|
||
| ## Plan | ||
|
|
||
| Write the effects in WGSL to the meanings the CSS Filter Effects and | ||
| Compositing specs define (color matrices, blur, drop-shadow, blend modes), so | ||
| an effect means the same thing as the CSS overlays use. The layout track | ||
| carries effect names and parameters, never shader code, since a shader from | ||
| the network costs every viewer's GPU and can lose the device. An effect the | ||
| browser cannot reproduce, like an ML model, is baked into its source's track | ||
| per the [composition epic](/quest/m3/studio/compose/README.md), so ML effects | ||
| apply to a source, never to one scene item. Prefer an | ||
| existing model crate for ML effects over training one. | ||
|
|
||
| ## Required | ||
|
|
||
| - [Undo and the scene model](/quest/m3/studio/scene-model.md) - per-item filters this extends |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # [L] Live encode control | ||
|
|
||
| ## Goal | ||
|
|
||
| `moq-studio` adapts its encode live without a restart: the program and each | ||
| source publish simulcast rungs that follow the relay's bandwidth grant, a rung | ||
| nobody watches stops encoding, and bitrate and resolution change mid-stream | ||
| through catalog updates. OBS's multitrack output does | ||
| part of this only on Windows with specific GPUs; this works on every | ||
| platform. | ||
|
|
||
| ## Plan | ||
|
|
||
| Today the studio publishes one H.264 rendition at fixed settings and never | ||
| reads `bandwidth::Allocator`. Reuse what exists rather than adding a studio | ||
| path: the grant follower in `moq_video::encode` and `moq_mux::rate`, the | ||
| catalog's in-place updates and new renditions (`doc/concept/hang.md`). The | ||
| encode producer already cuts a keyframe when a subscriber joins | ||
| (`encode/cuts.rs`), so each rung inherits that and can run a long GOP. Show | ||
| each rung's grant, bitrate, and viewers in the Output panel. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Simulcast rung disable](/quest/m1/simulcast-rung-disable.md) - grant-driven rung disable this builds on | ||
| - [moq-audio follows the grant](/quest/m2/2848-follow-the-bandwidth-grant-in-moq-audio-instead-of.md) - the audio half | ||
| - [Scene editing](/quest/m3/studio/scene-editing.md) - canvas resize through a new rendition |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # [M] Relay failover | ||
|
|
||
| ## Goal | ||
|
|
||
| `moq-studio` publishes the same broadcast epoch to two relays at once, so | ||
| when one relay or the path to it fails, viewers resume from the other | ||
| mid-group and the stream does not drop. | ||
|
|
||
| ## Plan | ||
|
|
||
| Publisher epochs already support this: the same epoch on several relays | ||
| resumes mid-group, and a restart is a newer epoch (`doc/concept/moq-lite.md`, | ||
| Publisher epochs and Multiple CDNs). The studio dials one relay today. Encode | ||
| once and publish to both. The second relay is configured, never inferred. | ||
| Both links must negotiate the same protocol and draft, one with publisher | ||
| epochs, since a copy whose track info differs ends the track; a relay that | ||
| does not match is refused at setup with the reason, rather than failing over | ||
| as a restart. Show each relay's state in the health panel. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Stream health](/quest/m3/studio/health.md) - where each relay's state shows |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.