Remedia

Rust output adapters

Implement bounded PCM and Opus consumers and configure encoding.

Media output contract

Implement MediaOutput for an output consumer. Each session owns one output instance. write_frame takes ownership of a PcmFrame and returns after that frame is accepted. This synchronous call applies backpressure directly: while an output is full, the session worker does not decode more audio. An output may use its own buffer, but it must bound that buffer and honor cancellation.

Outputs implement flush to discard pre-seek audio, pause and resume for downstream suspension, finish for natural end-of-stream, and thread-safe cancel to interrupt an in-progress operation during stop or failure. played_position may report a consumed/audible timeline. Return None when the output cannot provide one. The session holds no lock while calling output methods.

Output methods execute on the playback worker, except cancel, which can run concurrently to unblock them. A custom output must make cancel safe alongside write_frame, flush, and finish. stop() waits for those methods to return and then joins the worker. The current implementation uses one OS thread per active session and does not introduce a global scheduler or real-time pacing.

Opus encoding output

OpusEncoderOutput adapts normalized PCM to owned EncodedOpusPacket values and passes them to an OpusPacketSink. The default packet duration is 20 ms (960 samples per channel at 48 kHz), stereo. It accumulates variable-sized decoder frames and does not pace encoding. A final incomplete frame is zero-padded to 960 samples. Packet metadata reports both the encoded duration and the shorter amount of source audio represented. A seek drops partial PCM and resets the native encoder before sending frames from the new timeline.

Each Opus output owns its encoder and sink. The sink contract is synchronous and backpressured: implementations should use bounded storage and make cancel safe alongside a blocked send. The adapter does not expose Discord types and can be used with other packet consumers. Cargo builds libopus from bundled source and statically links it. A system libopus development package is not required.

Encoder configuration

OpusEncoderConfig::default() explicitly selects 128000 bits per second (128 kbit/s for the stereo stream), complexity 9, and variable bitrate (vbr: true). Higher bitrate generally improves quality while using more bandwidth. Complexity 0–10 trades encoding CPU for quality. Higher values use more computation. VBR distributes bits according to audio complexity. Disabling it requests constant bitrate. The validated bitrate range is 500–512000 bits per second, following the libopus control documentation. The application mode remains Application::Audio.

Rust consumers can pass reusable settings to an output:

use remedia_core::{OpusEncoderConfig, OpusEncoderOutput, OpusPacketSink, OutputError};

fn configured_output(sink: impl OpusPacketSink) -> Result<OpusEncoderOutput, OutputError> {
    let config = OpusEncoderConfig {
        bitrate: 96_000,
        complexity: 5,
        ..Default::default()
    };
    OpusEncoderOutput::new_with_config(sink, config)
}

config.validate() checks ranges without creating libopus state. new_with_config validates immediately. Native allocation and control errors propagate on the first PCM write. Use new_with_config_and_observability(sink, config, handle) to attach diagnostics as well. Existing new and new_with_observability constructors use the defaults. Each output owns its configuration and encoder. There are no global encoder settings. Seek resets retain the configured controls.

These controls apply only to PCM encoding. Compatible source Opus packets still pass through unchanged without initializing libopus. Their original bitrate and quality are unaffected. Filters or gain that require PCM activate encoding with the output's settings. The server resolves settings from audio.opus. Embedded Node.js uses core defaults without a configuration file or a new JavaScript API.

On this page