Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/lib/c/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and `target/include/moq.h`.
- **Connection health.** `moq_session_stats()` reports available metrics with per-field validity flags. `moq_session_snapshot()` samples those metrics and the negotiated draft name together from the same connection. Its protocol string is backed by static storage. Both return an offline error between reconnects and leave the destination untouched. `moq_session_bandwidth()` mints an allocator over the send estimate; `moq_bandwidth_reserve` claims a share for an app-owned track, and `moq_encode_video` / `moq_encode_audio` take the same handle so the built-in video encoder follows the grant.
- **Raw playback.** Raw audio and video consumers start at the newest cached group when opened, so rebuilding a live decoder skips the retained backlog.
- **Raw audio encode.** `moq_audio_encoder_output.codec` names the codec (only `"opus"` today), and `frame_duration_us` sets the Opus frame length: 2500, 5000, 10000, 20000, 40000, or 60000, with 0 meaning the 20 ms default.
- **Audio channel layouts.** A `channels` count also names the speaker layout, by the WAVE convention: 1 is mono, 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1, and 8 7.1, interleaved front left, front right, center, LFE, back, then side. `moq_decode_audio` remixes to the count you ask for; past 8 channels the samples pass through but can't be remixed.
- **Raw decode output.** `moq_video_decoder_output` selects the decoded CPU pixel format (`MOQ_VIDEO_PIXEL_FORMAT_I420` or `_RGBA`) and target size (`width`/`height`, both zero for native; otherwise even and non-zero). Unknown formats and invalid sizes fail `moq_decode_video` before subscribing; accepted requests deliver exactly that layout or fail on the terminal callback.
- **Encoded video metadata.** `moq_video_init.hint` is a zero-initialized `moq_video_hint` with `has_*` flags for coded dimensions, bitrate (bits per second), frame rate, and latency preference. Hints seed a video codec track's catalog; detected dimensions take precedence.
- **Client config.** A zeroed `moq_client_config` means the defaults for every knob, which is what lets a new one be appended without disturbing callers. Fields cover protocol (`versions`), TLS (`tls_fingerprints`, `tls_roots`, `tls_cert`/`_key`, `tls_host_name`), transport (`bind`, `connect_timeout_us`, the Happy Eyeballs delays, `websocket_enabled`/`_delay_us`), and tuning (reconnect backoff, `quic_*`). Every duration is in microseconds. A knob whose default isn't zero carries a `has_*` flag, so setting `backoff_timeout_us = 0` needs `has_backoff_timeout = true` to mean "retry forever" rather than "use the default". `moq_client_defaults()` reports what a NULL config dials with.
Expand Down
6 changes: 6 additions & 0 deletions doc/lib/go/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ one: `FetchGroup`/`FetchMediaGroup`, `Dynamic()` with `Requests(ctx)`,
`AppendDatagram`/`Datagrams(ctx)`, `SetCatalogSection`, `Demand()` for `Used`/`Unused`,
`Session().Stats()`. `moq.IsAuthError` and `moq.IsShutdown` classify errors. `moq.ProtocolError(err)` is the structured protocol failure (scope, verbatim code, kind) when the peer sent one.

Audio `Channels` also names the speaker layout, by the WAVE convention: 1 is
mono, 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1, and 8 7.1, interleaved
front left, front right, center, LFE, back, then side. Decoding remixes to the
count you ask for; past 8 channels the samples pass through but can't be
remixed.

`DecodeVideo` picks the decoded CPU pixel layout: `VideoDecoderOutput.Format`
is I420 when nil, or `VideoPixelFormatRgba` for four bytes a pixel, and every
`VideoDecodedFrame` repeats the layout it was decoded to. `Resize` is best
Expand Down
6 changes: 6 additions & 0 deletions doc/lib/kt/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ native side.
`AudioCodec.opus()`, and `AudioEncoderOutput.frameDurationUs` sets the Opus
frame length: 2500, 5000, 10000, 20000 (the default), 40000, or 60000.

Audio `channels` also names the speaker layout, by the WAVE convention: 1 is
mono, 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1, and 8 7.1, interleaved
front left, front right, center, LFE, back, then side. Decoding remixes to the
count you ask for; past 8 channels the samples pass through but can't be
remixed.

`decodeVideo` picks the decoded CPU pixel layout: `VideoDecoderOutput.format`
is `VideoPixelFormat.I420` when null, or `VideoPixelFormat.RGBA` for four bytes
a pixel, and every frame repeats the layout it was decoded to. `resize` is best
Expand Down
6 changes: 6 additions & 0 deletions doc/lib/py/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ WebSocket, TCP, and Unix sockets.
`moq.AudioCodec.opus()`, and `AudioEncoderOutput.frame_duration_us` sets the
Opus frame length: 2500, 5000, 10000, 20000 (the default), 40000, or 60000.

Audio `channels` also names the speaker layout, by the WAVE convention: 1 is
mono, 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1, and 8 7.1, interleaved
front left, front right, center, LFE, back, then side. Decoding remixes to the
count you ask for; past 8 channels the samples pass through but can't be
remixed.

`decode_video` picks the decoded CPU pixel layout: `VideoDecoderOutput.format`
is `VideoPixelFormat.I420` when unset, or `VideoPixelFormat.RGBA` for four
bytes a pixel, and every frame repeats the layout it was decoded to. `resize`
Expand Down
10 changes: 8 additions & 2 deletions doc/lib/rs/moq-audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ the far end. Everything is Rust, so there is no C toolchain, CMake step, or
codec to install.

`Layout` names speaker meaning separately from a channel count. `Mono` is center,
`Stereo` is left then right, and `Discrete(n)` preserves unnamed channels without
inventing speaker positions. Encoding keeps source PCM in `encode::Input` and
`Stereo` is left then right, and the surround layouts up to `SevenPointOne`
interleave in the SMPTE/WAVE order (front left, front right, center, LFE, back,
side). A catalog carries only a count, which reads as that count's WAVE default
(`Layout::from_channels`: 6 is 5.1, 8 is 7.1). `Discrete(n)` preserves unnamed
channels without inventing speaker positions, so it passes through but never
remixes. Decoding and playback downmix with the ITU-R BS.775 coefficients and
upmix by leaving the extra speakers silent; the playback mix runs in whatever
layout the output device opened. Encoding keeps source PCM in `encode::Input` and
codec requirements in `encode::Settings`; `encode::Options` adds publication
policy. Decoding likewise separates low-level `decode::Config`, PCM
`decode::Output`, and subscription `decode::Options`.
Expand Down
6 changes: 6 additions & 0 deletions doc/lib/swift/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ divides the connection's send estimate; pass it to `encodeVideo` /
`AudioCodec.opus()`, and `AudioEncoderOutput.frameDurationUs` sets the Opus
frame length: 2500, 5000, 10000, 20000 (the default), 40000, or 60000.

Audio `channels` also names the speaker layout, by the WAVE convention: 1 is
mono, 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1, and 8 7.1, interleaved
front left, front right, center, LFE, back, then side. Decoding remixes to the
count you ask for; past 8 channels the samples pass through but can't be
remixed.

`decodeVideo` picks the decoded CPU pixel layout: `VideoDecoderOutput.format`
is `.i420` when unset, or `.rgba` for four bytes a pixel, and every frame
repeats the layout it was decoded to. `resize` is best effort: only NVDEC has a
Expand Down
8 changes: 5 additions & 3 deletions js/hang/src/util/aac.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@ describe("audioSpecificConfig", () => {
expect(audioSpecificConfig(48000, 8)).toEqual(new Uint8Array([0x11, 0xb8]));
});

// Unsupported channel counts fall back to stereo (config 2).
it("unsupported channel count falls back to stereo", () => {
expect(audioSpecificConfig(48000, 7)).toEqual(audioSpecificConfig(48000, 2));
// A count with no channel configuration is refused rather than described as stereo.
it("refuses a channel count with no configuration", () => {
for (const count of [0, 7, 9]) {
expect(() => audioSpecificConfig(48000, count)).toThrow();
}
});

// Non-table sample rates use the 5-byte explicit-frequency form (freqIndex 0xF).
Expand Down
10 changes: 5 additions & 5 deletions js/hang/src/util/aac.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ export function pickRate(rate: number): number {
const AAC_LC = 2; // audioObjectType for AAC-LC

// Map a channel count to its AAC channelConfiguration (ISO 14496-3 Table 1.19). Configs 1..=6 are
// identity (5.1 is config 6 / 6 channels); 8 channels is config 7 (7.1). Anything else has no valid
// config, so fall back to stereo (matching the Rust muxer in rs/moq-mux/src/codec/aac).
// identity (5.1 is config 6 / 6 channels); 8 channels is config 7 (7.1). Anything else has no
// config, and describing it as stereo would mislabel every channel past the first two.
function channelConfig(channelCount: number): number {
if (channelCount >= 1 && channelCount <= 6) return channelCount;
if (Number.isInteger(channelCount) && channelCount >= 1 && channelCount <= 6) return channelCount;
if (channelCount === 8) return 7;
return 2;
throw new Error(`no AAC channel configuration for ${channelCount} channels`);
}

/**
* Build the AAC-LC AudioSpecificConfig that decoders need when frames are raw (no ADTS header).
*
* Standard sample rates produce the 2-byte form; non-table rates fall back to the 5-byte form
* with an explicit 24-bit frequency. Mirrors the Rust muxer so JS and Rust agree on the bytes.
* with an explicit 24-bit frequency. Throws for a channel count no configuration names.
*/
export function audioSpecificConfig(sampleRate: number, channelCount: number): Uint8Array {
const config = channelConfig(channelCount);
Expand Down
1 change: 0 additions & 1 deletion quest/m1/audio-codecs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ its own decode and encode quest so verification stays per host.
## Quests

- [TS export PCE](/quest/m1/audio-codecs/ts-export-pce.md) - a TS export of a PCE-described AAC track writes channel_config 0 and the PCE instead of a count-derived config
- [Layout](/quest/m1/audio-codecs/layout.md) - the settled `Layout` carries up to 7.1 through decode, resample, playback, and the FFI
- [Decode seam](/quest/m1/audio-codecs/decode-backend.md) - `decode::backend` selects a platform decoder before symphonia, mirroring moq-video
- [AudioToolbox decode](/quest/m1/audio-codecs/decode-audiotoolbox.md) - macOS and iOS decode HE-AAC, multichannel AAC, and what else the framework offers
- [Opus surround](/quest/m1/audio-codecs/opus-surround.md) - mapping family 1 decodes on every host through the multistream decoder
Expand Down
1 change: 0 additions & 1 deletion quest/m1/audio-codecs/decode-audiotoolbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ binding, alongside the `objc2-core-audio-types` the crate already carries.
## Required

- [Decode seam](/quest/m1/audio-codecs/decode-backend.md) - the candidate order this backend joins
- [Layout](/quest/m1/audio-codecs/layout.md) - what a multichannel frame is delivered as

## Related

Expand Down
4 changes: 0 additions & 4 deletions quest/m1/audio-codecs/decode-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,3 @@ stats and `moq play` surface.
backend like the video seam's `probe`.

The FFI does not expose `Kind` until a consumer asks.

## Related

- [Layout](/quest/m1/audio-codecs/layout.md) - independent; the platform backends need both
1 change: 0 additions & 1 deletion quest/m1/audio-codecs/encode-audiotoolbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@ the encode seam as the platform candidate on macOS and iOS.
## Required

- [Encode seam](/quest/m1/audio-codecs/encode-backend.md) - the candidate order this backend joins
- [Layout](/quest/m1/audio-codecs/layout.md) - the input layout the encoder accepts
- [AudioToolbox decode](/quest/m1/audio-codecs/decode-audiotoolbox.md) - the round-trip regression decodes through it
54 changes: 0 additions & 54 deletions quest/m1/audio-codecs/layout.md

This file was deleted.

4 changes: 0 additions & 4 deletions quest/m1/audio-codecs/opus-surround.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,3 @@ so this is the one multichannel path Linux gets.
channels.
- Regression: a family-1 5.1 fixture decodes to six channels in canonical
order; family 255 is refused at construction.

## Required

- [Layout](/quest/m1/audio-codecs/layout.md) - the type the mapping resolves to
1 change: 0 additions & 1 deletion quest/m2/aac-encode-refusal.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,3 @@ every count from 1 to 8 and one beyond.
## Related

- [AAC PCE](https://github.com/moq-dev/moq/pull/4093) - the parse half
- [Layout](/quest/m1/audio-codecs/layout.md) - the layout a PCE would be derived from
1 change: 0 additions & 1 deletion quest/m2/audio-decode-mediacodec.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ behind a new optional audio `mediacodec` feature and the decode seam, on `target
## Required

- [Decode seam](/quest/m1/audio-codecs/decode-backend.md) - the candidate order this backend joins
- [Layout](/quest/m1/audio-codecs/layout.md) - what a multichannel frame is delivered as

## Related

Expand Down
1 change: 0 additions & 1 deletion quest/m2/audio-decode-mediafoundation.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ ones. Behind the decode seam as the first candidate on `target_os =
## Required

- [Decode seam](/quest/m1/audio-codecs/decode-backend.md) - the candidate order this backend joins
- [Layout](/quest/m1/audio-codecs/layout.md) - what a multichannel frame is delivered as

## Related

Expand Down
1 change: 0 additions & 1 deletion quest/m2/audio-encode-mediacodec.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ behind the `mediacodec` feature and the encode seam.
## Required

- [Encode seam](/quest/m1/audio-codecs/encode-backend.md) - the candidate order this backend joins
- [Layout](/quest/m1/audio-codecs/layout.md) - the input layout the encoder accepts
- [MediaCodec decode](/quest/m2/audio-decode-mediacodec.md) - the round-trip regression decodes through it
1 change: 0 additions & 1 deletion quest/m2/audio-encode-mediafoundation.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ behind the encode seam on Windows.
## Required

- [Encode seam](/quest/m1/audio-codecs/encode-backend.md) - the candidate order this backend joins
- [Layout](/quest/m1/audio-codecs/layout.md) - the input layout the encoder accepts
- [Media Foundation decode](/quest/m2/audio-decode-mediafoundation.md) - the round-trip regression decodes through it

## Related
Expand Down
7 changes: 6 additions & 1 deletion rs/libmoq/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ pub struct moq_audio_encoder_input {
/// `moq_audio_sample_format` discriminant.
pub format: u32,
pub sample_rate: u32,
/// Interleaved channel count, which also names the speaker layout by the
/// WAVE convention: 1 mono, 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1,
/// 8 7.1, in front left, front right, center, LFE, back, side order.
pub channels: u32,
}

Expand Down Expand Up @@ -98,7 +101,9 @@ pub struct moq_audio_decoder_output {
pub format: u32,
/// 0 = deliver at the codec's native sample rate.
pub sample_rate: u32,
/// 0 = deliver at the codec's native channel count.
/// 0 = deliver at the codec's native channel count. A count names its
/// layout as `moq_audio_encoder_input.channels` describes, and the decoder
/// remixes to it.
pub channels: u32,
/// Upper bound on buffering before skipping a stalled group, in
/// microseconds. Same congestion-control knob as
Expand Down
Loading
Loading