Skip to main content

zvidlib_vorbis_decoder/
lib.rs

1//! Vorbis audio: decoder configuration, packet timing and native decoding.
2//!
3//! A Vorbis stream is described by three header packets - identification,
4//! comment and setup - which Matroska and WebM carry Xiph-laced as
5//! `CodecPrivate` and a `WebCodecs` `AudioDecoderConfig.description` takes in
6//! the same form. [`VorbisConfig`] parses and writes them, and reads from the
7//! setup header the one thing a container does not record exactly: how many
8//! samples each audio packet decodes to.
9//!
10//! Decoding runs on Symphonia's pure-Rust Vorbis decoder, and encoding on
11//! zvidlib's own port of the libvorbis encoder, so both work on every target
12//! zvidlib builds for, `wasm32` included.
13
14mod vorbis_decoder;
15#[doc(hidden)]
16pub mod vorbis_simd;
17
18#[allow(unused_imports)]
19use zvidlib_core::*;
20
21use crate::vorbis_decoder::VorbisDecoder;
22use crate::{
23    AudioBuffer, AudioDecoder, CancellationToken, EncodedAudioSample, Error, ErrorKind, Limits,
24    Result,
25};
26use symphonia_core::audio::{AudioBufferRef, SampleBuffer};
27use symphonia_core::codecs::{CODEC_TYPE_VORBIS, CodecParameters, Decoder, DecoderOptions};
28use symphonia_core::formats::Packet;
29
30#[doc(inline)]
31pub use zvidlib_vorbis_syntax::{VORBIS_PREROLL_PACKETS, VorbisConfig};
32
33/// A native, pure-Rust Vorbis decoder for streams of one to eight channels,
34/// producing interleaved `f32` PCM in the Vorbis channel order (Vorbis I
35/// section 4.3.9).
36///
37/// A packet decoded straight after [`AudioDecoder::reset`] has no previous
38/// block to overlap and decodes to silence; [`VORBIS_PREROLL_PACKETS`] is the
39/// preroll that keeps an [`crate::AudioSampleReader`] from returning it.
40pub struct NativeVorbisDecoder {
41    decoder: VorbisDecoder,
42    channels: u16,
43    sample_rate: u32,
44    /// Whether nothing has been decoded since the decoder was created or reset.
45    cold: bool,
46    limits: Limits,
47}
48
49impl NativeVorbisDecoder {
50    pub fn new(config: &VorbisConfig, limits: Limits) -> Result<Self> {
51        let channels = u16::from(config.channels);
52        if channels > limits.max_audio_channels || config.sample_rate > limits.max_sample_rate {
53            return Err(Error::new(
54                ErrorKind::ResourceLimit,
55                "Vorbis channel count or sample rate is outside configured limits",
56            ));
57        }
58        let mut extra_data = config.identification_header.clone();
59        extra_data.extend_from_slice(&config.setup_header);
60        let mut parameters = CodecParameters::new();
61        parameters
62            .for_codec(CODEC_TYPE_VORBIS)
63            .with_extra_data(extra_data.into_boxed_slice());
64        let decoder =
65            VorbisDecoder::try_new(&parameters, &DecoderOptions::default()).map_err(|error| {
66                unsupported(format!("Vorbis configuration is unsupported: {error}"))
67            })?;
68        Ok(Self {
69            decoder,
70            channels,
71            sample_rate: config.sample_rate,
72            cold: true,
73            limits,
74        })
75    }
76}
77
78impl AudioDecoder for NativeVorbisDecoder {
79    fn decode(
80        &mut self,
81        sample: &EncodedAudioSample,
82        cancellation: &CancellationToken,
83    ) -> Result<AudioBuffer> {
84        if cancellation.is_canceled() {
85            return Err(Error::new(ErrorKind::Canceled, "Vorbis decode canceled"));
86        }
87        let cold = std::mem::replace(&mut self.cold, false);
88        let channels = usize::from(self.channels);
89        let decoded = self
90            .decoder
91            .decode(&Packet::new_from_slice(
92                0,
93                sample.decoded_range.start,
94                sample.decoded_range.len(),
95                &sample.data,
96            ))
97            .map_err(|error| codec(format!("malformed Vorbis packet: {error}")))?;
98        let frames = decoded.frames();
99        let expected = sample.decoded_range.len();
100        let samples = if frames as u64 == expected {
101            interleave(decoded)
102        } else if frames == 0 && cold {
103            vec![
104                0.0;
105                usize::try_from(expected)
106                    .unwrap_or(usize::MAX)
107                    .saturating_mul(channels)
108            ]
109        } else {
110            return Err(codec(format!(
111                "Vorbis decoder produced {frames} frames for an indexed {expected}-frame interval"
112            )));
113        };
114        AudioBuffer::new(
115            sample.decoded_range,
116            self.sample_rate,
117            self.channels,
118            samples,
119            &self.limits,
120        )
121    }
122
123    fn reset(&mut self) -> Result<()> {
124        self.decoder.reset();
125        self.cold = true;
126        Ok(())
127    }
128}
129
130fn interleave(decoded: AudioBufferRef<'_>) -> Vec<f32> {
131    // Symphonia's interleaving copy panics on an empty buffer, which is what
132    // a stream's first packet decodes to.
133    if decoded.frames() == 0 {
134        return Vec::new();
135    }
136    let mut interleaved = SampleBuffer::<f32>::new(decoded.frames() as u64, *decoded.spec());
137    interleaved.copy_interleaved_ref(decoded);
138    interleaved.samples().to_vec()
139}
140
141fn codec(message: impl Into<String>) -> Error {
142    Error::new(ErrorKind::Codec, message)
143}
144
145fn unsupported(message: impl Into<String>) -> Error {
146    Error::new(ErrorKind::Unsupported, message)
147}
148
149/// The SIMD dispatch sites in this crate, each with the instruction set it
150/// resolves to right now. `zvidlib::simd::active_by_site` reports every crate's
151/// sites together and documents what each one covers.
152#[doc(hidden)]
153#[must_use]
154pub fn simd_sites() -> Vec<(&'static str, zvidlib_core::SimdIsa)> {
155    vec![("vorbis_decode", vorbis_simd::active_isa())]
156}