diff --git a/Cargo.lock b/Cargo.lock index f32a2191..cd157138 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -155,6 +155,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "combine" version = "4.6.7" @@ -805,6 +814,16 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opusic-sys" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cedad37fbb9d84c9f70a9cfa150798e7ae09466e353f911ea74ff108abeb50b1" +dependencies = [ + "cmake", + "libc", +] + [[package]] name = "parking_lot" version = "0.12.4" @@ -1050,6 +1069,7 @@ dependencies = [ "rstest_reuse", "rtrb", "symphonia", + "symphonia-adapter-libopus", "thiserror 2.0.12", "tracing", ] @@ -1252,6 +1272,16 @@ dependencies = [ "symphonia-metadata", ] +[[package]] +name = "symphonia-adapter-libopus" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a630acf003f3d39d93eaaa3b8d09c872170ea202ff39bef2248c96682de1a4" +dependencies = [ + "opusic-sys", + "symphonia-core", +] + [[package]] name = "symphonia-bundle-flac" version = "0.5.4" diff --git a/Cargo.toml b/Cargo.toml index 631b5279..418b52fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,8 @@ hound = ["dep:hound"] # WAV minimp3 = ["dep:minimp3_fixed"] # MP3 lewton = ["dep:lewton"] # Ogg Vorbis +# Third party codec example +libopus = ["dep:symphonia-adapter-libopus"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] @@ -124,6 +126,8 @@ atomic_float = { version = "1.1.0", optional = true } rtrb = { version = "0.3.2", optional = true } num-rational = "0.4.2" +symphonia-adapter-libopus = {version="0.2", optional = true} + [dev-dependencies] quickcheck = "1" rstest = "0.25" @@ -251,3 +255,7 @@ required-features = ["playback", "vorbis"] [[example]] name = "stereo" required-features = ["playback", "vorbis"] + +[[example]] +name = "third_party_codec" +required-features = ["playback", "symphonia", "libopus"] diff --git a/assets/music.opus b/assets/music.opus new file mode 100644 index 00000000..4c8e8224 Binary files /dev/null and b/assets/music.opus differ diff --git a/examples/third_party_codec.rs b/examples/third_party_codec.rs new file mode 100644 index 00000000..a9757c8d --- /dev/null +++ b/examples/third_party_codec.rs @@ -0,0 +1,26 @@ +use std::{error::Error, sync::Arc}; + +use rodio::decoder::DecoderBuilder; +use symphonia::{core::codecs::CodecRegistry, default::register_enabled_codecs}; +use symphonia_adapter_libopus::OpusDecoder; + +fn main() -> Result<(), Box> { + let stream_handle = rodio::OutputStreamBuilder::open_default_stream()?; + let sink = rodio::Sink::connect_new(stream_handle.mixer()); + + let mut codec_registry = CodecRegistry::new(); + codec_registry.register_all::(); + register_enabled_codecs(&mut codec_registry); + + let codec_registry_arc = Arc::new(codec_registry); + + let file = std::fs::File::open("../assets/music.opus")?; + let decoder = DecoderBuilder::new() + .with_codec_registry(codec_registry_arc) + .with_data(file).build()?; + sink.append(decoder); + + sink.sleep_until_end(); + + Ok(()) +} diff --git a/src/decoder/builder.rs b/src/decoder/builder.rs index 8d6dc247..047af652 100644 --- a/src/decoder/builder.rs +++ b/src/decoder/builder.rs @@ -39,10 +39,16 @@ use std::io::{Read, Seek}; +#[cfg(feature = "symphonia")] +use crate::decoder::symphonia::SymphoniaRegistry; + #[cfg(feature = "symphonia")] use self::read_seek_source::ReadSeekSource; #[cfg(feature = "symphonia")] -use ::symphonia::core::io::{MediaSource, MediaSourceStream}; +use ::symphonia::core::{ + codecs::CodecRegistry, + io::{MediaSource, MediaSourceStream}, +}; use super::*; @@ -77,6 +83,9 @@ pub struct Settings { /// Whether the decoder should report as seekable. pub(crate) is_seekable: bool, + + #[cfg(feature = "symphonia")] + pub(crate) codec_registry: SymphoniaRegistry, } impl Default for Settings { @@ -88,6 +97,7 @@ impl Default for Settings { hint: None, mime_type: None, is_seekable: false, + codec_registry: SymphoniaRegistry::Default, } } } @@ -231,6 +241,15 @@ impl DecoderBuilder { self } + /// Set a custom codec registry + /// + /// See the symphonia documentation of Registry for how to add additional (third party) codecs. + #[cfg(feature = "symphonia")] + pub fn with_codec_registry(mut self, codec_registry: Arc) -> Self { + self.settings.codec_registry = SymphoniaRegistry::Custom(codec_registry); + self + } + /// Configure whether the data supports random access seeking. Without this, /// only forward seeking may work. /// diff --git a/src/decoder/symphonia.rs b/src/decoder/symphonia.rs index 4154850f..63034a19 100644 --- a/src/decoder/symphonia.rs +++ b/src/decoder/symphonia.rs @@ -1,9 +1,9 @@ use core::time::Duration; -use std::sync::Arc; +use std::{fmt::Debug, sync::Arc}; use symphonia::{ core::{ audio::{AudioBufferRef, SampleBuffer, SignalSpec}, - codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL}, + codecs::{CodecRegistry, Decoder, DecoderOptions, CODEC_TYPE_NULL}, errors::Error, formats::{FormatOptions, FormatReader, SeekMode, SeekTo, SeekedTo}, io::MediaSourceStream, @@ -20,6 +20,25 @@ use crate::{ source, Source, }; +#[derive(Clone)] +/// Enum for choosing which codec registry to used with Symphonia decoders. +pub enum SymphoniaRegistry { + /// Use the symphonia default registry symphonia::default::get_codecs() + Default, + + ///Use a custom CodecRegistry + Custom(Arc), +} + +impl Debug for SymphoniaRegistry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Default => write!(f, "Default"), + Self::Custom(_) => write!(f, "Custom"), + } + } +} + pub(crate) struct SymphoniaDecoder { decoder: Box, current_span_offset: usize, @@ -102,8 +121,12 @@ impl SymphoniaDecoder { None => return Ok(None), }; - let mut decoder = symphonia::default::get_codecs() - .make(&track.codec_params, &DecoderOptions::default())?; + let mut decoder = match &settings.codec_registry + { + SymphoniaRegistry::Default => symphonia::default::get_codecs(), + SymphoniaRegistry::Custom(cr) => cr.as_ref() + }.make(&track.codec_params, &DecoderOptions::default())?; + let total_duration = stream .codec_params .time_base