This change is made with queue users in mind. Since sources of this kind *know* how to (re)create themselves, they can avoid being created at all until needed. This also adds machinery to preload tracks *before* they are needed, for gapless playback on queues and so on. Queues make use of the event system to do this.
190 lines
5.4 KiB
Rust
190 lines
5.4 KiB
Rust
//! Raw handlers for input bytestreams.
|
|
|
|
use super::*;
|
|
use std::{
|
|
fmt::{Debug, Error as FormatError, Formatter},
|
|
fs::File,
|
|
io::{
|
|
BufReader,
|
|
Cursor,
|
|
Error as IoError,
|
|
ErrorKind as IoErrorKind,
|
|
Read,
|
|
Result as IoResult,
|
|
Seek,
|
|
SeekFrom,
|
|
},
|
|
result::Result as StdResult,
|
|
};
|
|
use streamcatcher::{Catcher, TxCatcher};
|
|
|
|
/// Usable data/byte sources for an audio stream.
|
|
///
|
|
/// Users may define their own data sources using [`Extension`]
|
|
/// and [`ExtensionSeek`].
|
|
///
|
|
/// [`Extension`]: Reader::Extension
|
|
/// [`ExtensionSeek`]: Reader::ExtensionSeek
|
|
pub enum Reader {
|
|
/// Piped output of another program (i.e., [`ffmpeg`]).
|
|
///
|
|
/// Does not support seeking.
|
|
///
|
|
/// [`ffmpeg`]: super::ffmpeg
|
|
Pipe(BufReader<ChildContainer>),
|
|
/// A cached, raw in-memory store, provided by Songbird.
|
|
///
|
|
/// Supports seeking.
|
|
Memory(Catcher<Box<Reader>>),
|
|
/// A cached, Opus-compressed in-memory store, provided by Songbird.
|
|
///
|
|
/// Supports seeking.
|
|
Compressed(TxCatcher<Box<Input>, OpusCompressor>),
|
|
/// A source which supports seeking by recreating its inout stream.
|
|
///
|
|
/// Supports seeking.
|
|
Restartable(Restartable),
|
|
/// A source contained in a local file.
|
|
///
|
|
/// Supports seeking.
|
|
File(BufReader<File>),
|
|
/// A source contained as an array in memory.
|
|
///
|
|
/// Supports seeking.
|
|
Vec(Cursor<Vec<u8>>),
|
|
/// A basic user-provided source.
|
|
///
|
|
/// Does not support seeking.
|
|
Extension(Box<dyn Read + Send>),
|
|
/// A user-provided source which also implements [`Seek`].
|
|
///
|
|
/// Supports seeking.
|
|
///
|
|
/// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
|
|
ExtensionSeek(Box<dyn ReadSeek + Send>),
|
|
}
|
|
|
|
impl Reader {
|
|
/// Returns whether the given source implements [`Seek`].
|
|
///
|
|
/// [`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,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::single_match)]
|
|
pub(crate) fn prep_with_handle(&mut self, handle: Handle) {
|
|
use Reader::*;
|
|
match self {
|
|
Restartable(r) => r.prep_with_handle(handle),
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::single_match)]
|
|
pub(crate) fn make_playable(&mut self) {
|
|
use Reader::*;
|
|
match self {
|
|
Restartable(r) => r.make_playable(),
|
|
_ => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Read for Reader {
|
|
fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
|
|
use Reader::*;
|
|
match self {
|
|
Pipe(a) => Read::read(a, buffer),
|
|
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Seek for Reader {
|
|
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
|
use Reader::*;
|
|
match self {
|
|
Pipe(_) | Extension(_) => 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),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Debug for Reader {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), FormatError> {
|
|
use Reader::*;
|
|
let field = match self {
|
|
Pipe(a) => format!("{:?}", a),
|
|
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<Vec<u8>> for Reader {
|
|
fn from(val: Vec<u8>) -> 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<usize>;
|
|
/// 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<u64>;
|
|
}
|
|
|
|
impl Read for dyn ReadSeek {
|
|
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
ReadSeek::read(self, buf)
|
|
}
|
|
}
|
|
|
|
impl Seek for dyn ReadSeek {
|
|
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
|
ReadSeek::seek(self, pos)
|
|
}
|
|
}
|
|
|
|
impl<R: Read + Seek> ReadSeek for R {
|
|
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
|
|
Read::read(self, buf)
|
|
}
|
|
|
|
fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
|
|
Seek::seek(self, pos)
|
|
}
|
|
}
|