serenity-rs / songbird

An async Rust library for the Discord voice API
ISC License
395 stars 117 forks source link

How do I create an input from a command? #85

Closed SuperCuber closed 11 months ago

SuperCuber commented 3 years ago

Following the docs, I got this far:

let child = Command::new("ffmpeg")
    .args(...)
    .args(&["-f", "opus", "-"])
    .output().unwrap();
let reader = Reader::Vec(Cursor::new(child.stdout));
let track = call.play_only_source(Input::new(
    true,
    reader,
    Codec::Opus(OpusDecoderState::new().unwrap()),
    songbird::input::Container::Dca { first_frame: 0 },
    None,
));

However when the file starts playing, I get this error:

thread '<unnamed>' panicked at 'range end index 26447 out of range for slice of length 1432', ~/.cargo/registry/src/github.com-1ecc6299db9ec823/songbird-0.1.8/src/input/mod.rs:323:34

I suspect the issue is with the first_frame argument, but I can't find what I should put there from the docs or from google.

FelixMcFelix commented 3 years ago

It looks as though you're trying to pass raw/ogg-framed Opus data in as though it were DCA (a discord-focussed format). Unfortunately, songbird doesn't support ogg framing, and raw frames are a little tricky to split apart to send to Discord.

first_frame is a field we use to skip the metadata block on a seek back to the start. first_frame=0 means no DCA metadata, but that's nkt much good if it's in a different format.

I recommend either telling ffmpeg to give you raw PCM data, or if you can split apart frames then the DCA format is simple: encode each frame as ((frame_len as i16 [little endian]), frame). https://docs.rs/songbird/0.1.8/songbird/input/cached/struct.Compressed.html does this internally as well as converting to Opus.

-- Kyle

SuperCuber commented 3 years ago

So I tried changing the code to this:

let child = Command::new("ffmpeg")
    .args(...)
    .args(&["-f", "f32le", "-acodec", "pcm_f32le", "-"])
    .output().unwrap();
let reader = Reader::Vec(Cursor::new(child.stdout));
let track = call.play_only_source(Input::new(
    true,
    reader,
    Codec::FloatPcm,
    songbird::input::Container::Raw,
    None,
));

Now the song successfully starts playing but then it immediately emits the "end track" event... I chose f32le because of the doc on Codec::FloatPcm saying the bytestream is encoded using raw f32 samples.

SuperCuber commented 3 years ago

I found this code: lemme try copy it. https://docs.rs/songbird/0.1.8/src/songbird/input/ffmpeg_src.rs.html#41

SuperCuber commented 3 years ago

Seems the issue was with some other piece of code - the ffmpeg command itself was failing. This seems to work! Thanks for the help :D

Could this be perhaps better documented somewhere? The flags I ended up using were .args(&[ "-f", "s16le", "-ac", "2", "-ar", "48000", "-acodec", "pcm_f32le", "-", ])

FelixMcFelix commented 11 months ago

Closing as v0.4.x onwards no longer make use of ffmpeg or child processes for audio processing.