NVMultiurisrcbin with smart record

• Hardware Platform: Jetson
• DeepStream Version: 6.3
• JetPack Version: 5.1.2
• TensorRT Version: 8.5.2
• NVIDIA GPU Driver Version: R35

NVMultiurisrcbin has a strange behavior when using smart record

For the start record, I use a simple timer that stops after ten seconds.

My MP4 file was saved, but I received a segmentation fault immediately afterward and the pipeline stopped.

The only way to avoid the Segmentation fault is to call NvDsSRDestroy after NvDsSRStop but I cannot call NvDsSRStart after this.

What can I do to solve this problem?

/*
 * SPDX-FileCopyrightText: Copyright (c) 2018-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 * SPDX-License-Identifier: MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <gst/gst.h>
#include <glib.h>


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cuda_runtime_api.h>
#include "nvds_yml_parser.h"
#include <unordered_map>
#include <sstream>
#include <mutex>
#include <cstdlib>
#include "deepstream_config.h"
#include "nvdsinfer_custom_impl.h"
#include "nvds_version.h"
#include "gstnvdsmeta.h"
#include "gstdsexample.h"
#include <yaml-cpp/yaml.h>
#include <gst/app/gstappsrc.h>
#include <nvdsgstutils.h>
#include <iostream>
#include <string>
#include <algorithm>
#include <cmath>
#include "nvdspreprocess_meta.h"
#include <unordered_set>
#include "nvdsgstutils.h"
#include "gst-nvdssr.h"
#include "gst-nvevent.h"
#include "deepstream_c2d_msg.h"
#include "deepstream_image_save.h"
#include "gst-nvdscustommessage.h"
#include "deepstream_config_yaml.h"
#include "deepstream_common.h"
#include "gstdsnvurisrcbin.h"
#include "deepstream_perf.h"
#include "nvdsmeta_schema.h"
#define MAX_STREAMS 4

GstDsNvUriSrcBin* sub_bins[MAX_STREAMS];
NvDsSRInitParams params = { 0 };
GstElement* tee_rtsp_pre_decode;

/* Check for parsing error. */
#define RETURN_ON_PARSER_ERROR(parse_expr) \
  if (NVDS_YAML_PARSER_SUCCESS != parse_expr) { \
    g_printerr("Error in parsing configuration file.\n"); \
    return -1; \
  }
  
GST_DEBUG_CATEGORY (NVDS_APP);


typedef struct {
  GMutex *lock;
  int num_sources;
}LatencyCtx;


NvDsMsgConsumerConfig message_consumer_config;
NvDsC2DContext *c2d_ctx;
static GMainLoop *loop;
GstElement *pipeline; 

gint frame_number = 0;

GstClockTime last_ts[MAX_STREAMS];
gboolean startRecord[MAX_STREAMS];
GQuark dsMetaQuark;


static GstPadProbeReturn osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer u_data) {
    GstBuffer *buf = (GstBuffer *) info->data;
    NvDsObjectMeta *obj_meta = NULL;
    NvDsMetaList * l_frame = NULL;
    NvDsMetaList * l_obj = NULL;
    NvDsMetaList *l = NULL;
    NvDsMetaList *user_meta_list = NULL;
    NvDsUserMeta *user_meta = NULL;
    NvDsPayload *payload;
    GstMeta *gstMeta = NULL;
    NvDsBatchMeta *batch_meta = NULL;
    gpointer state = NULL;
    NvDsMeta *meta = NULL;

try {
    
  if (true) {
    GstClockTime timestamp = GST_BUFFER_PTS(buf);

    GstClockTime now = gst_clock_get_time(gst_system_clock_obtain ());    
    batch_meta = gst_buffer_get_nvds_batch_meta (buf);

    for (l_frame = batch_meta->frame_meta_list; l_frame; l_frame = l_frame->next) {
      NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)l_frame->data;
      user_meta_list = ((NvDsFrameMeta*)frame_meta)->frame_user_meta_list;

            if (!startRecord[frame_meta->source_id] && now - last_ts[frame_meta->source_id] >= GST_SECOND * 40){
              last_ts[frame_meta->source_id] = now;
              NvDsSRSessionId sessId = 0;
              startRecord[frame_meta->source_id] = true;
              NvDsSRStatus status = NvDsSRStart ( (NvDsSRContext *) sub_bins[frame_meta->source_id]->recordCtx, &sessId, 0, 30, NULL);
              GST_ERROR ("start record for source_id %d status %d",frame_meta->source_id, status);
            }

      //need to stop record 
      NvDsSRContext * recordCtx = (NvDsSRContext *) sub_bins[frame_meta->source_id]->recordCtx;
      if (recordCtx->recordOn && now - last_ts[frame_meta->source_id] >= GST_SECOND * 10 && startRecord[frame_meta->source_id]) {
          NvDsSRStatus status = NvDsSRStop (recordCtx, 0);
          GST_ERROR ("stop record for source_id %d status %d",frame_meta->source_id, status);
          // without this have segmentation fault
          //status =  NvDsSRDestroy(recordCtx);
          GST_ERROR ("NvDsSRDestroy record for source_id %d status %d",frame_meta->source_id, status);
          startRecord[frame_meta->source_id] = false;
      }    
    }
  }

}
catch (...) {
  GST_ERROR ("error start stop");
}
    return GST_PAD_PROBE_OK;
}



static gboolean
message_cb (GstBus * bus, GstMessage * message, gpointer user_data)
{
  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:{
      GError *err = NULL;
      gchar *name, *debug = NULL;

      name = gst_object_get_path_string (message->src);
      gst_message_parse_error (message, &err, &debug);

      g_printerr ("ERROR: from element %s: %s\n", name, err->message);
      if (debug != NULL)
        g_printerr ("Additional debug info:\n%s\n", debug);

      g_error_free (err);
      g_free (debug);
      g_free (name);

      g_main_loop_quit (loop);
      break;
    }
    case GST_MESSAGE_WARNING:{
      GError *err = NULL;
      gchar *name, *debug = NULL;

      name = gst_object_get_path_string (message->src);
      gst_message_parse_warning (message, &err, &debug);

      g_printerr ("ERROR: from element %s: %s\n", name, err->message);
      if (debug != NULL)
        g_printerr ("Additional debug info:\n%s\n", debug);

      g_error_free (err);
      g_free (debug);
      g_free (name);
      break;
    }
    case GST_EVENT_EOS:
    case GST_MESSAGE_EOS:
      g_print ("Got EOS\n");
      // if (c2d_ctx){
      //   stop_cloud_to_device_messaging (c2d_ctx);
      // }

      g_main_loop_quit (loop);
      gst_element_set_state (pipeline, GST_STATE_NULL);
      g_main_loop_unref (loop);
      gst_object_unref (pipeline);
      break;
    case GST_MESSAGE_STATE_CHANGED: 
      GstState old_state, new_state, pending_state;
      gst_message_parse_state_changed(message, &old_state, &new_state, &pending_state);

      GST_ERROR ("GST_OBJECT_NAME %s old_state %d new_state %d", GST_OBJECT_NAME (message->src), old_state, new_state);

      if (new_state == GST_STATE_PLAYING) {
        if (strncmp(GST_OBJECT_NAME (message->src) ,"dsnvurisrcbin", strlen("dsnvurisrcbin")) == 0) {
            GST_ERROR("dsnvurisrcbin play");
            std::string objectName(GST_OBJECT_NAME (message->src));
            int number = std::stoi(objectName.substr(strlen("dsnvurisrcbin")));
            GstDsNvUriSrcBin *nvUriSrcBin = (GstDsNvUriSrcBin *)GST_ELEMENT(message->src);
            sub_bins[number] = nvUriSrcBin;
        }
        else if (strncmp(GST_OBJECT_NAME (message->src) ,"tee_rtsp_pre_decode", strlen("tee_rtsp_pre_decode")) == 0) {
          tee_rtsp_pre_decode = GST_ELEMENT(message->src);
          GST_ERROR("tee_rtsp_pre_decode  found");
        }
      }

      break;
    default:
      break;
  }

  return TRUE;
}


int
main (int argc, char *argv[])
{
          g_print ("befibvdsvjvjvore try tp parse\n");

  GError *error = NULL;
  gst_init (&argc, &argv);

  if (!std::getenv("CONFIG_FILE_PATH")) {
    g_printerr ("CONFIG_FILE_PATH parmeters missing\n");
  }
  if (!std::getenv("BROKER_CONNECTION_STRING")) {
    g_printerr ("BROKER_CONNECTION_STRING parmeters missing\n");
  }
  if (!std::getenv("BROKER_TOPIC")) {
    g_printerr ("BROKER_TOPIC parmeters missing\n");
  }
  if (!std::getenv("BROKER_CONFIG_FILE_PATH")) {
    g_printerr ("BROKER_CONFIG_FILE_PATH parmeters missing\n");
  }

  if (!std::getenv("BROKER_PROTO_LIB_PATH")) {
    g_printerr ("BROKER_PROTO_LIB_PATH parmeters missing\n");
  }

  if (!std::getenv("SMART_RECORD_DIR_PATH")) {
    g_printerr ("SMART_RECORD_DIR_PATH parmeters missing\n");
  }

  
    // Load the YAML configuration file
  YAML::Node config = YAML::LoadFile(std::getenv("CONFIG_FILE_PATH"));
  std::stringstream stringBuilder;
   dsMetaQuark = g_quark_from_static_string (NVDS_META_STRING);

  // Count the number of source entries in the YAML configuration
  int numSources = 2;
  for (const auto& node : config) {
      if (node.second["list"]) {
          std::string str = node.second["list"].as<std::string>();
          numSources = std::count(str.begin(), str.end(), ',');
          stringBuilder << "nvmultiurisrcbin name=nvmultiurisrcbin smart-record=2 smart-rec-cache=2 "
                          " smart-rec-dir-path=" << std::string(std::getenv("SMART_RECORD_DIR_PATH")) << " attach-sys-ts=false "
                          " attach-sys-ts-as-ntp=0 gpu-id=0 height=3840 width=2160 ip-address=localhost max-batch-size=10" << 
                          " uri-list=" << node.second["list"] << " ";
          break;
      }
  }

  if (stringBuilder.str().empty()) {
      stringBuilder << "nvmultiurisrcbin name=nvmultiurisrcbin smart-record=2 smart-rec-default-duration=600 "
                  " smart-rec-dir-path=" << std::string(std::getenv("SMART_RECORD_DIR_PATH")) << " attach-sys-ts=false "
                  " attach-sys-ts-as-ntp=0 gpu-id=0 height=3840 width=2160 ip-address=localhost batched-push-timeout=66666 max-batch-size=" << std::to_string(numSources) << 
                  " drop-pipeline-eos=0 live-source=1 ";

  }

  stringBuilder << " "
  " ! queue ! dsmonitor name=monitor "
  " ! queue ! nvmsgbroker name=nvmsgbroker1 config='" << std::getenv("BROKER_CONFIG_FILE_PATH") << "' sync=0 async=false proto-lib=" << std::getenv("BROKER_PROTO_LIB_PATH") << " conn-str=" <<std::getenv("BROKER_CONNECTION_STRING") << " topic='" << std::getenv("BROKER_TOPIC") << "'"
  " ";

  g_print("%s", stringBuilder.str().c_str());
  g_print ("\n");

  pipeline = gst_parse_launch(stringBuilder.str().c_str(), &error);
        g_print ("befibjvjvore try dcscd tp parse\n");

  if (error) {
    g_printerr("Error: %s\n", error->message);
    g_error_free(error);
    return -1;
  }


  GstBus *bus = NULL;
  GstPad *osd_sink_pad = NULL, *osd_sink_pad2 = NULL;

  int current_device = -1;
  cudaGetDevice(&current_device);
  struct cudaDeviceProp prop;
  cudaGetDeviceProperties(&prop, current_device);


  /* Standard GStreamer initialization */
  loop = g_main_loop_new (NULL, FALSE);

  GstElement* nvmsgbroker1 = gst_bin_get_by_name(GST_BIN(pipeline), "nvmsgbroker1");
  g_object_set (G_OBJECT (nvmsgbroker1),"sync", 0, "async", false, "proto-lib", std::getenv("BROKER_PROTO_LIB_PATH"),
              "conn-str", std::getenv("BROKER_CONNECTION_STRING"), "sync", FALSE, "topic", std::getenv("BROKER_TOPIC"), "config", std::getenv("BROKER_CONFIG_FILE_PATH"), NULL);

  
  gboolean parse_err = !parse_msgconsumer_yaml (&message_consumer_config, "message-consumer0", std::getenv("CONFIG_FILE_PATH"));

  if (parse_err) {
      g_print ("Failed to parse_msgconsumer_yaml");
  }

  osd_sink_pad2 = gst_element_get_static_pad (nvmsgbroker1, "sink");

  if (!osd_sink_pad2)
    g_print ("Unable to get sink pad2\n");
  else {
    gst_pad_add_probe (osd_sink_pad2, GST_PAD_PROBE_TYPE_BUFFER, osd_sink_pad_buffer_probe, NULL, NULL);
  }


  /* we add a message handler */
  bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
  gst_bus_add_signal_watch(bus);
  g_signal_connect(G_OBJECT(bus), "message", G_CALLBACK(message_cb), NULL);
  gst_object_unref(GST_OBJECT(bus));
  gst_element_set_state(pipeline, GST_STATE_PLAYING);
  g_print("Starting loop");
  g_main_loop_run(loop);

  return 0;
}

Thanks

  1. thanks for the sharing! Noticing there are many custom codes, could you modify deepstream sample to reproduce this issue? deepstream-test5 will use NVMultiurisrcbin.
  2. if you still need to use custom code, please simply the code to narrow down this issue. and please share how to compile and run the app. Thanks!

demo.zip (7.5 KB)

Here is the simplest code that I can write to demonstrate the issue.
In line 198 of deepstream_demo.cpp, the RTSP address needs to be changed.

This zip contains a build_demo.sh script that builds and runs the demo application that raises a segmentation fault after calling NvDsSRStop.

Thanks a lot

Here is a workaround.
//NvDsSRStatus status = NvDsSRStart ( (NvDsSRContext *) sub_bins[frame_meta->source_id]->recordCtx, &sessId, 0, 30, NULL);
g_signal_emit_by_name(sub_bins[frame_meta->source_id], “start-sr”, &sessId, 2, 30, NULL);

//NvDsSRStatus status = NvDsSRStop (recordCtx, 0);
g_signal_emit_by_name(sub_bins[frame_meta->source_id], “stop-sr”, NULL);

It solved my problem
Thanks

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