I am trying to build a dynamic pipeline that includes a deinterleave
element and am running into a curious issue. ElementExt::foreach_pad
is showing two src pads src_0
and src_1
, yet a call to request_pad_simple("src_%u")
returns None
.
Here is the launch script I wish to implement:
gst-launch-1.0 -v \
filesrc location=yo.mp3 \
! decodebin \
! audioconvert \
! deinterleave name=d \
audiomixer name=mix \
! wavenc \
! filesink location=yo.wav \
d.src_0 ! mix.sink_0 \
d.src_1 ! mix.sink_1
It converts a 2-channel mp3
file into a mono wav
file.
Here is the Rust code that builds my pipeline:
let pipeline = gst::Pipeline::default();
let src = gst::ElementFactory::make("filesrc").property("location", "yo.mp3").build().unwrap();
let decodebin = gst::ElementFactory::make("decodebin").build().unwrap();
pipeline.add_many([&src, &decodebin]).unwrap();
gst::Element::link_many([&src, &decodebin]).unwrap();
let pipeline_weak = pipeline.downgrade();
decodebin.connect_pad_added(move |_, src_pad| {
let pipeline = pipeline_weak.upgrade().unwrap();
let audioconvert = gst::ElementFactory::make("audioconvert").build().unwrap();
let deinterleave = gst::ElementFactory::make("deinterleave").build().unwrap();
pipeline.add_many([&audioconvert, &deinterleave]).unwrap();
gst::Element::link_many([&audioconvert, &deinterleave]).unwrap();
for e in &[&audioconvert, &deinterleave] {
e.sync_state_with_parent().unwrap();
}
let audioconvert_sink = audioconvert.static_pad("sink").unwrap();
src_pad.link(&audioconvert_sink).unwrap();
let pipeline_weak = pipeline.downgrade();
deinterleave.connect_no_more_pads(move |d| {
let pipeline = pipeline_weak.upgrade().unwrap();
let audiomixer = gst::ElementFactory::make("audiomixer").build().unwrap();
let wavenc = gst::ElementFactory::make("wavenc").build().unwrap();
let filesink = gst::ElementFactory::make("filesink").property("location", "yo.wav").build().unwrap();
pipeline.add_many([&audiomixer, &wavenc, &filesink]).unwrap();
gst::Element::link_many([&audiomixer, &wavenc, &filesink]).unwrap();
for e in &[&audiomixer, &wavenc, &filesink] {
e.sync_state_with_parent().unwrap();
}
// Here is the source of my confusion; this loop outputs three lines: "sink", "src_0",
// and "src_1"...
d.foreach_pad(|_, pad| {
println!("deinterleave pad: {}", pad.name().as_str());
true
});
// ...and yet my code panics here with "called `Option::unwrap()` on a `None` value"
let deinterleave_src_0 = d.request_pad_simple("src_%u").unwrap();
let mixer_sink_0 = audiomixer.request_pad_simple("sink_%u").unwrap();
deinterleave_src_0.link(&mixer_sink_0).unwrap();
let deinterleave_src_1 = d.request_pad_simple("src_%u").unwrap();
let mixer_sink_1 = audiomixer.request_pad_simple("sink_%u").unwrap();
deinterleave_src_1.link(&mixer_sink_1).unwrap();
});
});
What is wrong with this code? How should I correctly connect a deinterleave
element's src pads to an audiomixer
element's sink(s)?
Update For those curious about this question, and (maybe) for more context...
After posting this question, I went back to throwing poop at ye olde wall and made this:
let pipeline = gst::Pipeline::default();
let src = gst::ElementFactory::make("filesrc").property("location", "yo.mp3").build().unwrap();
let decodebin = gst::ElementFactory::make("decodebin").build().unwrap();
pipeline.add_many([&src, &decodebin]).unwrap();
gst::Element::link_many([&src, &decodebin]).unwrap();
let pipeline_weak = pipeline.downgrade();
decodebin.connect_pad_added(move |_, src_pad| {
let pipeline = pipeline_weak.upgrade().unwrap();
let audioconvert = gst::ElementFactory::make("audioconvert").build().unwrap();
let deinterleave = gst::ElementFactory::make("deinterleave").build().unwrap();
pipeline.add_many([&audioconvert, &deinterleave]).unwrap();
gst::Element::link_many([&audioconvert, &deinterleave]).unwrap();
for e in &[&audioconvert, &deinterleave] {
e.sync_state_with_parent().unwrap();
}
let audiomixer = gst::ElementFactory::make("audiomixer").build().unwrap();
let wavenc = gst::ElementFactory::make("wavenc").build().unwrap();
let filesink = gst::ElementFactory::make("filesink").property("location", "yo.wav").build().unwrap();
pipeline.add_many([&audiomixer, &wavenc, &filesink]).unwrap();
gst::Element::link_many([&audiomixer, &wavenc, &filesink]).unwrap();
for e in &[&audiomixer, &wavenc, &filesink] {
e.set_state(State::Null).unwrap();
}
let audiomixer_weak = audiomixer.downgrade();
deinterleave.connect_pad_added(move |_, src_pad| {
let audiomixer = audiomixer_weak.upgrade().unwrap();
let audiomixer_sink = audiomixer.request_pad_simple("sink_%u").unwrap();
src_pad.link(&audiomixer_sink).unwrap();
});
let audioconvert_sink = audioconvert.static_pad("sink").unwrap();
src_pad.link(&audioconvert_sink).unwrap();
});
This doesn't get me any further toward understanding why I can't request a src pad from inside a connect_no_more_pads
method, but it does create a pipeline that gives the same result as the gst-launch
invocation above.
You cannot request a pad from deinterlave
. Deinterleave exposes as many pads as it makes sense depending on the input format. You connect them or you don't.
An audiomixer
on the other hand can mix up to an arbitrary number of channels. Audiomixer does not know about how many input pads you want/need so you have to request them individually.
From gst-inspect-1.0
:
$ gst-inspect-1.0 deinterleave
..
SRC template: 'src_%u'
Availability: Sometimes
$ gst-inspect-1.0 audiomixer
..
SINK template: 'sink_%u'
Availability: On request