31.[DSx_All_App] Use nvurisrcbin plugin to do smart record in Python
31.1 Apply the following patch and rebuild the pyds
diff --git a/bindings/src/bindfunctions.cpp b/bindings/src/bindfunctions.cpp
index eade8d5..7a4b459 100644
--- a/bindings/src/bindfunctions.cpp
+++ b/bindings/src/bindfunctions.cpp
@@ -586,7 +586,7 @@ namespace pydeepstream {
std::function<utils::RELEASEFUNC_SIG> const &func) {
utils::set_freefunc(meta, func);
},
- py::call_guard<py::gil_scoped_release>(),
+ py::call_guard<py::gil_scoped_release>(),
"meta"_a,
"func"_a,
pydsdoc::methodsDoc::user_releasefunc);
@@ -601,10 +601,9 @@ namespace pydeepstream {
// Required for backward compatibility
m.def("unset_callback_funcs",
[]() {
- utils::release_all_func();
- },
- py::call_guard<py::gil_scoped_release>()
- );
+ utils::release_all_func();
+ },
+ py::call_guard<py::gil_scoped_release>());
m.def("alloc_char_buffer",
[](size_t size) {
@@ -650,6 +649,14 @@ namespace pydeepstream {
py::return_value_policy::reference,
pydsdoc::methodsDoc::get_ptr);
+ m.def("get_native_ptr",
+ [](size_t ptr) {
+ return (gpointer) ptr;
+ },
+ "ptr"_a,
+ py::return_value_policy::reference,
+ pydsdoc::methodsDoc::get_ptr);
+
m.def("strdup",
[](size_t ptr) -> size_t {
char *str = (char *) ptr;
diff --git a/bindings/src/utils.cpp b/bindings/src/utils.cpp
index 9f97794..009314a 100644
--- a/bindings/src/utils.cpp
+++ b/bindings/src/utils.cpp
@@ -28,6 +28,7 @@
#include "nvdsmeta_schema.h"
#include "utils.hpp"
#include "nvds_obj_encode.h"
+#include "gst-nvdssr.h"
#include "bind_string_property_definitions.h"
#include "../../docstrings/utilsdoc.h"
@@ -103,6 +104,48 @@ namespace pydeepstream {
},
py::return_value_policy::reference,
pydsdoc::utilsdoc::NvDsObjEncUsrArgsDoc::cast);
+
+ py::enum_<NvDsSRContainerType>(m, "NvDsSRContainerType")
+ .value("NVDSSR_CONTAINER_MP4", NVDSSR_CONTAINER_MP4)
+ .value("NVDSSR_CONTAINER_MKV", NVDSSR_CONTAINER_MKV)
+ .export_values();
+
+ py::class_<NvDsSRRecordingInfo>(m, "NvDsSRRecordingInfo")
+ .def(py::init<>())
+ .def_property("filename", STRING_FREE_EXISTING(NvDsSRRecordingInfo, filename))
+ .def_property("dirpath", STRING_FREE_EXISTING(NvDsSRRecordingInfo, dirpath))
+ .def_readwrite("duration", &NvDsSRRecordingInfo::duration)
+ .def_readwrite("containerType", &NvDsSRRecordingInfo::containerType)
+ .def_readwrite("width", &NvDsSRRecordingInfo::width)
+ .def_readwrite("height", &NvDsSRRecordingInfo::height)
+ .def_readwrite("containsVideo", &NvDsSRRecordingInfo::containsVideo)
+ .def_readwrite("channels", &NvDsSRRecordingInfo::channels)
+ .def_readwrite("samplingRate", &NvDsSRRecordingInfo::samplingRate)
+ .def_readwrite("containsAudio", &NvDsSRRecordingInfo::containsAudio)
+ .def("cast", [](size_t data) {
+ return (NvDsSRRecordingInfo *) data;
+ },
+ py::return_value_policy::reference)
+ .def("cast", [](void* data) {
+ return (NvDsSRRecordingInfo *) data;
+ },
+ py::return_value_policy::reference);
+ struct SRUserContext {
+ int sessionid;
+ char name[32];
+ };
+ py::class_<SRUserContext>(m, "SRUserContext")
+ .def(py::init<>())
+ .def_readwrite("sessionid", &SRUserContext::sessionid)
+ .def_property("name", STRING_CHAR_ARRAY(SRUserContext, name))
+ .def("cast", [](size_t data) {
+ return (SRUserContext *) data;
+ },
+ py::return_value_policy::reference)
+ .def("cast", [](void* data) {
+ return (SRUserContext *) data;
+ },
+ py::return_value_policy::reference);
}
}
31.2 Build and reinstall the pyds-xxxx-py3-none-linux_xxxx.whl
# Please make sure you have compiled and installed pyds correctly.
# If the directory does not exist, please refer to sources/deepstream_python_apps/apps/README
cd /opt/nvidia/deepstream/deepstream/sources/deepstream_python_apps/bindings/build
# rebuild the pyds whl
make
# uninstall old version
pip uninstall pyds
# Reinstall the modified version
pip install ./pyds-xxxx-py3-none-linux_xxxx.whl
31.3 Sample code based on deeptream-test1.py
The nvurisrcbin plugin wraps the smart recording feature.
diff --git a/apps/deepstream-test1/deepstream_test_1.py b/apps/deepstream-test1/deepstream_test_1.py
index 1367fb4..ff913fe 100755
--- a/apps/deepstream-test1/deepstream_test_1.py
+++ b/apps/deepstream-test1/deepstream_test_1.py
@@ -25,6 +25,7 @@ gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst
from common.platform_info import PlatformInfo
from common.bus_call import bus_call
+import ctypes
import pyds
@@ -110,15 +111,62 @@ def osd_sink_pad_buffer_probe(pad,info,u_data):
# set(red, green, blue, alpha); set to Black
py_nvosd_text_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0)
# Using pyds.get_string() to get display_text as string
- print(pyds.get_string(py_nvosd_text_params.display_text))
+ # print(pyds.get_string(py_nvosd_text_params.display_text))
pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
try:
l_frame=l_frame.next
except StopIteration:
break
-
+
return Gst.PadProbeReturn.OK
+nvurisrcbin = None
+
+def stop_record_callback():
+ global nvurisrcbin
+ nvurisrcbin.emit('stop-sr', 0)
+
+# keep same as native for get size of native struct
+class SRUserContext(ctypes.Structure):
+ _fields_ = [
+ ("sessionid", ctypes.c_int),
+ ("name", ctypes.c_char * 32)
+ ]
+
+# GstElement * src, NvDsSRRecordingInfo* recordingInfo, void* data, void* user_data
+def record_done(nvurisrcbin, recordingInfo, user_ctx, user_data):
+ print('******sr done*****')
+ print(f"record_done nvurisrcbin {nvurisrcbin} recordingInfo {recordingInfo}"
+ f" user_ctx {user_ctx} user_data {user_data}")
+ info = pyds.NvDsSRRecordingInfo.cast(hash(recordingInfo))
+ sr = pyds.SRUserContext.cast(hash(user_ctx))
+ print(f"filename {info.filename} -- dir {info.dirpath} {info.width} x {info.height}")
+ print(f"session id {sr.sessionid} -- name {sr.name}")
+
+def cb_newpad(decodebin, decoder_src_pad, data):
+ print("In cb_newpad\n")
+ caps=decoder_src_pad.get_current_caps()
+ if not caps:
+ caps = decoder_src_pad.query_caps()
+ gststruct=caps.get_structure(0)
+ gstname=gststruct.get_name()
+ source_bin=data
+ features=caps.get_features(0)
+
+ # Need to check if the pad created by the decodebin is for video and not
+ # audio.
+ print("gstname=",gstname)
+ if(gstname.find("video")!=-1):
+ # Link the decodebin pad only if decodebin has picked nvidia
+ # decoder plugin nvdec_*. We do this by checking if the pad caps contain
+ # NVMM memory features.
+ print("features=",features)
+ if features.contains("memory:NVMM"):
+ # Get the source bin ghost pad
+ sink_pad=source_bin.request_pad_simple("sink_0")
+ ret = decoder_src_pad.link(sink_pad)
+ else:
+ sys.stderr.write(" Error: Decodebin did not pick nvidia decoder plugin.\n")
def main(args):
# Check input arguments
@@ -126,6 +174,7 @@ def main(args):
sys.stderr.write("usage: %s <media file or uri>\n" % args[0])
sys.exit(1)
+ uri = args[1]
platform_info = PlatformInfo()
# Standard GStreamer initialization
Gst.init(None)
@@ -138,30 +187,25 @@ def main(args):
if not pipeline:
sys.stderr.write(" Unable to create Pipeline \n")
- # Source element for reading from the file
- print("Creating Source \n ")
- source = Gst.ElementFactory.make("filesrc", "file-source")
- if not source:
- sys.stderr.write(" Unable to create Source \n")
-
- # Since the data format in the input file is elementary h264 stream,
- # we need a h264parser
- print("Creating H264Parser \n")
- h264parser = Gst.ElementFactory.make("h264parse", "h264-parser")
- if not h264parser:
- sys.stderr.write(" Unable to create h264 parser \n")
-
- # Use nvdec_h264 for hardware accelerated decode on GPU
- print("Creating Decoder \n")
- decoder = Gst.ElementFactory.make("nvv4l2decoder", "nvv4l2-decoder")
- if not decoder:
- sys.stderr.write(" Unable to create Nvv4l2 Decoder \n")
+ global nvurisrcbin
+ nvurisrcbin = Gst.ElementFactory.make("nvurisrcbin", "uri-decode-bin")
+ if not nvurisrcbin:
+ sys.stderr.write(" Unable to create nvurisrcbin \n")
+
+ nvurisrcbin.set_property("smart-record", 2) # enable smart record.
+ nvurisrcbin.set_property("smart-rec-dir-path", ".") # set record path.
# Create nvstreammux instance to form batches from one or more sources.
streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer")
if not streammux:
sys.stderr.write(" Unable to create NvStreamMux \n")
+ nvurisrcbin.set_property("uri", uri)
+ # Connect to the "pad-added" signal of the decodebin which generates a
+ # callback once a new pad for raw data has beed created by the decodebin
+ nvurisrcbin.connect("pad-added", cb_newpad, streammux)
+ nvurisrcbin.connect("sr-done", record_done, pipeline)
+
# Use nvinfer to run inferencing on decoder's output,
# behaviour of inferencing is set through config file
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference")
@@ -196,7 +240,6 @@ def main(args):
sys.stderr.write(" Unable to create egl sink \n")
print("Playing file %s " %args[1])
- source.set_property('location', args[1])
if os.environ.get('USE_NEW_NVSTREAMMUX') != 'yes': # Only set these properties if not using new gst-nvstreammux
streammux.set_property('width', 1920)
streammux.set_property('height', 1080)
@@ -206,9 +249,7 @@ def main(args):
pgie.set_property('config-file-path', "dstest1_pgie_config.txt")
print("Adding elements to Pipeline \n")
- pipeline.add(source)
- pipeline.add(h264parser)
- pipeline.add(decoder)
+ pipeline.add(nvurisrcbin)
pipeline.add(streammux)
pipeline.add(pgie)
pipeline.add(nvvidconv)
@@ -219,16 +260,7 @@ def main(args):
# file-source -> h264-parser -> nvh264-decoder ->
# nvinfer -> nvvidconv -> nvosd -> video-renderer
print("Linking elements in the Pipeline \n")
- source.link(h264parser)
- h264parser.link(decoder)
-
- sinkpad = streammux.request_pad_simple("sink_0")
- if not sinkpad:
- sys.stderr.write(" Unable to get the sink pad of streammux \n")
- srcpad = decoder.get_static_pad("src")
- if not srcpad:
- sys.stderr.write(" Unable to get source pad of decoder \n")
- srcpad.link(sinkpad)
+
streammux.link(pgie)
pgie.link(nvvidconv)
nvvidconv.link(nvosd)
@@ -252,12 +284,37 @@ def main(args):
# start play back and listen to events
print("Starting pipeline \n")
pipeline.set_state(Gst.State.PLAYING)
+
+ sessionid = pyds.get_native_ptr(pyds.alloc_buffer(4))
+ print(f"sessionid {sessionid}")
+
+ sr_user_context_size = ctypes.sizeof(SRUserContext)
+ sr_user_context_buf = pyds.get_native_ptr(pyds.alloc_buffer(sr_user_context_size))
+ sr_user_context = pyds.SRUserContext.cast(sr_user_context_buf)
+ sr_user_context.sessionid = 42
+ sr_user_context.name = "sr-demo"
+ print(f"sr_user_context_buf {sr_user_context_buf}")
+
+ # signal_info = GObject.signal_query('start-sr', nvurisrcbin)
+ # print(f"signal_info {signal_info.param_types}")
+
+ # stop smart record manually, optional
+ # GLib.timeout_add(5000, stop_record_callback)
+ def start_sr_function():
+ nvurisrcbin.emit('start-sr', sessionid, 2, 5, sr_user_context_buf)
+ print('******start sr*****')
+ return True
+ timer_id = GLib.timeout_add_seconds(10, start_sr_function)
+
try:
loop.run()
except:
pass
# cleanup
pipeline.set_state(Gst.State.NULL)
+ # release native memory
+ pyds.free_gbuffer(sessionid)
+ pyds.free_gbuffer(sr_user_context_buf)
if __name__ == '__main__':
sys.exit(main(sys.argv))
31.4 Below is an explanation of the behavior of the sample code
-
Set parameters for smart record
After creating the nvurisrcbin
element, you need to set smart record parameters before starting pipeline. The
explanation of the parameters can be found in nvurisrcbin.html.
# For more parameters, please run gst-inspect-1.0 nvurisrcbin to check.
nvurisrcbin.set_property("smart-record", 2) # enable smart record.
nvurisrcbin.set_property("smart-rec-dir-path", ".") # set record path.
-
Send a start record signal
# The sessionid must be of gpointer type, so it needs to be created using native code(pyds).
# This value is currently a reserved value and the default is 0.
sessionid = pyds.get_native_ptr(pyds.alloc_buffer(4))
print(f"sessionid {sessionid}")
sr_user_context_size = ctypes.sizeof(SRUserContext)
sr_user_context_buf = pyds.get_native_ptr(pyds.alloc_buffer(sr_user_context_size))
sr_user_context = pyds.SRUserContext.cast(sr_user_context_buf)
sr_user_context.sessionid = 42
sr_user_context.name = "sr-demo"
# The user context parameter is optional.
# If not needed, set it to None. Similarly, the type of this parameter is gpointer type and needs to be created
# using pyds native code. Please refer to the patch above.
nvurisrcbin.emit('start-sr', sessionid, 2, 7, sr_user_context_buf)
-
Send a stop record signal(Optional)
# Currently session id can only be set to 0
nvurisrcbin.emit('stop-sr', 0)
-
Wait the sr-done action (Optional)
Here is the documentation of NvDsSRRecordingInfo
nvurisrcbin.connect("sr-done", record_done, pipeline)
related topic:
[Smart recording on Azure IOTEDGE]
[Smart record in Python]
[Smart Record in python]
[Using smart-record in python]