From 347c4823d93b7944100d0a1b06b7cb9dd6480731 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Wed, 11 Oct 2023 14:14:51 +0200 Subject: [PATCH] fixes seek in m4a files, fixes seeking having 1 second granularity --- src/decoder/symphonia.rs | 51 ++++++++++++++++++++++++---------------- tests/seek.rs | 24 +++++++++---------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/decoder/symphonia.rs b/src/decoder/symphonia.rs index d8d9924c..3038bd0b 100644 --- a/src/decoder/symphonia.rs +++ b/src/decoder/symphonia.rs @@ -155,36 +155,47 @@ impl Source for SymphoniaDecoder { .total_duration() .is_some_and(|dur| dur.saturating_sub(pos).as_millis() < 1); - if seek_beyond_end { - self.format.seek( - SeekMode::Accurate, - SeekTo::Time { - time: self.total_duration.unwrap(), - track_id: None, - }, - )?; + let time = if seek_beyond_end { + let time = self.total_duration.expect("if guarentees this is Some"); + adjust_down_a_bit(time) // some decoders can only seek to just before the end } else { let frac = if pos.subsec_nanos() == 0 { 0f64 } else { - 1f64 / pos.subsec_nanos() as f64 + let res = pos.subsec_nanos() as f64 / 1_000_000_000.0; + res }; - self.format.seek( - SeekMode::Accurate, - SeekTo::Time { - time: Time { - seconds: pos.as_secs(), - frac, - }, - track_id: None, - }, - )?; - } + Time { + seconds: pos.as_secs(), + frac, + } + }; + self.format.seek( + SeekMode::Accurate, + SeekTo::Time { + time, + track_id: None, + }, + )?; Ok(()) } } +fn adjust_down_a_bit( + Time { + mut seconds, + mut frac, + }: Time, +) -> Time { + frac -= 0.0001; + if frac < 0.0 { + seconds = seconds.saturating_sub(1); + frac = 1.0 - frac; + } + Time { seconds, frac } +} + impl Iterator for SymphoniaDecoder { type Item = i16; diff --git a/tests/seek.rs b/tests/seek.rs index 04d0bf09..7b97f482 100644 --- a/tests/seek.rs +++ b/tests/seek.rs @@ -36,37 +36,37 @@ fn sink_and_decoder(format: &str) -> (Sink, Decoder) { // --features symphonia-flac --features symphonia-isomp4 --features minimp3 fn format_decoder_info() -> &'static [(&'static str, bool, &'static str)] { &[ - #[cfg(feature = "minimp3")] + #[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))] ("mp3", false, "minimp3"), #[cfg(feature = "symphonia-mp3")] ("mp3", true, "symphonia"), - #[cfg(feature = "hound")] + #[cfg(all(feature = "wav", not(feature = "symphonia-wav")))] ("wav", true, "hound"), #[cfg(feature = "symphonia-wav")] ("wav", true, "symphonia"), - #[cfg(feature = "lewton")] + #[cfg(all(feature = "vorbis", not(feature = "symphonia-vorbis")))] ("ogg", true, "lewton"), - // note: disabled, symphonia returns error unsupported format + // note: disabled, broken decoder see issue: #516 // #[cfg(feature = "symphonia-vorbis")] // ("ogg", true, "symphonia"), - #[cfg(feature = "claxon")] + #[cfg(all(feature = "flac", not(feature = "symphonia-flac")))] ("flac", false, "claxon"), #[cfg(feature = "symphonia-flac")] ("flac", true, "symphonia"), // note: disabled, symphonia returns error unsupported format - // #[cfg(feature = "symphonia-isomp4")] - // ("m4a", true, "symphonia"), + #[cfg(feature = "symphonia-isomp4")] + ("m4a", true, "symphonia"), ] } #[test] fn seek_returns_err_if_unsupported() { - for (format, supported, decoder) in format_decoder_info().iter().cloned() { - println!("trying: {format},\t\tby: {decoder},\t\tshould support seek: {supported}"); + for (format, supported, decoder_name) in format_decoder_info().iter().cloned() { + println!("trying: {format},\t\tby: {decoder_name},\t\tshould support seek: {supported}"); let (sink, decoder) = sink_and_decoder(format); sink.append(decoder); - let res = sink.try_seek(Duration::from_secs(2)); - assert_eq!(res.is_ok(), supported); + let res = sink.try_seek(Duration::from_millis(2500)); + assert_eq!(res.is_ok(), supported, "decoder: {decoder_name}"); } } @@ -83,7 +83,7 @@ fn seek_beyond_end_saturates() { println!("seeking beyond end for: {format}\t decoded by: {decoder_name}"); let res = sink.try_seek(Duration::from_secs(999)); - assert!(res.is_ok()); + assert!(dbg!(res).is_ok()); let now = Instant::now(); sink.sleep_until_end();