moq-audio 0.0.15

Native audio encoding/decoding for Media over QUIC
Documentation

Native audio capture, encoding, and decoding for Media over QUIC.

Counterpart to moq-video for audio tracks, and shaped the same way. Sits on top of [moq_mux] and [hang] and adds the missing piece for native callers: Rust-native Opus and uncompressed PCM codecs that turn raw samples into HANG audio tracks and back.

  • capture describes an audio source (capture::Config) and grabs buffers per platform: a microphone via cpal (CoreAudio / WASAPI / ALSA) everywhere, or macOS system audio via ScreenCaptureKit. capture::Source picks between them and capture::devices lists the inputs and hands back the ids it takes. Requires the capture feature, so these names are unlinked here: they don't exist in a default build.
  • [encode] encodes PCM and publishes it through moq_mux::container, registering the rendition in the hang catalog. Two entry points:
    • encode::publish_capture captures a microphone and publishes it (turnkey). It encodes strictly on demand: the track and catalog are advertised up front, but the device opens only while a subscriber is listening and is released when the last one leaves.
    • [encode::Producer] publishes PCM you hand it.
  • [decode] subscribes to an encoded track and decodes it back to PCM. [decode::Consumer] is the mirror of [encode::Producer].
  • playback plays decoded PCM out a speaker. playback::Engine owns the output device and mixes the playback::Sinks registered with it, so one device serves every track in a call. Requires the playback feature, so these names are unlinked here too.
  • aec keeps the speaker out of the microphone, which is what a conference on a laptop needs to not send itself back. playback::Engine::canceller builds an aec::Canceller from the mix it is playing and capture::Config::aec hands it to the microphone. Requires the aec feature, which implies both of the above.

[Format] mirrors WebCodecs AudioData.format; the helpers convert between any supported layout and the interleaved f32 representation libopus expects. [Frame] is a thin owned buffer: a timestamp and a payload. PCM layout lives on the producer / consumer via [encode::Input] / [decode::Config], not on each frame, so callers can't drift between calls.