Connect Gstreamer Bin's with Ghostpads in Rust

Viewed 415

I want to stream a webcam (vp8 and opus) with webrtcbin from gstreamer to a janus gateway. There is an example from gstreamer playing video only which is working.

Now I struggle adding the audio part too.

This ist the pipeline I want to prepare

gst-launch-1.0 webrtcbin name=web videotestsrc is-live=true ! videoconvert ! vp8enc ! rtpvp8pay ! web.sink_%u audiotestsrc is-live=true wave=8 ! audioconvert ! audioresample ! opusenc ! rtpopuspay ! web.sink_%u

But the following failes with

Error: Pads have no common grandparent
use gst::gst_element_error;
use gst::prelude::*;

async fn run() -> Result<(), anyhow::Error> {
    gst::init()?;

    let pipeline = gst::Pipeline::new(Some("test"));

    let webrtc_bin = gst::parse_bin_from_description("webrtcbin name=web", false)?;
    pipeline.add(&webrtc_bin)?;

    let video_bin = gst::parse_bin_from_description("autovideosrc ! videoconvert ! vp8enc ! rtpvp8pay", true)?;
    pipeline.add(&video_bin)?;

    let audio_bin = gst::parse_bin_from_description("autoaudiosrc ! audioconvert ! opusenc ! rtpopuspay", true)?;
    pipeline.add(&audio_bin)?;

    let webrtcbin = pipeline.get_by_name("web").expect("can't find webrtcbin");

    let video_src = video_bin.get_static_pad("src").expect("Unable to get video src pad");
    let video_sink = webrtcbin.get_request_pad("sink_%u").expect("Unable to request outgoing webrtcbin pad");

    if let Ok(webrtc_ghost_video_src) = gst::GhostPad::with_target(Some("webrtc_video_src"), &video_src) {
        video_bin.add_pad(&webrtc_ghost_video_src)?;
        webrtc_ghost_video_src.link(&video_sink)?;
    }

    let audio_src = audio_bin.get_static_pad("src").expect("Unable to get audio src pad");
    let audio_sink = webrtcbin.get_request_pad("sink_%u").expect("Unable to request outgoing webrtcbin pad");

    if let Ok(webrtc_ghost_audio_src) = gst::GhostPad::with_target(Some("webrtc_audio_src"), &audio_src) {
        audio_bin.add_pad(&webrtc_ghost_audio_src)?;
        webrtc_ghost_audio_src.link(&audio_sink)?;
    }

    let bus = pipeline.get_bus().unwrap();
    let _ = bus.add_watch_local(move |_bus, msg| {
        println!("{:#?}", msg);
        glib::Continue(true)
    });

    pipeline.call_async(|pipeline| {
        // If this fails, post an error on the bus so we exit
        if pipeline.set_state(gst::State::Playing).is_err() {
            gst_element_error!(
                pipeline,
                gst::LibraryError::Failed,
                ("Failed to set pipeline to Playing")
            );
        }
    });

    Ok(())
}

fn main() -> Result<(), anyhow::Error> {
    let main_context = glib::MainContext::default();
    main_context.block_on(run())
}

1 Answers

Your problem is that you use gst::parse_bin_from_description() for the webrtcbin too. Like this it will create a webrtcbin and place it inside a new bin. So for linking that webrtcbin with any of the other bins inside the pipeline you would have to also add a ghost pad on the webrtc_bin for proxying the video_sink.

Alternatively you could just not put the webrtcbin into its own bin by either doing the following to tell parse_bin_from_description() to not place a single element into a new bin

let webrtc_bin = gst::parse_bin_from_description_full("webrtcbin name=web", false, None, gst::ParseFlags::NO_SINGLE_ELEMENT_BINS)?;

Or by simply using

let webrtc_bin = gst::ElementFactory::make("webrtcbin", Some("web"))?;

In either case you don't have to call pipeline.get_by_name("web") anymore because you already have it right there in your webrtc_bin variable already.

Related