Search code examples
rustgstreamergstreamer-rs

Why is gstreamer-rs deinterleave element showing src pad that isn't there?


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.


Solution

  • 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