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)
}
}