DeepStream SDK FAQ

35.How to add custom metadata in a GStreamer native plugin and access it in Python.

Here is an example based on deepstream-test1.py and dsexample.

35.1. Add custom metadata in the dsexample plugin.

diff --git a/sources/gst-plugins/gst-dsexample/gstdsexample.cpp b/sources/gst-plugins/gst-dsexample/gstdsexample.cpp
index d5399c7..764ec0a 100644
--- a/sources/gst-plugins/gst-dsexample/gstdsexample.cpp
+++ b/sources/gst-plugins/gst-dsexample/gstdsexample.cpp
@@ -318,6 +318,76 @@ gst_dsexample_get_property (GObject * object, guint prop_id,
   }
 }
 
+#define NVDS_GST_META_DSEXAMPLE (nvds_get_user_meta_type((char *)"NVIDIA.NVDS_GST_META_DSEXAMPLE"))
+
+typedef struct _DsExampleMeta
+{
+  gchar *timestamp;
+} DsExampleMeta;
+
+/* gst meta copy function set by user */
+static gpointer dsexample_meta_copy_func(gpointer data, gpointer user_data)
+{
+  NvDsUserMeta *user_meta = (NvDsUserMeta *) data;
+  DsExampleMeta *src_meta = (DsExampleMeta *)user_meta->user_meta_data;
+  DsExampleMeta *dst_meta = (DsExampleMeta*)g_malloc0(sizeof(DsExampleMeta));
+  if (src_meta->timestamp == NULL)
+  {
+    g_print("no buffer abort !!!! \n");
+    abort();
+  }
+  dst_meta->timestamp = g_strdup(src_meta->timestamp);
+  memcpy(dst_meta, src_meta, sizeof(DsExampleMeta));
+  return (gpointer)dst_meta;
+}
+
+/* gst meta release function set by user */
+static void dsexample_meta_release_func(gpointer data, gpointer user_data)
+{
+  NvDsUserMeta *user_meta = (NvDsUserMeta *) data;
+  DsExampleMeta *meta = (DsExampleMeta *)user_meta->user_meta_data;
+  if (meta) {
+    if (meta->timestamp) {
+      g_free(meta->timestamp);
+      meta->timestamp = NULL;
+    }
+    g_free(meta);
+    meta = NULL;
+  }
+  user_meta->user_meta_data = NULL;
+}
+
+void attach_frame_custom_metadata (NvDsBatchMeta *batch_meta, NvDsFrameMeta *frame_meta)
+{
+  DsExampleMeta *meta = (DsExampleMeta *)g_malloc0(sizeof(DsExampleMeta));
+  if (meta == NULL)
+  {
+    g_print("no buffer abort !!!! \n");
+    abort();
+  }
+  struct timeval time;
+  gettimeofday(&time, NULL);
+  double cur = (time.tv_sec) * 1000.0;
+  cur += (time.tv_usec) / 1000.0;
+  /* Add dummy metadata */
+  meta->timestamp = (gchar *)g_malloc0(128);
+  snprintf(meta->timestamp, 128, "cur %.3f ms", cur);
+
+  NvDsUserMeta *user_meta =
+            nvds_acquire_user_meta_from_pool (batch_meta);
+  if (user_meta) {
+    user_meta->user_meta_data = (void *) meta;
+    user_meta->base_meta.meta_type = NVDS_GST_META_DSEXAMPLE;
+    user_meta->base_meta.copy_func =
+        (NvDsMetaCopyFunc) dsexample_meta_copy_func;
+    user_meta->base_meta.release_func =
+        (NvDsMetaReleaseFunc) dsexample_meta_release_func;
+    nvds_add_user_meta_to_frame (frame_meta, user_meta);
+  }
+
+  g_print("Attached DsExampleMeta from native ==> %s\n", meta->timestamp);
+}
+
 /**
  * Initialize all resources and start the output thread
  */
@@ -759,6 +829,7 @@ gst_dsexample_transform_ip (GstBaseTransform * btrans, GstBuffer * inbuf)
 #endif
       /* Attach the metadata for the full frame */
       attach_metadata_full_frame (dsexample, frame_meta, scale_ratio, output, i);
+      attach_frame_custom_metadata(batch_meta, frame_meta);
       i++;
       free (output);
     }


35.2. Modify the corresponding Python bindings.

diff --git a/bindings/src/bindnvdsmeta.cpp b/bindings/src/bindnvdsmeta.cpp
index 95b0585..2aec39f 100644
--- a/bindings/src/bindnvdsmeta.cpp
+++ b/bindings/src/bindnvdsmeta.cpp
@@ -22,6 +22,13 @@
 
 namespace py = pybind11;
 
+#define NVDS_GST_META_DSEXAMPLE (nvds_get_user_meta_type((char *)"NVIDIA.NVDS_GST_META_DSEXAMPLE"))
+
+typedef struct _DsExampleMeta
+{
+  gchar *timestamp;
+} DsExampleMeta;
+
 namespace pydeepstream {
 
     void bindnvdsmeta(py::module &m) {
@@ -77,8 +84,25 @@ namespace pydeepstream {
                        pydsdoc::nvmeta::MetaTypeDoc::NVDS_START_USER_META)
                 .value("NVDS_FORCE32_META", NVDS_FORCE32_META,
                        pydsdoc::nvmeta::MetaTypeDoc::NVDS_FORCE32_META)
+                .value("NVDS_GST_META_DSEXAMPLE", NVDS_GST_META_DSEXAMPLE)
                 .export_values();
 
+        py::class_<DsExampleMeta>(m, "DsExampleMeta")
+                .def(py::init<>())
+                .def_property("timestamp",
+                              STRING_PROPERTY(DsExampleMeta,
+                                              timestamp))
+                .def("cast",
+                     [](void *data) {
+                         return (DsExampleMeta *) data;
+                     },
+                     py::return_value_policy::reference)
+
+                .def("cast",
+                     [](size_t data) {
+                         return (DsExampleMeta *) data;
+                     },
+                     py::return_value_policy::reference);
 
         py::class_<NvDsComp_BboxInfo>(m, "NvDsComp_BboxInfo",
                                       pydsdoc::nvmeta::NvDsComp_BboxInfoDoc::descr)

35.3.Access the custom metadata in Python code.

diff --git a/apps/deepstream-test1/deepstream_test_1.py b/apps/deepstream-test1/deepstream_test_1.py
index 861cefc..a3e2809 100755
--- a/apps/deepstream-test1/deepstream_test_1.py
+++ b/apps/deepstream-test1/deepstream_test_1.py
@@ -47,6 +47,7 @@ def osd_sink_pad_buffer_probe(pad,info,u_data):
     # Note that pyds.gst_buffer_get_nvds_batch_meta() expects the
     # C address of gst_buffer as input, which is obtained with hash(gst_buffer)
     batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
+
     l_frame = batch_meta.frame_meta_list
     while l_frame is not None:
         try:
@@ -82,6 +83,19 @@ def osd_sink_pad_buffer_probe(pad,info,u_data):
             except StopIteration:
                 break
 
+        l_user=frame_meta.frame_user_meta_list
+        while l_user is not None:
+            try:
+                user_meta = pyds.NvDsUserMeta.cast(l_user.data)
+                if (user_meta and user_meta.base_meta.meta_type == pyds.NvDsMetaType.NVDS_GST_META_DSEXAMPLE):
+                    dsexample_meta = pyds.DsExampleMeta.cast(user_meta.user_meta_data)
+                    print(f"access dsmeta from python {pyds.get_string(dsexample_meta.timestamp)}")
+            except StopIteration:
+                break
+            try: 
+                l_user=l_user.next
+            except StopIteration:
+                break
         # Acquiring a display meta object. The memory ownership remains in
         # the C code so downstream plugins can still access it. Otherwise
         # the garbage collector will claim it when this probe function exits.
@@ -167,6 +181,10 @@ def main(args):
     if not pgie:
         sys.stderr.write(" Unable to create pgie \n")
 
+    dsexample = Gst.ElementFactory.make("dsexample", "dsexample")
+    if not dsexample:
+        sys.stderr.write(" Unable to create dsexample \n")
+
     # Use convertor to convert from NV12 to RGBA as required by nvosd
     nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor")
     if not nvvidconv:
@@ -186,7 +204,8 @@ def main(args):
             sys.stderr.write(" Unable to create nv3dsink \n")
     else:
         print("Creating EGLSink \n")
-        sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
+        # sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer")
+        sink = Gst.ElementFactory.make("fakesink", "nvvideo-renderer")
         if not sink:
             sys.stderr.write(" Unable to create egl sink \n")
 
@@ -206,6 +225,7 @@ def main(args):
     pipeline.add(decoder)
     pipeline.add(streammux)
     pipeline.add(pgie)
+    pipeline.add(dsexample)
     pipeline.add(nvvidconv)
     pipeline.add(nvosd)
     pipeline.add(sink)
@@ -225,7 +245,8 @@ def main(args):
         sys.stderr.write(" Unable to get source pad of decoder \n")
     srcpad.link(sinkpad)
     streammux.link(pgie)
-    pgie.link(nvvidconv)
+    pgie.link(dsexample)
+    dsexample.link(nvvidconv)
     nvvidconv.link(nvosd)
     nvosd.link(sink)

35.4.Recompile and install dsexample and pyds, and then run deepstream_test1.py. You will see the following log in the output

Attached DsExampleMeta from native ==> cur 1712056757649.727 ms
access dsmeta from python cur 1712056757649.727 ms
Frame Number=303 Number of Objects=31 Vehicle_count=26 Person_count=5
Attached DsExampleMeta from native ==> cur 1712056757651.504 ms
access dsmeta from python cur 1712056757651.504 ms
Frame Number=304 Number of Objects=27 Vehicle_count=22 Person_count=5
Attached DsExampleMeta from native ==> cur 1712056757653.223 ms
access dsmeta from python cur 1712056757653.223 ms
Frame Number=305 Number of Objects=29 Vehicle_count=23 Person_count=6
Attached DsExampleMeta from native ==> cur 1712056757654.953 ms
access dsmeta from python cur 1712056757654.953 ms
Frame Number=306 Number of Objects=29 Vehicle_count=24 Person_count=5
Attached DsExampleMeta from native ==> cur 1712056757656.722 ms
access dsmeta from python cur 1712056757656.722 ms
Frame Number=307 Number of Objects=29 Vehicle_count=25 Person_count=4
Attached DsExampleMeta from native ==> cur 1712056757658.446 ms
access dsmeta from python cur 1712056757658.446 ms