diff --git a/src/decoder/flac.rs b/src/decoder/flac.rs index 27c761c..88d3d0a 100644 --- a/src/decoder/flac.rs +++ b/src/decoder/flac.rs @@ -88,7 +88,7 @@ where #[inline] fn can_seek(&self) -> bool { - true + false } } diff --git a/src/decoder/symphonia.rs b/src/decoder/symphonia.rs index 9663882..85fcb67 100644 --- a/src/decoder/symphonia.rs +++ b/src/decoder/symphonia.rs @@ -153,7 +153,7 @@ impl Source for SymphoniaDecoder { .seek( SeekMode::Accurate, SeekTo::Time { - time: Time::new(pos.as_secs(), dbg!(pos_fract)), + time: Time::new(pos.as_secs(), pos_fract), track_id: None, }, ) diff --git a/src/sink.rs b/src/sink.rs index cbccfe7..db26cec 100644 --- a/src/sink.rs +++ b/src/sink.rs @@ -201,19 +201,19 @@ impl Sink { /// Try to seek to a pos, returns [`SeekNotSupported`] if seeking is not /// supported by the current source. /// - /// We do not expose a `can_seek()` method here on purpose as its impossible to + /// We do not expose a `can_seek()` method here on purpose as its impossible to /// use correctly. In between checking if the playing source supports seeking and - /// actually seeking the sink can switch to a new source that potentially does not - /// support seeking. If you find a reason you need `Sink::can_seek()` here please + /// actually seeking the sink can switch to a new source that potentially does not + /// support seeking. If you find a reason you need `Sink::can_seek()` here please /// open an issue pub fn try_seek(&self, pos: Duration) -> Result<(), SeekNotSupported> { let (order, feedback) = SeekOrder::new(pos); *self.controls.seek.lock().unwrap() = Some(order); match feedback.recv() { Ok(seek_res) => seek_res, - // The feedback channel closed. Probably another seekorder was set + // The feedback channel closed. Probably another seekorder was set // invalidating this one and closing the feedback channel - // ... or the audio thread panicked. + // ... or the audio thread panicked. Err(_) => Ok(()), } } diff --git a/tests/seek.rs b/tests/seek.rs index 046ec2a..76c0890 100644 --- a/tests/seek.rs +++ b/tests/seek.rs @@ -1,21 +1,37 @@ -use std::io::BufReader; +use std::io::{BufReader, Read, Seek}; use std::path::Path; +use std::sync::Once; use std::time::Duration; -use rodio::source::SeekNotSupported; +use rodio::{Decoder, OutputStream, OutputStreamHandle, Sink, Source}; -fn play_and_seek(asset_path: &Path) -> Result<(), SeekNotSupported> { - let (_stream, handle) = rodio::OutputStream::try_default().unwrap(); - let sink = rodio::Sink::try_new(&handle).unwrap(); +static mut STREAM: Option = None; +static mut STREAM_HANDLE: Option = None; +static INIT: Once = Once::new(); - let file = std::fs::File::open(asset_path).unwrap(); - sink.append(rodio::Decoder::new(BufReader::new(file)).unwrap()); - sink.try_seek(Duration::from_secs(2)) +fn global_stream_handle() -> &'static OutputStreamHandle { + // mutable global access is guarded by Once therefore + // can only happen Once and will not race + unsafe { + INIT.call_once(|| { + let (stream, handle) = rodio::OutputStream::try_default().unwrap(); + STREAM = Some(stream); + STREAM_HANDLE = Some(handle); + }); + STREAM_HANDLE.as_ref().unwrap() + } } -#[test] -fn seek_returns_err_if_unsupported() { - let formats = [ +fn sink_and_decoder(format: &str) -> (Sink, Decoder) { + let sink = rodio::Sink::try_new(global_stream_handle()).unwrap(); + let asset = Path::new("assets/music").with_extension(format); + let file = std::fs::File::open(asset).unwrap(); + let decoder = rodio::Decoder::new(BufReader::new(file)).unwrap(); + (sink, decoder) +} + +fn format_decoder_info() -> &'static [(&'static str, bool, &'static str)] { + &[ #[cfg(feature = "minimp3")] ("mp3", true, "minimp3"), #[cfg(feature = "symphonia-mp3")] @@ -34,12 +50,30 @@ fn seek_returns_err_if_unsupported() { ("flac", true, "symphonia"), #[cfg(feature = "symphonia-isomp4")] ("m4a", true, "_"), - ]; + ] +} - for (format, supported, decoder) in formats { - println!("trying: {format} by {decoder}, should support seek: {supported}"); - let asset = Path::new("assets/music").with_extension(format); - let res = play_and_seek(&asset); +#[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}"); + let (sink, decoder) = sink_and_decoder(format); + assert_eq!(decoder.can_seek(), supported); + sink.append(decoder); + let res = sink.try_seek(Duration::from_secs(2)); assert_eq!(res.is_ok(), supported); } } + +#[test] +fn seek_beyond_end_does_not_crash() { + for (format, _, decoder_name) in format_decoder_info().iter().cloned() { + let (sink, decoder) = sink_and_decoder(format); + if !decoder.can_seek() { + continue; + } + println!("seeking beyond end for: {format}\t decoded by: {decoder_name}"); + sink.append(decoder); + sink.try_seek(Duration::from_secs(999)).unwrap(); + } +}