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
3 changes: 3 additions & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [Announce compression](/quest/m1/announce-compression.md) - a lite-07 announce reuses the path head and hop-chain tail of a live announcement on its stream instead of resending them
- [Rust track tail](/quest/m1/rust-track-tail.md) - a moq-net subscriber accepts groups that arrive after the subscription's end, and PublishDone carries the real stream count
- [Session death error](/quest/m1/session-death-error.md) - a dying session ends its tracks with its own error in Rust and JS, never a clean end, `Dropped`, or `Cancel`
- [JS bare FIN](/quest/m1/js-bare-fin.md) - a `@moq/net` subscriber aborts a track whose subscribe stream FINs before its declared end, like Rust
- [Signal.race cleanup](/quest/m1/signal-race.md) - `Signal.race` releases its signal listeners when its result loses a race
- [Origin narrowing](/quest/m1/origin-narrowing.md) - a live origin grant narrows in place and ends the subscriptions it no longer covers, the deafen boundary #2714 asked for
- [Auth expiry clock](/quest/m1/auth-expiry-clock.md) - moq-auth and the relay hold one fixed expiry deadline and honour the same skew allowance
Expand All @@ -39,6 +40,7 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [IETF announce count](/quest/m1/ietf-announce-count.md) - an opt-in moq-transport extension carries the replay count, so IETF announce consumers go live without a timer

- [Jitter clock](/quest/m1/jitter-flush-clock.md) - renditions advertise `delay` (lag behind the earliest track) and `jitter` (spread), measured at encoder flush, never lowered; js/watch sizes playout over what it subscribes
- [Import discontinuity](/quest/m1/import-discontinuity.md) - a seek or pause resets the flush jitter baseline, from moqsink, libmoq, and moq-ffi
- [Data jitter](/quest/m1/data-jitter.md) - JSON and binary tracks with a capture time advertise a detected `delay` and `jitter`
- [Play tune-in backpressure](/quest/m1/play-tunein-backpressure.md) - moq play: a tune-in burst larger than the video queue parks the decoder, so the clock never reaches live at a wide `--delay`
- [JavaScript FETCH](/quest/m1/js-fetch.md) - generic on-demand group serving and IETF FETCH for browser publishers
Expand All @@ -47,6 +49,7 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [Tooling](/quest/m1/tooling/README.md) - justfiles become a one-line menu over `sh/`, one impact map scopes CI, and every workflow step runs a recipe
- [Path patterns](/quest/m1/path-patterns.md) - one matcher for every predicate over broadcast paths: tokens, origins, interest
- [In-band auth](/quest/m1/auth/README.md) - a session tells its peer what it may publish and subscribe to, unions tokens presented in band, and fails loud on an out-of-scope publish
- [Tests under load](/quest/m1/test-flakes.md) - three tests that time out or run out of file descriptors under `just check` are fixed at the cause
- [Decoded frame ownership](/quest/m1/decoded-frames.md) - retain moq-video Frames across bindings, with native views or CPU conversion as needed
- [C++ through moq-ffi](/quest/m1/cpp/README.md) - generated C++ over moq-ffi with futures and expected-style errors, shipped as a tarball, vcpkg, and Conan, and adopted by the OBS plugin
- [OBS native codecs](/quest/m1/obs-moq-video/README.md) - remove FFmpeg decoding dependencies, deliver GPU frames, and use native audio/video encoders
Expand Down
2 changes: 2 additions & 0 deletions quest/m1/archive/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ owned by that prerequisite, not duplicated in archive storage.

- [Recording writer](/quest/m1/archive/writer.md) - feed the segmenter from a `broadcast::Consumer`, store each segment, then commit its record
- [Recording reader](/quest/m1/archive/reader.md) - serve archived FETCH through a supplied `broadcast::Producer`
- [Paced replay](/quest/m1/archive/paced-replay.md) - a replay pushes its groups to live subscribers on one shared clock, so any live player plays it
- [Replay provenance](/quest/m1/archive/provenance.md) - a replay's catalog names its timeline, replay path, store URL, and format version
Comment on lines +108 to +109

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add prerequisites for the replay quests

These quests are immediately ready even though their assumed foundation does not exist: a repo-wide search finds no moq_archive::Reader, no Archive CLI source/sink, and no moq-archive dependency in moq-cli; those capabilities belong to the reader/writer siblings. Quest-list order is priority only, so without Required edges quest ready can dispatch paced replay or provenance first, forcing either an out-of-scope implementation of the archive stack or a blocked quest. Add the reader/writer dependencies, and make provenance depend on whichever quest owns the archive CLI modes.

Useful? React with 馃憤聽/ 馃憥.

- [Browser archive](/quest/m1/archive/browser.md) - the same contract for browser-published broadcasts
- [Offline archive HLS](/quest/m1/archive/hls.md) - render playlists from the archive timeline and fetch segment media lazily
- [DVR rewind](/quest/m1/archive/dvr.md) - seek through a bounded archive and return to live playback
Expand Down
27 changes: 27 additions & 0 deletions quest/m1/archive/paced-replay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# [M] Paced replay

## Goal

A live player plays a replayed recording unchanged: `moq import archive`
publishes each recorded track to SUBSCRIBE as well as FETCH, pushing its groups
when a shared clock reaches their media time, like `ffmpeg -re`. `export ts`
and web watch play a replay the way they play a live broadcast, and a viewer
who joins late joins mid-replay.

## Plan

- Today `moq_archive::Reader` publishes only the timeline track live and serves
media groups on FETCH through `track::Dynamic`, so a subscriber sees none.
- One clock per import, not per subscriber. It starts at the earliest recorded
timestamp across the selected tracks and every track paces against it, so
tracks stay in sync and every viewer sees the same moment.
- A timeline gap is skipped at pace (the clock keeps running). A growing
archive is followed as new timeline segments land. A finite archive ends
each track after its last group.
Comment on lines +18 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Define how paced replay learns finality

The archive contract explicitly has no completion marker (quest/m1/archive/README.md:60-75), and the reader deliberately treats a missing tail as potentially growing, crashed, or incomplete (quest/m1/archive/reader.md:45-49). Consequently, after the last currently visible segment, the importer cannot distinguish a finite archive that should end its tracks from a growing archive it should continue following. Require an explicit out-of-band finality or one-shot replay mode before promising both behaviors.

Useful? React with 馃憤聽/ 馃憥.

- FETCH keeps serving any advertised group, so DVR and HLS are unaffected.
- The API shape (a reader option vs a separate paced publisher) and the CLI
default follow the root API rules; report both in the PR. Update
`doc/bin/cli.md`.
- Test with paused time: record, replay paced, and assert a plain subscriber
gets every group in order at media pace, that a late subscriber starts at the
current group, and that two tracks stay aligned.
Comment on lines +25 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Benchmark replay across tracks and subscribers

The functional test with two tracks does not cover the feature's two fan-out axes. A shared pacing implementation could scan every track per tick or repeat pacing work per subscriber without this test detecting the resulting slope. Add a benchmark sweeping selected-track count and concurrent subscriber count so the shared-clock design is verified to scale with the touched path.

AGENTS.md reference: AGENTS.md:L35-L35

Useful? React with 馃憤聽/ 馃憥.

18 changes: 18 additions & 0 deletions quest/m1/archive/provenance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# [S] Replay provenance

## Goal

The catalog a replay publishes carries the root `archive` entry the line
promises: the timeline track, the replay path, the store URL, and the format
version. A player or tool can tell a replay from the live source and find the
recording.

## Plan

- Check which of these fields `rs/hang` and `js/hang` already define. Add any
missing ones as optional fields in both, and in the Recording section of
`drafts/draft-lcurley-moq-hang.md`.
- Never advertise credentials: strip userinfo and query from the store URL, and
let the importer opt out of advertising the URL at all.
- `moq import archive` fills the entry. Test that a round trip through
`export archive` and `import archive` advertises it.
1 change: 1 addition & 0 deletions quest/m1/broadcast-close/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ removal.

- [Rust close](/quest/m1/broadcast-close/rust.md) - moq-net gains `close()`, deprecates `finish`/`abort`/`is_finished`, and every Rust and JS caller moves over
- [Binding close](/quest/m1/broadcast-close/bindings.md) - moq-ffi, libmoq, and every wrapper expose `close()` and deprecate `finish`
- [Kotlin end](/quest/m1/broadcast-close/kotlin-end.md) - Kotlin exposes `close()` as `end()`, since `AutoCloseable.close()` takes the name
- [Remove finish](/quest/m1/broadcast-close/remove.md) - on dev, the deprecated broadcast end APIs are gone and `closed()` carries no cause

## Related
Expand Down
18 changes: 18 additions & 0 deletions quest/m1/broadcast-close/kotlin-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# [XS] Kotlin end

## Goal

Kotlin can force-end a broadcast after `finish` is removed. Its
`MoqBroadcastProducer` exposes the moq-ffi `close()` as `end()`, because a
generated `close()` would collide with `AutoCloseable.close()`.

## Plan

- Rename the method for Kotlin only in `rs/moq-ffi/uniffi.toml`; every other
binding keeps `close()`. Kotlin's `close()` still only releases the handle.
Comment on lines +11 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire the Kotlin rename into binding generation

rs/moq-ffi/uniffi.toml does not exist, and neither Kotlin generation path loads such a file: kt/scripts/generate.sh:80-82 and rs/moq-ffi/build.sh:166-170 invoke bindgen without --config (only the Dart invocation supplies one). Implementing this bullet as written therefore cannot produce end(), while remove.md now depends on this quest before deleting Kotlin's only force-end method. Specify a generated API or wrapper approach that both generation paths actually consume.

AGENTS.md reference: AGENTS.md:L64-L67

Useful? React with 馃憤聽/ 馃憥.

- Test that `end()` ends the broadcast while a `dynamic()` handle is still
held. Document it in `doc/lib/kt`.

## Required

- [Binding close](/quest/m1/broadcast-close/bindings.md) - moq-ffi gains the `close()` this renames
1 change: 1 addition & 0 deletions quest/m1/broadcast-close/remove.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ This is a published API break, so it targets `dev`.
## Required

- [Binding close](/quest/m1/broadcast-close/bindings.md) - every binding already has `close()` to move to
- [Kotlin end](/quest/m1/broadcast-close/kotlin-end.md) - Kotlin keeps a forced end once `finish` is gone
20 changes: 20 additions & 0 deletions quest/m1/import-discontinuity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# [S] Import discontinuity

## Goal

A publisher that seeks or pauses tells its importer so, and the flush jitter
measurement restarts instead of counting the break. `moqsink` does it on a
seek, and C and FFI publishers can do it too.

## Plan

- `moq_mux::container::Producer::discontinuity()` already resets the flush
baseline (`catalog/estimate.rs`). `import::Track` gains a `discontinuity()`
that forwards to it, and the codec importers without one gain it too. Only
the baseline resets; advertised values are never lowered.
- `moqsink` calls it when a pad re-anchors after a flush or a new segment
(`rs/moq-gst/src/sink/pad.rs`). Test that a seek on an `encoder` pad does
not raise the advertised jitter.
Comment on lines +15 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset encoder baselines on pause transitions

A plain PLAYING -> PAUSED -> PLAYING cycle sends neither a flush nor a new segment: change_state only calls leave_playing/enter_playing, while the existing segment remains active. Consequently, this proposed call site never invokes discontinuity() for the pause scenario in the Goal, and the next encoder flush still counts the paused wall time as jitter. Handle the resume transition explicitly and regress the pause cycle rather than testing only a flushing seek.

AGENTS.md reference: AGENTS.md:L16-L18

Useful? React with 馃憤聽/ 馃憥.

- Expose it as `moq_publish_media_discontinuity` in libmoq and as
`discontinuity()` in moq-ffi and its hand-written wrappers, per the
Cross-Package Sync table. This is additive, so it lands on `main`.
12 changes: 5 additions & 7 deletions quest/m1/jitter-flush-clock.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,11 @@ media span of each emitted batch and advertise no `delay`.
it. The provisional PTS-gap floor is gone, and `moq_mux::Error::JitterDecreased`
plus zero-as-absent text jitter enforce never-lower in Rust and JS.
`moq-gst` pads opt in with `encoder=true`; imports stay clock-free.
What remains below is `delay` and the player. libmoq and moq-ffi expose
`flush` but no discontinuity, so a binding publisher that pauses and resumes
on a re-anchored PTS within the window would count the pause; add one when
such a caller appears. A `moq-gst` encoder pad has the same gap across a
`PLAYING -> PAUSED -> PLAYING` cycle (running time stops, the wall clock
does not) and a flushing seek, since `import::Track` forwards no
discontinuity.
What remains below is `delay` and the player. A seek or pause resetting the
baseline from moqsink and the bindings is
[Import discontinuity](/quest/m1/import-discontinuity.md), including a
`moq-gst` encoder pad across a `PLAYING -> PAUSED -> PLAYING` cycle (running
time stops, the wall clock does not) and a flushing seek.
- **Measurement.** Lateness is `now - timestamp`, observed by the existing
`flush` calls, so no call site changes. Each rendition keeps its own
baseline, the minimum lateness over a sliding window (about 10 s), so a media
Expand Down
18 changes: 18 additions & 0 deletions quest/m1/js-bare-fin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# [S] JS bare FIN

## Goal

A `@moq/net` subscriber aborts a track whose subscribe stream FINs before the
publisher declared its end, over moq-lite and IETF, as Rust does since #4083.
A bare FIN is a failed request, never a clean end.

## Plan

- The drafts agree: draft-19 section 3.3.2 treats a FIN before the required
messages (PUBLISH_DONE) as a failure, and the moq-lite draft has a publisher
FIN only after SUBSCRIBE_END.
- Abort with the error Rust uses, so both languages report the same thing. Add
an interop case where a publisher FINs without declaring an end, in both
directions.

The clean-end path this tightens landed in #4086.
22 changes: 22 additions & 0 deletions quest/m1/test-flakes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# [M] Tests hold up under load

## Goal

Three tests that pass alone but fail under a full `just check` pass reliably,
fixed at the cause rather than by raising a timeout or adding a retry:

- `js/json/src/snapshot/snapshot.test.ts:359`, "a compressed delta is gated
on its encoded size", which takes about 4.3 s against a 5 s limit.
- The `js/net/src/declarations.test.ts` test that times out at 5 s.
- `rs/moq-tokio/tests/backend.rs:739` `noq_cert_reload`, which fails with
"Too many open files".

## Plan

- Find why each JS test is slow. It should shrink its input or reveal a real
slowdown in the code under test; fix whichever it is.
- For `noq_cert_reload`, find what holds the descriptors: a leak in the test
or code under test, or nextest parallelism against the file limit. Fix a leak
at its source, and otherwise cap the test's concurrency in
`.config/nextest.toml`.
- Prove it by running `just check --all` several times on a loaded machine.
4 changes: 4 additions & 0 deletions quest/m2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ upstream release waits in [m4](/quest/m4/README.md).
- [AV1 metadata separation](/quest/m2/av1-metadata.md) - retain metadata OBUs inline while evaluating separate delivery
- [SEI separation](/quest/m2/sei/README.md) - retain inline SEI until measured savings or a metadata-only consumer justify a split
- [Catalog track identity](/quest/m2/catalog-tracks.md) - compare immutable track definitions with explicit catalog-to-group binding
- [Archive recovery listing](/quest/m2/archive-recovery-listing.md) - a resumed DVR lists what changed since its checkpoint, not every stored group
- [Archive backward timestamps](/quest/m2/archive-backward-timestamps.md) - a resumed recording refuses a track whose timestamps go backward
- [Mobile ownership](/quest/m2/mobile-ownership.md) - decide whether Rust or platform code owns mobile capture, codecs, and rendering
- [iOS capture](/quest/m2/mobile-capture-ios.md) - camera and screen capture if the mobile ownership decision selects Rust
- [Android capture](/quest/m2/mobile-capture-android.md) - NDK/JNI capture using the existing codecs if mobile ownership selects Rust
Expand All @@ -34,6 +36,7 @@ upstream release waits in [m4](/quest/m4/README.md).
- [MediaCodec decode](/quest/m2/audio-decode-mediacodec.md) - Android decodes HE-AAC, multichannel AAC, and what else the device offers
- [MediaCodec encode](/quest/m2/audio-encode-mediacodec.md) - Android encodes AAC-LC
- [AAC encode refusal](/quest/m2/aac-encode-refusal.md) - AAC config encode refuses channel counts it cannot name, on dev
- [OBS channel layouts](/quest/m2/obs-wave-layout.md) - the OBS source maps channel counts to the WAVE default layouts, like moq-audio
- [Video codec coverage](/quest/m2/video-codec-coverage.md) - prioritize remaining native AV1 and portable decoder gaps
- [#2147](/quest/m2/2147-moq-video-10-bit-hevc-and-av1-support-in-the-nvidia-codec.md) - moq-video: 10-bit HEVC and AV1 support in the NVIDIA codec path
- [NVENC buffer pool](/quest/m2/nvenc-pool.md) - NVENC reuses input and output buffers instead of allocating per frame, if a benchmark shows it wins
Expand Down Expand Up @@ -75,6 +78,7 @@ upstream release waits in [m4](/quest/m4/README.md).
- [SIP media stack](/quest/m2/sip-stack.md) - terminate one inbound SIP audio call leg and expose it as Opus frames
- [Carrier voice](/quest/m2/carrier-voice/README.md) - determine whether MoQ should be the call fabric for programmable carrier voice
- [LiveKit WebRTC bridge](/quest/m2/livekit-webrtc-bridge.md) - a go/no-go verdict, backed by a spike, on per-track LiveKit-to-MoQ bridging
- [Expired token error](/quest/m2/auth-expired-error.md) - an expired token reports `Error::Expired`, not `Unauthorized`, in Rust, JS, and the bindings
- [Common Access Tokens](/quest/m2/cat/README.md) - a moq-transport client presents a CAT in SETUP and `moq auth serve` admits it with the scope its `moqt` claim names
- [Runtime QA hosts](/quest/m2/runtime-qa-hosts.md) - run exact source snapshots on accessible Linux and device hosts with retrievable debug evidence
- [Media QA on other engines](/quest/m2/browser-media-qa-engines.md) - the media harness measures a Firefox or WebKit player over the fallback and names what each engine lacks
Expand Down
16 changes: 8 additions & 8 deletions quest/m2/aac-encode-refusal.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

Writing an AudioSpecificConfig for a channel count that no AAC
channelConfiguration names is an error, not a stereo config with a warning,
in `moq_mux::codec::aac::Config::encode` and `@moq/hang`'s
`audioSpecificConfig`. This mirrors the parse side, which since #4093 refuses
reserved values instead of guessing stereo.
in `moq_mux::codec::aac::Config::encode`, as `@moq/hang`'s
`audioSpecificConfig` already refuses since #4119. This mirrors the parse
side, which since #4093 refuses reserved values instead of guessing stereo.
Comment on lines +7 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the JavaScript AAC refusal in scope

@moq/hang does not already refuse unsupported channel counts: js/hang/src/util/aac.ts:47-53 returns stereo configuration 2 for every unsupported count, and its test explicitly verifies that 7 channels encode as stereo. Limiting this quest to Rust therefore leaves the silent channel-layout corruption in JavaScript. Restore the JS fallibility change and its tests instead of treating #4119 as having landed it.

AGENTS.md reference: AGENTS.md:L16-L17

Useful? React with 馃憤聽/ 馃憥.


## Plan

Both functions become fallible, a published API break in each language, so
this targets `dev`. Counts with a PCE-free configuration map as today. For
the others, either write channelConfiguration 0 with a PCE derived from the
layout, or refuse. Pick one at PR time and apply it in both languages. Test
every count from 1 to 8 and one beyond.
`Config::encode` becomes fallible, a published API break, so this targets
`dev`. Counts with a PCE-free configuration map as today. Refuse the others,
matching JS; writing channelConfiguration 0 with a PCE derived from the layout
is a later additive change in both languages. Test every count from 1 to 8 and
one beyond.

## Related

Expand Down
16 changes: 16 additions & 0 deletions quest/m2/archive-backward-timestamps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# [XS] Archive refuses backward timestamps

## Goal

A resumed recording refuses a track whose timestamps go backward past the
recovered timeline, failing loud like the group-ID check, instead of writing
overlapping media time. The caller starts a new prefix.

## Plan

Check the first group's timestamp against the recovered track's last recorded
timestamp at enrollment, and test both a backward and a forward restart.

## Required

- [Archive](/quest/m1/archive/README.md) - the recovery this hardens ships with the line
19 changes: 19 additions & 0 deletions quest/m2/archive-recovery-listing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# [M] Archive recovery listing

## Goal

A DVR writer resuming a recording lists storage in proportion to what changed
since its last checkpoint, not every stored `groups/` object, while still never
deleting a referenced object.

## Plan

- Recovery today reconciles a complete `groups/` listing of every recorded
track before accepting input. Compare bounding it with an ordered
`list_with_offset` from the oldest retained range, with orphans below it left
to a background sweep, against a checkpointed listing marker.
- Benchmark recovery time and requests over archive size before and after.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Benchmark recovery against both history and delta

Benchmarking only over aggregate archive size cannot establish the stated proportional-to-change behavior. An implementation can still rescan all retained history or all tracks while looking acceptable when only one combined size varies. Hold the post-checkpoint delta fixed while growing retained history, then grow the delta independently, and sweep track count as the other fan-out axis so a full-table slope is visible.

AGENTS.md reference: AGENTS.md:L35-L35

Useful? React with 馃憤聽/ 馃憥.


## Required

- [Archive](/quest/m1/archive/README.md) - the recovery this bounds ships with the line
19 changes: 19 additions & 0 deletions quest/m2/auth-expired-error.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# [S] Expired token error

## Goal

A session whose token expired reports `moq_net::Error::Expired`, not
`Unauthorized`, over moq-lite and moq-transport, mirrored in `@moq/net` and
the bindings. A client can then refresh its token instead of treating the
refusal as final.

## Plan

- Add the variant to the `#[non_exhaustive]` error, so the change is additive
on `main`. Map it from lite's `AUTH_ERROR { Expired }` and moq-transport's
`EXPIRED_AUTH_TOKEN`, and back again when refusing.
- Carry it through moq-ffi's error mapping and each wrapper.

## Required

- [In-band auth](/quest/m1/auth/README.md) - the AUTH streams that carry these codes
17 changes: 17 additions & 0 deletions quest/m2/obs-wave-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# [XS] OBS channel layouts

## Goal

The OBS source maps a channel count to the same default layout moq-audio does,
the WAVE convention (3 is 2.1, 4 is quad, 6 is 5.1, 8 is 7.1), so a
multichannel broadcast plays with its speakers where the publisher put them.

## Plan

`audio_layout_to_speakers` in `cpp/obs/src/moq-source.cpp` maps from FFmpeg
layouts today. Map each count to the nearest OBS `speaker_layout` and refuse
the ones OBS cannot place rather than guess. Note the mapping in `doc/bin/obs.md`.

## Related

- [Audio codecs](/quest/m1/audio-codecs/README.md) - the channel layouts this mirrors
Comment on lines +15 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require the layout contract before mapping OBS channels

Make the layout quest a prerequisite rather than merely related. At this commit moq_audio::Layout::from_channels maps every count above two to Discrete(n), whose speaker positions are deliberately unspecified, while audio-codecs/layout.md is the work that changes binding channel counts to WAVE defaults. Because this quest is otherwise ready now, implementing it first would reinterpret arbitrary discrete channels as 2.1/quad/5.1/7.1 and route them to incorrect speakers.

Useful? React with 馃憤聽/ 馃憥.

Loading