Deadlock when try to seek in pipeline

Hi there, a follow-up on this issue that was reported on Xavier device with jetpack 4 (Deadlock when try to seek in pipeline). I tested the provided sample code on Orin with JP 5.1.2 and noticed this this issue still persist. Is there any fix for this yet?

I do find a temp solution that seems to be working without having the deadlock issues by adding queue around the nvv decoder as shown below, however, I don’t think this is a complete solution. This is just adding a buffer or decoupling in the pipeline. Is there any fundamental solution for this issue? Thanks

    std::string seek_pipeline =
        "filesrc location=" + video_file + " ! "
        "matroskademux name=demuxer demuxer.video_0 ! "
        "tee name=t ! "
            "queue max-size-buffers=30 max-size-time=1000000000 max-size-bytes=0 ! "
        "h264parse ! "
        "nvv4l2decoder ! "
            "queue max-size-buffers=30 max-size-time=1000000000 max-size-bytes=0 ! "
        "nvvidconv ! "
        "video/x-raw ! "
        "appsink name=sink emit-signals=true sync=false";

Hi,
Please try Jetpack 6.0GA and see if you still hits the issue. If yes, please share the step-by-step method and we will set up AGX Orin developer kit to replicate the issue.

Your solution of adding queue plugin looks fine, if you cannot upgrade to later version. Please apply it on 5.1.2.

unfortunately, we are not planning to have deployment on Jetpack 6.0 yet. since this issues persists through jp4 to jp5, are there significant changes made on JP6 that you believe could resolve this?

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;
}

Hi,
We try to decode h264 and h265 on AGX Orin developer kit/Jetpack 6.1. Do hit the issue.

  1. Modify the script to generate h264(or h265):
$ gst-launch-1.0 videotestsrc num-buffers=4500 ! video/x-raw, format=BGR, width=1920, height=1080, framerate=15/1 ! videoconvert ! nvvidconv ! "video/x-raw(memory:NVMM), format=NV12" ! nvv4l2h264enc maxperf-enable=1 bitrate=8000000 ! h264parse ! matroskamux ! filesink location=video.mkv
  1. Modify the pipeline in the sample code:
    std::string seek_pipeline =
        "filesrc location=" + video_file + " ! "
        "matroskademux name=demuxer demuxer.video_0 ! "
        " h264parse ! nvv4l2decoder ! "
        "nvvidconv ! "
        "video/x-raw ! "
        "appsink name=sink emit-signals=true sync=false";

Please try this and see if it works. Would like to align that this setup also works in your environment first.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.