Gstreamer Periodically get the current capabilities from a stream

Viewed 104

I am ingesting a video feed from a camera and I want to periodically check if the capabilities of the stream have changed (resolution/audio/encoding(h264/265).

For this I added connect_pad_added signal to the rtspsrc and when a pad is available I use the pad to check for capabilities.

I use the same pad again in future to get the current caps. However the capabilities are not updating when the resolution changes.

Will the get_current_caps for the source pad not get the latest caps?

What is the right way to achieve this?

Here is a simplified version of my code in Rust

use glib::{Cast, ObjectExt};
use gst::MessageView;
use gst::prelude::{ElementExt, GstBinExt, GstObjectExt};
use gst::Pad;
use gst::prelude::PadExt;

use std::sync::{Arc, Mutex};

#[cfg(not(target_os = "macos"))]
pub fn run<T, F: FnOnce() -> T + Send + 'static>(main: F) -> T
    where
        T: Send + 'static,
{
    main()
}

#[derive(Debug, Clone)]
struct SourcePads {
    video_pad: Option<Pad>,
    audio_pad: Option<Pad>,
}

impl SourcePads {
    fn update_audio(&mut self, x: Option<&Pad>) {
        match x {
            Some(y) => self.audio_pad = Some(y.clone()),
            None => self.audio_pad = None,
        }
    }
    fn update_video(&mut self, x: Option<&Pad>) {
        match x {
            Some(y) => self.video_pad = Some(y.clone()),
            None => self.video_pad = None,
        }
    }
}


fn typefinder_main() {
    gst::init().unwrap();
    let pipe_str = format!("rtspsrc location={} name=rtspsrc0 rtspsrc0. ! rtph265depay ! tee name=t t.! queue ! hlssink2 name=ingest1 target-duration=2 playlist-location=stream.m3u8 location=%d.ts ", "rtsp://admin:pass@10.0.0.12:554/");
    let pipeline = gst::parse_launch(pipe_str.as_str()).unwrap();

    let pipeline = pipeline.dynamic_cast::<gst::Pipeline>().unwrap();

   let mut source_pads = SourcePads{video_pad: None, audio_pad: None};
   let source_pads = Mutex::new(source_pads);
   let source_pads = Arc::new(source_pads);

    pipeline.set_state(gst::State::Playing);

    let bus = pipeline.bus().expect("unable to get bus");
    let rtspsrc = pipeline.by_name("rtspsrc0").unwrap();
    let mut src_pad_clone = source_pads.clone();

    rtspsrc.connect_pad_added(move |src, src_pad | {
        let new_pad_caps = src_pad.current_caps().expect("failed to get caps of new pad");
        let new_pad_struct = new_pad_caps.structure(0).expect("Failed to get first structure of caps");

        for x in 0..new_pad_struct.n_fields() {
            let field_name = new_pad_struct.nth_field_name(x).unwrap();
new_pad_struct.value(new_pad_struct.nth_field_name(x).unwrap()).unwrap());
            if field_name.starts_with("media") {
                let media_type = new_pad_struct.value(field_name).unwrap();
                let field_value = media_type.get::<&str>().unwrap();
                if field_value.starts_with("audio") {
                    if let Ok(mut x) = src_pad_clone.lock() {
                        x.update_audio(Some(src_pad));
                    }
                } else if field_value.starts_with("video") {
                    if let Ok(mut x) = src_pad_clone.lock() {
                        x.update_video(Some(src_pad));
                    }
                }
            }
        }
    });

    let pipeline_weak = pipeline.downgrade();

    //////// use depay to get the current caps ////
    let depay = pipeline.by_name("rtph265depay0").unwrap();
    let pad = depay.static_pad("src").unwrap();
    pad.connect_notify(Some("caps"), |pad, _pspec| {
        let current_caps = pad.current_caps().expect("failed to get caps from depay");
        let new_pad_struct = current_caps.structure(0).expect("failed to get first structure");
        println!("DEPAY CAPS={:#?}", new_pad_struct);
    });
    //////// use depay to get the current caps ////

    for msg in bus.iter_timed(gst::ClockTime::NONE) {

        match msg.view() {
            MessageView::Eos(..) => {println!("end of stream");
                match pipeline_weak.upgrade() {
                Some(pipeline) => {
                    if let Ok(mut x) = source_pads.lock() {
                        x.update_audio(None);
                        x.update_video(None);

                    }
                    pipeline.set_state(gst::State::Ready);
                    pipeline.set_state(gst::State::Playing);
                },
                None => {println!("Could not get the pipeline")},
            };
            }
            MessageView::Error(err) => println!("received an error e: {:#?}", err),
            MessageView::StateChanged(s) => {
                println!(
                    "State changed from {:?}: {:?} -> {:?} ({:?})",
                    s.src().map(|s| s.path_string()),
                    s.old(),
                    s.current(),
                    s.pending()
                );
                if let Ok(x) = source_pads.lock() {
                    match &x.video_pad {
                        Some(video_pad) => {
                            let new_pad_caps = video_pad.current_caps().expect("failed to get caps for video pad");
                            let new_pad_struct = new_pad_caps.structure(0).expect("failed to get first structure of caps");
                            println!("CAPS FROM BUS={:#?}", new_pad_struct);
                        }
                        None => println!("video pad not found"),
                    }
                }
            }
            _ => (),
        }
    }
    pipeline.set_state(gst::State::Null).unwrap();
}

fn main() {
     typefinder_main();
}

Thanks a lot for your help!

1 Answers

Pad::get_current_caps() will get you the latest caps at any time but in case of rtspsrc's source pads these caps generally don't contain the resolution and especially won't change if the resolution changes as they're based on the static SDP.

You probably want to check the caps on a pad further downstream, e.g. the source pad of the video decoder (or the depayloader or parser).

To get notified whenever the caps are changing you could use

let pad = video_decoder.static_pad("src").unwrap();
pad.connect_notify(Some("caps"), |pad, _pspec| {
  let current_caps = pad.current_caps();
  [...]
});
Related