Hi! Unfortunately I can not make an updates in the original issue that I created (Deadlock when try to seek in pipeline), because it was marked as “solved” by nvidia, despite it wasn’t. So I write it here in case if that will be useful to anybody. There are solution to that problem, basically I described it here: Deadlock when try to seek in pipeline - #12 by andrei.liaukovich - but there were an additional problem that I didn’t figure out at that time. For some reason nvidia decoder doesn’t respect any key frames, except first one. So for solve that I just saved the first key frame (that is first buffer in the pipeline) and force it after dynamic pipeline change to nvv decoder. And that works well. There are just a minor issue, when seek_simple return false during pipeline change, despite seeking was actually successful, but it is rear and it doesn’t produce deadlock.
Here is a minimal example:
#include <gst/app/app.h>
#include <gst/app/gstappsink.h>
#include <gst/gst.h>
#include <gst/gstbin.h>
#include <gst/gstbuffer.h>
#include <gst/gstclock.h>
#include <gst/gstelement.h>
#include <gst/gstelementfactory.h>
#include <gst/gstevent.h>
#include <gst/gstformat.h>
#include <gst/gstmemory.h>
#include <gst/gstobject.h>
#include <gst/gstpad.h>
#include <gst/gstparse.h>
#include <gst/gstsample.h>
#include <gst/gstsegment.h>
#include <gst/gstutils.h>
#include <atomic>
#include <cassert>
#include <csignal>
#include <cstddef>
#include <iostream>
std::atomic_bool done{false};
void done_handler(int sig) {
done = true;
signal(sig, SIG_DFL);
}
static GstPadProbeReturn
change_pipeline_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
static GstPadProbeReturn
save_keyframe_cb(GstPad *, GstPadProbeInfo *info, gpointer);
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "set video file as first arg" << std::endl;
return EXIT_FAILURE;
}
std::cout << "play file: " << argv[1] << std::endl;
std::cout << "note that file should contain not less then 5 min of video "
"vp9, 1920x1080, 15fps"
<< std::endl;
std::string video_file = argv[1];
std::signal(SIGINT, done_handler);
std::signal(SIGTERM, done_handler);
gst_init(NULL, NULL);
// clang-format off
std::string pipeline_str =
"filesrc location=" + video_file + " ! "
"matroskademux name=demuxer demuxer.video_0 ! "
"identity name=for-link ! "
"video/x-vp9, width=1920, height=1080, framerate=15/1 ! "
"identity name=before signal-handoffs=FALSE ! "
"vp9dec name=filter ! "
"identity name=after signal-handoffs=FALSE ! "
"video/x-raw, format=I420, framerate=15/1, width=1920, height=1080 ! "
"appsink name=appsink sync=false";
// clang-format on
GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), NULL);
assert(pipeline);
GstElement *demuxer = gst_bin_get_by_name(GST_BIN(pipeline), "demuxer");
GstElement *next = gst_bin_get_by_name(GST_BIN(pipeline), "for-link");
assert(demuxer);
assert(next);
GstElement *before = gst_bin_get_by_name(GST_BIN(pipeline), "before");
GstElement *after = gst_bin_get_by_name(GST_BIN(pipeline), "after");
GstElement *appsink = gst_bin_get_by_name(GST_BIN(pipeline), "appsink");
assert(before);
assert(after);
assert(appsink);
GstBuffer *key_buf = NULL;
GstPad *blockpad = gst_element_get_static_pad(before, "src");
gst_pad_add_probe(
blockpad,
GstPadProbeType(GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER),
save_keyframe_cb,
&key_buf,
NULL);
printf("set paused state\n");
gst_element_set_state(pipeline, GST_STATE_PAUSED);
gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
blockpad = gst_element_get_static_pad(before, "src");
gst_pad_add_probe(
blockpad,
GstPadProbeType(GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_BUFFER),
change_pipeline_cb,
key_buf,
NULL);
printf("seek\n");
bool ok = gst_element_seek_simple(pipeline,
GST_FORMAT_TIME,
GstSeekFlags(GST_SEEK_FLAG_FLUSH),
60 * GST_SECOND);
assert(ok);
printf("play the pipeline\n");
gst_element_set_state(pipeline, GST_STATE_PLAYING);
gst_element_get_state(pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
printf("state updated\n");
for (size_t i = 0; i < 10; ++i) {
GstSample *sample = gst_app_sink_pull_sample(GST_APP_SINK(appsink));
if (sample == NULL) {
break;
}
GstBuffer *buf = gst_sample_get_buffer(sample);
printf("sample: %li\n", GST_BUFFER_PTS(buf));
gst_sample_unref(sample);
}
printf("finish pipeline\n");
gst_element_set_state(pipeline, GST_STATE_NULL);
return EXIT_SUCCESS;
}
static GstPadProbeReturn
save_keyframe_cb(GstPad *, GstPadProbeInfo *info, gpointer user_data) {
GstBuffer **for_save = (GstBuffer **)user_data;
*for_save = gst_buffer_ref(GST_PAD_PROBE_INFO_BUFFER(info));
return GST_PAD_PROBE_REMOVE;
}
static GstPadProbeReturn
change_pipeline_cb(GstPad *pad, GstPadProbeInfo *info, gpointer user_data) {
GstBuffer *key_buffer = (GstBuffer *)user_data;
printf("pad callback\n");
GstElement *before = gst_pad_get_parent_element(pad);
GstObject *pipeline = gst_element_get_parent(before);
GstElement *after = gst_bin_get_by_name(GST_BIN(pipeline), "after");
GstElement *filter = gst_bin_get_by_name(GST_BIN(pipeline), "filter");
assert(before);
assert(pipeline);
assert(after);
assert(filter);
printf("unlink filter\n");
gst_element_unlink_many(before, filter, after, NULL);
printf("remove filter\n");
gst_element_set_state(filter, GST_STATE_NULL);
gst_bin_remove(GST_BIN(pipeline), filter);
GstElement *first = gst_element_factory_make("nvv4l2decoder", "first");
GstElement *second = gst_element_factory_make("nvvidconv", "second");
assert(first);
assert(second);
printf("link before and after\n");
gst_bin_add_many(GST_BIN(pipeline), first, second, NULL);
gst_element_link_many(before, first, second, after, NULL);
printf("syncing the state\n");
gst_element_sync_state_with_parent(first);
gst_element_sync_state_with_parent(second);
printf("remove the probe\n");
gst_pad_remove_probe(pad, GST_PAD_PROBE_INFO_ID(info));
// XXX for some reason nvv decoder respects only first key frame
// also practice show that this frame will not be recieved via appsrc
printf("push first key frame\n");
gst_pad_push(pad, gst_buffer_ref(key_buffer));
printf("pad callback exit\n");
return GST_PAD_PROBE_OK;
}