Search code examples
rustgstreamerrtp

How do I link a udpsrc to rtpbin in Gstreamer Rust?


I've attempted to create a pipeline for receiving RTP video/audio streams via Gstreamer using the gstreamer-rs crate, but I am not having much luck. Here is a quick distillation of my approach:

let udp_src = ElementFactory::make("udpsrc", Some("udp_src"))?;
    udp_src.set_property("port", &5004);
    udp_src.set_property("caps", &"application/x-rtp".to_string());

let rtpbin = ElementFactory::make("rtpbin", Some("rtp_bin"))?;
let pipeline = Pipeline::new(Some("RTP Pipeline"));
    pipeline.add_many(&[&udp_src, &rtpbin]);

udp_src.link(&rtpbin)?;

rtpbin.connect_pad_added(move |src, src_pad| {
        println!("Received new pad {} from {}", src_pad.get_name(), src.get_name());

        let new_pad_caps = src_pad.get_current_caps().expect("Failed to get caps of new pad");
        let new_pad_struct = new_pad_caps.get_structure(0).expect("Failed to get first structure of caps");
        let new_pad_type = new_pad_struct.get_name();

        println!("{}", new_pad_type);
    });

But I'm not getting anything from the connect_pad_added when I run the code and send UDP signals to the specified port. I'm not even sure if this is the right approach, and would appreciate any directions on where to find examples or pointers on how to use rtpbin with udpsrc.


Solution

  • You can link a udp source to an rtpbin via the following:

    fn get_static_pad(element: &Element, pad_name: &'static str) -> Result<Pad, Error> {
        let pad = element.get_static_pad(pad_name);
        pad.ok_or(Error::msg(format!("Failed to get pad: {}", pad_name)))
    }
    
    fn get_request_pad(element: &Element, pad_name: &'static str) -> Result<Pad, Error> {
        match element.get_request_pad(pad_name) {
            Some(pad) => Ok(pad),
            None => {
                let element_name = element.get_name();
                Err(Error::msg(pad_name))
            }
        }
    }
    
    let rtp_udp_src = ElementFactory::make("udpsrc", Some("rtp_udp_src"))?;
    let caps = Caps::new_simple("application/x-rtp", &[("clock-rate", &90000i32)]);
        rtp_udp_src.set_property("port", &5006);
        rtp_udp_src.set_property("caps", &caps);
    
    let rtpbin = ElementFactory::make("rtpbin", Some("rtp_bin"))?;
    
        rtpbin.connect("request-pt-map", false, |values| {
            let pt = values[2].get::<u32>().expect("Invalid argument");
            match pt {
                Some(100) => Some(
                    Caps::new_simple(
                        "application/x-rtp",
                        &[
                            ("media", &"video"),
                            ("clock-rate", &90000i32),
                        ],
                    )
                        .to_value(),
                ),
                Some(96) => Some(
                    Caps::new_simple(
                        "application/x-rtp",
                        &[
                            ("media", &"video"),
                            ("clock-rate", &90000i32),
                            ("encoding-name", &"VP8"),
                        ],
                    )
                        .to_value(),
                ),
                _ => None,
            }
        })?;
    
    let rtp_udp_src_pad = get_static_pad(&rtp_udp_src, "src")?;
    let rtp_recv_sink_pad = get_request_pad(&rtpbin, "recv_rtp_sink_0")?;
        rtp_udp_src_pad.link(&rtp_recv_sink_pad).expect("Failed to link rtp_udp_src_pad and rtpbin");
    

    This will correctly fire off the connect_pad_added sequence and let you use it with other elements.