I have a c program with a pipeline.
In the pipeline is a decodebin3 element. This uses a video decoder.
Unfortunately, it can happen that the video decoder runs into a problem.
I’d like to reset this element without restarting the whole pipeline. Shouldn’t that be possible?
I get the element from the bin and thought I could do it like this:
Hi, I think it’s possible, but there’re few things:
if you reset the decoder while it receives the data, then after reset it will start receiving the data that doesn’t start from the beginning of the GOP. So probably it makes sense to better reset the whole decodebin3, so it will reset the parser as well.
Obviously resetting the decoder in the middle of the GOP will make it loose the rest of the GOP, so there will be a visible gap in the playback.
In order to stop an element while it receives the data you need to do that from a probe on a peer of it’s sinkpad, so this way you make sure it doesn’t receive the data while it stops.
For me this code snippet works.
#include <gst/gst.h>
/* Compile: gcc reset-decoder.c $(pkg-config --libs --cflags gstreamer-1.0) -o reset-decoder */
static GstPadProbeReturn
reset_decoder_probe (GstPad *pad, GstPadProbeInfo *info, gpointer user_data)
{
GstElement *dec = GST_ELEMENT (user_data);
g_message ("resetting decoder");
gst_element_set_state (dec, GST_STATE_NULL);
gst_element_sync_state_with_parent (dec);
g_message ("decoder got reset");
return GST_PAD_PROBE_REMOVE;
}
static void
decodebin_pad_added (GstElement * dec,
GstPad * pad,
GstElement *pipeline)
{
g_message ("pad_added again");
GstElement *queue = gst_bin_get_by_name (GST_BIN (pipeline), "q");
gst_element_link (dec, queue);
gst_object_unref (queue);
}
int main (int argc, char**argv) {
gst_init (&argc, &argv);
GstElement *pipeline = gst_parse_launch ("souphttpsrc location=\"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4\" "
" ! qtdemux name=mux"
" mux.video_0 ! decodebin3 name=dec"
" ! queue name=q ! autovideosink", NULL);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
// play for 5 seconds
g_usleep (G_TIME_SPAN_SECOND * 5);
// now reset the decoder
{
GstPad *peer;
GstElement *dec = gst_bin_get_by_name (GST_BIN (pipeline), "dec");
GstPad *sinkpad = gst_element_get_static_pad (dec, "sink");
peer = gst_pad_get_peer (sinkpad);
// When we teardown the decodebin it will remove it's pads, and when we start
// it again it will add new pads. So we have to link the pad to the queue again.
// Looks like gst_parse_launch could actually do that, but however it doesn't.
g_signal_connect (dec, "pad-added", G_CALLBACK (decodebin_pad_added), pipeline);
gst_pad_add_probe (peer, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
reset_decoder_probe, dec, NULL);
gst_object_unref (peer);
gst_object_unref (sinkpad);
gst_object_unref (dec);
}
// play for 15 seconds more
g_usleep (G_TIME_SPAN_SECOND * 15);
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
return 0;
}
IMHO if the decoder returns an GST_FLOW_ERROR from the chain function, it will be propagated to the souphttpsrc that will stop the download. To avoid that you need some element that would not propagate this error to the upstream, I’m not aware of such one.