RustAudio / rodio

Rust audio playback library
Apache License 2.0
1.66k stars 214 forks source link

Cannot use symphonia ReadOnlySource due to no seek support #580

Closed yuvadm closed 1 month ago

yuvadm commented 1 month ago

I'm trying to run a basic scenario of playing an MP3 stream from network by using a ReadOnlySource from symphonia.

This is my current usage:

use rodio::decoder::Decoder;
use rodio::OutputStream;
use symphonia::core::io::{MediaSourceStream, ReadOnlySource};

fn main() {
    let (_stream, stream_handle) = OutputStream::try_default().unwrap();
    let url = "http://example.com/stream.mp3";
    let response = reqwest::blocking::get(url).unwrap();
    let ros = ReadOnlySource::new(response);
    let source = MediaSourceStream::new(Box::new(ros), Default::default());
    let decoder = Decoder::new(source).unwrap();
    let sink = rodio::Sink::try_new(&stream_handle).unwrap();

    sink.append(decoder);
    sink.set_volume(1.0);
    // ...
}

While this does compile, it fails with:

$ cargo run
thread 'main' panicked at /home/yuval/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rodio-0.18.1/src/decoder/wav.rs:185:48:
called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: "source does not support seeking" }

What would be the right way to use a non-seeking source? Ideally would like to use any available pre-existing traits from symphonia/rodio, but can also implement any custom traits as necessary.

yuvadm commented 1 month ago

Also, this is might be related to ongoing seek behavior changes i.e. #176 ?

dvdsk commented 1 month ago

That's actually a different kind of seek :)

The problem is your MediaSourceStream does not implement the right IO traits. Instead of fixing that you might be better of using something like: https://github.com/aschey/stream-download-rs

If you want to do this yourself take a look at what the decoder needs. Basically you'll have to write a struct that wraps the reqwest output and implements the Read and Seek traits while it is also Send and Sync. Thats not easy as you want to download while playing so you need multiple threads.

Ive been working on something like this myself but its not fully done yet

yuvadm commented 1 month ago

@dvdsk thanks for the quick response! Would stream-download-rs be the right approach even if I'm doing blocking IO / threads? Generally speaking I don't require async, but I do want the easiest interface for hooking up a stream to rodio.

I've bumped into the recommendation in #439 but minimp3 seems to be a rather broken approach.

dvdsk commented 1 month ago

I recommend you read through https://tokio.rs/tokio/topics/bridging. Almost all of rusts io-libs (reqwest::blocking in your code snippet above does the same as described in the article above. ) are async under the hood, even if the provide a blocking API. Once you get the whole bridging async and non-async code bit it isn't a big deal anymore.

yuvadm commented 1 month ago

Appreciate the clarification, indeed stream-download-rs does the job and has great useful examples.