use std::fs;
use symphonia::{
core::{
audio::GenericAudioBufferRef,
codecs::audio::{AudioDecoder, AudioDecoderOptions},
errors::Error as SymphoniaError,
formats::{FormatOptions, FormatReader, TrackType},
io::{MediaSourceStream, MediaSourceStreamOptions},
},
default::formats::{AiffReader, FlacReader, MpaReader, OggReader, WavReader},
};
use thiserror::Error;
use crate::media_container::{ContainerError, ContainerFormat, MediaContainer, Stream};
#[derive(Debug, Error)]
pub enum DecodingError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Symphonia(#[from] SymphoniaError),
#[error("{0}")]
Container(#[from] ContainerError),
#[error("Player attempted to play an unsupported container: '{0:?}'")]
UnsupportedContainer(ContainerFormat),
}
pub struct RawDecoder {
pub stream: Stream,
pub format_reader: Box<dyn FormatReader>,
pub decoder: Box<dyn AudioDecoder>,
}
impl RawDecoder {
pub fn from_container(
container: &MediaContainer,
buffer_len: usize,
) -> Result<Self, DecodingError> {
let file = fs::File::open(container.path())?;
let options = MediaSourceStreamOptions { buffer_len };
let mss = MediaSourceStream::new(Box::new(file), options);
let fmt_opts = FormatOptions::default();
let format_reader: Box<dyn FormatReader> = match container.format() {
ContainerFormat::Flac => Box::new(FlacReader::try_new(mss, fmt_opts)?),
ContainerFormat::Mpa => Box::new(MpaReader::try_new(mss, fmt_opts)?),
ContainerFormat::Ogg => Box::new(OggReader::try_new(mss, fmt_opts)?),
ContainerFormat::Wav => Box::new(WavReader::try_new(mss, fmt_opts)?),
ContainerFormat::Aiff => Box::new(AiffReader::try_new(mss, fmt_opts)?),
other => return Err(DecodingError::UnsupportedContainer(*other)),
};
let track = format_reader
.default_track(TrackType::Audio)
.expect("Playable file does not have any supported codecs");
let dec_opts: AudioDecoderOptions = Default::default();
let params = track
.codec_params
.as_ref()
.expect("Track is not playable")
.audio()
.unwrap();
let decoder = symphonia::default::get_codecs().make_audio_decoder(params, &dec_opts)?;
Ok(Self {
stream: Stream::try_from(track)?,
format_reader,
decoder,
})
}
pub fn decode_next_packet(
&mut self,
) -> Result<Option<GenericAudioBufferRef<'_>>, DecodingError> {
loop {
let packet = match self.format_reader.next_packet() {
Ok(Some(packet)) => packet,
Err(SymphoniaError::ResetRequired) => {
self.decoder.reset();
continue;
}
Err(SymphoniaError::IoError(err))
if matches!(err.kind(), std::io::ErrorKind::UnexpectedEof) =>
{
return Ok(None);
}
Ok(None) => return Ok(None),
Err(err) => return Err(DecodingError::Symphonia(err)),
};
while !self.format_reader.metadata().is_latest() {
self.format_reader.metadata().pop();
}
if packet.track_id() == self.stream.id {
return match self.decoder.decode(&packet) {
Ok(decoded) => Ok(Some(decoded)),
Err(SymphoniaError::DecodeError(_)) => Ok(None),
Err(SymphoniaError::IoError(_)) => Ok(None),
Err(err) => Err(DecodingError::Symphonia(err)),
};
}
}
}
}