Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upAudio decoder #52
Audio decoder #52
Conversation
| .map_err(|_| ()) | ||
| }; | ||
|
|
||
| if !is_audio || insert_sink().is_err() { |
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 13, 2018
Contributor
You probably also do not want to add a new sink for each audio stream (there could be multiple), but only take one. Or do you want to take them all?
| }); | ||
|
|
||
| let appsrc = appsrc.downcast::<AppSrc>().map_err(|_| ())?; | ||
| appsrc.set_property_format(gst::Format::Time); |
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 13, 2018
Contributor
Time format requires you to properly timestamp the data you put into the appsrc for each buffer. You probably can't do that, so set it to bytes instead :)
| appsrc_ | ||
| .push_buffer(buffer) | ||
| .into_result() | ||
| .expect("pushing buffer failed"); |
This comment has been minimized.
This comment has been minimized.
| buffer | ||
| .copy_from_slice(0, &data[offset..next_offset]) | ||
| .expect("copying failed"); | ||
| offset = next_offset; |
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 13, 2018
Contributor
You could use std::io::Cursor and Read on the Vec to prevent having to keep track of the offset yourself
|
With these patches I am able to decode and play an .mp3 file, but with very poor quality (lots of noise). I am not sure why yet. |
|
Seems like you need a capsfilter before the appsink so that the audio samples are converted to the right format expected by the backend. |
Or you set the caps property on appsink accordingly. That has the same effect and is fewer elements |
| )), | ||
| ); | ||
| graph.resume(); | ||
| thread::sleep(time::Duration::from_millis(5000)); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
ferjm
Jun 18, 2018
Author
Member
Because if the graph is dropped, the rendering thread is closed and we won't hear the audio playback.
|
With the appsink caps set, the sound is perfect. Thank you! |
|
Looks okay. I don't understand gstreamer enough to comment much on the actual decode implementation but Sebastian and Philippe seem to be helping with that |
|
|
||
| /// Asynchronously decodes the audio file data contained in the given | ||
| /// buffer. | ||
| pub fn decode_audio_data(&self, data: Vec<u8>, callbacks: AudioDecoderCallbacks) { |
This comment has been minimized.
This comment has been minimized.
Manishearth
Jun 18, 2018
Member
We should eventually split out the GStreamer stuff as a separate crate with traits, but that's going to be annoying for now so we don't need to do it yet
(it's also low priority)
This comment has been minimized.
This comment has been minimized.
ferjm
Jun 19, 2018
Author
Member
We already have the AudioSink and AudioDecoder traits, so it shouldn't be too hard ™ :)
| .extend_from_slice((*buffer).as_ref()); | ||
| }) | ||
| .build(); | ||
| graph.decode_audio_data(bytes.to_vec(), callbacks); |
This comment has been minimized.
This comment has been minimized.
Manishearth
Jun 18, 2018
Member
Should we eventually make it such that this can be plugged into a source element on the render thread side, or do we want this to be separate to make the network stuff work?
This comment has been minimized.
This comment has been minimized.
ferjm
Jun 19, 2018
Author
Member
For media elements (and MediaElementAudioSourceNodes) we will likely use a different approach and use GstPlayer directly (which handles all the decoding work internally), check #43.
For MediaStream*AudioSourceNodes, I am not so sure yet, I still need to read more of these specs, but in general, I have the feeling that all decoding should happen outside of any WebAudio source element, and certainly off the render thread.
In any case, AudioDecoder and GStreamerAudioDecoder are meant to be reusable.
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 19, 2018
Contributor
and certainly off the render thread.
Note that this is trivial with GStreamer, so shout when you need to move things to different threads, etc and I'll help :)
| println!("Decoding audio"); | ||
| receiver.recv().unwrap(); | ||
| println!("Audio decoded"); | ||
| let buffer_source = graph.create_node(AudioNodeType::AudioBufferSourceNode); |
This comment has been minimized.
This comment has been minimized.
Manishearth
Jun 18, 2018
Member
How do we plan to make this work with ongoing network fetches? Perhaps a BufferSourceNode you can message extra buffers to?
(not something we need to solve now, just want your thoughts)
This comment has been minimized.
This comment has been minimized.
ferjm
Jun 19, 2018
Author
Member
AFAICT AudioBufferSourceNodes are meant for in-memory audio assets. For network-backed assets the spec suggests the usage of AudioWorkletNodes, which is a completely different war :).
| let pipeline_ = pipeline.clone(); | ||
| let pipeline__ = pipeline.clone(); | ||
| let callbacks_ = callbacks.clone(); | ||
| let callbacks__ = callbacks.clone(); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
ferjm
Jun 19, 2018
Author
Member
Yeah, I am not too happy either, but I couldn't think of a better approach :\
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 19, 2018
Contributor
You could use a macro like clone_army! https://docs.rs/closet/0.2.2/closet/macro.clone_army.html
But it's all not very nice, not sure if this can be improved in any way though unless some kind of auto-cloning is added to the language (please no)
|
Thanks for the review and feedback, folks! |
| .error(|| { | ||
| eprintln!("Error decoding audio"); | ||
| }) | ||
| .progress(move |buffer| { |
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 19, 2018
Contributor
Is there another user for this already apart from the example? Some code that does not actually copy all the samples directly but reuses the slice?
This comment has been minimized.
This comment has been minimized.
| let appsink = sink.clone().dynamic_cast::<AppSink>().map_err(|_| ())?; | ||
|
|
||
| // XXX Issue #18 channels support. | ||
| let audio_info = gst_audio::AudioInfo::new(gst_audio::AUDIO_FORMAT_F32, 44100, 1) |
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 19, 2018
Contributor
If 44100 Hz are not required, 48000 might be nicer... or can the sample rate even be signalled somewhere via webaudio so you wouldn't have to constrain it here?
This comment has been minimized.
This comment has been minimized.
ferjm
Jun 19, 2018
Author
Member
Yeah, the sample rate should probably be given by the AudioContext. I'll write a patch for this.
This comment has been minimized.
This comment has been minimized.
sdroege
Jun 19, 2018
Contributor
You then want to set the caps similar to this (and possibly later also allow a different number of channels): https://github.com/sdroege/gstreamer-rs/blob/db3fe694154c697afdaf3efb6ec65332546942e0/examples/src/bin/appsink.rs#L54-L62
ferjm commentedJun 13, 2018
•
edited
Fixes #41