Segmentation fault when copying NvDsFrameMeta in a unit test

I’m working on a GStreamer plugin/element to process NvDsBatchMeta metadata from the DeepStream elements. While the element is working in a pipeline, I’m running into a segmentation fault when writing unit tests for my element. Specifically, I want to generate buffers with some NvDsBatchMeta metadata attached and see if the output is as I expect. The only method I’ve found to attach that metadata is to call gst_buffer_add_nvds_meta. That function takes a copy function and I want to pass nvds_batch_meta_copy_func as the copy function (i.e. the copy_func parameter), but I’m getting a crash when nvds_batch_meta_copy_func is called and the batch meta has a frame meta added to it.

Here’s my code to reproduce the problem, using only nvds_batch_meta_copy_func.

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

int main(int argc, char **argv)
{
  gst_init(&argc, &argv);
  NvDsBatchMeta *batch_meta = nvds_create_batch_meta(2);
  NvDsFrameMeta *frame_meta = nvds_acquire_frame_meta_from_pool(batch_meta);
  nvds_add_frame_meta_to_batch(batch_meta, frame_meta);
  gpointer test_copy = nvds_batch_meta_copy_func(batch_meta, nullptr);
}

In a related post (Deepstream deepcopy metadata) I saw a solution saying to use nvds_copy_frame_meta_list, so I tried again, without success.

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

static gpointer
alt_batch_meta_copy_func(gpointer data, gpointer user_data)
{
  NvDsBatchMeta *batch_meta = (NvDsBatchMeta *)data;
  NvDsBatchMeta *other = nvds_create_batch_meta(batch_meta->max_frames_in_batch);
  nvds_copy_frame_meta_list(batch_meta->frame_meta_list, other);

  return other;
}

int main(int argc, char **argv)
{
  gst_init(&argc, &argv);
  NvDsBatchMeta *batch_meta = nvds_create_batch_meta(2);
  NvDsFrameMeta *frame_meta = nvds_acquire_frame_meta_from_pool(batch_meta);
  nvds_add_frame_meta_to_batch(batch_meta, frame_meta);
  gpointer test_copy = alt_batch_meta_copy_func(batch_meta, nullptr);
}

Here’s the back trace from GDB, for the first program.

Program received signal SIGSEGV, Segmentation fault.
0x0000007fb7fa3504 in ?? () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
(gdb) bt
#0  0x0000007fb7fa3504 in ?? () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
#1  0x0000007fb7fa2a2c in nvds_copy_frame_meta_list () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
#2  0x0000007fb7fa2ab4 in ?? () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
#3  0x000000555555590c in main (argc=<optimized out>, argv=<optimized out>) at min1.cpp:10

Is there some initialization step that I’m missing? Is there another way to attach the metadata to a GStreamer buffer?

I’m running DeepStream 6.0.1 on a Jetson Nano. The MD5sum of /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so is 4c9b0870fd9e2e256a54c04814023c86.

It seems you didn’t add anything on the framemeta before you copied it.
1.create batch meta: nvds_create_batch_meta
2.add to the gstbuffer: gst_buffer_add_nvds_meta
3.assign values to batch meta: copy_func, release_func
4.get a real frame meta and add it to batch: nvds_add_frame_meta_to_batch

Maybe I misunderstood your advice, but my interpretation of your suggestion doesn’t appear to perform any better; it still crashes with a segmentation fault in step 5.

I’m trying to attach a NvDsBatchMeta structure to a buffer so that it can be retrieved using gst_buffer_get_nvds_batch_meta. Is gst_buffer_add_nvds_meta the correct function to call?

#include <stdio.h>

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

static gpointer
my_copy_func(gpointer data, gpointer user_data)
{
  fprintf(stderr, "my_copy_func - ENTER.\n");
  gpointer ret = nvds_batch_meta_copy_func(data, user_data);
  fprintf(stderr, "my_copy_func - LEAVE.\n");
  return ret;
}

static void
my_release_func(gpointer data, gpointer user_data)
{
  fprintf(stderr, "my_copy_func - ENTER.\n");
  nvds_batch_meta_release_func(data, user_data);
  fprintf(stderr, "my_copy_func - LEAVE.\n");
}

int main(int argc, char **argv)
{
  gst_init(&argc, &argv);

  // Step 1: Create batch meta
  NvDsBatchMeta *batch_meta = nvds_create_batch_meta(2);

  // Step 2: Add it to the buffer
  GstBuffer *buf = gst_buffer_new();
  NvDsMeta *meta = gst_buffer_add_nvds_meta(buf, batch_meta, nullptr, nvds_batch_meta_copy_func, nvds_batch_meta_release_func);
  if (meta == nullptr)
  {
    fprintf(stderr, "Failed to add nvds meta.\n");
    return 1;
  }

  fprintf(stderr, "meta_data is %sNULL, user_data is %sNULL, copyfunc is %sNULL, freefunc is %sNULL, "
      "transform_func is %sNULL, release_func is %sNULL.\n",
      meta->meta_data ? "not " : "",
      meta->user_data ? "not " : "",
      meta->copyfunc ? "not " : "",
      meta->freefunc ? "not " : "",
      meta->gst_to_nvds_meta_transform_func ? "not " : "",
      meta->gst_to_nvds_meta_release_func ? "not " : "");

  // Step 3: Assign values to batch meta
  fprintf(stderr, "copy_func is %sNULL, release_func is %sNULL.\n",
      batch_meta->base_meta.copy_func ? "not " : "",
      batch_meta->base_meta.release_func ? "not " : "");
  batch_meta->base_meta.copy_func = my_copy_func;
  batch_meta->base_meta.release_func = my_release_func;

  // Step 4: Get a real frame meta and add it to batch meta
  NvDsFrameMeta *frame_meta = nvds_acquire_frame_meta_from_pool(batch_meta);
  nvds_add_frame_meta_to_batch(batch_meta, frame_meta);

  // Step 5: See if we can copy the batch meta
  gpointer test_copy = nvds_batch_meta_copy_func(batch_meta, nullptr);
}

The output and back trace is as follows. (Line 60 of min3.cpp corresponds to step 5.)

meta_data is not NULL, user_data is NULL, copyfunc is not NULL, freefunc is not NULL, transform_func is NULL, release_func is NULL.
copy_func is not NULL, release_func is not NULL.

Program received signal SIGSEGV, Segmentation fault.
0x0000007fb7f83504 in ?? () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
(gdb) bt
#0  0x0000007fb7f83504 in ?? () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
#1  0x0000007fb7f82a2c in nvds_copy_frame_meta_list () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
#2  0x0000007fb7f82ab4 in ?? () from /opt/nvidia/deepstream/deepstream-6.0/lib/libnvds_meta.so
#3  0x0000005555555be4 in main (argc=<optimized out>, argv=<optimized out>) at min3.cpp:60

Yes, you can just use it like this: gst_buffer_add_nvds_meta(buf, batch_meta, NULL, nvds_batch_meta_copy_func, nvds_batch_meta_release_func).
Some of the internal pointers in frame_meta may not have values.
Could you add your code in our demo code like: sources\apps\sample_apps\deepstream-gst-metadata-test\deepstream_gst_metadata.c?

I’ve modified that sample app as follows. I added a log message to the function to copy the batch metadata and it appears that the program doesn’t copy the batch metadata. Please see the diff below.

--- apps/sample_apps/deepstream-gst-metadata-test/deepstream_gst_metadata.c.orig	2023-05-03 17:37:21.672356994 -0400
+++ apps/sample_apps/deepstream-gst-metadata-test/deepstream_gst_metadata.c	2023-05-04 11:40:46.847549993 -0400
@@ -90,6 +90,30 @@
   return (gpointer)dst_decoder_meta;
 }
 
+/* add logging so we know whether copies are occurring */
+static gpointer noisy_nvds_batch_meta_copy_func(gpointer data, gpointer user_data)
+{
+  static int num_copies = 0;
+  gpointer ret;
+
+  g_print("Call #%d to nvds_batch_meta_copy_func is about to start.\n", ++num_copies);
+  ret = nvds_batch_meta_copy_func(data, user_data);
+  g_print("Call #%d to nvds_batch_meta_copy_func has finished.\n", num_copies);
+
+  return ret;
+}
+
+/* batch to nvds transform function set by user. "data" holds a pointer to NvDsUserMeta */
+static gpointer batch_to_nvds_meta_transform_func(gpointer data, gpointer user_data)
+{
+  NvDsUserMeta *user_meta = (NvDsUserMeta *)data;
+  NvDsBatchMeta *src_batch_meta =
+    (NvDsBatchMeta*)user_meta->user_meta_data;
+  NvDsBatchMeta *dst_batch_meta =
+    (NvDsBatchMeta *)noisy_nvds_batch_meta_copy_func(src_batch_meta, NULL);
+  return (gpointer)dst_batch_meta;
+}
+
 /* release function set by user to release gst to nvds transformed metadata.
  * "data" holds a pointer to NvDsUserMeta */
 static void decoder_gst_nvds_meta_release_func(gpointer data, gpointer user_data)
@@ -99,6 +123,15 @@
   decoder_meta_release_func(decoder_meta, NULL);
 }
 
+/* release function set by user to release batch to nvds transformed metadata.
+ * "data" holds a pointer to NvDsUserMeta */
+static void batch_nvds_meta_release_func(gpointer data, gpointer user_data)
+{
+  NvDsUserMeta *user_meta = (NvDsUserMeta *) data;
+  NvDsBatchMeta *batch_meta = (NvDsBatchMeta *)user_meta->user_meta_data;
+  nvds_batch_meta_release_func(batch_meta, NULL);
+}
+
 typedef struct _H264parseMeta
 {
   guint parser_frame_num;
@@ -163,6 +196,8 @@
   NvDecoderMeta * decoder_meta = NULL;
   H264parseMeta * h264parse_meta = NULL;
   NvDsMetaList * l_user_meta = NULL;
+  NvDsBatchMeta *user_batch_meta = NULL;
+  NvDsFrameMeta *user_frame_meta = NULL;
 
   NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
 
@@ -173,6 +208,22 @@
         for (l_user_meta = frame_meta->frame_user_meta_list; l_user_meta != NULL;
             l_user_meta = l_user_meta->next) {
           user_meta = (NvDsUserMeta *) (l_user_meta->data);
+          if(user_meta->base_meta.meta_type == NVDS_BATCH_GST_META)
+          {
+            user_batch_meta = (NvDsBatchMeta *)user_meta->user_meta_data;
+            if (user_batch_meta == NULL || user_batch_meta->num_frames_in_batch == 0
+                || user_batch_meta->frame_meta_list == NULL
+                || (user_frame_meta = nvds_get_nth_frame_meta(user_batch_meta->frame_meta_list, 0)) == NULL)
+            {
+              g_print("Batch Meta NOT retrieved as NVDS USER METADATA\n");
+            }
+            else
+            {
+              g_print("Batch Meta retrieved as NVDS USER METADATA For Frame_Num = %d\n",
+                  user_frame_meta->frame_num);
+            }
+          }
+
           if(user_meta->base_meta.meta_type == NVDS_DECODER_GST_META_EXAMPLE)
           {
             decoder_meta = (NvDecoderMeta *)user_meta->user_meta_data;
@@ -251,35 +302,27 @@
   GstBuffer *buf = (GstBuffer *) info->data;
   NvDsMeta *meta = NULL;
 
-  NvDecoderMeta *decoder_meta = (NvDecoderMeta *)g_malloc0(sizeof(NvDecoderMeta));
-  if(decoder_meta == NULL)
-  {
-    return GST_FLOW_ERROR;
-  }
-  /* Add dummy metadata */
-  decoder_meta->frame_type = (frame_number % 3);
-  decoder_meta->frame_num = frame_number++;
-  decoder_meta->dec_err = ((frame_number % 4) / 3);
+  NvDsBatchMeta *batch_meta = nvds_create_batch_meta(2);
+  NvDsFrameMeta *frame_meta = nvds_acquire_frame_meta_from_pool(batch_meta);
+  frame_meta->frame_num = ++frame_number;
+  nvds_add_frame_meta_to_batch(batch_meta, frame_meta);
 
   /* Attach decoder metadata to gst buffer using gst_buffer_add_nvds_meta() */
-  meta = gst_buffer_add_nvds_meta (buf, decoder_meta, NULL,
-      decoder_meta_copy_func, decoder_meta_release_func);
+  meta = gst_buffer_add_nvds_meta (buf, batch_meta, NULL,
+      noisy_nvds_batch_meta_copy_func, nvds_batch_meta_release_func);
 
   /* Set metadata type */
-  meta->meta_type = (GstNvDsMetaType)NVDS_DECODER_GST_META_EXAMPLE;
+  meta->meta_type = (GstNvDsMetaType)NVDS_BATCH_GST_META;
 
   /* Set transform function to transform decoder metadata from Gst meta to
    * nvds meta */
-  meta->gst_to_nvds_meta_transform_func = decoder_gst_to_nvds_meta_transform_func;
+  meta->gst_to_nvds_meta_transform_func = batch_to_nvds_meta_transform_func;
 
   /* Set release function to release the transformed nvds metadata */
-  meta->gst_to_nvds_meta_release_func = decoder_gst_nvds_meta_release_func;
+  meta->gst_to_nvds_meta_release_func = batch_nvds_meta_release_func;
 
-  g_print("GST Dec Meta attached with gst decoder output buffer for Frame_Num = %d\n",
-      decoder_meta->frame_num);
-  g_print("Attached decoder Metadata: frame type = %d, frame_num = %d decode_error_status = %d\n\n",
-      decoder_meta->frame_type, decoder_meta->frame_num,
-      decoder_meta->dec_err);
+  g_print("Batch Meta attached with gst decoder output buffer for Frame_Num = %d\n",
+      frame_number);
 
   return GST_PAD_PROBE_OK;
 }

That said, as you might’ve guessed from the delay in my reply, I’ve found a workaround for my problem: rather than using the pipeline appsrc ! my_element ! appsink and attaching metadata in the appsrc, I’ve used the pipeline videotestsrc ! nvvideoconvert ! mux.sink_0 nvstreammux name=mux width=1280 height=720 batch-size=1 ! nvstreamdemux name=dm dm.src_0 ! my_element ! appsink name=sink sync=false and attached a probe to the src pad of the mux where I can modify the metadata as needed. This idea was inspired by another post to this forum. Sadly, I cannot find the post right now to thank the author.

Thank you for your help.

Glad to hear that. You can attach the link here too. We may have missed out on thanking the author.

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