Recording a video along with live streaming upon user request

Hi,

Currently, i am recording a video data and store it in files using below pipeline mechanism.

camera_src → caps_filter → encoder → clockoverlay → h265parse → queue → mpegtsmux → filesink

I want to add RTSP live streaming while a recording a video based on user request. How we can add rtspclientsink plugin dynamically in running pipeline?

Also when a user stop live streaming request, then rtspclinetsink plugin should be removed from the pipeline and back to normal behaviour, just recording a video.
Can you please provide me sample code for this requirement?

Thanks in advance!!

Thanks,

1 Like

I have done something similar!

What you want to do is make a pipeline with a tee, and then once the user requests it connect a streaming branch to this tee.

Here is my code (it is in Rust, but you can probably adapt it to your liking, either on your own or with the help from ChatGPT). Note that I am always streaming and only recording upon user request.

This is the initial pipeline setup:

     // Link pre-tee elements
    gst::Element::link_many(&[&src, &src_caps, &nvvidconv, &nvjpegenc, &tee])?;

    // Link streaming branch
    gst::Element::link_many(&[&queue_stream, &multipartmux, &appsink.upcast_ref()])?;

    // Link tee to streaming branch
    let tee_stream_pad = tee
        .request_pad_simple("src_0")
        .ok_or("Failed to get tee_stream_pad")?;
    info!("Obtained request pad {}", tee_stream_pad.name());
    let queue_stream_pad = queue_stream
        .static_pad("sink")
        .ok_or("Failed to get queue_stream_pad")?;
    tee_stream_pad.link(&queue_stream_pad)?;
    info!("Linked tee and queue_stream");

    pipeline.set_state(gst::State::Playing)?;
    info!("Pipeline started");

This is the recording branch connection upon user request

    // Recording branch
    let queue_record = gst::ElementFactory::make("queue")
        .property_from_str("leaky", "downstream")
        .name("queue_record")
        .build()?;
    let multifilesink = gst::ElementFactory::make("multifilesink")
        .property("location", format!("{}/frame_%d.jpg", directory))
        .name("multifilesink")
        .build()?;

    // Add recording branch to pipeline
    pipeline.add_many(&[&queue_record, &multifilesink])?;

    gst::Element::link_many(&[&queue_record, &multifilesink])?;

    let tee = pipeline.by_name("tee").ok_or("Failed to get tee element")?;
    let tee_record_pad = tee
        .request_pad_simple("src_1")
        .ok_or("Failed to get tee_file_pad")?;
    info!("Obtained request pad {}", tee_record_pad.name());
    let queue_record_pad = queue_record
        .static_pad("sink")
        .ok_or("Failed to get queue_record_pad")?;
    tee_record_pad.link(&queue_record_pad)?;
    info!("Linked tee and queue_record");

    pipeline.set_state(gst::State::Playing)?;
    info!("File recording started");

To remove the branch later:

    // Get recording branch elements
    let tee = pipeline.by_name("tee").ok_or("Failed to get tee")?;
    let queue_record = pipeline
        .by_name("queue_record")
        .ok_or("Failed to get queue_record")?;
    let multifilesink = pipeline
        .by_name("multifilesink")
        .ok_or("Failed to get multifilesink")?;

    // Get the tee pad connected to the recording branch and unlink it
    let tee_src_pad = tee.static_pad("src_1").ok_or("Failed to get tee src pad")?;
    let queue_record_sink_pad = queue_record
        .static_pad("sink")
        .ok_or("Failed to get queue_record sink pad")?;

    // Unlink the recording branch
    if tee_src_pad.is_linked() {
        tee_src_pad.unlink(&queue_record_sink_pad)?;
        tee.release_request_pad(&tee_src_pad);
    }
    info!("Unlinked tee and queue_record");

    // Send EOS event to the filesink branch
    queue_record.send_event(gst::event::Eos::new());

    // Set the state of the recording branch to NULL
    queue_record.set_state(gst::State::Null)?;
    multifilesink.set_state(gst::State::Null)?;

    // Remove the recording branch elements from the pipeline
    pipeline.remove_many(&[&queue_record, &multifilesink])?;

    // Set the state of the filesink branch to NULL
    info!("File recording stopped");

Thanks @Pandananana for providing a sample code. I will convert code into C and then check it. You have given good pointers here.

Thanks

I am facing one issue during removing streaming element from the pipeline. Issue is GStreamer pipeline hangs sometimes on gst_element_get_state().

e.g.
gst_element_set_state(TeeQueueLS, GST_STATE_NULL); // hang this function and never return back.

What is the reason gst_element_set_state() function is hung?

Thanks

Correct the above statement “hung in gst_element_set_state”.

What is the reason this function is getting hung?