diff --git a/Cargo.toml b/Cargo.toml index 805f46b..8d7be6f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" tracing = { version = "0.1", features = ["log"] } tracing-futures = "0.2" +symphonia-core = "0.2" [dependencies.async-trait] optional = true diff --git a/src/input/dca.rs b/src/input/dca.rs index 87e6ea6..dd37b39 100644 --- a/src/input/dca.rs +++ b/src/input/dca.rs @@ -1,6 +1,6 @@ use super::{codec::OpusDecoderState, error::DcaError, Codec, Container, Input, Metadata, Reader}; use serde::Deserialize; -use std::{ffi::OsStr, io::BufReader, mem}; +use std::{ffi::OsStr, mem}; #[cfg(not(feature = "tokio-02-marker"))] use tokio::{fs::File as TokioFile, io::AsyncReadExt}; #[cfg(feature = "tokio-02-marker")] @@ -46,7 +46,7 @@ async fn _dca(path: &OsStr) -> Result { .await .map_err(DcaError::IoError)?; - let reader = BufReader::new(json_reader.into_inner().into_std().await); + let reader = json_reader.into_inner().into_std().await; let metadata: Metadata = serde_json::from_slice::(raw_json.as_slice()) .map_err(DcaError::InvalidMetadata)? @@ -56,7 +56,7 @@ async fn _dca(path: &OsStr) -> Result { Ok(Input::new( stereo, - Reader::File(reader), + Reader::from_file(reader), Codec::Opus(OpusDecoderState::new().map_err(DcaError::Opus)?), Container::Dca { first_frame: (size as usize) + mem::size_of::() + header.len(), diff --git a/src/input/reader.rs b/src/input/reader.rs index 47534df..98e0055 100644 --- a/src/input/reader.rs +++ b/src/input/reader.rs @@ -17,14 +17,13 @@ use std::{ result::Result as StdResult, }; use streamcatcher::{Catcher, TxCatcher}; +pub use symphonia_core::io::MediaSource; /// Usable data/byte sources for an audio stream. /// -/// Users may define their own data sources using [`Extension`] -/// and [`ExtensionSeek`]. +/// Users may define their own data sources using [`Extension`]. /// /// [`Extension`]: Reader::Extension -/// [`ExtensionSeek`]: Reader::ExtensionSeek pub enum Reader { /// Piped output of another program (i.e., [`ffmpeg`]). /// @@ -44,40 +43,38 @@ pub enum Reader { /// /// Supports seeking. Restartable(Restartable), - /// A source contained in a local file. - /// - /// Supports seeking. - File(BufReader), - /// A source contained as an array in memory. - /// - /// Supports seeking. - Vec(Cursor>), /// A basic user-provided source. /// - /// Does not support seeking. - Extension(Box), - /// A user-provided source which also implements [`Seek`]. - /// - /// Supports seeking. - /// - /// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html - ExtensionSeek(Box), + /// Seeking support depends on underlying `MediaSource` implementation. + Extension(Box), } impl Reader { /// Returns whether the given source implements [`Seek`]. /// + /// This might be an expensive operation and might involve blocking IO. In such cases, it is + /// advised to cache the return value when possible. + /// /// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html pub fn is_seekable(&self) -> bool { use Reader::*; match self { Restartable(_) | Compressed(_) | Memory(_) => true, - Extension(_) => false, - ExtensionSeek(_) => true, + Extension(source) => source.is_seekable(), _ => false, } } + /// A source contained in a local file. + pub fn from_file(file: File) -> Self { + Self::Extension(Box::new(file)) + } + + /// A source contained as an array in memory. + pub fn from_memory(buf: Vec) -> Self { + Self::Extension(Box::new(Cursor::new(buf))) + } + #[allow(clippy::single_match)] pub(crate) fn prep_with_handle(&mut self, handle: Handle) { use Reader::*; @@ -105,10 +102,7 @@ impl Read for Reader { Memory(a) => Read::read(a, buffer), Compressed(a) => Read::read(a, buffer), Restartable(a) => Read::read(a, buffer), - File(a) => Read::read(a, buffer), - Vec(a) => Read::read(a, buffer), Extension(a) => a.read(buffer), - ExtensionSeek(a) => a.read(buffer), } } } @@ -117,16 +111,22 @@ impl Seek for Reader { fn seek(&mut self, pos: SeekFrom) -> IoResult { use Reader::*; match self { - Pipe(_) | Extension(_) => Err(IoError::new( + Pipe(_) => Err(IoError::new( IoErrorKind::InvalidInput, "Seeking not supported on Reader of this type.", )), Memory(a) => Seek::seek(a, pos), Compressed(a) => Seek::seek(a, pos), - File(a) => Seek::seek(a, pos), Restartable(a) => Seek::seek(a, pos), - Vec(a) => Seek::seek(a, pos), - ExtensionSeek(a) => a.seek(pos), + Extension(a) => + if a.is_seekable() { + a.seek(pos) + } else { + Err(IoError::new( + IoErrorKind::InvalidInput, + "Seeking not supported on Reader of this type.", + )) + }, } } } @@ -139,51 +139,14 @@ impl Debug for Reader { Memory(a) => format!("{:?}", a), Compressed(a) => format!("{:?}", a), Restartable(a) => format!("{:?}", a), - File(a) => format!("{:?}", a), - Vec(a) => format!("{:?}", a), Extension(_) => "Extension".to_string(), - ExtensionSeek(_) => "ExtensionSeek".to_string(), }; f.debug_tuple("Reader").field(&field).finish() } } impl From> for Reader { - fn from(val: Vec) -> Reader { - Reader::Vec(Cursor::new(val)) - } -} - -/// Fusion trait for custom input sources which allow seeking. -pub trait ReadSeek { - /// See [`Read::read`]. - /// - /// [`Read::read`]: https://doc.rust-lang.org/nightly/std/io/trait.Read.html#tymethod.read - fn read(&mut self, buf: &mut [u8]) -> IoResult; - /// See [`Seek::seek`]. - /// - /// [`Seek::seek`]: https://doc.rust-lang.org/nightly/std/io/trait.Seek.html#tymethod.seek - fn seek(&mut self, pos: SeekFrom) -> IoResult; -} - -impl Read for dyn ReadSeek { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - ReadSeek::read(self, buf) - } -} - -impl Seek for dyn ReadSeek { - fn seek(&mut self, pos: SeekFrom) -> IoResult { - ReadSeek::seek(self, pos) - } -} - -impl ReadSeek for R { - fn read(&mut self, buf: &mut [u8]) -> IoResult { - Read::read(self, buf) - } - - fn seek(&mut self, pos: SeekFrom) -> IoResult { - Seek::seek(self, pos) + fn from(val: Vec) -> Self { + Self::from_memory(val) } }