use core::time::Duration;
use symphonia::{
core::{
audio::{AudioBufferRef, SampleBuffer, SignalSpec},
codecs::{Decoder, DecoderOptions, CODEC_TYPE_NULL},
errors::Error,
formats::{FormatOptions, FormatReader, SeekMode, SeekTo, SeekedTo},
io::MediaSourceStream,
meta::MetadataOptions,
probe::Hint,
units,
},
default::get_probe,
};
use super::Settings;
use rodio::{decoder::DecoderError, source, ChannelCount, Sample, SampleRate, Source};
pub struct SymphoniaDecoder {
decoder: Box<dyn Decoder>,
current_span_offset: usize,
format: Box<dyn FormatReader>,
total_duration: Option<Duration>,
buffer: SampleBuffer<Sample>,
spec: SignalSpec,
seek_mode: SeekMode,
}
impl SymphoniaDecoder {
pub fn new(mss: MediaSourceStream, settings: &Settings) -> Result<Self, DecoderError> {
match SymphoniaDecoder::init(mss, settings) {
Err(e) => match e {
Error::IoError(e) => Err(DecoderError::IoError(e.to_string())),
Error::DecodeError(e) => Err(DecoderError::DecodeError(e)),
Error::SeekError(_) => {
unreachable!("Seek errors should not occur during initialization")
}
Error::Unsupported(_) => Err(DecoderError::UnrecognizedFormat),
Error::LimitError(e) => Err(DecoderError::LimitError(e)),
Error::ResetRequired => Err(DecoderError::ResetRequired),
},
Ok(Some(decoder)) => Ok(decoder),
Ok(None) => Err(DecoderError::NoStreams),
}
}
#[inline]
pub(crate) fn into_inner(self) -> MediaSourceStream {
self.format.into_inner()
}
fn init(
mss: MediaSourceStream,
settings: &Settings,
) -> symphonia::core::errors::Result<Option<SymphoniaDecoder>> {
let mut hint = Hint::new();
if let Some(ext) = settings.hint.as_ref() {
hint.with_extension(ext);
}
if let Some(typ) = settings.mime_type.as_ref() {
hint.mime_type(typ);
}
let format_opts: FormatOptions = FormatOptions {
enable_gapless: settings.gapless,
..Default::default()
};
let metadata_opts: MetadataOptions = Default::default();
let seek_mode = if settings.coarse_seek {
SeekMode::Coarse
} else {
SeekMode::Accurate
};
let mut probed = get_probe().format(&hint, mss, &format_opts, &metadata_opts)?;
if let Some(metadata_rev) = probed.metadata.get().as_ref().and_then(|m| m.current()) {
println!("Tags: {:?}", metadata_rev.tags());
}
let stream = match probed.format.default_track() {
Some(stream) => stream,
None => return Ok(None),
};
let track_id = probed
.format
.tracks()
.iter()
.find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
.ok_or(symphonia::core::errors::Error::Unsupported(
"No track with supported codec",
))?
.id;
let track = match probed
.format
.tracks()
.iter()
.find(|track| track.id == track_id)
{
Some(track) => track,
None => return Ok(None),
};
let mut decoder = symphonia::default::get_codecs()
.make(&track.codec_params, &DecoderOptions::default())?;
let total_duration = stream
.codec_params
.time_base
.zip(stream.codec_params.n_frames)
.map(|(base, spans)| base.calc_time(spans).into());
let decoded = loop {
let current_span = match probed.format.next_packet() {
Ok(packet) => packet,
Err(Error::IoError(_)) => break decoder.last_decoded(),
Err(e) => return Err(e),
};
if current_span.track_id() != track_id {
continue;
}
match decoder.decode(¤t_span) {
Ok(decoded) => break decoded,
Err(e) => match e {
Error::DecodeError(_) => {
continue;
}
_ => return Err(e),
},
}
};
let spec = decoded.spec().to_owned();
let buffer = SymphoniaDecoder::get_buffer(decoded, &spec);
Ok(Some(SymphoniaDecoder {
decoder,
current_span_offset: 0,
format: probed.format,
total_duration,
buffer,
spec,
seek_mode,
}))
}
#[inline]
fn get_buffer(decoded: AudioBufferRef, spec: &SignalSpec) -> SampleBuffer<Sample> {
let duration = units::Duration::from(decoded.capacity() as u64);
let mut buffer = SampleBuffer::<Sample>::new(duration, *spec);
buffer.copy_interleaved_ref(decoded);
buffer
}
}
impl Source for SymphoniaDecoder {
#[inline]
fn current_span_len(&self) -> Option<usize> {
Some(self.buffer.len())
}
#[inline]
fn channels(&self) -> ChannelCount {
self.spec.channels.count() as ChannelCount
}
#[inline]
fn sample_rate(&self) -> SampleRate {
self.spec.rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.total_duration
}
fn try_seek(&mut self, pos: Duration) -> Result<(), source::SeekError> {
if matches!(self.seek_mode, SeekMode::Accurate)
&& self.decoder.codec_params().time_base.is_none()
{
return Err(source::SeekError::SymphoniaDecoder(
rodio::decoder::symphonia::SeekError::AccurateSeekNotSupported,
));
}
let mut target = pos;
if let Some(total_duration) = self.total_duration {
if target > total_duration {
target = total_duration;
}
}
let active_channel = self.current_span_offset % self.channels() as usize;
let seek_res = match self.format.seek(
self.seek_mode,
SeekTo::Time {
time: target.into(),
track_id: None,
},
) {
Err(Error::SeekError(symphonia::core::errors::SeekErrorKind::ForwardOnly)) => {
return Err(source::SeekError::SymphoniaDecoder(
rodio::decoder::symphonia::SeekError::RandomAccessNotSupported,
));
}
other => other.map_err(rodio::decoder::symphonia::SeekError::Demuxer),
}?;
self.decoder.reset();
self.current_span_offset = usize::MAX;
if matches!(self.seek_mode, SeekMode::Accurate) {
self.refine_position(seek_res)?;
}
for _ in 0..active_channel {
self.next();
}
Ok(())
}
}
impl SymphoniaDecoder {
fn refine_position(&mut self, seek_res: SeekedTo) -> Result<(), source::SeekError> {
let mut samples_to_skip = (Duration::from(
self.decoder
.codec_params()
.time_base
.expect("time base availability guaranteed by caller")
.calc_time(seek_res.required_ts.saturating_sub(seek_res.actual_ts)),
)
.as_secs_f32()
* self.sample_rate() as f32
* self.channels() as f32)
.ceil() as usize;
samples_to_skip -= samples_to_skip % self.channels() as usize;
for _ in 0..samples_to_skip {
self.next();
}
Ok(())
}
}
impl Iterator for SymphoniaDecoder {
type Item = Sample;
fn next(&mut self) -> Option<Self::Item> {
if self.current_span_offset >= self.buffer.len() {
let decoded = loop {
let packet = self.format.next_packet().ok()?;
let decoded = match self.decoder.decode(&packet) {
Ok(decoded) => decoded,
Err(Error::DecodeError(_)) => {
continue;
}
Err(_) => return None,
};
if decoded.frames() > 0 {
break decoded;
}
};
decoded.spec().clone_into(&mut self.spec);
self.buffer = SymphoniaDecoder::get_buffer(decoded, &self.spec);
self.current_span_offset = 0;
}
let sample = *self.buffer.samples().get(self.current_span_offset)?;
self.current_span_offset += 1;
Some(sample)
}
}