Sending GstForceKeyUnit to appsink pipeline (Rust)

I’m using the very nice Rust gstreamer bindings to create a pipeline that delivers video from an rtpsrc to an appsink, for delivery to another external system. The part of the pipeline I’m setting up looks like this:

rtspsrc → rtph264depay → h264parse → appsink

The problem I’m having is that when a new client on the external system connects, I would like the pipeline to request a key frame from the rtsp server. Based on this document, my plan so far was to inject a GstForceKeyUnit upstream to encourage the rtpsession to send a PLI packet.

However, I can’t seem to get it working. The send_event() call always returns false and the PLI isn’t generated. I’ve tried a few different strategies, including sending the event from appsink and from the src pad of the depay.

Is what I’m trying to do possible?
Thanks!

Sending the event to the appsink:

fn send_force_keyunit_upstream(data: Weak<Mutex<CustomData>>) {
    let event = gstreamer_video::UpstreamForceKeyUnitEvent::builder()
        .all_headers(true)
        .build();

    let data = data
        .upgrade()
        .expect("failed to get data");

    let mut d = data.lock().unwrap();

    if let Some(appsink) = &d.video_appsink {
        let pad = appsink
            .static_pad("sink")
            .expect("failed to get appsink pad");

        if pad.send_event(event) {
            info!("GstForceKeyUnit Send Suceessfullyl!");
        } else {
            error!("SENDING GstForceKeyUnit Failed!");
        }
    }
}

Sending the event to the depay src pad:

fn send_force_keyunit_upstream(pipeline: &gstreamer::Pipeline) {
    let iter = pipeline.iterate_recurse();
    for element in iter {
        if let Ok(element) = element {
            if element
                .factory()
                .map(|f| f.name() == "rtph264depay")
                .unwrap_or(false)
            {
                let event = gstreamer_video::UpstreamForceKeyUnitEvent::builder()
                    .all_headers(true)
                    .build();

                if let Some(pad) = element.static_pad("src") {
                    if !pad.send_event(event) {
                        error!("Sending key unit request FAILED");
                    }
                } else {
                    error!("failed to get depay src pad!");
                }
            }
        }
    }
}

For the appsink case, the appsink’s sink pad is the wrong direction for sending upstream events. See the documentation of GstPad which explicitly states:

If pad is a source pad, event should be an upstream event. If pad is a sink pad, event should be a downstream event.

As for using the depayloader’s src pad, that is using the correct pad and the event likely makes it to rtpsession correctly (check the debug logs to make sure). However in order for rtpsession to send a PLI, the RTP caps (built from the SDP in rtspsrc) must include some attributes that allow PLI’s to be sent to the peer. One of those options is the rtcp-fb-nack-pli=true caps field. Without that, PLI requests will not be sent to the peer.

1 Like

Thanks for confirming that was the correct strategy. I’ll look into the SDP attributes for the rtcp-fb options. Thanks again!