├── .gitignore
├── Makefile
├── README.md
├── common
└── includes
│ ├── deepstream_common.h
│ ├── deepstream_config.h
│ ├── gst-nvdssr.h
│ └── nvdsgstutils.h
├── config
└── pgie_config.txt
├── deepstream-rtspsrc-yolo-app
├── src
├── deepstream_rtspsrc_yolo.cpp
└── main.h
├── system_drawing.png
└── yolo
├── README
└── nvdsinfer_custom_impl_Yolo
├── Makefile
├── kernels.cu
├── nvdsinfer_yolo_engine.cpp
├── nvdsparsebbox_Yolo.cpp
├── trt_utils.cpp
├── trt_utils.h
├── yolo.cpp
├── yolo.h
├── yoloPlugins.cpp
└── yoloPlugins.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | *.onnx
4 | *.engine
5 | *.mp4
6 | *.sh
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a
5 | # copy of this software and associated documentation files (the "Software"),
6 | # to deal in the Software without restriction, including without limitation
7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | # and/or sell copies of the Software, and to permit persons to whom the
9 | # Software is furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | # DEALINGS IN THE SOFTWARE.
21 | ################################################################################
22 |
23 | CUDA_VER?=
24 | ifeq ($(CUDA_VER),)
25 | $(error "CUDA_VER is not set")
26 | endif
27 |
28 | APP:= deepstream-rtspsrc-yolo-app
29 | CC = g++
30 | TARGET_DEVICE = $(shell gcc -dumpmachine | cut -f1 -d -)
31 |
32 | NVDS_VERSION:=5.0
33 |
34 | LIB_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/lib/
35 | APP_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/bin/
36 |
37 | SRCS:= $(wildcard src/*.cpp)
38 | INCS:= $(wildcard *.h)
39 | INCS:= $(wildcard common/includes/*.h)
40 |
41 | ifeq ($(TARGET_DEVICE),aarch64)
42 | PKGS:= gstreamer-1.0 opencv4 gstreamer-video-1.0 x11 gstreamer-rtsp-1.0
43 | else
44 | PKGS:= gstreamer-1.0 opencv gstreamer-video-1.0 x11 gstreamer-rtsp-1.0
45 | endif
46 |
47 | CFLAGS:= -fPIC -std=c++11 \
48 | -I /opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/sources/includes \
49 | -I /usr/local/cuda-$(CUDA_VER)/include
50 |
51 | CFLAGS+= `pkg-config --cflags $(PKGS)`
52 | LIBS:= `pkg-config --libs $(PKGS)`
53 |
54 | LIBS+= -L$(LIB_INSTALL_DIR) -lnvdsgst_meta -lnvds_meta -lnvds_inferutils -lnvds_utils -lnvdsgst_helper -lm \
55 | -lgstrtspserver-1.0 -ldl -L/usr/local/cuda-$(CUDA_VER)/lib64/ -lcudart \
56 | -Wl,-rpath,$(LIB_INSTALL_DIR)
57 |
58 | ifeq ($(TARGET_DEVICE),aarch64)
59 | CFLAGS+= -DPLATFORM_TEGRA
60 | SRCS+=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/sources/libs/nvdsinfer_customparser/nvdsinfer_custombboxparser.cpp
61 | else
62 | LIBS+= -L$(LIB_INSTALL_DIR) -lnvds_infercustomparser
63 | endif
64 |
65 | OBJS:= $(SRCS:.cpp=.o)
66 |
67 | all: $(APP)
68 |
69 | %.o: %.cpp $(INCS) Makefile
70 | $(CC) -c -o $@ $(CFLAGS) $<
71 |
72 | $(APP): $(OBJS) Makefile
73 | $(CC) -o $(APP) $(OBJS) $(LIBS)
74 |
75 | install: $(APP)
76 | cp -rv $(APP) $(APP_INSTALL_DIR)
77 |
78 | clean:
79 | rm -rf $(OBJS) $(APP)
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deepstream rtspsrc YOLO
2 |
3 |
4 |
5 | This app detect objects from RTSP source and create RTSP output.
6 |
7 | This app is made from Deepstream sample-apps.
8 |
9 | YOLOv4 pre-trained model is trained using [https://github.com/AlexeyAB/darknet](https://github.com/AlexeyAB/darknet).
10 |
11 | ONNX model is created using [https://github.com/Tianxiaomo/pytorch-YOLOv4](https://github.com/Tianxiaomo/pytorch-YOLOv4).
12 |
13 | Pre-trained model detects 10 classes of shells.
14 |
15 | When RTSP source is disconnected, the APP wait for restarting RTSP source and try reset pipeline.
16 |
17 | ## Demo
18 |
19 | [](http://www.youtube.com/watch?v=t9wc6LU26wg "demo")
20 |
21 | ## Environment
22 |
23 | ### Jetson
24 |
25 | - higher model than Jetson Nano Developer Kit(2GB)
26 | - Jetpack 4.4.1
27 | - CUDA 10.2
28 | - TensorRT 7.1.3.0
29 | - 64GB microSD card
30 |
31 | ## Set up Jetson
32 |
33 | Doc for Jetson Nano: [https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit](https://developer.nvidia.com/embedded/learn/get-started-jetson-nano-devkit)
34 |
35 | ## YOLO
36 |
37 | ### Build custom YOLOv4
38 |
39 | ```
40 | cd yolo/nvdsinfer_custom_impl_Yolo
41 | make
42 | ```
43 |
44 | You will find `yolo/nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so` and use it as pgie's `custom-lib-path`.
45 |
46 | ### Download pre-trained ONNX model
47 |
48 | This YOLOv4 model detects 10 classes of shell.
49 |
50 | Google Drive: [https://drive.google.com/file/d/1MDd2u1Rn9xemOjd4fjJhm8SIHGqoieF8/view?usp=sharing](https://drive.google.com/file/d/1MDd2u1Rn9xemOjd4fjJhm8SIHGqoieF8/view?usp=sharing)
51 |
52 | ### Convert ONNX model to TensorRT model
53 |
54 | Set path to `trtexec`.
55 |
56 | ```
57 | export PATH=/usr/local/cuda-10.2/bin:/usr/src/tensorrt/bin:$PATH
58 | ```
59 |
60 | Convert `` to ``.
61 |
62 | ```
63 | cd yolo
64 | trtexec \
65 | --onnx= \
66 | --explicitBatch \
67 | --saveEngine= \
68 | --minShapes=input:1x3x416x416 \
69 | --optShapes=input:1x3x416x416 \
70 | --maxShapes=input:1x3x416x416 \
71 | --shapes=input:1x3x416x416 \
72 | --fp16
73 | ```
74 |
75 | ## Run prediction
76 |
77 | Compile app.
78 |
79 | ```
80 | export CUDA_VER=10.2
81 | make
82 | ```
83 |
84 | Run app with ``.
85 |
86 | ```
87 | ./deepstream-rtspsrc-yolo-app \
88 |
89 | ```
90 |
91 | Rum app with multi streams.
92 |
93 | ```
94 | ./deepstream-rtspsrc-yolo-app \
95 | \
96 |
97 | ```
98 |
99 | ### Watch predicted streams.
100 |
101 | `rtsp://:8554/dt-test`
102 |
103 | ### Others
104 |
105 | #### PGIE's interval
106 |
107 | Update interval in `config/pgie_config.txt` to decrease Jetson's work load.
108 |
109 | This config means skip frames of RTSP source every `interval` number.
110 |
111 | ```
112 | interval=5
113 | ```
114 |
115 | #### Custom YOLO model
116 |
117 | If you want to use custom YOLOv4 or YOLOv4-tiny model, you need the following tasks.
118 |
119 | - train model and get weight file.
120 | - convert weight file to onnx file.
121 | - convert onnx file to tensorRT file.
122 |
123 | #### Custom classes number
124 |
125 | If you want to change pgie classes number form 10, you neet to change
126 |
127 | - `NUM_CLASSES_YOLO` in `yolo/nvdsinfer_custom_impl_Yolo/nvdsparsebbox_Yolo.cpp`
128 | - `PGIE_DETECTED_CLASS_NUM` and `pgie_classes_str` in `src/deepstream_rtspsrc_yolo.cpp`
129 | - `num-detected-classes` in `config/pgie_config.txt`
130 |
131 | *****************************************************************************
132 | * Copyright (c) 2019-2020 NVIDIA Corporation. All rights reserved.
133 | *
134 | * NVIDIA Corporation and its licensors retain all intellectual property
135 | * and proprietary rights in and to this software, related documentation
136 | * and any modifications thereto. Any use, reproduction, disclosure or
137 | * distribution of this software and related documentation without an express
138 | * license agreement from NVIDIA Corporation is strictly prohibited.
139 | *****************************************************************************
140 |
--------------------------------------------------------------------------------
/common/includes/deepstream_common.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #ifndef __NVGSTDS_COMMON_H__
24 | #define __NVGSTDS_COMMON_H__
25 |
26 | #ifdef __cplusplus
27 | extern "C"
28 | {
29 | #endif
30 |
31 | #include
32 |
33 | #include "deepstream_config.h"
34 |
35 | #define NVGSTDS_ERR_MSG_V(msg, ...) \
36 | g_print("** ERROR: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
37 |
38 | #define NVGSTDS_INFO_MSG_V(msg, ...) \
39 | g_print("** INFO: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
40 |
41 | #define NVGSTDS_WARN_MSG_V(msg, ...) \
42 | g_print("** WARN: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
43 |
44 | #define NVGSTDS_LINK_ELEMENT(elem1, elem2) \
45 | do { \
46 | if (!gst_element_link (elem1,elem2)) { \
47 | GstCaps *src_caps, *sink_caps; \
48 | src_caps = gst_pad_query_caps ((GstPad *) (elem1)->srcpads->data, NULL); \
49 | sink_caps = gst_pad_query_caps ((GstPad *) (elem2)->sinkpads->data, NULL); \
50 | NVGSTDS_ERR_MSG_V ("Failed to link '%s' (%s) and '%s' (%s)", \
51 | GST_ELEMENT_NAME (elem1), \
52 | gst_caps_to_string (src_caps), \
53 | GST_ELEMENT_NAME (elem2), \
54 | gst_caps_to_string (sink_caps)); \
55 | goto done; \
56 | } \
57 | } while (0)
58 |
59 | #define NVGSTDS_LINK_ELEMENT_FULL(elem1, elem1_pad_name, elem2, elem2_pad_name) \
60 | do { \
61 | GstPad *elem1_pad = gst_element_get_static_pad(elem1, elem1_pad_name); \
62 | GstPad *elem2_pad = gst_element_get_static_pad(elem2, elem2_pad_name); \
63 | GstPadLinkReturn ret = gst_pad_link (elem1_pad,elem2_pad); \
64 | if (ret != GST_PAD_LINK_OK) { \
65 | gchar *n1 = gst_pad_get_name (elem1_pad); \
66 | gchar *n2 = gst_pad_get_name (elem2_pad); \
67 | NVGSTDS_ERR_MSG_V ("Failed to link '%s' and '%s': %d", \
68 | n1, n2, ret); \
69 | g_free (n1); \
70 | g_free (n2); \
71 | gst_object_unref (elem1_pad); \
72 | gst_object_unref (elem2_pad); \
73 | goto done; \
74 | } \
75 | gst_object_unref (elem1_pad); \
76 | gst_object_unref (elem2_pad); \
77 | } while (0)
78 |
79 | #define NVGSTDS_BIN_ADD_GHOST_PAD_NAMED(bin, elem, pad, ghost_pad_name) \
80 | do { \
81 | GstPad *gstpad = gst_element_get_static_pad (elem, pad); \
82 | if (!gstpad) { \
83 | NVGSTDS_ERR_MSG_V ("Could not find '%s' in '%s'", pad, \
84 | GST_ELEMENT_NAME(elem)); \
85 | goto done; \
86 | } \
87 | gst_element_add_pad (bin, gst_ghost_pad_new (ghost_pad_name, gstpad)); \
88 | gst_object_unref (gstpad); \
89 | } while (0)
90 |
91 | #define NVGSTDS_BIN_ADD_GHOST_PAD(bin, elem, pad) \
92 | NVGSTDS_BIN_ADD_GHOST_PAD_NAMED (bin, elem, pad, pad)
93 |
94 | #define NVGSTDS_ELEM_ADD_PROBE(probe_id, elem, pad, probe_func, probe_type, probe_data) \
95 | do { \
96 | GstPad *gstpad = gst_element_get_static_pad (elem, pad); \
97 | if (!gstpad) { \
98 | NVGSTDS_ERR_MSG_V ("Could not find '%s' in '%s'", pad, \
99 | GST_ELEMENT_NAME(elem)); \
100 | goto done; \
101 | } \
102 | probe_id = gst_pad_add_probe(gstpad, (probe_type), probe_func, probe_data, NULL); \
103 | gst_object_unref (gstpad); \
104 | } while (0)
105 |
106 | #define NVGSTDS_ELEM_REMOVE_PROBE(probe_id, elem, pad) \
107 | do { \
108 | if (probe_id == 0 || !elem) { \
109 | break; \
110 | } \
111 | GstPad *gstpad = gst_element_get_static_pad (elem, pad); \
112 | if (!gstpad) { \
113 | NVGSTDS_ERR_MSG_V ("Could not find '%s' in '%s'", pad, \
114 | GST_ELEMENT_NAME(elem)); \
115 | break; \
116 | } \
117 | gst_pad_remove_probe(gstpad, probe_id); \
118 | gst_object_unref (gstpad); \
119 | } while (0)
120 |
121 | #define GET_FILE_PATH(path) ((path) + (((path) && strstr ((path), "file://")) ? 7 : 0))
122 |
123 |
124 | /**
125 | * Function to link sink pad of an element to source pad of tee.
126 | *
127 | * @param[in] tee Tee element.
128 | * @param[in] sinkelem downstream element.
129 | *
130 | * @return true if link successful.
131 | */
132 | gboolean
133 | link_element_to_tee_src_pad (GstElement * tee, GstElement * sinkelem);
134 |
135 | /**
136 | * Function to link source pad of an element to sink pad of muxer element.
137 | *
138 | * @param[in] streammux muxer element.
139 | * @param[in] elem upstream element.
140 | * @param[in] index pad index of muxer element.
141 | *
142 | * @return true if link successful.
143 | */
144 | gboolean
145 | link_element_to_streammux_sink_pad (GstElement *streammux, GstElement *elem,
146 | gint index);
147 |
148 | /**
149 | * Function to unlink source pad of an element from sink pad of muxer element.
150 | *
151 | * @param[in] streammux muxer element.
152 | * @param[in] elem upstream element.
153 | *
154 | * @return true if unlinking was successful.
155 | */
156 | gboolean
157 | unlink_element_from_streammux_sink_pad (GstElement *streammux, GstElement *elem);
158 |
159 | /**
160 | * Function to link sink pad of an element to source pad of demux element.
161 | *
162 | * @param[in] demux demuxer element.
163 | * @param[in] elem downstream element.
164 | * @param[in] index pad index of demuxer element.
165 | *
166 | * @return true if link successful.
167 | */
168 | gboolean
169 | link_element_to_demux_src_pad (GstElement *demux, GstElement *elem,
170 | guint index);
171 |
172 | /*
173 | * Function to replace string with another string.
174 | * Make sure @ref src is big enough to accommodate replacements.
175 | *
176 | * @param[in] str string to search in.
177 | * @param[in] replace string to replace.
178 | * @param[in] replace_with string to replace @ref replace with.
179 | */
180 | void
181 | str_replace (gchar * str, const gchar * replace, const gchar * replace_with);
182 |
183 | #ifdef __cplusplus
184 | }
185 | #endif
186 |
187 | #endif
188 |
--------------------------------------------------------------------------------
/common/includes/deepstream_config.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #ifndef __NVGSTDS_CONFIG_H__
24 | #define __NVGSTDS_CONFIG_H__
25 |
26 | #ifdef __aarch64__
27 | #define IS_TEGRA
28 | #endif
29 |
30 | #define MEMORY_FEATURES "memory:NVMM"
31 |
32 | #ifdef IS_TEGRA
33 | #define NVDS_ELEM_SRC_CAMERA_CSI "nvarguscamerasrc"
34 | #else
35 | #define NVDS_ELEM_SRC_CAMERA_CSI "videotestsrc"
36 | #endif
37 | #define NVDS_ELEM_SRC_CAMERA_V4L2 "v4l2src"
38 | #define NVDS_ELEM_SRC_URI "uridecodebin"
39 | #define NVDS_ELEM_SRC_MULTIFILE "multifilesrc"
40 | #define NVDS_ELEM_SRC_ALSA "alsasrc"
41 |
42 | #define NVDS_ELEM_DECODEBIN "decodebin"
43 | #define NVDS_ELEM_WAVPARSE "wavparse"
44 |
45 | #define NVDS_ELEM_QUEUE "queue"
46 | #define NVDS_ELEM_CAPS_FILTER "capsfilter"
47 | #define NVDS_ELEM_TEE "tee"
48 |
49 | #define NVDS_ELEM_PGIE "nvinfer"
50 | #define NVDS_ELEM_SGIE "nvinfer"
51 | #define NVDS_ELEM_INFER_SERVER "nvinferserver"
52 | #define NVDS_ELEM_TRACKER "nvtracker"
53 |
54 | #define NVDS_ELEM_VIDEO_CONV "nvvideoconvert"
55 | #define NVDS_ELEM_STREAM_MUX "nvstreammux"
56 | #define NVDS_ELEM_STREAM_DEMUX "nvstreamdemux"
57 | #define NVDS_ELEM_TILER "nvmultistreamtiler"
58 | #define NVDS_ELEM_OSD "nvdsosd"
59 | #define NVDS_ELEM_DSANALYTICS_ELEMENT "nvdsanalytics"
60 | #define NVDS_ELEM_DSEXAMPLE_ELEMENT "dsexample"
61 |
62 | #define NVDS_ELEM_DEWARPER "nvdewarper"
63 | #define NVDS_ELEM_SPOTANALYSIS "nvspot"
64 | #define NVDS_ELEM_NVAISLE "nvaisle"
65 | #define NVDS_ELEM_BBOXFILTER "nvbboxfilter"
66 | #define NVDS_ELEM_MSG_CONV "nvmsgconv"
67 | #define NVDS_ELEM_MSG_BROKER "nvmsgbroker"
68 |
69 | #define NVDS_ELEM_SINK_FAKESINK "fakesink"
70 | #define NVDS_ELEM_SINK_FILE "filesink"
71 | #define NVDS_ELEM_SINK_EGL "nveglglessink"
72 |
73 | #define NVDS_ELEM_SINK_OVERLAY "nvoverlaysink"
74 | #define NVDS_ELEM_EGLTRANSFORM "nvegltransform"
75 |
76 | #define NVDS_ELEM_MUX_MP4 "qtmux"
77 | #define NVDS_ELEM_MKV "matroskamux"
78 |
79 | #define NVDS_ELEM_ENC_H264_HW "nvv4l2h264enc"
80 | #define NVDS_ELEM_ENC_H265_HW "nvv4l2h265enc"
81 | #define NVDS_ELEM_ENC_MPEG4 "avenc_mpeg4"
82 |
83 | #define NVDS_ELEM_ENC_H264_SW "x264enc"
84 | #define NVDS_ELEM_ENC_H265_SW "x265enc"
85 |
86 | #define MAX_SOURCE_BINS 1024
87 | #define MAX_SINK_BINS (1024)
88 | #define MAX_SECONDARY_GIE_BINS (16)
89 | #define MAX_MESSAGE_CONSUMERS (16)
90 |
91 | #endif
92 |
--------------------------------------------------------------------------------
/common/includes/gst-nvdssr.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019 NVIDIA Corporation. All rights reserved.
3 | *
4 | * NVIDIA Corporation and its licensors retain all intellectual property
5 | * and proprietary rights in and to this software, related documentation
6 | * and any modifications thereto. Any use, reproduction, disclosure or
7 | * distribution of this software and related documentation without an express
8 | * license agreement from NVIDIA Corporation is strictly prohibited.
9 | *
10 | */
11 | /**
12 | * @file
13 | * NVIDIA DeepStream: Smart recording API
14 | */
15 | /**
16 | * @defgroup custom_gstreamer Custom Gstreamer APIs
17 | *
18 | * This section defines custom Gstreamer APIs
19 | *
20 | */
21 |
22 | #ifndef NVDSSR_H_
23 | #define NVDSSR_H_
24 |
25 | #include
26 |
27 | /**
28 | *
29 | * @defgroup gstreamer_nvdssr Smart Record
30 | *
31 | * Specifies APIs relating to smart recording.
32 | *
33 | * @ingroup custom_gstreamer
34 | * @{
35 | */
36 | #ifdef __cplusplus
37 | extern "C"
38 | {
39 | #endif
40 |
41 | typedef struct NvDsSRRecordingInfo NvDsSRRecordingInfo;
42 |
43 | typedef gpointer (*NvDsSRCallbackFunc) (NvDsSRRecordingInfo *info, gpointer userData);
44 |
45 | typedef guint32 NvDsSRSessionId;
46 |
47 | /**
48 | * Specifies container types.
49 | */
50 | typedef enum {
51 | NVDSSR_CONTAINER_MP4,
52 | NVDSSR_CONTAINER_MKV
53 | } NvDsSRContainerType;
54 |
55 | /**
56 | * Specifies API return status.
57 | */
58 | typedef enum {
59 | NVDSSR_STATUS_OK,
60 | NVDSSR_STATUS_INVALID_VAL,
61 | NVDSSR_STATUS_INVALID_OP,
62 | NVDSSR_STATUS_ERROR,
63 |
64 | NVDSSR_STATUS_CUSTOM1 = 100,
65 | NVDSSR_STATUS_CUSTOM2 = 101,
66 | NVDSSR_STATUS_CUSTOM3 = 102
67 | } NvDsSRStatus;
68 |
69 | /**
70 | * Holds initializtion paramerters required to create \ref NvDsSRContext.
71 | */
72 | typedef struct NvDsSRInitParams
73 | {
74 | /** callback function gets called once recording is complete */
75 | NvDsSRCallbackFunc callback;
76 | /** recording video container, MP4 / MKV */
77 | NvDsSRContainerType containerType;
78 | /** optional, recording video width, 0 means no transcode */
79 | guint width;
80 | /** optional, recording video height, 0 means no transcode */
81 | guint height;
82 | /** recorded file name prefix */
83 | gchar *fileNamePrefix;
84 | /** store recorded file under directory path */
85 | gchar *dirpath;
86 | /** default recording duration in seconds */
87 | guint defaultDuration;
88 | /** size of video cache in seconds. */
89 | guint videoCacheSize;
90 | } NvDsSRInitParams;
91 |
92 | /**
93 | * Holds information about smart record instance.
94 | */
95 | typedef struct NvDsSRContext
96 | {
97 | /** parent bin element. */
98 | GstElement *recordbin;
99 | /** queue element to cache the content. */
100 | GstElement *recordQue;
101 | /** child bin to save the content to file. */
102 | GstElement *encodebin;
103 | /** filesink element */
104 | GstElement *filesink;
105 | /** flag to check the key frame. */
106 | gboolean gotKeyFrame;
107 | /** flag to check if recording is on */
108 | gboolean recordOn;
109 | /** flag to check if encodebin is reset */
110 | gboolean resetDone;
111 | /** flag to check if encodebin is in playing state. */
112 | gboolean isPlaying;
113 | /** initialization parameters */
114 | NvDsSRInitParams initParams;
115 | /** mutex to control the flow */
116 | GMutex flowLock;
117 | /** thread to reset the encodebin */
118 | GThread *resetThread;
119 | /** pointer to user provided data */
120 | gpointer uData;
121 | /** pointer to private data */
122 | gpointer privData;
123 | } NvDsSRContext;
124 |
125 | /**
126 | * Hold information about video recorded.
127 | */
128 | typedef struct NvDsSRRecordingInfo
129 | {
130 | /** SR bin context */
131 | NvDsSRContext *ctx;
132 | /** recording session-id */
133 | NvDsSRSessionId sessionId;
134 | /** recorded file name */
135 | gchar *filename;
136 | /** recorded file dir path */
137 | gchar *dirpath;
138 | /** duration in milliseconds */
139 | guint64 duration;
140 | /** recorded video container, MP4 / MKV */
141 | NvDsSRContainerType containerType;
142 | /** recorded video width*/
143 | guint width;
144 | /** recorded video height*/
145 | guint height;
146 | } NvDsSRRecordingInfo;
147 |
148 | /**
149 | * \brief Creates the instance of smart record.
150 | *
151 | * This function creates the instance of smart record and returns the pointer
152 | * to an allocated \ref NvDsSRContext. The \a params structure must be filled
153 | * with initialization parameters required to create the instance.
154 | *
155 | * recordbin of \ref NvDsSRContext is smart record bin which must be added
156 | * to the pipeline. It expects encoded frames which will be muxed and saved to
157 | * the file. Add this bin after parser element in the pipeline.
158 | *
159 | * Call NvDsSRDestroy() to free resources allocated by this function.
160 | *
161 | * @param[out] ctx An indirect pointer to the smart record instance.
162 | * @param[in] params A pointer to a \ref NvDsSRInitParams structure.
163 | *
164 | * @return NVDSSR_STATUS_OK if successful, or corresponding error otherwise.
165 | */
166 | NvDsSRStatus NvDsSRCreate (NvDsSRContext **ctx, NvDsSRInitParams *params);
167 |
168 | /**
169 | * \brief Starts the video recording.
170 | *
171 | * This function starts writing the cached video data to a file. It returns
172 | * the session id which later can be used in NvDsSRStop() to stop the
173 | * corresponding recording.
174 | *
175 | * Here startTime specifies the seconds before the current time and duration
176 | * specifies the seconds after the start of recording.
177 | * If current time is t1, content from t1 - startTime to t1 + duration will
178 | * be saved to file. Therefore a total of startTime + duration seconds of data
179 | * will be recorded.
180 | *
181 | * @param[in] ctx A pointer to a \ref NvDsSRContext.
182 | * @param[out] sessionId A pointer to a \ref NvDsSRSessionId.
183 | * @param[in] startTime Seconds before the current time. Should be less than video cache size.
184 | * @param[in] duration Duration value in seconds after the start of recording.
185 | * @param[in] userData A pointer to user specified data.
186 | *
187 | * @return NVDSSR_STATUS_OK if successful, or corresponding error otherwise.
188 | */
189 | NvDsSRStatus NvDsSRStart (NvDsSRContext *ctx, NvDsSRSessionId *sessionId,
190 | guint startTime, guint duration, gpointer userData);
191 |
192 | /**
193 | * \brief Stops the previously started recording.
194 | *
195 | * @param[in] ctx A pointer to a \ref NvDsSRContext.
196 | * @param[in] sessionId Id of seesion to stop.
197 | *
198 | * @return NVDSSR_STATUS_OK if successful, or corresponding error otherwise.
199 | */
200 | NvDsSRStatus NvDsSRStop (NvDsSRContext *ctx, NvDsSRSessionId sessionId);
201 |
202 | /**
203 | * \brief Destroys the instance of smart record.
204 | *
205 | * This function releases the resources previously allocated by NvDsSRCreate().
206 | *
207 | * @param[in] ctx A pointer to a \ref NvDsSRContext to be freed.
208 | *
209 | * @return NVDSSR_STATUS_OK if successful, or corresponding error otherwise.
210 | */
211 | NvDsSRStatus NvDsSRDestroy (NvDsSRContext *ctx);
212 |
213 | #ifdef __cplusplus
214 | }
215 | #endif
216 | #endif /* NVDSSR_H_ */
217 |
218 | /** @} */
219 |
--------------------------------------------------------------------------------
/common/includes/nvdsgstutils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * NVIDIA Corporation and its licensors retain all intellectual property
5 | * and proprietary rights in and to this software, related documentation
6 | * and any modifications thereto. Any use, reproduction, disclosure or
7 | * distribution of this software and related documentation without an express
8 | * license agreement from NVIDIA Corporation is strictly prohibited.
9 | */
10 |
11 | /**
12 | * @file
13 | * Defines NVIDIA DeepStream GStreamer Utilities
14 | *
15 | * @b Description: This file specifies the NVIDIA DeepStream GStreamer utility
16 | * functions.
17 | *
18 | */
19 | /**
20 | * @defgroup gstreamer_utils Utilities: Gstreamer utilities API
21 | *
22 | * Specifies GStreamer utilities functions, used to configure the source to generate NTP Sync values.
23 | *
24 | * @ingroup NvDsUtilsApi
25 | * @{
26 | */
27 | #ifndef __NVDS_GSTUTILS_H__
28 | #define __NVDS_GSTUTILS_H__
29 |
30 | #include
31 |
32 | #ifdef __cplusplus
33 | extern "C" {
34 | #endif
35 | #include
36 |
37 | /**
38 | * Configure the source to generate NTP sync values for RTSP sources.
39 | *
40 | * These values are used by the DeepStream GStreamer element NvStreamMux to
41 | * calculate the NTP time of the frames at the source.
42 | *
43 | * This functionality is dependent on the RTSP sending the RTCP Sender Reports.
44 | * source.
45 | *
46 | * This function only works for RTSP sources i.e. GStreamer elements "rtspsrc"
47 | * or "uridecodebin" with an RTSP uri.
48 | *
49 | * params[in] src_elem GStreamer source element to be configured.
50 | */
51 | void configure_source_for_ntp_sync (GstElement *src_elem);
52 |
53 | #ifdef __cplusplus
54 | }
55 | #endif
56 |
57 | #endif
58 |
59 | /** @} */
--------------------------------------------------------------------------------
/config/pgie_config.txt:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a
5 | # copy of this software and associated documentation files (the "Software"),
6 | # to deal in the Software without restriction, including without limitation
7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | # and/or sell copies of the Software, and to permit persons to whom the
9 | # Software is furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | # DEALINGS IN THE SOFTWARE.
21 | ################################################################################
22 |
23 | # Following properties are mandatory when engine files are not specified:
24 | # int8-calib-file(Only in INT8)
25 | # Caffemodel mandatory properties: model-file, proto-file, output-blob-names
26 | # UFF: uff-file, input-dims, uff-input-blob-name, output-blob-names
27 | # ONNX: onnx-file
28 | #
29 | # Mandatory properties for detectors:
30 | # num-detected-classes
31 | #
32 | # Optional properties for detectors:
33 | # cluster-mode(Default=Group Rectangles), interval(Primary mode only, Default=0)
34 | # custom-lib-path,
35 | # parse-bbox-func-name
36 | #
37 | # Mandatory properties for classifiers:
38 | # classifier-threshold, network-type (Default=0 i.e. Detector)
39 | #
40 | # Optional properties for classifiers:
41 | # classifier-async-mode(Secondary mode only, Default=false)
42 | #
43 | # Optional properties in secondary mode:
44 | # operate-on-gie-id(Default=0), operate-on-class-ids(Defaults to all classes),
45 | # input-object-min-width, input-object-min-height, input-object-max-width,
46 | # input-object-max-height
47 | #
48 | # Following properties are always recommended:
49 | # batch-size(Default=1)
50 | #
51 | # Other optional properties:
52 | # net-scale-factor(Default=1), network-mode(Default=0 i.e FP32),
53 | # model-color-format(Default=0 i.e. RGB) model-engine-file, labelfile-path,
54 | # mean-file, gie-unique-id(Default=0), offsets, process-mode (Default=1 i.e. primary),
55 | # custom-lib-path, network-mode(Default=0 i.e FP32)
56 | #
57 | # The values in the config file are overridden by values set through GObject
58 | # properties.
59 |
60 | [property]
61 | gpu-id=0
62 | net-scale-factor=0.0039215697906911373
63 | #0=RGB, 1=BGR, 2=GRAY
64 | model-color-format=0
65 | model-engine-file=../yolo/yolov4_1_3_416_416_asari.engine
66 | ## 0=FP32, 1=INT8, 2=FP16 mode
67 | network-mode=2
68 | num-detected-classes=10
69 | gie-unique-id=1
70 | network-type=0
71 | is-classifier=0
72 | ## 0=Group Rectangles, 1=DBSCAN, 2=NMS, 3= DBSCAN+NMS Hybrid, 4 = None(No clustering)
73 | cluster-mode=2
74 | maintain-aspect-ratio=0
75 | parse-bbox-func-name=NvDsInferParseCustomYoloV4
76 | custom-lib-path=../yolo/nvdsinfer_custom_impl_Yolo/libnvdsinfer_custom_impl_Yolo.so
77 | engine-create-func-name=NvDsInferYoloCudaEngineGet
78 | #scaling-filter=0
79 | #scaling-compute-hw=0
80 | batch-size=1
81 | interval=1
82 |
83 | [class-attrs-all]
84 | pre-cluster-threshold=0.2
85 | eps=0.2
86 | group-threshold=1
87 |
--------------------------------------------------------------------------------
/deepstream-rtspsrc-yolo-app:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/belarbi2733/deepstream-rtspsrc-yolo/d90fc9c325f01478cb7ffc26879bd89f5c7b6735/deepstream-rtspsrc-yolo-app
--------------------------------------------------------------------------------
/src/deepstream_rtspsrc_yolo.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "cuda_runtime_api.h"
11 | #include "gstnvdsmeta.h"
12 | #include "gstnvdsinfer.h"
13 | #include "nvdsinfer_custom_impl.h"
14 | #include "nvds_version.h"
15 | #include "main.h"
16 |
17 | #define NVINFER_PLUGIN "nvinfer"
18 | #define INFER_PGIE_CONFIG_FILE "config/pgie_config.txt"
19 |
20 | #define MUXER_OUTPUT_WIDTH 1920
21 | #define MUXER_OUTPUT_HEIGHT 1080
22 | #define MUXER_BATCH_TIMEOUT_USEC 40000
23 | #define MEMORY_FEATURES "memory:NVMM"
24 | #define TILED_OUTPUT_WIDTH 1920
25 | #define TILED_OUTPUT_HEIGHT 1080
26 | #define PGIE_CLASS_ID_0 0
27 | #define PGIE_CLASS_ID_1 1
28 | #define PGIE_CLASS_ID_2 2
29 | #define PGIE_CLASS_ID_3 3
30 | #define PGIE_CLASS_ID_4 4
31 | #define PGIE_CLASS_ID_5 5
32 | #define PGIE_CLASS_ID_6 6
33 | #define PGIE_CLASS_ID_7 7
34 | #define PGIE_CLASS_ID_8 8
35 | #define PGIE_CLASS_ID_9 9
36 | #define PGIE_DETECTED_CLASS_NUM 10
37 | #define MAX_SINK_BINS (1024)
38 | #define MAX_INSTANCES 128
39 | #define MAX_DISPLAY_LEN 64
40 |
41 | AppCtx *appCtx[MAX_INSTANCES];
42 |
43 | GMainLoop *loop = NULL;
44 | static gboolean install_mux_eosmonitor_probe = FALSE;
45 | gint frame_number = 0;
46 | const gchar pgie_classes_str[PGIE_DETECTED_CLASS_NUM][32] =
47 | { "ibokisago",
48 | "karasu",
49 | "hototogisu",
50 | "aramujiro",
51 | "mate",
52 | "tsumeta",
53 | "baka",
54 | "shiofuki",
55 | "kagami",
56 | "asari" };
57 |
58 | unsigned int nvds_lib_major_version = NVDS_VERSION_MAJOR;
59 | unsigned int nvds_lib_minor_version = NVDS_VERSION_MINOR;
60 | static GstRTSPServer *server [MAX_SINK_BINS];
61 | static guint server_count = 0;
62 | static GMutex server_cnt_lock;
63 | guint num_sources = 0;
64 |
65 | static GstPadProbeReturn
66 | osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
67 | {
68 | GstBuffer *buf = (GstBuffer *) info->data;
69 | guint num_rects = 0;
70 | NvDsObjectMeta *obj_meta = NULL;
71 | guint count_0 = 0;
72 | guint count_1 = 0;
73 | guint count_2 = 0;
74 | guint count_3 = 0;
75 | guint count_4 = 0;
76 | guint count_5 = 0;
77 | guint count_6 = 0;
78 | guint count_7 = 0;
79 | guint count_8 = 0;
80 | guint count_9 = 0;
81 | guint truck_count = 0;
82 | NvDsMetaList *l_frame = NULL;
83 | NvDsMetaList *l_obj = NULL;
84 | NvDsMetaList *l_cls = NULL;
85 | NvDsDisplayMeta *display_meta3 = NULL;
86 | NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
87 |
88 | for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
89 | l_frame = l_frame->next) {
90 | NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
91 | int offset = 0;
92 | for (l_obj = frame_meta->obj_meta_list; l_obj != NULL; l_obj = l_obj->next) {
93 | obj_meta = (NvDsObjectMeta *) (l_obj->data);
94 | display_meta3 = nvds_acquire_display_meta_from_pool (batch_meta);
95 | NvOSD_TextParams *txt_params3 = &display_meta3->text_params[0];
96 | for (l_cls = obj_meta->classifier_meta_list; l_cls != NULL; l_cls = l_cls->next) {
97 | NvDsClassifierMeta *cls_meta = (NvDsClassifierMeta *) (l_cls->data);
98 | }
99 | switch (obj_meta->class_id) {
100 | case PGIE_CLASS_ID_0:
101 | count_0++;
102 | num_rects++;
103 | txt_params3->text_bg_clr.red = 0.0;
104 | txt_params3->text_bg_clr.green = 0.0;
105 | txt_params3->text_bg_clr.blue = 0.0;
106 | break;
107 | case PGIE_CLASS_ID_1:
108 | count_1++;
109 | num_rects++;
110 | txt_params3->text_bg_clr.red = 0.5;
111 | txt_params3->text_bg_clr.green = 0.0;
112 | txt_params3->text_bg_clr.blue = 0.0;
113 | break;
114 | case PGIE_CLASS_ID_2:
115 | count_2++;
116 | num_rects++;
117 | txt_params3->text_bg_clr.red = 0.0;
118 | txt_params3->text_bg_clr.green = 0.5;
119 | txt_params3->text_bg_clr.blue = 0.0;
120 | break;
121 | case PGIE_CLASS_ID_3:
122 | count_3++;
123 | num_rects++;
124 | txt_params3->text_bg_clr.red = 1.0;
125 | txt_params3->text_bg_clr.green = 1.0;
126 | txt_params3->text_bg_clr.blue = 1.0;
127 | break;
128 | case PGIE_CLASS_ID_4:
129 | count_4++;
130 | num_rects++;
131 | txt_params3->text_bg_clr.red = 0.0;
132 | txt_params3->text_bg_clr.green = 1.0;
133 | txt_params3->text_bg_clr.blue = 1.0;
134 | break;
135 | case PGIE_CLASS_ID_5:
136 | count_5++;
137 | num_rects++;
138 | txt_params3->text_bg_clr.red = 1.0;
139 | txt_params3->text_bg_clr.green = 0.0;
140 | txt_params3->text_bg_clr.blue = 1.0;
141 | break;
142 | case PGIE_CLASS_ID_6:
143 | count_6++;
144 | num_rects++;
145 | txt_params3->text_bg_clr.red = 1.0;
146 | txt_params3->text_bg_clr.green = 1.0;
147 | txt_params3->text_bg_clr.blue = 0.0;
148 | break;
149 | case PGIE_CLASS_ID_7:
150 | count_7++;
151 | num_rects++;
152 | txt_params3->text_bg_clr.red = 0.0;
153 | txt_params3->text_bg_clr.green = 0.0;
154 | txt_params3->text_bg_clr.blue = 1.0;
155 | break;
156 | case PGIE_CLASS_ID_8:
157 | count_8++;
158 | num_rects++;
159 | txt_params3->text_bg_clr.red = 0.0;
160 | txt_params3->text_bg_clr.green = 1.0;
161 | txt_params3->text_bg_clr.blue = 0.0;
162 | break;
163 | case PGIE_CLASS_ID_9:
164 | count_9++;
165 | num_rects++;
166 | txt_params3->text_bg_clr.red = 1.0;
167 | txt_params3->text_bg_clr.green = 0.0;
168 | txt_params3->text_bg_clr.blue = 0.0;
169 | break;
170 | }
171 |
172 | NvOSD_RectParams rect_params = obj_meta->rect_params;
173 | float left = rect_params.left;
174 | float top = rect_params.top;
175 | float width = rect_params.width;
176 | float height = rect_params.height;
177 | float confidence = obj_meta->confidence;
178 |
179 | std::string result;
180 | char result_0[32];
181 | char result_1[32];
182 | display_meta3->num_labels = 1;
183 | txt_params3->display_text = (gchar *) g_malloc0 (MAX_DISPLAY_LEN);
184 |
185 | snprintf(result_0, 32, "%s ", pgie_classes_str[obj_meta->class_id]);
186 | snprintf(result_1, 32, "%.0f%", confidence * 100);
187 |
188 | result.append(result_0);
189 | result.append(result_1);
190 |
191 | offset = snprintf (
192 | txt_params3->display_text, MAX_DISPLAY_LEN, "%s", result.c_str()
193 | );
194 | /* Now set the offsets where the string should appear */
195 | txt_params3->x_offset = left;
196 | txt_params3->y_offset = top-40;
197 |
198 | /* Font , font-color and font-size */
199 | txt_params3->font_params.font_name = (gchar *) "Serif";
200 | txt_params3->font_params.font_size = 20;
201 | txt_params3->font_params.font_color.red = 0.0;
202 | txt_params3->font_params.font_color.green = 0.0;
203 | txt_params3->font_params.font_color.blue = 0.0;
204 | txt_params3->font_params.font_color.alpha = 1.0;
205 |
206 | /* Text background color */
207 | txt_params3->set_bg_clr = 1;
208 | // txt_params3->text_bg_clr.red = 1.0;
209 | // txt_params3->text_bg_clr.green = 1.0;
210 | // txt_params3->text_bg_clr.blue = 1.0;
211 | txt_params3->text_bg_clr.alpha = 0.5;
212 |
213 | nvds_add_display_meta_to_frame (frame_meta, display_meta3);
214 |
215 | }
216 | if (num_rects > 0) {
217 | g_print ("Frame Number = %d Number of objects = %d "
218 | "ID_0: %d ID_1: %d ID_2: %d ID_3: %d ID_4: %d ID_5: %d ID_6: %d ID_7: %d ID_8: %d ID_9: %d \n",
219 | frame_meta->frame_num, num_rects, count_0, count_1, count_2, count_3, count_4, count_5, count_6, count_7, count_8, count_9);
220 | }
221 | }
222 |
223 | frame_number++;
224 | return GST_PAD_PROBE_OK;
225 | }
226 |
227 | gboolean
228 | link_element_to_streammux_sink_pad (GstElement *streammux, GstElement *elem,
229 | gint index)
230 | {
231 | gboolean ret = FALSE;
232 | GstPad *mux_sink_pad = NULL;
233 | GstPad *src_pad = NULL;
234 | gchar pad_name[16];
235 |
236 | if (index >= 0) {
237 | g_snprintf (pad_name, 16, "sink_%u", index);
238 | pad_name[15] = '\0';
239 | } else {
240 | strcpy (pad_name, "sink_%u");
241 | }
242 |
243 | mux_sink_pad = gst_element_get_request_pad (streammux, pad_name);
244 | if (!mux_sink_pad) {
245 | NVGSTDS_ERR_MSG_V ("Failed to get sink pad from streammux");
246 | goto done;
247 | }
248 |
249 | src_pad = gst_element_get_static_pad (elem, "src");
250 | if (!src_pad) {
251 | NVGSTDS_ERR_MSG_V ("Failed to get src pad from '%s'",
252 | GST_ELEMENT_NAME (elem));
253 | goto done;
254 | }
255 |
256 | if (gst_pad_link (src_pad, mux_sink_pad) != GST_PAD_LINK_OK) {
257 | NVGSTDS_ERR_MSG_V ("Failed to link '%s' and '%s'", GST_ELEMENT_NAME (streammux),
258 | GST_ELEMENT_NAME (elem));
259 | goto done;
260 | }
261 |
262 | ret = TRUE;
263 |
264 | done:
265 | if (mux_sink_pad) {
266 | gst_object_unref (mux_sink_pad);
267 | }
268 | if (src_pad) {
269 | gst_object_unref (src_pad);
270 | }
271 | return ret;
272 | }
273 |
274 | gboolean
275 | link_element_to_tee_src_pad (GstElement *tee, GstElement *sinkelem)
276 | {
277 | gboolean ret = FALSE;
278 | GstPad *tee_src_pad = NULL;
279 | GstPad *sinkpad = NULL;
280 | GstPadTemplate *padtemplate = NULL;
281 |
282 | padtemplate = (GstPadTemplate *)
283 | gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee),
284 | "src_%u");
285 | tee_src_pad = gst_element_request_pad (tee, padtemplate, NULL, NULL);
286 | if (!tee_src_pad) {
287 | NVGSTDS_ERR_MSG_V ("Failed to get src pad from tee");
288 | goto done;
289 | }
290 |
291 | sinkpad = gst_element_get_static_pad (sinkelem, "sink");
292 | if (!sinkpad) {
293 | NVGSTDS_ERR_MSG_V ("Failed to get sink pad from '%s'",
294 | GST_ELEMENT_NAME (sinkelem));
295 | goto done;
296 | }
297 |
298 | if (gst_pad_link (tee_src_pad, sinkpad) != GST_PAD_LINK_OK) {
299 | NVGSTDS_ERR_MSG_V ("Failed to link '%s' and '%s'", GST_ELEMENT_NAME (tee),
300 | GST_ELEMENT_NAME (sinkelem));
301 | goto done;
302 | }
303 |
304 | ret = TRUE;
305 |
306 | done:
307 | if (tee_src_pad) {
308 | gst_object_unref (tee_src_pad);
309 | }
310 | if (sinkpad) {
311 | gst_object_unref (sinkpad);
312 | }
313 | return ret;
314 | }
315 |
316 | /**
317 | * Function called at regular interval when source bin is
318 | * changing state async. This function watches the state of
319 | * the source bin and sets it to PLAYING if the state of source
320 | * bin stops at PAUSED when changing state ASYNC.
321 | */
322 | static gboolean
323 | watch_source_async_state_change (gpointer data)
324 | {
325 | NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
326 | GstState state, pending;
327 | GstStateChangeReturn ret;
328 |
329 | ret = gst_element_get_state (src_bin->bin, &state, &pending, 0);
330 |
331 | // Bin is still changing state ASYNC. Wait for some more time.
332 | if (ret == GST_STATE_CHANGE_ASYNC)
333 | return TRUE;
334 |
335 | // Bin state change failed / failed to get state
336 | if (ret == GST_STATE_CHANGE_FAILURE) {
337 | src_bin->async_state_watch_running = FALSE;
338 | return FALSE;
339 | }
340 |
341 | // Bin successfully changed state to PLAYING. Stop watching state
342 | if (state == GST_STATE_PLAYING) {
343 | src_bin->reconfiguring = FALSE;
344 | src_bin->async_state_watch_running = FALSE;
345 | return FALSE;
346 | }
347 |
348 | // Bin has stopped ASYNC state change but has not gone into
349 | // PLAYING. Expliclity set state to PLAYING and keep watching
350 | // state
351 | gst_element_set_state (src_bin->bin, GST_STATE_PLAYING);
352 |
353 | return TRUE;
354 | }
355 |
356 | /**
357 | * Probe function to monitor data output from rtspsrc.
358 | */
359 | static GstPadProbeReturn
360 | rtspsrc_monitor_probe_func (GstPad * pad, GstPadProbeInfo * info,
361 | gpointer u_data)
362 | {
363 | NvDsSrcBin *bin = (NvDsSrcBin *) u_data;
364 | if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
365 | g_mutex_lock(&bin->bin_lock);
366 | gettimeofday (&bin->last_buffer_time, NULL);
367 | g_mutex_unlock(&bin->bin_lock);
368 | }
369 | return GST_PAD_PROBE_OK;
370 | }
371 |
372 | gboolean
373 | reset_source_pipeline (gpointer data)
374 | {
375 | NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
376 | GstState state, pending;
377 | GstStateChangeReturn ret;
378 |
379 | g_mutex_lock(&src_bin->bin_lock);
380 | gettimeofday (&src_bin->last_buffer_time, NULL);
381 | gettimeofday (&src_bin->last_reconnect_time, NULL);
382 | g_mutex_unlock(&src_bin->bin_lock);
383 |
384 | if (gst_element_set_state (src_bin->bin,
385 | GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
386 | GST_ERROR_OBJECT (src_bin->bin, "Can't set source bin to NULL");
387 | return FALSE;
388 | }
389 | NVGSTDS_INFO_MSG_V ("Resetting source %d", src_bin->bin_id);
390 |
391 | // GST_CAT_INFO (NVDS_APP, "Reset source pipeline %s %p\n,", __func__, src_bin);
392 | if (!gst_element_sync_state_with_parent (src_bin->bin)) {
393 | GST_ERROR_OBJECT (src_bin->bin, "Couldn't sync state with parent");
394 | }
395 |
396 | ret = gst_element_get_state (src_bin->bin, &state, &pending, 0);
397 |
398 | if (ret == GST_STATE_CHANGE_ASYNC || ret == GST_STATE_CHANGE_NO_PREROLL) {
399 | if (!src_bin->async_state_watch_running)
400 | g_timeout_add (20, watch_source_async_state_change, src_bin);
401 | src_bin->async_state_watch_running = TRUE;
402 | src_bin->reconfiguring = TRUE;
403 | } else if (ret == GST_STATE_CHANGE_SUCCESS && state == GST_STATE_PLAYING) {
404 | src_bin->reconfiguring = FALSE;
405 | }
406 | return FALSE;
407 | }
408 |
409 | /**
410 | * Function called at regular interval to check if NV_DS_SOURCE_RTSP type
411 | * source in the pipeline is down / disconnected. This function try to
412 | * reconnect the source by resetting that source pipeline.
413 | */
414 | static gboolean
415 | watch_source_status (gpointer data)
416 | {
417 | NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
418 | struct timeval current_time;
419 | gettimeofday (¤t_time, NULL);
420 | static struct timeval last_reset_time_global = {0, 0};
421 | gdouble time_diff_msec_since_last_reset =
422 | 1000.0 * (current_time.tv_sec - last_reset_time_global.tv_sec) +
423 | (current_time.tv_usec - last_reset_time_global.tv_usec) / 1000.0;
424 |
425 | if (src_bin->reconfiguring) {
426 | guint time_since_last_reconnect_sec =
427 | current_time.tv_sec - src_bin->last_reconnect_time.tv_sec;
428 | if (time_since_last_reconnect_sec >= SOURCE_RESET_INTERVAL_SEC) {
429 | if (time_diff_msec_since_last_reset > 3000) {
430 | last_reset_time_global = current_time;
431 | // source is still not up, reconfigure it again.
432 | reset_source_pipeline (src_bin);
433 | }
434 | }
435 | } else {
436 | gint time_since_last_buf_sec = 0;
437 | g_mutex_lock (&src_bin->bin_lock);
438 | if (src_bin->last_buffer_time.tv_sec != 0) {
439 | time_since_last_buf_sec =
440 | current_time.tv_sec - src_bin->last_buffer_time.tv_sec;
441 | }
442 | g_mutex_unlock (&src_bin->bin_lock);
443 |
444 | // Reset source bin if no buffers are received in the last
445 | // `rtsp_reconnect_interval_sec` seconds.
446 | if (src_bin->rtsp_reconnect_interval_sec > 0 &&
447 | time_since_last_buf_sec >= src_bin->rtsp_reconnect_interval_sec) {
448 | if (time_diff_msec_since_last_reset > 3000) {
449 | last_reset_time_global = current_time;
450 |
451 | NVGSTDS_WARN_MSG_V ("No data from source %d since last %u sec. Trying reconnection",
452 | src_bin->bin_id, time_since_last_buf_sec);
453 | reset_source_pipeline (src_bin);
454 | }
455 | }
456 |
457 | }
458 | return TRUE;
459 | }
460 |
461 | /**
462 | * callback function to receive messages from components
463 | * in the pipeline.
464 | */
465 | static gboolean
466 | bus_callback (GstBus * bus, GstMessage * message, gpointer data)
467 | {
468 | AppCtx *appCtx = (AppCtx *) data;
469 | switch (GST_MESSAGE_TYPE (message)) {
470 | case GST_MESSAGE_INFO:{
471 | GError *error = NULL;
472 | gchar *debuginfo = NULL;
473 | gst_message_parse_info (message, &error, &debuginfo);
474 | g_printerr ("INFO from %s: %s\n",
475 | GST_OBJECT_NAME (message->src), error->message);
476 | if (debuginfo) {
477 | g_printerr ("Debug info: %s\n", debuginfo);
478 | }
479 | g_error_free (error);
480 | g_free (debuginfo);
481 | break;
482 | }
483 | case GST_MESSAGE_WARNING:{
484 | GError *error = NULL;
485 | gchar *debuginfo = NULL;
486 | gst_message_parse_warning (message, &error, &debuginfo);
487 | g_printerr ("WARNING from %s: %s\n",
488 | GST_OBJECT_NAME (message->src), error->message);
489 | if (debuginfo) {
490 | g_printerr ("Debug info: %s\n", debuginfo);
491 | }
492 | g_error_free (error);
493 | g_free (debuginfo);
494 | break;
495 | }
496 | case GST_MESSAGE_ERROR:{
497 | GError *error = NULL;
498 | gchar *debuginfo = NULL;
499 | guint i = 0;
500 | gst_message_parse_error (message, &error, &debuginfo);
501 | g_printerr ("ERROR from %s: %s\n",
502 | GST_OBJECT_NAME (message->src), error->message);
503 | if (debuginfo) {
504 | g_printerr ("Debug info: %s\n", debuginfo);
505 | }
506 | NvDsSrcParentBin *bin = &appCtx->pipeline.multi_src_bin;
507 | GstElement *msg_src_elem = (GstElement *) GST_MESSAGE_SRC (message);
508 | gboolean bin_found = FALSE;
509 | /* Find the source bin which generated the error. */
510 | while (msg_src_elem && !bin_found) {
511 | for (i = 0; i < bin->num_bins && !bin_found; i++) {
512 | printf("msg_src_elem: %s\n", gst_element_get_name(msg_src_elem));
513 |
514 | if (!bin->sub_bins[i].src_elem) {
515 | printf("bin->sub_bins[i].src_elem is empty\n");
516 | goto done;
517 | }
518 | if (bin->sub_bins[i].src_elem == msg_src_elem ||
519 | bin->sub_bins[i].bin == msg_src_elem) {
520 | bin_found = TRUE;
521 | printf("bin to reset is found\n");
522 | break;
523 | }
524 | }
525 | msg_src_elem = GST_ELEMENT_PARENT (msg_src_elem);
526 | }
527 |
528 | if (i != bin->num_bins) {
529 | // Error from one of RTSP source.
530 | NvDsSrcBin *subBin = &bin->sub_bins[i];
531 | if (!subBin->reconfiguring ||
532 | g_strrstr(debuginfo, "500 (Internal Server Error)")) {
533 | subBin->reconfiguring = TRUE;
534 | printf("trying to call reset_source_pipeline\n");
535 | g_timeout_add (0, reset_source_pipeline, subBin);
536 | }
537 | g_error_free (error);
538 | g_free (debuginfo);
539 | return TRUE;
540 | }
541 |
542 | done:
543 | g_error_free (error);
544 | g_free (debuginfo);
545 | appCtx->return_value = -1;
546 | appCtx->quit = TRUE;
547 | break;
548 | }
549 | case GST_MESSAGE_STATE_CHANGED:{
550 | GstState oldstate, newstate;
551 | gst_message_parse_state_changed (message, &oldstate, &newstate, NULL);
552 | if (GST_ELEMENT (GST_MESSAGE_SRC (message)) == appCtx->pipeline.pipeline) {
553 | switch (newstate) {
554 | case GST_STATE_PLAYING:
555 | NVGSTDS_INFO_MSG_V ("Pipeline running\n");
556 | GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->
557 | pipeline.pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
558 | "ds-app-playing");
559 | break;
560 | case GST_STATE_PAUSED:
561 | if (oldstate == GST_STATE_PLAYING) {
562 | NVGSTDS_INFO_MSG_V ("Pipeline paused\n");
563 | }
564 | break;
565 | case GST_STATE_READY:
566 | GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->pipeline.
567 | pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "ds-app-ready");
568 | if (oldstate == GST_STATE_NULL) {
569 | NVGSTDS_INFO_MSG_V ("Pipeline ready\n");
570 | } else {
571 | NVGSTDS_INFO_MSG_V ("Pipeline stopped\n");
572 | }
573 | break;
574 | case GST_STATE_NULL:
575 | g_mutex_lock (&appCtx->app_lock);
576 | g_cond_broadcast (&appCtx->app_cond);
577 | g_mutex_unlock (&appCtx->app_lock);
578 | break;
579 | default:
580 | break;
581 | }
582 | }
583 | break;
584 | }
585 | case GST_MESSAGE_EOS:{
586 | /*
587 | * In normal scenario, this would use g_main_loop_quit() to exit the
588 | * loop and release the resources. Since this application might be
589 | * running multiple pipelines through configuration files, it should wait
590 | * till all pipelines are done.
591 | */
592 | NVGSTDS_INFO_MSG_V ("Received EOS. Exiting ...\n");
593 | appCtx->quit = TRUE;
594 | return FALSE;
595 | break;
596 | }
597 | default:
598 | break;
599 | }
600 | return TRUE;
601 | }
602 |
603 |
604 | /* Returning FALSE from this callback will make rtspsrc ignore the stream.
605 | * Ignore audio and add the proper depay element based on codec. */
606 | static gboolean
607 | cb_rtspsrc_select_stream (GstElement *rtspsrc, guint num, GstCaps *caps,
608 | gpointer user_data)
609 | {
610 | GstStructure *str = gst_caps_get_structure (caps, 0);
611 | const gchar *media = gst_structure_get_string (str, "media");
612 | const gchar *encoding_name = gst_structure_get_string (str, "encoding-name");
613 | gchar elem_name[50];
614 | NvDsSrcBin *bin = (NvDsSrcBin *) user_data;
615 | gboolean ret = FALSE;
616 |
617 | gboolean is_video = (!g_strcmp0 (media, "video"));
618 |
619 | if (!is_video)
620 | return FALSE;
621 |
622 | /* Create and add depay element only if it is not created yet. */
623 | if (!bin->depay) {
624 | g_snprintf (elem_name, sizeof (elem_name), "depay_elem%d", bin->bin_id);
625 |
626 | /* Add the proper depay element based on codec. */
627 | if (!g_strcmp0 (encoding_name, "H264")) {
628 | bin->depay = gst_element_factory_make ("rtph264depay", elem_name);
629 | g_snprintf (elem_name, sizeof (elem_name), "h264parse_elem%d", bin->bin_id);
630 | bin->parser = gst_element_factory_make ("h264parse", elem_name);
631 | } else if (!g_strcmp0 (encoding_name, "H265")) {
632 | printf("passes rtph265depay\n");
633 | bin->depay = gst_element_factory_make ("rtph265depay", elem_name);
634 | g_snprintf (elem_name, sizeof (elem_name), "h265parse_elem%d", bin->bin_id);
635 | bin->parser = gst_element_factory_make ("h265parse", elem_name);
636 | } else {
637 | NVGSTDS_WARN_MSG_V ("%s not supported", encoding_name);
638 | return FALSE;
639 | }
640 |
641 | if (!bin->depay) {
642 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
643 | return FALSE;
644 | }
645 | gst_bin_add_many (GST_BIN (bin->bin), bin->depay, bin->parser, NULL);
646 |
647 | NVGSTDS_LINK_ELEMENT (bin->depay, bin->parser);
648 | NVGSTDS_LINK_ELEMENT (bin->parser, bin->tee_rtsp_pre_decode);
649 |
650 | if (!gst_element_sync_state_with_parent (bin->depay)) {
651 | NVGSTDS_ERR_MSG_V ("'%s' failed to sync state with parent", elem_name);
652 | return FALSE;
653 | }
654 | gst_element_sync_state_with_parent (bin->parser);
655 | }
656 |
657 | ret = TRUE;
658 | done:
659 | return ret;
660 | }
661 |
662 | static void
663 | cb_newpad2 (GstElement * decodebin, GstPad * pad, gpointer data)
664 | {
665 | GstCaps *caps = gst_pad_query_caps (pad, NULL);
666 | const GstStructure *str = gst_caps_get_structure (caps, 0);
667 | const gchar *name = gst_structure_get_name (str);
668 | gint input_width = 1920;
669 | gint input_height = 1080;
670 | gint input_fps_n = 1;
671 | gint input_fps_d = 1;
672 | if (!strncmp (name, "video", 5)) {
673 | NvDsSrcBin *bin = (NvDsSrcBin *) data;
674 | GstPad *sinkpad = gst_element_get_static_pad (bin->cap_filter, "sink");
675 | if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) {
676 |
677 | NVGSTDS_ERR_MSG_V ("Failed to link decodebin to pipeline");
678 | } else {
679 | gst_structure_get_int (str, "width", &input_width);
680 | gst_structure_get_int (str, "height", &input_height);
681 | gst_structure_get_fraction (str, "framerate", &input_fps_n, &input_fps_d);
682 | }
683 | gst_object_unref (sinkpad);
684 | }
685 | gst_caps_unref (caps);
686 | }
687 |
688 | static void
689 | cb_newpad3 (GstElement * decodebin, GstPad * pad, gpointer data)
690 | {
691 | GstCaps *caps = gst_pad_query_caps (pad, NULL);
692 | const GstStructure *str = gst_caps_get_structure (caps, 0);
693 | const gchar *name = gst_structure_get_name (str);
694 | if (g_strrstr (name, "x-rtp")) {
695 | NvDsSrcBin *bin = (NvDsSrcBin *) data;
696 | GstPad *sinkpad = gst_element_get_static_pad (bin->depay, "sink");
697 | if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) {
698 |
699 | NVGSTDS_ERR_MSG_V ("Failed to link depay loader to rtsp src");
700 | }
701 | gst_object_unref (sinkpad);
702 | }
703 | gst_caps_unref (caps);
704 | }
705 |
706 | static void
707 | cb_newpad (GstElement * decodebin, GstPad * decoder_src_pad, gpointer data)
708 | {
709 | g_print ("In cb_newpad\n");
710 | GstCaps *caps = gst_pad_get_current_caps (decoder_src_pad);
711 | const GstStructure *str = gst_caps_get_structure (caps, 0);
712 | const gchar *name = gst_structure_get_name (str);
713 | GstElement *source_bin = (GstElement *) data;
714 | GstCapsFeatures *features = gst_caps_get_features (caps, 0);
715 |
716 | if (!strncmp (name, "video", 5)) {
717 | if (gst_caps_features_contains (features, MEMORY_FEATURES)) {
718 | GstPad *bin_ghost_pad = gst_element_get_static_pad (source_bin, "src");
719 | if (!gst_ghost_pad_set_target (GST_GHOST_PAD (bin_ghost_pad),
720 | decoder_src_pad)) {
721 | g_printerr ("Failed to link decoder src pad to source bin ghost pad\n");
722 | }
723 | gst_object_unref (bin_ghost_pad);
724 | } else {
725 | g_printerr ("Error: Decodebin did not pick nvidia decoder plugin.\n");
726 | }
727 | }
728 | }
729 |
730 | static void
731 | decodebin_child_added (GstChildProxy * child_proxy, GObject * object,
732 | gchar * name, gpointer user_data)
733 | {
734 | NvDsSrcBin *bin = (NvDsSrcBin *) user_data;
735 |
736 | if (g_strrstr (name, "decodebin") == name) {
737 | g_signal_connect (G_OBJECT (object), "child-added",
738 | G_CALLBACK (decodebin_child_added), user_data);
739 | }
740 | if ((g_strrstr (name, "h264parse") == name) ||
741 | (g_strrstr (name, "h265parse") == name)) {
742 | g_object_set (object, "config-interval", -1, NULL);
743 | }
744 | if (g_strrstr (name, "fakesink") == name) {
745 | g_object_set (object, "enable-last-sample", FALSE, NULL);
746 | }
747 | if (g_strrstr (name, "nvcuvid") == name) {
748 | g_object_set (object, "gpu-id", 0, NULL);
749 | }
750 | if (g_strstr_len (name, -1, "omx") == name) {
751 | g_object_set (object, "skip-frames", 2, NULL);
752 | g_object_set (object, "disable-dvfs", TRUE, NULL);
753 | }
754 | if (g_strstr_len (name, -1, "nvjpegdec") == name) {
755 | g_object_set (object, "DeepStream", TRUE, NULL);
756 | }
757 | if (g_strstr_len (name, -1, "nvv4l2decoder") == name) {
758 | #ifdef __aarch64__
759 | g_object_set (object, "enable-max-performance", TRUE, NULL);
760 | #endif
761 | }
762 | done:
763 | return;
764 | }
765 |
766 | static gboolean
767 | create_rtsp_src_bin (guint index, gchar * location, NvDsSrcBin * bin)
768 | {
769 | NvDsSRContext *ctx = NULL;
770 | gboolean ret = FALSE;
771 | gchar elem_name[50];
772 | GstCaps *caps = NULL;
773 | GstCapsFeatures *feature = NULL;
774 |
775 | bin->rtsp_reconnect_interval_sec = 10;
776 |
777 | g_snprintf (elem_name, sizeof (elem_name), "src_elem%d", bin->bin_id);
778 | bin->src_elem = gst_element_factory_make ("rtspsrc", elem_name);
779 | if (!bin->src_elem) {
780 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
781 | goto done;
782 | }
783 |
784 | g_signal_connect (G_OBJECT(bin->src_elem), "select-stream",
785 | G_CALLBACK(cb_rtspsrc_select_stream),
786 | bin);
787 |
788 | g_object_set (G_OBJECT (bin->src_elem), "location", location, NULL);
789 | g_object_set (G_OBJECT (bin->src_elem), "latency", bin->latency, NULL);
790 | g_object_set (G_OBJECT (bin->src_elem), "drop-on-latency", TRUE, NULL);
791 | configure_source_for_ntp_sync (bin->src_elem);
792 | g_signal_connect (G_OBJECT (bin->src_elem), "pad-added",
793 | G_CALLBACK (cb_newpad3), bin);
794 |
795 | g_snprintf (elem_name, sizeof (elem_name), "tee_rtsp_elem%d", bin->bin_id);
796 | bin->tee_rtsp_pre_decode = gst_element_factory_make ("tee", elem_name);
797 | if (!bin->tee_rtsp_pre_decode) {
798 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
799 | goto done;
800 | }
801 |
802 | g_snprintf (elem_name, sizeof (elem_name), "dec_que%d", bin->bin_id);
803 | bin->dec_que = gst_element_factory_make ("queue", elem_name);
804 | if (!bin->dec_que) {
805 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
806 | goto done;
807 | }
808 |
809 | if (bin->rtsp_reconnect_interval_sec > 0) {
810 | printf("rtsp_reconnect_interval_sec: %u\n", bin->rtsp_reconnect_interval_sec);
811 | NVGSTDS_ELEM_ADD_PROBE (bin->rtspsrc_monitor_probe, bin->dec_que,
812 | "sink", rtspsrc_monitor_probe_func,
813 | GST_PAD_PROBE_TYPE_BUFFER,
814 | bin);
815 | install_mux_eosmonitor_probe = TRUE;
816 | }
817 |
818 | g_snprintf (elem_name, sizeof (elem_name), "decodebin_elem%d", bin->bin_id);
819 | bin->decodebin = gst_element_factory_make ("decodebin", elem_name);
820 | if (!bin->decodebin) {
821 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
822 | goto done;
823 | }
824 |
825 | g_signal_connect (G_OBJECT (bin->decodebin), "pad-added",
826 | G_CALLBACK (cb_newpad2), bin);
827 | g_signal_connect (G_OBJECT (bin->decodebin), "child-added",
828 | G_CALLBACK (decodebin_child_added), bin);
829 |
830 |
831 | g_snprintf (elem_name, sizeof (elem_name), "src_que%d", bin->bin_id);
832 | bin->cap_filter = gst_element_factory_make ("queue", elem_name);
833 | if (!bin->cap_filter) {
834 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
835 | goto done;
836 | }
837 |
838 | g_mutex_init (&bin->bin_lock);
839 |
840 | g_snprintf(elem_name, sizeof(elem_name), "nvvidconv_elem%d", bin->bin_id);
841 | bin->nvvidconv = gst_element_factory_make("nvvideoconvert", elem_name);
842 | if (!bin->nvvidconv)
843 | {
844 | NVGSTDS_ERR_MSG_V("Could not create element 'nvvidconv_elem'");
845 | goto done;
846 | }
847 | caps = gst_caps_new_empty_simple("video/x-raw");
848 | feature = gst_caps_features_new("memory:NVMM", NULL);
849 | gst_caps_set_features(caps, 0, feature);
850 |
851 | bin->cap_filter1 =
852 | gst_element_factory_make("capsfilter", "src_cap_filter_nvvidconv");
853 | if (!bin->cap_filter1)
854 | {
855 | NVGSTDS_ERR_MSG_V("Could not create 'queue'");
856 | goto done;
857 | }
858 |
859 | g_object_set(G_OBJECT(bin->cap_filter1), "caps", caps, NULL);
860 | gst_caps_unref(caps);
861 |
862 | gst_bin_add_many (GST_BIN(bin->bin),
863 | bin->src_elem,
864 | bin->tee_rtsp_pre_decode,
865 | bin->dec_que,
866 | bin->decodebin,
867 | bin->cap_filter,
868 | bin->nvvidconv,
869 | bin->cap_filter1,
870 | NULL);
871 |
872 | link_element_to_tee_src_pad(bin->tee_rtsp_pre_decode, bin->dec_que);
873 | NVGSTDS_LINK_ELEMENT (bin->dec_que, bin->decodebin);
874 |
875 | if (ctx)
876 | link_element_to_tee_src_pad(bin->tee_rtsp_pre_decode, ctx->recordbin);
877 |
878 | NVGSTDS_LINK_ELEMENT (bin->cap_filter, bin->nvvidconv);
879 | NVGSTDS_LINK_ELEMENT (bin->nvvidconv, bin->cap_filter1);
880 | NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter1, "src");
881 |
882 | ret = TRUE;
883 |
884 | g_timeout_add (1000, watch_source_status, bin);
885 |
886 | done:
887 | if (!ret) {
888 | NVGSTDS_ERR_MSG_V ("%s failed", __func__);
889 | }
890 | return ret;
891 |
892 | }
893 |
894 | /**
895 | * Probe function to drop EOS events from nvstreammux when RTSP sources
896 | * are being used so that app does not quit from EOS in case of RTSP
897 | * connection errors and tries to reconnect.
898 | */
899 | static GstPadProbeReturn
900 | nvstreammux_eosmonitor_probe_func (GstPad * pad, GstPadProbeInfo * info,
901 | gpointer u_data)
902 | {
903 | if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
904 | GstEvent *event = (GstEvent *) info->data;
905 | if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
906 | return GST_PAD_PROBE_DROP;
907 | }
908 | return GST_PAD_PROBE_OK;
909 | }
910 |
911 | static gboolean
912 | start_rtsp_streaming (guint rtsp_port_num, guint updsink_port_num, guint64 udp_buffer_size)
913 | {
914 | GstRTSPMountPoints *mounts;
915 | GstRTSPMediaFactory *factory;
916 | char udpsrc_pipeline[512];
917 | char port_num_Str[64] = { 0 };
918 |
919 | if (udp_buffer_size == 0)
920 | udp_buffer_size = 512 * 1024;
921 |
922 | sprintf (udpsrc_pipeline,
923 | "( udpsrc name=pay0 port=%d buffer-size=%lu caps=\"application/x-rtp, media=video, "
924 | "clock-rate=90000, encoding-name=%s, payload=96 \" )",
925 | updsink_port_num, udp_buffer_size, "H264");
926 |
927 | sprintf (port_num_Str, "%d", rtsp_port_num);
928 |
929 | g_mutex_lock (&server_cnt_lock);
930 |
931 | server [server_count] = gst_rtsp_server_new ();
932 | g_object_set (server [server_count], "service", port_num_Str, NULL);
933 |
934 | mounts = gst_rtsp_server_get_mount_points (server [server_count]);
935 |
936 | factory = gst_rtsp_media_factory_new ();
937 | gst_rtsp_media_factory_set_launch (factory, udpsrc_pipeline);
938 |
939 | gst_rtsp_mount_points_add_factory (mounts, "/ds-test", factory);
940 |
941 | g_object_unref (mounts);
942 |
943 | gst_rtsp_server_attach (server [server_count], NULL);
944 |
945 | server_count++;
946 |
947 | g_mutex_unlock (&server_cnt_lock);
948 |
949 | g_print
950 | ("\n *** DeepStream: Launched RTSP Streaming at rtsp://localhost:%d/ds-test ***\n\n",
951 | rtsp_port_num);
952 |
953 | return TRUE;
954 | }
955 |
956 |
957 | static void
958 | usage(const char *bin)
959 | {
960 | g_printerr
961 | ("Usage: %s [-t infer-type] ... \n",
962 | bin);
963 | g_printerr
964 | (" -t infer-type: select form [infer, inferserver], infer by default\n");
965 | }
966 |
967 | int
968 | main (int argc, char *argv[])
969 | {
970 | printf("app start..\n");
971 | appCtx[0] = (AppCtx *) g_malloc0 (sizeof (AppCtx));
972 | NvDsPipeline *pipeline = &appCtx[0]->pipeline;
973 | GstElement *streammux = NULL,
974 | *sink = NULL,
975 | *pgie = NULL,
976 | *nvosd = NULL,
977 | *queue2 = NULL,
978 | *nvvidconv2 = NULL,
979 | *caps_filter = NULL,
980 | *encoder = NULL,
981 | *codecparser = NULL,
982 | *rtppay = NULL,
983 | *tiler = NULL;
984 | GstCaps *caps = NULL;
985 | GstBus *bus = NULL;
986 | guint i=0;
987 | guint tiler_rows, tiler_columns;
988 | GstPad *osd_sink_pad = NULL;
989 | guint pgie_batch_size;
990 | const char *infer_plugin = NVINFER_PLUGIN;
991 | gboolean ret = FALSE;
992 |
993 | /* define numsorces from argc */
994 | if (argc < 2) {
995 | g_printerr ("Usage: %s \n", argv[0]);
996 | return -1;
997 | }
998 | num_sources = argc - 1;
999 | /* define numsorces from argc */
1000 |
1001 | nvds_version(&nvds_lib_major_version, &nvds_lib_minor_version);
1002 |
1003 | gst_init (&argc, &argv);
1004 |
1005 | pipeline->pipeline = gst_pipeline_new ("pipeline");
1006 | if (!pipeline->pipeline) {
1007 | NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
1008 | }
1009 |
1010 | bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline->pipeline));
1011 | pipeline->bus_id = gst_bus_add_watch (bus, bus_callback, appCtx[0]);
1012 | gst_object_unref (bus);
1013 |
1014 | pipeline->multi_src_bin.reset_thread = NULL;
1015 |
1016 | pipeline->multi_src_bin.bin = gst_bin_new ("multi_src_bin");
1017 | if (!pipeline->multi_src_bin.bin) {
1018 | NVGSTDS_ERR_MSG_V ("Failed to create element 'multi_src_bin'");
1019 | goto done;
1020 | }
1021 | g_object_set (pipeline->multi_src_bin.bin, "message-forward", TRUE, NULL);
1022 |
1023 | pipeline->multi_src_bin.streammux = gst_element_factory_make ("nvstreammux", "src_bin_muxer");
1024 | if (!pipeline->multi_src_bin.streammux) {
1025 | NVGSTDS_ERR_MSG_V ("Failed to create element 'src_bin_muxer'");
1026 | goto done;
1027 | }
1028 | gst_bin_add (GST_BIN (pipeline->multi_src_bin.bin), pipeline->multi_src_bin.streammux);
1029 |
1030 | /* create rtsp src bin */
1031 | for (i = 0; i < num_sources; i++) {
1032 | printf("%s: %u\n", "start createing source bins", i);
1033 | GstPad *sinkpad, *srcpad;
1034 | gchar elem_name[50];
1035 | g_snprintf (elem_name, sizeof (elem_name), "src_sub_bin%d", i);
1036 | pipeline->multi_src_bin.sub_bins[i].bin = gst_bin_new (elem_name);
1037 | if (!pipeline->multi_src_bin.sub_bins[i].bin) {
1038 | NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
1039 | goto done;
1040 | }
1041 |
1042 | pipeline->multi_src_bin.sub_bins[i].bin_id = pipeline->multi_src_bin.sub_bins[i].source_id = i;
1043 | pipeline->multi_src_bin.live_source = TRUE;
1044 | pipeline->multi_src_bin.sub_bins[i].eos_done = TRUE;
1045 | pipeline->multi_src_bin.sub_bins[i].reset_done = TRUE;
1046 |
1047 | printf("argv[2]: %s\n", argv[i+1]);
1048 | if(!create_rtsp_src_bin (i, argv[i + 1], &pipeline->multi_src_bin.sub_bins[i])){
1049 | g_printerr ("Failed to create source bin. Exiting.\n");
1050 | }
1051 | gst_bin_add (GST_BIN (pipeline->multi_src_bin.bin), pipeline->multi_src_bin.sub_bins[i].bin);
1052 |
1053 | if (!link_element_to_streammux_sink_pad (pipeline->multi_src_bin.streammux,
1054 | pipeline->multi_src_bin.sub_bins[i].bin, i)) {
1055 | NVGSTDS_ERR_MSG_V ("source %d cannot be linked to mux's sink pad %p\n", i, pipeline->multi_src_bin.streammux);
1056 | goto done;
1057 | }
1058 | pipeline->multi_src_bin.num_bins++;
1059 | printf("pipeline->multi_src_bin.num_bins: %d\n", pipeline->multi_src_bin.num_bins);
1060 | }
1061 | /* create rtsp src bin */
1062 |
1063 | NVGSTDS_BIN_ADD_GHOST_PAD (pipeline->multi_src_bin.bin, pipeline->multi_src_bin.streammux, "src");
1064 |
1065 | if (install_mux_eosmonitor_probe) {
1066 | NVGSTDS_ELEM_ADD_PROBE (pipeline->multi_src_bin.nvstreammux_eosmonitor_probe, pipeline->multi_src_bin.streammux,
1067 | "src", nvstreammux_eosmonitor_probe_func,
1068 | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
1069 | &pipeline->multi_src_bin);
1070 | }
1071 |
1072 | done:
1073 |
1074 | loop = g_main_loop_new (NULL, FALSE);
1075 |
1076 | pgie = gst_element_factory_make (infer_plugin, "primary-nvinference-engine");
1077 | tiler = gst_element_factory_make ("nvmultistreamtiler", "nvtiler");
1078 | nvosd = gst_element_factory_make ("nvdsosd", "nv-onscreendisplay");
1079 | g_object_set (G_OBJECT (nvosd), "display-text", 1, NULL);
1080 | g_object_set (G_OBJECT (nvosd), "display-clock", 1, NULL);
1081 | g_object_set (G_OBJECT (nvosd), "display-bbox", 1, NULL);
1082 | g_object_set (G_OBJECT (nvosd), "display-mask", 1, NULL);
1083 | g_object_set (G_OBJECT (nvosd), "process-mode", 2, NULL);
1084 | queue2 = gst_element_factory_make ("queue", "queue2");
1085 | nvvidconv2 = gst_element_factory_make ("nvvideoconvert", "convertor2");
1086 | guint gpu_id = 0;
1087 | caps_filter = gst_element_factory_make ("capsfilter", "capsfilter");
1088 | caps = gst_caps_from_string ("video/x-raw(memory:NVMM), format=I420");
1089 | encoder = gst_element_factory_make ("nvv4l2h264enc", "encoder");
1090 | guint profile = 0;
1091 | guint bitrate = 1000000;
1092 | guint iframeinterval = 60;
1093 | codecparser = gst_element_factory_make ("h264parse", "h264-parser2");
1094 | rtppay = gst_element_factory_make ("rtph264pay", "rtppay");
1095 | sink = gst_element_factory_make ("udpsink", "udpsink");
1096 |
1097 | g_object_set (G_OBJECT (pipeline->multi_src_bin.streammux), "width", MUXER_OUTPUT_WIDTH, "height",
1098 | MUXER_OUTPUT_HEIGHT, "batch-size", num_sources,
1099 | "batched-push-timeout", MUXER_BATCH_TIMEOUT_USEC, NULL);
1100 | g_object_get (G_OBJECT (pgie), "batch-size", &pgie_batch_size, NULL);
1101 | if (pgie_batch_size != num_sources) {
1102 | g_printerr
1103 | ("WARNING: Overriding infer-config batch-size (%d) with number of sources (%d)\n",
1104 | pgie_batch_size, num_sources);
1105 | g_object_set (G_OBJECT (pgie), "batch-size", num_sources, NULL);
1106 | }
1107 | g_object_set (G_OBJECT (pgie), "batch-size", 1, NULL);
1108 | g_object_set (G_OBJECT (pgie), "config-file-path", INFER_PGIE_CONFIG_FILE, NULL);
1109 | g_object_set (G_OBJECT (nvvidconv2), "gpu-id", gpu_id, NULL);
1110 | g_object_set (G_OBJECT (caps_filter), "caps", caps, NULL);
1111 | g_object_set (G_OBJECT (encoder), "profile", profile, NULL);
1112 | g_object_set (G_OBJECT (encoder), "iframeinterval", iframeinterval, NULL);
1113 | g_object_set (G_OBJECT (encoder), "bitrate", bitrate, NULL);
1114 | g_object_set (G_OBJECT (encoder), "preset-level", 1, NULL);
1115 | g_object_set (G_OBJECT (encoder), "insert-sps-pps", 1, NULL);
1116 | g_object_set (G_OBJECT (encoder), "bufapi-version", 1, NULL);
1117 | g_object_set (G_OBJECT (sink), "host", "224.224.255.255", "port",
1118 | 5400, "async", FALSE, "sync", 0, NULL);
1119 |
1120 | if (!pgie) {
1121 | g_printerr ("pgie could not be created. Exiting.\n");
1122 | return -1;
1123 | }
1124 | if (!tiler) {
1125 | g_printerr ("tiler could not be created. Exiting.\n");
1126 | return -1;
1127 | }
1128 | if (!nvosd) {
1129 | g_printerr ("nvosd could not be created. Exiting.\n");
1130 | return -1;
1131 | }
1132 | if (!sink) {
1133 | g_printerr ("sink could not be created. Exiting.\n");
1134 | return -1;
1135 | }
1136 | if (!queue2 || !nvvidconv2 || !caps_filter || !encoder || !codecparser ) {
1137 | g_printerr ("One element could not be created. Exiting.\n");
1138 | return -1;
1139 | }
1140 | if (!rtppay) {
1141 | g_printerr ("rtppay could not be created. Exiting.\n");
1142 | return -1;
1143 | }
1144 |
1145 | /* a tee after the tiler which shall be connected to sink(s) */
1146 | pipeline->tiler_tee = gst_element_factory_make ("tee", "tiler_tee");
1147 | if (!pipeline->tiler_tee) {
1148 | NVGSTDS_ERR_MSG_V ("Failed to create element 'tiler_tee'");
1149 | }
1150 | /* Create demuxer only if tiled display is disabled. */
1151 | pipeline->demuxer =
1152 | gst_element_factory_make ("nvstreamdemux", "demuxer");
1153 | if (!pipeline->demuxer) {
1154 | NVGSTDS_ERR_MSG_V ("Failed to create element 'demuxer'");
1155 | }
1156 |
1157 | /* setting tiler */
1158 | tiler_rows = (guint) sqrt (num_sources);
1159 | tiler_columns = (guint) ceil (1.0 * num_sources / tiler_rows);
1160 | g_object_set (G_OBJECT (tiler), "rows", tiler_rows, "columns", tiler_columns,
1161 | "width", TILED_OUTPUT_WIDTH, "height", TILED_OUTPUT_HEIGHT, NULL);
1162 |
1163 | gst_bin_add_many (GST_BIN (pipeline->pipeline),
1164 | pipeline->multi_src_bin.bin,
1165 | pipeline->multi_src_bin.streammux,
1166 | pgie,
1167 | tiler,
1168 | nvosd,
1169 | queue2,
1170 | nvvidconv2,
1171 | caps_filter,
1172 | encoder,
1173 | codecparser,
1174 | rtppay,
1175 | sink,
1176 | NULL);
1177 |
1178 | if (!gst_element_link_many (pipeline->multi_src_bin.bin,
1179 | pgie,
1180 | tiler,
1181 | nvosd,
1182 | queue2,
1183 | nvvidconv2,
1184 | caps_filter,
1185 | encoder,
1186 | codecparser,
1187 | rtppay,
1188 | sink,
1189 | NULL)) {
1190 | g_printerr ("Elements could not be linked. Exiting.\n");
1191 | return -1;
1192 | }
1193 |
1194 | osd_sink_pad = gst_element_get_static_pad (pgie, "src");
1195 | if (!osd_sink_pad)
1196 | g_print ("Unable to get sink pad\n");
1197 | else
1198 | gst_pad_add_probe (osd_sink_pad, GST_PAD_PROBE_TYPE_BUFFER,
1199 | osd_sink_pad_buffer_probe, NULL, NULL);
1200 |
1201 | ret = TRUE;
1202 | ret = start_rtsp_streaming (8554, 5400, 0);
1203 | if (ret != TRUE) {
1204 | g_print ("%s: start_rtsp_straming function failed\n", __func__);
1205 | }
1206 |
1207 | g_print ("Now playing...\n");
1208 | gst_element_set_state (pipeline->pipeline, GST_STATE_PLAYING);
1209 | g_print ("Running...\n");
1210 | g_main_loop_run (loop);
1211 | g_print ("Returned, stopping playback\n");
1212 | gst_element_set_state (pipeline->pipeline, GST_STATE_NULL);
1213 | g_print ("Deleting pipeline\n");
1214 | gst_object_unref (GST_OBJECT (pipeline->pipeline));
1215 | g_source_remove (pipeline->bus_id);
1216 | g_main_loop_unref (loop);
1217 | return 0;
1218 | }
1219 |
--------------------------------------------------------------------------------
/src/main.h:
--------------------------------------------------------------------------------
1 | #ifndef __NVGSTDS_APP_H__
2 | #define __NVGSTDS_APP_H__
3 |
4 | #ifdef __cplusplus
5 | extern "C"
6 | {
7 | #endif
8 |
9 | #include
10 | #include
11 | #include
12 | #include "../common/includes/gst-nvdssr.h"
13 | #include "../common/includes/deepstream_common.h"
14 | #include "../common/includes/nvdsgstutils.h"
15 |
16 | #define NVGSTDS_ERR_MSG_V(msg, ...) \
17 | g_print("** ERROR: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
18 |
19 | #define NVGSTDS_INFO_MSG_V(msg, ...) \
20 | g_print("** INFO: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
21 |
22 | #define NVGSTDS_WARN_MSG_V(msg, ...) \
23 | g_print("** WARN: <%s:%d>: " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
24 |
25 | #define MAX_SOURCE_BINS 1024
26 | #define SOURCE_RESET_INTERVAL_SEC 30
27 |
28 | typedef struct _AppCtx AppCtx;
29 |
30 | typedef enum
31 | {
32 | NV_DS_SOURCE_CAMERA_V4L2 = 1,
33 | NV_DS_SOURCE_URI,
34 | NV_DS_SOURCE_URI_MULTIPLE,
35 | NV_DS_SOURCE_RTSP,
36 | NV_DS_SOURCE_CAMERA_CSI,
37 | } NvDsSourceType;
38 |
39 |
40 | typedef struct
41 | {
42 | GstElement *bin;
43 | GstElement *src_elem;
44 | GstElement *cap_filter;
45 | GstElement *cap_filter1;
46 | GstElement *depay;
47 | GstElement *parser;
48 | GstElement *enc_que;
49 | GstElement *dec_que;
50 | GstElement *decodebin;
51 | GstElement *enc_filter;
52 | GstElement *encbin_que;
53 | GstElement *tee;
54 | GstElement *tee_rtsp_pre_decode;
55 | GstElement *fakesink_queue;
56 | GstElement *fakesink;
57 | GstElement *nvvidconv;
58 |
59 | gboolean do_record;
60 | guint64 pre_event_rec;
61 | GMutex bin_lock;
62 | guint bin_id;
63 | gint rtsp_reconnect_interval_sec;
64 | struct timeval last_buffer_time;
65 | struct timeval last_reconnect_time;
66 | gulong src_buffer_probe;
67 | gulong rtspsrc_monitor_probe;
68 | gpointer bbox_meta;
69 | GstBuffer *inbuf;
70 | gchar *location;
71 | gchar *file;
72 | gchar *direction;
73 | gint latency;
74 | gboolean got_key_frame;
75 | gboolean eos_done;
76 | gboolean reset_done;
77 | gboolean live_source;
78 | gboolean reconfiguring;
79 | gboolean async_state_watch_running;
80 | // NvDsDewarperBin dewarper_bin;
81 | gulong probe_id;
82 | guint64 accumulated_base;
83 | guint64 prev_accumulated_base;
84 | guint source_id;
85 | // NvDsSourceConfig *config;
86 | gpointer recordCtx;
87 | } NvDsSrcBin;
88 |
89 | typedef struct
90 | {
91 | GstElement *bin;
92 | GstElement *streammux;
93 | GThread *reset_thread;
94 | NvDsSrcBin sub_bins[MAX_SOURCE_BINS];
95 | guint num_bins;
96 | guint num_fr_on;
97 | gboolean live_source;
98 | gulong nvstreammux_eosmonitor_probe;
99 | } NvDsSrcParentBin;
100 |
101 | typedef struct
102 | {
103 | // gulong primary_bbox_buffer_probe_id;
104 | guint bus_id;
105 | GstElement *pipeline;
106 | NvDsSrcParentBin multi_src_bin;
107 | // NvDsInstanceBin instance_bins[MAX_SOURCE_BINS];
108 | // NvDsInstanceBin demux_instance_bins[MAX_SOURCE_BINS];
109 | // NvDsInstanceBin common_elements;
110 | GstElement *tiler_tee;
111 | // NvDsTiledDisplayBin tiled_display_bin;
112 | GstElement *demuxer;
113 | // NvDsDsExampleBin dsexample_bin;
114 | AppCtx *appCtx;
115 | } NvDsPipeline;
116 |
117 | struct _AppCtx
118 | {
119 | // gboolean version;
120 | // gboolean cintr;
121 | // gboolean show_bbox_text;
122 | // gboolean seeking;
123 | gboolean quit;
124 | // gint person_class_id;
125 | // gint car_class_id;
126 | gint return_value;
127 | // guint index;
128 | // gint active_source_index;
129 |
130 | GMutex app_lock;
131 | GCond app_cond;
132 |
133 | NvDsPipeline pipeline;
134 | // NvDsConfig config;
135 | // NvDsConfig override_config;
136 | // NvDsInstanceData instance_data[MAX_SOURCE_BINS];
137 | // NvDsC2DContext *c2d_ctx[MAX_MESSAGE_CONSUMERS];
138 | // NvDsAppPerfStructInt perf_struct;
139 | // bbox_generated_callback bbox_generated_post_analytics_cb;
140 | // bbox_generated_callback all_bbox_generated_cb;
141 | // overlay_graphics_callback overlay_graphics_cb;
142 | // NvDsFrameLatencyInfo *latency_info;
143 | // GMutex latency_lock;
144 | // GThread *ota_handler_thread;
145 | // guint ota_inotify_fd;
146 | // guint ota_watch_desc;
147 | };
148 |
149 | #ifdef __cplusplus
150 | }
151 | #endif
152 |
153 | #endif
--------------------------------------------------------------------------------
/system_drawing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/belarbi2733/deepstream-rtspsrc-yolo/d90fc9c325f01478cb7ffc26879bd89f5c7b6735/system_drawing.png
--------------------------------------------------------------------------------
/yolo/README:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
3 | #
4 | # NVIDIA Corporation and its licensors retain all intellectual property
5 | # and proprietary rights in and to this software, related documentation
6 | # and any modifications thereto. Any use, reproduction, disclosure or
7 | # distribution of this software and related documentation without an express
8 | # license agreement from NVIDIA Corporation is strictly prohibited.
9 | #
10 | ################################################################################
11 |
12 | This sample shows how to integrate a detector Model like Yolo having layers
13 | supported through IPlugin interface by TensorRT and with custom output layer
14 | parsing for detected objects with DeepStreamSDK. The sample uses the
15 | IPluginFactory interface to create specific layers in Yolo and configure yolo
16 | plugin provided by TensorRT. This sample demonstrates how to reconstruct
17 | user-defined TensorRT layers from engine file in runtime.
18 |
19 | --------------------------------------------------------------------------------
20 | Sample contents:
21 | - deepstream_app_config_yolo[V3,V3_tiny,V2,V2_tiny,tlt].txt - DeepStream reference
22 | app configuration file for using YoloV2/yoloV2-tiny/yolo/yolo-tiny/tlt model
23 | as the primary detector.
24 | - config_infer_primary_yolo[V3,V3_tiny,V2,V2_tiny,tlt].txt - Configuration file for the GStreamer
25 | nvinfer plugin for the Yolo detector model.
26 | - yolov3-calibration.table.trt7.0 - yoloV3 INT8 calibration binary on TensorRT 7.0+
27 | - nvdsinfer_custom_impl_Yolo/nvdsinfer_yolo_engine.cpp -
28 | Implementation of 'NvDsInferCreateModelParser'/IModelParser for nvdsinfer to
29 | parse custom models. Alternatively, also contains implementation of
30 | 'NvDsInferYoloCudaEngineGet' for nvdsinfer to directly create cuda engine.
31 | To use the 'NvDsInferYoloCudaEngineGet' interface, enable the macro
32 | USE_CUDA_ENGINE_GET_API in nvdsinfer_yolo_engine.cpp
33 | - nvdsinfer_custom_impl_Yolo/nvdsparsebbox_Yolo.cpp - Output layer
34 | parsing function for detected objects for the Yolo model.
35 | - nvdsinfer_custom_impl_Yolo/yoloPlugins.h -
36 | Declaration of YoloLayerV3 and YoloLayerV3PluginCreator.
37 | - nvdsinfer_custom_impl_Yolo/yoloPlugins.cpp -
38 | Implementation of YoloLayerV3 and YoloLayerV3PluginCreator.
39 | - nvdsinfer_custom_impl_Yolo/kernels.cu - Implementation of cuda kernels for
40 | - nvdsinfer_custom_impl_Yolo/trt_utils.h - Utilities to setup tensorRT networks
41 | - nvdsinfer_custom_impl_Yolo/trt_utils.cpp - Implementation of Utilities to setup
42 | tensorRT networks
43 | - nvdsinfer_custom_impl_Yolo/yolo.h - Interface to create Yolo Cuda-Engine
44 | - nvdsinfer_custom_impl_Yolo/yolo.cpp - Implementation to create Yolo Cuda-Engine
45 |
46 | --------------------------------------------------------------------------------
47 | Pre-requisites:
48 | - Download yolo config and weights files
49 | $ ./prebuild.sh
50 | - Set correct yolo config/weights file in config_infer_primary_yolo[...].txt.
51 | custom-network-config # path to yolo config
52 | model-file # path to yolo weights
53 | - Enable INT8 precision detection if there is a calibration cache file, update
54 | config_infer_primary_yolo[...].txt.
55 | int8-calib-file=yolo[...]-calibration.table.trt5.1
56 | - Other INT8 precision calibration table need to be calibrated by user.
57 | - The yolo-tlt sample makes use of a few TensorRT OSS plugins. Download the
58 | TensorRT OSS repo (https://github.com/NVIDIA/TensorRT/) and checkout the
59 | 'release/7.0' branch. Follow the instructions in the README to build the
60 | plugin library 'libnvinfer_plugin.so.7.0.0' corresponding to your dGPU/Jetson
61 | platform. This library needs to be used with LD_PRELOAD to let nvinfer plugin
62 | access the TRT OSS plugin layers.
63 | - Set the path of the etlt model (tlt-encoded-model) and the tlt model key
64 | (tlt-model-key) in config_infer_primary_yolo_tlt.txt before running the sample.
65 |
66 | Note:
67 | - There is a quality regression of masks in yolov3-tiny.cfg, please see comments
68 | https://github.com/pjreddie/darknet/commit/f86901f6177dfc6116360a13cc06ab680e0c86b0#r30200016
69 | Before official fix in yolov3-tiny.weights, end users can revert the masks to
70 | [1, 2, 3]. There is also a hardcode of masks in nvdsparsebbox_Yolo.cpp.
71 |
72 | --------------------------------------------------------------------------------
73 | Compile the custom library:
74 | # Based on the API to use 'NvDsInferCreateModelParser' or 'NvDsInferCudaEngineGet'
75 | # set the macro USE_CUDA_ENGINE_GET_API to 0 or 1 in
76 | # nvdsinfer_custom_impl_Yolo/nvdsinfer_yolo_engine.cpp
77 |
78 | # Export correct CUDA version (e.g. 10.2, 10.1)
79 | $ export CUDA_VER=10.2
80 | $ make -C nvdsinfer_custom_impl_Yolo
81 |
82 | --------------------------------------------------------------------------------
83 | Run the sample:
84 | The "nvinfer" config file config_infer_primary_yolo.txt specifies the path to
85 | the custom library and the custom output parsing function through the properties
86 | "custom-lib-path" and "parse-bbox-func-name" respectively.
87 | The first-time a "model_b1_int8.engine" would be generated as the engine-file
88 |
89 | - With deepstream-app
90 | $ deepstream-app -c deepstream_app_config_yoloV3.txt
91 | $ deepstream-app -c deepstream_app_config_yoloV3_tiny.txt
92 | $ deepstream-app -c deepstream_app_config_yoloV2.txt
93 | $ deepstream-app -c deepstream_app_config_yoloV2_tiny.txt
94 | $ LD_PRELOAD= deepstream-app -c deepstream_app_config_yolo_tlt.txt
95 |
--------------------------------------------------------------------------------
/yolo/nvdsinfer_custom_impl_Yolo/Makefile:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining a
5 | # copy of this software and associated documentation files (the "Software"),
6 | # to deal in the Software without restriction, including without limitation
7 | # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | # and/or sell copies of the Software, and to permit persons to whom the
9 | # Software is furnished to do so, subject to the following conditions:
10 | #
11 | # The above copyright notice and this permission notice shall be included in
12 | # all copies or substantial portions of the Software.
13 | #
14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | # DEALINGS IN THE SOFTWARE.
21 | ################################################################################
22 |
23 | CUDA_VER:=10.2
24 | NVDS_VERSION:=5.0
25 |
26 | CC:= g++
27 | NVCC:=/usr/local/cuda-$(CUDA_VER)/bin/nvcc
28 |
29 | CFLAGS:= -Wall -std=c++11 -shared -fPIC -Wno-error=deprecated-declarations
30 | CFLAGS+= -I/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/sources/includes -I/usr/local/cuda-$(CUDA_VER)/include
31 |
32 | LIBS:= -lnvinfer_plugin -lnvinfer -lnvparsers -L/usr/local/cuda-$(CUDA_VER)/lib64 -lcudart -lcublas -lstdc++fs
33 | LFLAGS:= -shared -Wl,--start-group $(LIBS) -Wl,--end-group
34 |
35 | INCS:= $(wildcard *.h)
36 | SRCFILES:= nvdsinfer_yolo_engine.cpp \
37 | nvdsparsebbox_Yolo.cpp \
38 | yoloPlugins.cpp \
39 | trt_utils.cpp \
40 | yolo.cpp \
41 | kernels.cu
42 | TARGET_LIB:= libnvdsinfer_custom_impl_Yolo.so
43 |
44 | TARGET_OBJS:= $(SRCFILES:.cpp=.o)
45 | TARGET_OBJS:= $(TARGET_OBJS:.cu=.o)
46 |
47 | all: $(TARGET_LIB)
48 |
49 | %.o: %.cpp $(INCS) Makefile
50 | $(CC) -c -o $@ $(CFLAGS) $<
51 |
52 | %.o: %.cu $(INCS) Makefile
53 | $(NVCC) -c -o $@ --compiler-options '-fPIC' $<
54 |
55 | $(TARGET_LIB) : $(TARGET_OBJS)
56 | $(CC) -o $@ $(TARGET_OBJS) $(LFLAGS)
57 |
58 | clean:
59 | rm -rf $(TARGET_LIB)
60 |
--------------------------------------------------------------------------------
/yolo/nvdsinfer_custom_impl_Yolo/kernels.cu:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018-2019 NVIDIA Corporation. All rights reserved.
3 | *
4 | * NVIDIA Corporation and its licensors retain all intellectual property
5 | * and proprietary rights in and to this software, related documentation
6 | * and any modifications thereto. Any use, reproduction, disclosure or
7 | * distribution of this software and related documentation without an express
8 | * license agreement from NVIDIA Corporation is strictly prohibited.
9 | *
10 | */
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | inline __device__ float sigmoidGPU(const float& x) { return 1.0f / (1.0f + __expf(-x)); }
19 |
20 | __global__ void gpuYoloLayerV3(const float* input, float* output, const uint gridSize, const uint numOutputClasses,
21 | const uint numBBoxes)
22 | {
23 | uint x_id = blockIdx.x * blockDim.x + threadIdx.x;
24 | uint y_id = blockIdx.y * blockDim.y + threadIdx.y;
25 | uint z_id = blockIdx.z * blockDim.z + threadIdx.z;
26 |
27 | if ((x_id >= gridSize) || (y_id >= gridSize) || (z_id >= numBBoxes))
28 | {
29 | return;
30 | }
31 |
32 | const int numGridCells = gridSize * gridSize;
33 | const int bbindex = y_id * gridSize + x_id;
34 |
35 | output[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 0)]
36 | = sigmoidGPU(input[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 0)]);
37 |
38 | output[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 1)]
39 | = sigmoidGPU(input[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 1)]);
40 |
41 | output[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 2)]
42 | = __expf(input[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 2)]);
43 |
44 | output[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 3)]
45 | = __expf(input[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 3)]);
46 |
47 | output[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 4)]
48 | = sigmoidGPU(input[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + 4)]);
49 |
50 | for (uint i = 0; i < numOutputClasses; ++i)
51 | {
52 | output[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + (5 + i))]
53 | = sigmoidGPU(input[bbindex + numGridCells * (z_id * (5 + numOutputClasses) + (5 + i))]);
54 | }
55 | }
56 |
57 | cudaError_t cudaYoloLayerV3(const void* input, void* output, const uint& batchSize, const uint& gridSize,
58 | const uint& numOutputClasses, const uint& numBBoxes,
59 | uint64_t outputSize, cudaStream_t stream);
60 |
61 | cudaError_t cudaYoloLayerV3(const void* input, void* output, const uint& batchSize, const uint& gridSize,
62 | const uint& numOutputClasses, const uint& numBBoxes,
63 | uint64_t outputSize, cudaStream_t stream)
64 | {
65 | dim3 threads_per_block(16, 16, 4);
66 | dim3 number_of_blocks((gridSize / threads_per_block.x) + 1,
67 | (gridSize / threads_per_block.y) + 1,
68 | (numBBoxes / threads_per_block.z) + 1);
69 | for (unsigned int batch = 0; batch < batchSize; ++batch)
70 | {
71 | gpuYoloLayerV3<<>>(
72 | reinterpret_cast(input) + (batch * outputSize),
73 | reinterpret_cast(output) + (batch * outputSize), gridSize, numOutputClasses,
74 | numBBoxes);
75 | }
76 | return cudaGetLastError();
77 | }
78 |
--------------------------------------------------------------------------------
/yolo/nvdsinfer_custom_impl_Yolo/nvdsinfer_yolo_engine.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #include "nvdsinfer_custom_impl.h"
24 | #include "nvdsinfer_context.h"
25 | #include "yoloPlugins.h"
26 | #include "yolo.h"
27 |
28 | #include
29 |
30 | #define USE_CUDA_ENGINE_GET_API 1
31 |
32 | static bool getYoloNetworkInfo (NetworkInfo &networkInfo, const NvDsInferContextInitParams* initParams)
33 | {
34 | std::string yoloCfg = initParams->customNetworkConfigFilePath;
35 | std::string yoloType;
36 |
37 | std::transform (yoloCfg.begin(), yoloCfg.end(), yoloCfg.begin(), [] (uint8_t c) {
38 | return std::tolower (c);});
39 |
40 | if (yoloCfg.find("yolov2") != std::string::npos) {
41 | if (yoloCfg.find("yolov2-tiny") != std::string::npos)
42 | yoloType = "yolov2-tiny";
43 | else
44 | yoloType = "yolov2";
45 | } else if (yoloCfg.find("yolov3") != std::string::npos) {
46 | if (yoloCfg.find("yolov3-tiny") != std::string::npos)
47 | yoloType = "yolov3-tiny";
48 | else
49 | yoloType = "yolov3";
50 | } else {
51 | std::cerr << "Yolo type is not defined from config file name:"
52 | << yoloCfg << std::endl;
53 | return false;
54 | }
55 |
56 | networkInfo.networkType = yoloType;
57 | networkInfo.configFilePath = initParams->customNetworkConfigFilePath;
58 | networkInfo.wtsFilePath = initParams->modelFilePath;
59 | networkInfo.deviceType = (initParams->useDLA ? "kDLA" : "kGPU");
60 | networkInfo.inputBlobName = "data";
61 |
62 | if (networkInfo.configFilePath.empty() ||
63 | networkInfo.wtsFilePath.empty()) {
64 | std::cerr << "Yolo config file or weights file is NOT specified."
65 | << std::endl;
66 | return false;
67 | }
68 |
69 | if (!fileExists(networkInfo.configFilePath) ||
70 | !fileExists(networkInfo.wtsFilePath)) {
71 | std::cerr << "Yolo config file or weights file is NOT exist."
72 | << std::endl;
73 | return false;
74 | }
75 |
76 | return true;
77 | }
78 |
79 | #if !USE_CUDA_ENGINE_GET_API
80 | IModelParser* NvDsInferCreateModelParser(
81 | const NvDsInferContextInitParams* initParams) {
82 | NetworkInfo networkInfo;
83 | if (!getYoloNetworkInfo(networkInfo, initParams)) {
84 | return nullptr;
85 | }
86 |
87 | return new Yolo(networkInfo);
88 | }
89 | #else
90 | extern "C"
91 | bool NvDsInferYoloCudaEngineGet(nvinfer1::IBuilder * const builder,
92 | const NvDsInferContextInitParams * const initParams,
93 | nvinfer1::DataType dataType,
94 | nvinfer1::ICudaEngine *& cudaEngine);
95 |
96 | extern "C"
97 | bool NvDsInferYoloCudaEngineGet(nvinfer1::IBuilder * const builder,
98 | const NvDsInferContextInitParams * const initParams,
99 | nvinfer1::DataType dataType,
100 | nvinfer1::ICudaEngine *& cudaEngine)
101 | {
102 | NetworkInfo networkInfo;
103 | if (!getYoloNetworkInfo(networkInfo, initParams)) {
104 | return false;
105 | }
106 |
107 | Yolo yolo(networkInfo);
108 | cudaEngine = yolo.createEngine (builder);
109 | if (cudaEngine == nullptr)
110 | {
111 | std::cerr << "Failed to build cuda engine on "
112 | << networkInfo.configFilePath << std::endl;
113 | return false;
114 | }
115 |
116 | return true;
117 | }
118 | #endif
119 |
--------------------------------------------------------------------------------
/yolo/nvdsinfer_custom_impl_Yolo/nvdsparsebbox_Yolo.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include "nvdsinfer_custom_impl.h"
31 | #include "trt_utils.h"
32 |
33 | static const int NUM_CLASSES_YOLO = 10;
34 |
35 | extern "C" bool NvDsInferParseCustomYoloV3(
36 | std::vector const& outputLayersInfo,
37 | NvDsInferNetworkInfo const& networkInfo,
38 | NvDsInferParseDetectionParams const& detectionParams,
39 | std::vector& objectList);
40 |
41 | extern "C" bool NvDsInferParseCustomYoloV3Tiny(
42 | std::vector const& outputLayersInfo,
43 | NvDsInferNetworkInfo const& networkInfo,
44 | NvDsInferParseDetectionParams const& detectionParams,
45 | std::vector& objectList);
46 |
47 | extern "C" bool NvDsInferParseCustomYoloV2(
48 | std::vector const& outputLayersInfo,
49 | NvDsInferNetworkInfo const& networkInfo,
50 | NvDsInferParseDetectionParams const& detectionParams,
51 | std::vector& objectList);
52 |
53 | extern "C" bool NvDsInferParseCustomYoloV2Tiny(
54 | std::vector const& outputLayersInfo,
55 | NvDsInferNetworkInfo const& networkInfo,
56 | NvDsInferParseDetectionParams const& detectionParams,
57 | std::vector& objectList);
58 |
59 | extern "C" bool NvDsInferParseCustomYoloTLT(
60 | std::vector const& outputLayersInfo,
61 | NvDsInferNetworkInfo const& networkInfo,
62 | NvDsInferParseDetectionParams const& detectionParams,
63 | std::vector& objectList);
64 |
65 | extern "C" bool NvDsInferParseCustomYoloV4(
66 | std::vector const& outputLayersInfo,
67 | NvDsInferNetworkInfo const& networkInfo,
68 | NvDsInferParseDetectionParams const& detectionParams,
69 | std::vector& objectList);
70 |
71 | /* This is a sample bounding box parsing function for the sample YoloV3 detector model */
72 | static NvDsInferParseObjectInfo convertBBox(const float& bx, const float& by, const float& bw,
73 | const float& bh, const int& stride, const uint& netW,
74 | const uint& netH)
75 | {
76 | NvDsInferParseObjectInfo b;
77 | // Restore coordinates to network input resolution
78 | float xCenter = bx * stride;
79 | float yCenter = by * stride;
80 | float x0 = xCenter - bw / 2;
81 | float y0 = yCenter - bh / 2;
82 | float x1 = x0 + bw;
83 | float y1 = y0 + bh;
84 |
85 | x0 = clamp(x0, 0, netW);
86 | y0 = clamp(y0, 0, netH);
87 | x1 = clamp(x1, 0, netW);
88 | y1 = clamp(y1, 0, netH);
89 |
90 | b.left = x0;
91 | b.width = clamp(x1 - x0, 0, netW);
92 | b.top = y0;
93 | b.height = clamp(y1 - y0, 0, netH);
94 |
95 | return b;
96 | }
97 |
98 | static void addBBoxProposal(const float bx, const float by, const float bw, const float bh,
99 | const uint stride, const uint& netW, const uint& netH, const int maxIndex,
100 | const float maxProb, std::vector& binfo)
101 | {
102 | NvDsInferParseObjectInfo bbi = convertBBox(bx, by, bw, bh, stride, netW, netH);
103 | if (bbi.width < 1 || bbi.height < 1) return;
104 |
105 | bbi.detectionConfidence = maxProb;
106 | bbi.classId = maxIndex;
107 | binfo.push_back(bbi);
108 | }
109 |
110 | static std::vector
111 | decodeYoloV2Tensor(
112 | const float* detections, const std::vector &anchors,
113 | const uint gridSizeW, const uint gridSizeH, const uint stride, const uint numBBoxes,
114 | const uint numOutputClasses, const uint& netW,
115 | const uint& netH)
116 | {
117 | std::vector binfo;
118 | for (uint y = 0; y < gridSizeH; ++y) {
119 | for (uint x = 0; x < gridSizeW; ++x) {
120 | for (uint b = 0; b < numBBoxes; ++b)
121 | {
122 | const float pw = anchors[b * 2];
123 | const float ph = anchors[b * 2 + 1];
124 |
125 | const int numGridCells = gridSizeH * gridSizeW;
126 | const int bbindex = y * gridSizeW + x;
127 | const float bx
128 | = x + detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 0)];
129 | const float by
130 | = y + detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 1)];
131 | const float bw
132 | = pw * exp (detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 2)]);
133 | const float bh
134 | = ph * exp (detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 3)]);
135 |
136 | const float objectness
137 | = detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 4)];
138 |
139 | float maxProb = 0.0f;
140 | int maxIndex = -1;
141 |
142 | for (uint i = 0; i < numOutputClasses; ++i)
143 | {
144 | float prob
145 | = (detections[bbindex
146 | + numGridCells * (b * (5 + numOutputClasses) + (5 + i))]);
147 |
148 | if (prob > maxProb)
149 | {
150 | maxProb = prob;
151 | maxIndex = i;
152 | }
153 | }
154 | maxProb = objectness * maxProb;
155 |
156 | addBBoxProposal(bx, by, bw, bh, stride, netW, netH, maxIndex, maxProb, binfo);
157 | }
158 | }
159 | }
160 | return binfo;
161 | }
162 |
163 | static std::vector
164 | decodeYoloV3Tensor(
165 | const float* detections, const std::vector &mask, const std::vector &anchors,
166 | const uint gridSizeW, const uint gridSizeH, const uint stride, const uint numBBoxes,
167 | const uint numOutputClasses, const uint& netW,
168 | const uint& netH)
169 | {
170 | std::vector binfo;
171 | for (uint y = 0; y < gridSizeH; ++y) {
172 | for (uint x = 0; x < gridSizeW; ++x) {
173 | for (uint b = 0; b < numBBoxes; ++b)
174 | {
175 | const float pw = anchors[mask[b] * 2];
176 | const float ph = anchors[mask[b] * 2 + 1];
177 |
178 | const int numGridCells = gridSizeH * gridSizeW;
179 | const int bbindex = y * gridSizeW + x;
180 | const float bx
181 | = x + detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 0)];
182 | const float by
183 | = y + detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 1)];
184 | const float bw
185 | = pw * detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 2)];
186 | const float bh
187 | = ph * detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 3)];
188 |
189 | const float objectness
190 | = detections[bbindex + numGridCells * (b * (5 + numOutputClasses) + 4)];
191 |
192 | float maxProb = 0.0f;
193 | int maxIndex = -1;
194 |
195 | for (uint i = 0; i < numOutputClasses; ++i)
196 | {
197 | float prob
198 | = (detections[bbindex
199 | + numGridCells * (b * (5 + numOutputClasses) + (5 + i))]);
200 |
201 | if (prob > maxProb)
202 | {
203 | maxProb = prob;
204 | maxIndex = i;
205 | }
206 | }
207 | maxProb = objectness * maxProb;
208 |
209 | addBBoxProposal(bx, by, bw, bh, stride, netW, netH, maxIndex, maxProb, binfo);
210 | }
211 | }
212 | }
213 | return binfo;
214 | }
215 |
216 | static inline std::vector
217 | SortLayers(const std::vector & outputLayersInfo)
218 | {
219 | std::vector outLayers;
220 | for (auto const &layer : outputLayersInfo) {
221 | outLayers.push_back (&layer);
222 | }
223 | std::sort(outLayers.begin(), outLayers.end(),
224 | [](const NvDsInferLayerInfo* a, const NvDsInferLayerInfo* b) {
225 | return a->inferDims.d[1] < b->inferDims.d[1];
226 | });
227 | return outLayers;
228 | }
229 |
230 | static bool NvDsInferParseYoloV3(
231 | std::vector const& outputLayersInfo,
232 | NvDsInferNetworkInfo const& networkInfo,
233 | NvDsInferParseDetectionParams const& detectionParams,
234 | std::vector& objectList,
235 | const std::vector &anchors,
236 | const std::vector> &masks)
237 | {
238 | const uint kNUM_BBOXES = 3;
239 |
240 | const std::vector sortedLayers =
241 | SortLayers (outputLayersInfo);
242 |
243 | if (sortedLayers.size() != masks.size()) {
244 | std::cerr << "ERROR: yoloV3 output layer.size: " << sortedLayers.size()
245 | << " does not match mask.size: " << masks.size() << std::endl;
246 | return false;
247 | }
248 |
249 | if (NUM_CLASSES_YOLO != detectionParams.numClassesConfigured)
250 | {
251 | std::cerr << "WARNING: Num classes mismatch. Configured:"
252 | << detectionParams.numClassesConfigured
253 | << ", detected by network: " << NUM_CLASSES_YOLO << std::endl;
254 | }
255 |
256 | std::vector objects;
257 |
258 | for (uint idx = 0; idx < masks.size(); ++idx) {
259 | const NvDsInferLayerInfo &layer = *sortedLayers[idx]; // 255 x Grid x Grid
260 |
261 | assert(layer.inferDims.numDims == 3);
262 | const uint gridSizeH = layer.inferDims.d[1];
263 | const uint gridSizeW = layer.inferDims.d[2];
264 | const uint stride = DIVUP(networkInfo.width, gridSizeW);
265 | assert(stride == DIVUP(networkInfo.height, gridSizeH));
266 |
267 | std::vector outObjs =
268 | decodeYoloV3Tensor((const float*)(layer.buffer), masks[idx], anchors, gridSizeW, gridSizeH, stride, kNUM_BBOXES,
269 | NUM_CLASSES_YOLO, networkInfo.width, networkInfo.height);
270 | objects.insert(objects.end(), outObjs.begin(), outObjs.end());
271 | }
272 |
273 |
274 | objectList = objects;
275 |
276 | return true;
277 | }
278 |
279 | static NvDsInferParseObjectInfo convertBBoxYoloV4(const float& bx1, const float& by1, const float& bx2,
280 | const float& by2, const uint& netW, const uint& netH)
281 | {
282 | NvDsInferParseObjectInfo b;
283 | // Restore coordinates to network input resolution
284 |
285 | float x1 = bx1 * netW;
286 | float y1 = by1 * netH;
287 | float x2 = bx2 * netW;
288 | float y2 = by2 * netH;
289 |
290 | x1 = clamp(x1, 0, netW);
291 | y1 = clamp(y1, 0, netH);
292 | x2 = clamp(x2, 0, netW);
293 | y2 = clamp(y2, 0, netH);
294 |
295 | b.left = x1;
296 | b.width = clamp(x2 - x1, 0, netW);
297 | b.top = y1;
298 | b.height = clamp(y2 - y1, 0, netH);
299 |
300 | return b;
301 | }
302 |
303 | static void addBBoxProposalYoloV4(const float bx, const float by, const float bw, const float bh,
304 | const uint& netW, const uint& netH, const int maxIndex,
305 | const float maxProb, std::vector& binfo)
306 | {
307 | NvDsInferParseObjectInfo bbi = convertBBoxYoloV4(bx, by, bw, bh, netW, netH);
308 | if (bbi.width < 1 || bbi.height < 1) return;
309 |
310 | bbi.detectionConfidence = maxProb;
311 | bbi.classId = maxIndex;
312 | binfo.push_back(bbi);
313 | }
314 |
315 | static std::vector
316 | decodeYoloV4Tensor(
317 | const float* boxes, const float* scores,
318 | const uint num_bboxes, NvDsInferParseDetectionParams const& detectionParams,
319 | const uint& netW, const uint& netH)
320 | {
321 | std::vector binfo;
322 |
323 | uint bbox_location = 0;
324 | uint score_location = 0;
325 | for (uint b = 0; b < num_bboxes; ++b)
326 | {
327 | float bx1 = boxes[bbox_location];
328 | float by1 = boxes[bbox_location + 1];
329 | float bx2 = boxes[bbox_location + 2];
330 | float by2 = boxes[bbox_location + 3];
331 |
332 | float maxProb = 0.0f;
333 | int maxIndex = -1;
334 |
335 | for (uint c = 0; c < detectionParams.numClassesConfigured; ++c)
336 | {
337 | float prob = scores[score_location + c];
338 | if (prob > maxProb)
339 | {
340 | maxProb = prob;
341 | maxIndex = c;
342 | }
343 | }
344 |
345 | if (maxProb > detectionParams.perClassPreclusterThreshold[maxIndex])
346 | {
347 | addBBoxProposalYoloV4(bx1, by1, bx2, by2, netW, netH, maxIndex, maxProb, binfo);
348 | }
349 |
350 | bbox_location += 4;
351 | score_location += detectionParams.numClassesConfigured;
352 | }
353 |
354 | return binfo;
355 | }
356 |
357 |
358 | /* C-linkage to prevent name-mangling */
359 |
360 | static bool NvDsInferParseYoloV4(
361 | std::vector const& outputLayersInfo,
362 | NvDsInferNetworkInfo const& networkInfo,
363 | NvDsInferParseDetectionParams const& detectionParams,
364 | std::vector& objectList)
365 | {
366 | if (NUM_CLASSES_YOLO != detectionParams.numClassesConfigured)
367 | {
368 | std::cerr << "WARNING: Num classes mismatch. Configured:"
369 | << detectionParams.numClassesConfigured
370 | << ", detected by network: " << NUM_CLASSES_YOLO << std::endl;
371 | }
372 |
373 | std::vector objects;
374 |
375 | const NvDsInferLayerInfo &boxes = outputLayersInfo[0]; // num_boxes x 4
376 | const NvDsInferLayerInfo &scores = outputLayersInfo[1]; // num_boxes x num_classes
377 | const NvDsInferLayerInfo &subbox = outputLayersInfo[2];
378 | //* printf("%d\n", subbox.inferDims.numDims);
379 | // 3 dimensional: [num_boxes, 1, 4]
380 | assert(boxes.inferDims.numDims == 3);
381 | // 2 dimensional: [num_boxes, num_classes]
382 | assert(scores.inferDims.numDims == 2);
383 |
384 | // The second dimension should be num_classes
385 | assert(detectionParams.numClassesConfigured == scores.inferDims.d[1]);
386 |
387 | uint num_bboxes = boxes.inferDims.d[0];
388 |
389 | // std::cout << "Network Info: " << networkInfo.height << " " << networkInfo.width << std::endl;
390 |
391 | std::vector outObjs =
392 | decodeYoloV4Tensor(
393 | (const float*)(boxes.buffer), (const float*)(scores.buffer), num_bboxes, detectionParams,
394 | networkInfo.width, networkInfo.height);
395 |
396 | objects.insert(objects.end(), outObjs.begin(), outObjs.end());
397 |
398 | objectList = objects;
399 |
400 | return true;
401 | }
402 |
403 | extern "C" bool NvDsInferParseCustomYoloV4(
404 | std::vector const& outputLayersInfo,
405 | NvDsInferNetworkInfo const& networkInfo,
406 | NvDsInferParseDetectionParams const& detectionParams,
407 | std::vector& objectList)
408 | {
409 | return NvDsInferParseYoloV4 (
410 | outputLayersInfo, networkInfo, detectionParams, objectList);
411 | }
412 |
413 | extern "C" bool NvDsInferParseCustomYoloV3(
414 | std::vector const& outputLayersInfo,
415 | NvDsInferNetworkInfo const& networkInfo,
416 | NvDsInferParseDetectionParams const& detectionParams,
417 | std::vector& objectList)
418 | {
419 | static const std::vector kANCHORS = {
420 | 10.0, 13.0, 16.0, 30.0, 33.0, 23.0, 30.0, 61.0, 62.0,
421 | 45.0, 59.0, 119.0, 116.0, 90.0, 156.0, 198.0, 373.0, 326.0};
422 | static const std::vector> kMASKS = {
423 | {6, 7, 8},
424 | {3, 4, 5},
425 | {0, 1, 2}};
426 | return NvDsInferParseYoloV3 (
427 | outputLayersInfo, networkInfo, detectionParams, objectList,
428 | kANCHORS, kMASKS);
429 | }
430 |
431 | extern "C" bool NvDsInferParseCustomYoloV3Tiny(
432 | std::vector const& outputLayersInfo,
433 | NvDsInferNetworkInfo const& networkInfo,
434 | NvDsInferParseDetectionParams const& detectionParams,
435 | std::vector& objectList)
436 | {
437 | static const std::vector kANCHORS = {
438 | 10, 14, 23, 27, 37, 58, 81, 82, 135, 169, 344, 319};
439 | static const std::vector> kMASKS = {
440 | {3, 4, 5},
441 | //{0, 1, 2}}; // as per output result, select {1,2,3}
442 | {1, 2, 3}};
443 |
444 | return NvDsInferParseYoloV3 (
445 | outputLayersInfo, networkInfo, detectionParams, objectList,
446 | kANCHORS, kMASKS);
447 | }
448 |
449 | static bool NvDsInferParseYoloV2(
450 | std::vector const& outputLayersInfo,
451 | NvDsInferNetworkInfo const& networkInfo,
452 | NvDsInferParseDetectionParams const& detectionParams,
453 | std::vector& objectList)
454 | {
455 | // copy anchor data from yolov2.cfg file
456 | std::vector anchors = {0.57273, 0.677385, 1.87446, 2.06253, 3.33843,
457 | 5.47434, 7.88282, 3.52778, 9.77052, 9.16828};
458 | const uint kNUM_BBOXES = 5;
459 |
460 | if (outputLayersInfo.empty()) {
461 | std::cerr << "Could not find output layer in bbox parsing" << std::endl;;
462 | return false;
463 | }
464 | const NvDsInferLayerInfo &layer = outputLayersInfo[0];
465 |
466 | if (NUM_CLASSES_YOLO != detectionParams.numClassesConfigured)
467 | {
468 | std::cerr << "WARNING: Num classes mismatch. Configured:"
469 | << detectionParams.numClassesConfigured
470 | << ", detected by network: " << NUM_CLASSES_YOLO << std::endl;
471 | }
472 |
473 | assert(layer.inferDims.numDims == 3);
474 | const uint gridSizeH = layer.inferDims.d[1];
475 | const uint gridSizeW = layer.inferDims.d[2];
476 | const uint stride = DIVUP(networkInfo.width, gridSizeW);
477 | assert(stride == DIVUP(networkInfo.height, gridSizeH));
478 | for (auto& anchor : anchors) {
479 | anchor *= stride;
480 | }
481 | std::vector objects =
482 | decodeYoloV2Tensor((const float*)(layer.buffer), anchors, gridSizeW, gridSizeH, stride, kNUM_BBOXES,
483 | NUM_CLASSES_YOLO, networkInfo.width, networkInfo.height);
484 |
485 | objectList = objects;
486 |
487 | return true;
488 | }
489 |
490 | extern "C" bool NvDsInferParseCustomYoloV2(
491 | std::vector const& outputLayersInfo,
492 | NvDsInferNetworkInfo const& networkInfo,
493 | NvDsInferParseDetectionParams const& detectionParams,
494 | std::vector& objectList)
495 | {
496 | return NvDsInferParseYoloV2 (
497 | outputLayersInfo, networkInfo, detectionParams, objectList);
498 | }
499 |
500 | extern "C" bool NvDsInferParseCustomYoloV2Tiny(
501 | std::vector const& outputLayersInfo,
502 | NvDsInferNetworkInfo const& networkInfo,
503 | NvDsInferParseDetectionParams const& detectionParams,
504 | std::vector& objectList)
505 | {
506 | return NvDsInferParseYoloV2 (
507 | outputLayersInfo, networkInfo, detectionParams, objectList);
508 | }
509 |
510 | extern "C" bool NvDsInferParseCustomYoloTLT(
511 | std::vector const& outputLayersInfo,
512 | NvDsInferNetworkInfo const& networkInfo,
513 | NvDsInferParseDetectionParams const& detectionParams,
514 | std::vector& objectList)
515 | {
516 |
517 | if(outputLayersInfo.size() != 4)
518 | {
519 | std::cerr << "Mismatch in the number of output buffers."
520 | << "Expected 4 output buffers, detected in the network :"
521 | << outputLayersInfo.size() << std::endl;
522 | return false;
523 | }
524 |
525 | const int topK = 200;
526 | const int* keepCount = static_cast (outputLayersInfo.at(0).buffer);
527 | const float* boxes = static_cast (outputLayersInfo.at(1).buffer);
528 | const float* scores = static_cast (outputLayersInfo.at(2).buffer);
529 | const float* cls = static_cast (outputLayersInfo.at(3).buffer);
530 |
531 | for (int i = 0; (i < keepCount[0]) && (objectList.size() <= topK); ++i)
532 | {
533 | const float* loc = &boxes[0] + (i * 4);
534 | const float* conf = &scores[0] + i;
535 | const float* cls_id = &cls[0] + i;
536 |
537 | if(conf[0] > 1.001)
538 | continue;
539 |
540 | if((loc[0] < 0) || (loc[1] < 0) || (loc[2] < 0) || (loc[3] < 0))
541 | continue;
542 |
543 | if((loc[0] > networkInfo.width) || (loc[2] > networkInfo.width) || (loc[1] > networkInfo.height) || (loc[3] > networkInfo.width))
544 | continue;
545 |
546 | if((loc[2] < loc[0]) || (loc[3] < loc[1]))
547 | continue;
548 |
549 | if(((loc[3] - loc[1]) > networkInfo.height) || ((loc[2]-loc[0]) > networkInfo.width))
550 | continue;
551 |
552 | NvDsInferParseObjectInfo curObj{static_cast(cls_id[0]),
553 | loc[0],loc[1],(loc[2]-loc[0]),
554 | (loc[3]-loc[1]), conf[0]};
555 | objectList.push_back(curObj);
556 |
557 | }
558 |
559 | return true;
560 | }
561 |
562 | /* Check that the custom function has been defined correctly */
563 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomYoloV4);
564 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomYoloV3);
565 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomYoloV3Tiny);
566 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomYoloV2);
567 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomYoloV2Tiny);
568 | CHECK_CUSTOM_PARSE_FUNC_PROTOTYPE(NvDsInferParseCustomYoloTLT);
569 |
--------------------------------------------------------------------------------
/yolo/nvdsinfer_custom_impl_Yolo/trt_utils.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | #include "trt_utils.h"
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | #include "NvInferPlugin.h"
33 |
34 | static void leftTrim(std::string& s)
35 | {
36 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !isspace(ch); }));
37 | }
38 |
39 | static void rightTrim(std::string& s)
40 | {
41 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !isspace(ch); }).base(), s.end());
42 | }
43 |
44 | std::string trim(std::string s)
45 | {
46 | leftTrim(s);
47 | rightTrim(s);
48 | return s;
49 | }
50 |
51 | float clamp(const float val, const float minVal, const float maxVal)
52 | {
53 | assert(minVal <= maxVal);
54 | return std::min(maxVal, std::max(minVal, val));
55 | }
56 |
57 | bool fileExists(const std::string fileName, bool verbose)
58 | {
59 | if (!std::experimental::filesystem::exists(std::experimental::filesystem::path(fileName)))
60 | {
61 | if (verbose) std::cout << "File does not exist : " << fileName << std::endl;
62 | return false;
63 | }
64 | return true;
65 | }
66 |
67 | std::vector loadWeights(const std::string weightsFilePath, const std::string& networkType)
68 | {
69 | assert(fileExists(weightsFilePath));
70 | std::cout << "Loading pre-trained weights..." << std::endl;
71 | std::ifstream file(weightsFilePath, std::ios_base::binary);
72 | assert(file.good());
73 | std::string line;
74 |
75 | if (networkType == "yolov2")
76 | {
77 | // Remove 4 int32 bytes of data from the stream belonging to the header
78 | file.ignore(4 * 4);
79 | }
80 | else if ((networkType == "yolov3") || (networkType == "yolov3-tiny")
81 | || (networkType == "yolov2-tiny"))
82 | {
83 | // Remove 5 int32 bytes of data from the stream belonging to the header
84 | file.ignore(4 * 5);
85 | }
86 | else
87 | {
88 | std::cout << "Invalid network type" << std::endl;
89 | assert(0);
90 | }
91 |
92 | std::vector weights;
93 | char floatWeight[4];
94 | while (!file.eof())
95 | {
96 | file.read(floatWeight, 4);
97 | assert(file.gcount() == 4);
98 | weights.push_back(*reinterpret_cast(floatWeight));
99 | if (file.peek() == std::istream::traits_type::eof()) break;
100 | }
101 | std::cout << "Loading weights of " << networkType << " complete!"
102 | << std::endl;
103 | std::cout << "Total Number of weights read : " << weights.size() << std::endl;
104 | return weights;
105 | }
106 |
107 | std::string dimsToString(const nvinfer1::Dims d)
108 | {
109 | std::stringstream s;
110 | assert(d.nbDims >= 1);
111 | for (int i = 0; i < d.nbDims - 1; ++i)
112 | {
113 | s << std::setw(4) << d.d[i] << " x";
114 | }
115 | s << std::setw(4) << d.d[d.nbDims - 1];
116 |
117 | return s.str();
118 | }
119 |
120 | void displayDimType(const nvinfer1::Dims d)
121 | {
122 | std::cout << "(" << d.nbDims << ") ";
123 | for (int i = 0; i < d.nbDims; ++i)
124 | {
125 | switch (d.type[i])
126 | {
127 | case nvinfer1::DimensionType::kSPATIAL: std::cout << "kSPATIAL "; break;
128 | case nvinfer1::DimensionType::kCHANNEL: std::cout << "kCHANNEL "; break;
129 | case nvinfer1::DimensionType::kINDEX: std::cout << "kINDEX "; break;
130 | case nvinfer1::DimensionType::kSEQUENCE: std::cout << "kSEQUENCE "; break;
131 | }
132 | }
133 | std::cout << std::endl;
134 | }
135 |
136 | int getNumChannels(nvinfer1::ITensor* t)
137 | {
138 | nvinfer1::Dims d = t->getDimensions();
139 | assert(d.nbDims == 3);
140 |
141 | return d.d[0];
142 | }
143 |
144 | uint64_t get3DTensorVolume(nvinfer1::Dims inputDims)
145 | {
146 | assert(inputDims.nbDims == 3);
147 | return inputDims.d[0] * inputDims.d[1] * inputDims.d[2];
148 | }
149 |
150 | nvinfer1::ILayer* netAddMaxpool(int layerIdx, std::map& block,
151 | nvinfer1::ITensor* input, nvinfer1::INetworkDefinition* network)
152 | {
153 | assert(block.at("type") == "maxpool");
154 | assert(block.find("size") != block.end());
155 | assert(block.find("stride") != block.end());
156 |
157 | int size = std::stoi(block.at("size"));
158 | int stride = std::stoi(block.at("stride"));
159 |
160 | nvinfer1::IPoolingLayer* pool
161 | = network->addPooling(*input, nvinfer1::PoolingType::kMAX, nvinfer1::DimsHW{size, size});
162 | assert(pool);
163 | std::string maxpoolLayerName = "maxpool_" + std::to_string(layerIdx);
164 | pool->setStride(nvinfer1::DimsHW{stride, stride});
165 | pool->setPaddingMode(nvinfer1::PaddingMode::kSAME_UPPER);
166 | pool->setName(maxpoolLayerName.c_str());
167 |
168 | return pool;
169 | }
170 |
171 | nvinfer1::ILayer* netAddConvLinear(int layerIdx, std::map& block,
172 | std::vector& weights,
173 | std::vector& trtWeights, int& weightPtr,
174 | int& inputChannels, nvinfer1::ITensor* input,
175 | nvinfer1::INetworkDefinition* network)
176 | {
177 | assert(block.at("type") == "convolutional");
178 | assert(block.find("batch_normalize") == block.end());
179 | assert(block.at("activation") == "linear");
180 | assert(block.find("filters") != block.end());
181 | assert(block.find("pad") != block.end());
182 | assert(block.find("size") != block.end());
183 | assert(block.find("stride") != block.end());
184 |
185 | int filters = std::stoi(block.at("filters"));
186 | int padding = std::stoi(block.at("pad"));
187 | int kernelSize = std::stoi(block.at("size"));
188 | int stride = std::stoi(block.at("stride"));
189 | int pad;
190 | if (padding)
191 | pad = (kernelSize - 1) / 2;
192 | else
193 | pad = 0;
194 | // load the convolution layer bias
195 | nvinfer1::Weights convBias{nvinfer1::DataType::kFLOAT, nullptr, filters};
196 | float* val = new float[filters];
197 | for (int i = 0; i < filters; ++i)
198 | {
199 | val[i] = weights[weightPtr];
200 | weightPtr++;
201 | }
202 | convBias.values = val;
203 | trtWeights.push_back(convBias);
204 | // load the convolutional layer weights
205 | int size = filters * inputChannels * kernelSize * kernelSize;
206 | nvinfer1::Weights convWt{nvinfer1::DataType::kFLOAT, nullptr, size};
207 | val = new float[size];
208 | for (int i = 0; i < size; ++i)
209 | {
210 | val[i] = weights[weightPtr];
211 | weightPtr++;
212 | }
213 | convWt.values = val;
214 | trtWeights.push_back(convWt);
215 | nvinfer1::IConvolutionLayer* conv = network->addConvolution(
216 | *input, filters, nvinfer1::DimsHW{kernelSize, kernelSize}, convWt, convBias);
217 | assert(conv != nullptr);
218 | std::string convLayerName = "conv_" + std::to_string(layerIdx);
219 | conv->setName(convLayerName.c_str());
220 | conv->setStride(nvinfer1::DimsHW{stride, stride});
221 | conv->setPadding(nvinfer1::DimsHW{pad, pad});
222 |
223 | return conv;
224 | }
225 |
226 | nvinfer1::ILayer* netAddConvBNLeaky(int layerIdx, std::map& block,
227 | std::vector& weights,
228 | std::vector& trtWeights, int& weightPtr,
229 | int& inputChannels, nvinfer1::ITensor* input,
230 | nvinfer1::INetworkDefinition* network)
231 | {
232 | assert(block.at("type") == "convolutional");
233 | assert(block.find("batch_normalize") != block.end());
234 | assert(block.at("batch_normalize") == "1");
235 | assert(block.at("activation") == "leaky");
236 | assert(block.find("filters") != block.end());
237 | assert(block.find("pad") != block.end());
238 | assert(block.find("size") != block.end());
239 | assert(block.find("stride") != block.end());
240 |
241 | bool batchNormalize, bias;
242 | if (block.find("batch_normalize") != block.end())
243 | {
244 | batchNormalize = (block.at("batch_normalize") == "1");
245 | bias = false;
246 | }
247 | else
248 | {
249 | batchNormalize = false;
250 | bias = true;
251 | }
252 | // all conv_bn_leaky layers assume bias is false
253 | assert(batchNormalize == true && bias == false);
254 | UNUSED(batchNormalize);
255 | UNUSED(bias);
256 |
257 | int filters = std::stoi(block.at("filters"));
258 | int padding = std::stoi(block.at("pad"));
259 | int kernelSize = std::stoi(block.at("size"));
260 | int stride = std::stoi(block.at("stride"));
261 | int pad;
262 | if (padding)
263 | pad = (kernelSize - 1) / 2;
264 | else
265 | pad = 0;
266 |
267 | /***** CONVOLUTION LAYER *****/
268 | /*****************************/
269 | // batch norm weights are before the conv layer
270 | // load BN biases (bn_biases)
271 | std::vector bnBiases;
272 | for (int i = 0; i < filters; ++i)
273 | {
274 | bnBiases.push_back(weights[weightPtr]);
275 | weightPtr++;
276 | }
277 | // load BN weights
278 | std::vector bnWeights;
279 | for (int i = 0; i < filters; ++i)
280 | {
281 | bnWeights.push_back(weights[weightPtr]);
282 | weightPtr++;
283 | }
284 | // load BN running_mean
285 | std::vector bnRunningMean;
286 | for (int i = 0; i < filters; ++i)
287 | {
288 | bnRunningMean.push_back(weights[weightPtr]);
289 | weightPtr++;
290 | }
291 | // load BN running_var
292 | std::vector bnRunningVar;
293 | for (int i = 0; i < filters; ++i)
294 | {
295 | // 1e-05 for numerical stability
296 | bnRunningVar.push_back(sqrt(weights[weightPtr] + 1.0e-5));
297 | weightPtr++;
298 | }
299 | // load Conv layer weights (GKCRS)
300 | int size = filters * inputChannels * kernelSize * kernelSize;
301 | nvinfer1::Weights convWt{nvinfer1::DataType::kFLOAT, nullptr, size};
302 | float* val = new float[size];
303 | for (int i = 0; i < size; ++i)
304 | {
305 | val[i] = weights[weightPtr];
306 | weightPtr++;
307 | }
308 | convWt.values = val;
309 | trtWeights.push_back(convWt);
310 | nvinfer1::Weights convBias{nvinfer1::DataType::kFLOAT, nullptr, 0};
311 | trtWeights.push_back(convBias);
312 | nvinfer1::IConvolutionLayer* conv = network->addConvolution(
313 | *input, filters, nvinfer1::DimsHW{kernelSize, kernelSize}, convWt, convBias);
314 | assert(conv != nullptr);
315 | std::string convLayerName = "conv_" + std::to_string(layerIdx);
316 | conv->setName(convLayerName.c_str());
317 | conv->setStride(nvinfer1::DimsHW{stride, stride});
318 | conv->setPadding(nvinfer1::DimsHW{pad, pad});
319 |
320 | /***** BATCHNORM LAYER *****/
321 | /***************************/
322 | size = filters;
323 | // create the weights
324 | nvinfer1::Weights shift{nvinfer1::DataType::kFLOAT, nullptr, size};
325 | nvinfer1::Weights scale{nvinfer1::DataType::kFLOAT, nullptr, size};
326 | nvinfer1::Weights power{nvinfer1::DataType::kFLOAT, nullptr, size};
327 | float* shiftWt = new float[size];
328 | for (int i = 0; i < size; ++i)
329 | {
330 | shiftWt[i]
331 | = bnBiases.at(i) - ((bnRunningMean.at(i) * bnWeights.at(i)) / bnRunningVar.at(i));
332 | }
333 | shift.values = shiftWt;
334 | float* scaleWt = new float[size];
335 | for (int i = 0; i < size; ++i)
336 | {
337 | scaleWt[i] = bnWeights.at(i) / bnRunningVar[i];
338 | }
339 | scale.values = scaleWt;
340 | float* powerWt = new float[size];
341 | for (int i = 0; i < size; ++i)
342 | {
343 | powerWt[i] = 1.0;
344 | }
345 | power.values = powerWt;
346 | trtWeights.push_back(shift);
347 | trtWeights.push_back(scale);
348 | trtWeights.push_back(power);
349 | // Add the batch norm layers
350 | nvinfer1::IScaleLayer* bn = network->addScale(
351 | *conv->getOutput(0), nvinfer1::ScaleMode::kCHANNEL, shift, scale, power);
352 | assert(bn != nullptr);
353 | std::string bnLayerName = "batch_norm_" + std::to_string(layerIdx);
354 | bn->setName(bnLayerName.c_str());
355 | /***** ACTIVATION LAYER *****/
356 | /****************************/
357 | nvinfer1::ITensor* bnOutput = bn->getOutput(0);
358 | nvinfer1::IActivationLayer* leaky = network->addActivation(
359 | *bnOutput, nvinfer1::ActivationType::kLEAKY_RELU);
360 | leaky->setAlpha(0.1);
361 | assert(leaky != nullptr);
362 | std::string leakyLayerName = "leaky_" + std::to_string(layerIdx);
363 | leaky->setName(leakyLayerName.c_str());
364 |
365 | return leaky;
366 | }
367 |
368 | nvinfer1::ILayer* netAddUpsample(int layerIdx, std::map& block,
369 | std::vector& weights,
370 | std::vector& trtWeights, int& inputChannels,
371 | nvinfer1::ITensor* input, nvinfer1::INetworkDefinition* network)
372 | {
373 | assert(block.at("type") == "upsample");
374 | nvinfer1::Dims inpDims = input->getDimensions();
375 | assert(inpDims.nbDims == 3);
376 | assert(inpDims.d[1] == inpDims.d[2]);
377 | int h = inpDims.d[1];
378 | int w = inpDims.d[2];
379 | int stride = std::stoi(block.at("stride"));
380 | // add pre multiply matrix as a constant
381 | nvinfer1::Dims preDims{3,
382 | {1, stride * h, w},
383 | {nvinfer1::DimensionType::kCHANNEL, nvinfer1::DimensionType::kSPATIAL,
384 | nvinfer1::DimensionType::kSPATIAL}};
385 | int size = stride * h * w;
386 | nvinfer1::Weights preMul{nvinfer1::DataType::kFLOAT, nullptr, size};
387 | float* preWt = new float[size];
388 | /* (2*h * w)
389 | [ [1, 0, ..., 0],
390 | [1, 0, ..., 0],
391 | [0, 1, ..., 0],
392 | [0, 1, ..., 0],
393 | ...,
394 | ...,
395 | [0, 0, ..., 1],
396 | [0, 0, ..., 1] ]
397 | */
398 | for (int i = 0, idx = 0; i < h; ++i)
399 | {
400 | for (int s = 0; s < stride; ++s)
401 | {
402 | for (int j = 0; j < w; ++j, ++idx)
403 | {
404 | preWt[idx] = (i == j) ? 1.0 : 0.0;
405 | }
406 | }
407 | }
408 | preMul.values = preWt;
409 | trtWeights.push_back(preMul);
410 | nvinfer1::IConstantLayer* preM = network->addConstant(preDims, preMul);
411 | assert(preM != nullptr);
412 | std::string preLayerName = "preMul_" + std::to_string(layerIdx);
413 | preM->setName(preLayerName.c_str());
414 | // add post multiply matrix as a constant
415 | nvinfer1::Dims postDims{3,
416 | {1, h, stride * w},
417 | {nvinfer1::DimensionType::kCHANNEL, nvinfer1::DimensionType::kSPATIAL,
418 | nvinfer1::DimensionType::kSPATIAL}};
419 | size = stride * h * w;
420 | nvinfer1::Weights postMul{nvinfer1::DataType::kFLOAT, nullptr, size};
421 | float* postWt = new float[size];
422 | /* (h * 2*w)
423 | [ [1, 1, 0, 0, ..., 0, 0],
424 | [0, 0, 1, 1, ..., 0, 0],
425 | ...,
426 | ...,
427 | [0, 0, 0, 0, ..., 1, 1] ]
428 | */
429 | for (int i = 0, idx = 0; i < h; ++i)
430 | {
431 | for (int j = 0; j < stride * w; ++j, ++idx)
432 | {
433 | postWt[idx] = (j / stride == i) ? 1.0 : 0.0;
434 | }
435 | }
436 | postMul.values = postWt;
437 | trtWeights.push_back(postMul);
438 | nvinfer1::IConstantLayer* post_m = network->addConstant(postDims, postMul);
439 | assert(post_m != nullptr);
440 | std::string postLayerName = "postMul_" + std::to_string(layerIdx);
441 | post_m->setName(postLayerName.c_str());
442 | // add matrix multiply layers for upsampling
443 | nvinfer1::IMatrixMultiplyLayer* mm1
444 | = network->addMatrixMultiply(*preM->getOutput(0), nvinfer1::MatrixOperation::kNONE, *input,
445 | nvinfer1::MatrixOperation::kNONE);
446 | assert(mm1 != nullptr);
447 | std::string mm1LayerName = "mm1_" + std::to_string(layerIdx);
448 | mm1->setName(mm1LayerName.c_str());
449 | nvinfer1::IMatrixMultiplyLayer* mm2
450 | = network->addMatrixMultiply(*mm1->getOutput(0), nvinfer1::MatrixOperation::kNONE,
451 | *post_m->getOutput(0), nvinfer1::MatrixOperation::kNONE);
452 | assert(mm2 != nullptr);
453 | std::string mm2LayerName = "mm2_" + std::to_string(layerIdx);
454 | mm2->setName(mm2LayerName.c_str());
455 | return mm2;
456 | }
457 |
458 | void printLayerInfo(std::string layerIndex, std::string layerName, std::string layerInput,
459 | std::string layerOutput, std::string weightPtr)
460 | {
461 | std::cout << std::setw(6) << std::left << layerIndex << std::setw(15) << std::left << layerName;
462 | std::cout << std::setw(20) << std::left << layerInput << std::setw(20) << std::left
463 | << layerOutput;
464 | std::cout << std::setw(6) << std::left << weightPtr << std::endl;
465 | }
466 |
--------------------------------------------------------------------------------
/yolo/nvdsinfer_custom_impl_Yolo/trt_utils.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 |
24 | #ifndef __TRT_UTILS_H__
25 | #define __TRT_UTILS_H__
26 |
27 | #include
28 | #include