This commit is contained in:
bi 2022-08-22 15:11:33 +07:00
commit e07e24d3ca
12 changed files with 36329 additions and 0 deletions

77
Makefile Executable file
View File

@ -0,0 +1,77 @@
################################################################################
# Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
################################################################################
CUDA_VER?=
ifeq ($(CUDA_VER),)
$(error "CUDA_VER is not set")
endif
APP:= deepstream-app
CC:=g++
TARGET_DEVICE = $(shell gcc -dumpmachine | cut -f1 -d -)
NVDS_VERSION:=6.0
LIB_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/lib/
APP_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/bin/
ifeq ($(TARGET_DEVICE),aarch64)
CFLAGS:= -DPLATFORM_TEGRA
endif
SRCS:= $(wildcard *.c)
SRCS+= $(wildcard ../../apps-common/src/*.c)
INCS:= $(wildcard *.h)
PKGS:= gstreamer-1.0 gstreamer-video-1.0 x11 json-glib-1.0
OBJS:= $(SRCS:.c=.o)
CFLAGS+= -I./ -I../../apps-common/includes \
-I../../../includes -DDS_VERSION_MINOR=1 -DDS_VERSION_MAJOR=5 \
-I /usr/local/cuda-$(CUDA_VER)/include
LIBS:= -L/usr/local/cuda-$(CUDA_VER)/lib64/ -lcudart
LIBS+= -L$(LIB_INSTALL_DIR) -lnvdsgst_meta -lnvds_meta -lnvdsgst_helper \
-lnvdsgst_smartrecord -lnvds_utils -lnvds_msgbroker -lm \
-lcuda -lgstrtspserver-1.0 -ldl -Wl,-rpath,$(LIB_INSTALL_DIR) \
-pthread
CFLAGS+= $(shell pkg-config --cflags $(PKGS))
LIBS+= $(shell pkg-config --libs $(PKGS))
all: $(APP)
%.o: %.c $(INCS) Makefile
$(CC) -c -o $@ $(CFLAGS) $<
$(APP): $(OBJS) Makefile
$(CC) -o $(APP) $(OBJS) $(LIBS)
install: $(APP)
cp -rv $(APP) $(APP_INSTALL_DIR)
clean:
rm -rf $(OBJS) $(APP)

41
README Executable file
View File

@ -0,0 +1,41 @@
*****************************************************************************
* Copyright (c) 2018 NVIDIA Corporation. All rights reserved.
*
* NVIDIA Corporation and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto. Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA Corporation is strictly prohibited.
*****************************************************************************
*****************************************************************************
deepstream-app
README
*****************************************************************************
Follow these procedures to use the deepstream-app application for native
compilation.
You must have the following development packages installed
GStreamer-1.0
GStreamer-1.0 Base Plugins
GStreamer-1.0 gstrtspserver
X11 client-side library
Glib json library - json-glib-1.0
1. To install these packages, execute the following command:
sudo apt-get install libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev \
libgstrtspserver-1.0-dev libx11-dev libjson-glib-dev
2. Build the sources by executing the command:
$ Set CUDA_VER in the MakeFile as per platform.
For Jetson, CUDA_VER=10.2
For x86, CUDA_VER=11.4
$ sudo make
3. Run the application by executing the command:
./deepstream-app -c <config-file>
Please refer "../../apps-common/includes/deepstream_config.h" to modify
application parameters like maximum number of sources etc.

BIN
deepstream-app Executable file

Binary file not shown.

1704
deepstream_app.c Executable file

File diff suppressed because it is too large Load Diff

214
deepstream_app.h Executable file
View File

@ -0,0 +1,214 @@
/*
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __NVGSTDS_APP_H__
#define __NVGSTDS_APP_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <gst/gst.h>
#include <stdio.h>
#include "deepstream_app_version.h"
#include "deepstream_common.h"
#include "deepstream_config.h"
#include "deepstream_osd.h"
#include "deepstream_perf.h"
#include "deepstream_preprocess.h"
#include "deepstream_primary_gie.h"
#include "deepstream_sinks.h"
#include "deepstream_sources.h"
#include "deepstream_streammux.h"
#include "deepstream_tiled_display.h"
#include "deepstream_dsanalytics.h"
#include "deepstream_dsexample.h"
#include "deepstream_tracker.h"
#include "deepstream_secondary_gie.h"
#include "deepstream_c2d_msg.h"
#include "deepstream_image_save.h"
typedef struct _AppCtx AppCtx;
typedef void (*bbox_generated_callback) (AppCtx *appCtx, GstBuffer *buf,
NvDsBatchMeta *batch_meta, guint index);
typedef gboolean (*overlay_graphics_callback) (AppCtx *appCtx, GstBuffer *buf,
NvDsBatchMeta *batch_meta, guint index);
typedef struct
{
guint index;
gulong all_bbox_buffer_probe_id;
gulong primary_bbox_buffer_probe_id;
gulong fps_buffer_probe_id;
GstElement *bin;
GstElement *tee;
GstElement *msg_conv;
NvDsPreProcessBin preprocess_bin;
NvDsPrimaryGieBin primary_gie_bin;
NvDsOSDBin osd_bin;
NvDsSecondaryGieBin secondary_gie_bin;
NvDsTrackerBin tracker_bin;
NvDsSinkBin sink_bin;
NvDsSinkBin demux_sink_bin;
NvDsDsAnalyticsBin dsanalytics_bin;
NvDsDsExampleBin dsexample_bin;
AppCtx *appCtx;
} NvDsInstanceBin;
typedef struct
{
gulong primary_bbox_buffer_probe_id;
guint bus_id;
GstElement *pipeline;
NvDsSrcParentBin multi_src_bin;
NvDsInstanceBin instance_bins[MAX_SOURCE_BINS];
NvDsInstanceBin demux_instance_bins[MAX_SOURCE_BINS];
NvDsInstanceBin common_elements;
GstElement *tiler_tee;
NvDsTiledDisplayBin tiled_display_bin;
GstElement *demuxer;
NvDsDsExampleBin dsexample_bin;
AppCtx *appCtx;
} NvDsPipeline;
typedef struct
{
gboolean enable_perf_measurement;
gint file_loop;
gint pipeline_recreate_sec;
gboolean source_list_enabled;
guint total_num_sources;
guint num_source_sub_bins;
guint num_secondary_gie_sub_bins;
guint num_sink_sub_bins;
guint num_message_consumers;
guint perf_measurement_interval_sec;
guint sgie_batch_size;
gchar *bbox_dir_path;
gchar *kitti_track_dir_path;
gchar **uri_list;
NvDsSourceConfig multi_source_config[MAX_SOURCE_BINS];
NvDsStreammuxConfig streammux_config;
NvDsOSDConfig osd_config;
NvDsPreProcessConfig preprocess_config;
NvDsGieConfig primary_gie_config;
NvDsTrackerConfig tracker_config;
NvDsGieConfig secondary_gie_sub_bin_config[MAX_SECONDARY_GIE_BINS];
NvDsSinkSubBinConfig sink_bin_sub_bin_config[MAX_SINK_BINS];
NvDsMsgConsumerConfig message_consumer_config[MAX_MESSAGE_CONSUMERS];
NvDsTiledDisplayConfig tiled_display_config;
NvDsDsAnalyticsConfig dsanalytics_config;
NvDsDsExampleConfig dsexample_config;
NvDsSinkMsgConvBrokerConfig msg_conv_config;
NvDsImageSave image_save_config;
} NvDsConfig;
typedef struct
{
gulong frame_num;
} NvDsInstanceData;
struct _AppCtx
{
gboolean version;
gboolean cintr;
gboolean show_bbox_text;
gboolean seeking;
gboolean quit;
gint person_class_id;
gint car_class_id;
gint return_value;
guint index;
gint active_source_index;
GMutex app_lock;
GCond app_cond;
NvDsPipeline pipeline;
NvDsConfig config;
NvDsConfig override_config;
NvDsInstanceData instance_data[MAX_SOURCE_BINS];
NvDsC2DContext *c2d_ctx[MAX_MESSAGE_CONSUMERS];
NvDsAppPerfStructInt perf_struct;
bbox_generated_callback bbox_generated_post_analytics_cb;
bbox_generated_callback all_bbox_generated_cb;
overlay_graphics_callback overlay_graphics_cb;
NvDsFrameLatencyInfo *latency_info;
GMutex latency_lock;
GThread *ota_handler_thread;
guint ota_inotify_fd;
guint ota_watch_desc;
};
/**
* @brief Create DS Anyalytics Pipeline per the appCtx
* configurations
* @param appCtx [IN/OUT] The application context
* providing the config info and where the
* pipeline resources are maintained
* @param bbox_generated_post_analytics_cb [IN] This callback
* shall be triggered after analytics
* (PGIE, Tracker or the last SGIE appearing
* in the pipeline)
* More info: create_common_elements()
* @param all_bbox_generated_cb [IN]
* @param perf_cb [IN]
* @param overlay_graphics_cb [IN]
*/
gboolean create_pipeline (AppCtx * appCtx,
bbox_generated_callback bbox_generated_post_analytics_cb,
bbox_generated_callback all_bbox_generated_cb,
perf_callback perf_cb,
overlay_graphics_callback overlay_graphics_cb);
gboolean pause_pipeline (AppCtx * appCtx);
gboolean resume_pipeline (AppCtx * appCtx);
gboolean seek_pipeline (AppCtx * appCtx, glong milliseconds, gboolean seek_is_relative);
void toggle_show_bbox_text (AppCtx * appCtx);
void destroy_pipeline (AppCtx * appCtx);
void restart_pipeline (AppCtx * appCtx);
/**
* Function to read properties from configuration file.
*
* @param[in] config pointer to @ref NvDsConfig
* @param[in] cfg_file_path path of configuration file.
*
* @return true if parsed successfully.
*/
gboolean
parse_config_file (NvDsConfig * config, gchar * cfg_file_path);
#ifdef __cplusplus
}
#endif
#endif

BIN
deepstream_app.o Normal file

Binary file not shown.

545
deepstream_app_config_parser.c Executable file
View File

@ -0,0 +1,545 @@
/*
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#include "deepstream_app.h"
#include "deepstream_config_file_parser.h"
#define CONFIG_GROUP_APP "application"
#define CONFIG_GROUP_APP_ENABLE_PERF_MEASUREMENT "enable-perf-measurement"
#define CONFIG_GROUP_APP_PERF_MEASUREMENT_INTERVAL "perf-measurement-interval-sec"
#define CONFIG_GROUP_APP_GIE_OUTPUT_DIR "gie-kitti-output-dir"
#define CONFIG_GROUP_APP_GIE_TRACK_OUTPUT_DIR "kitti-track-output-dir"
#define CONFIG_GROUP_TESTS "tests"
#define CONFIG_GROUP_TESTS_FILE_LOOP "file-loop"
#define CONFIG_GROUP_TESTS_PIPELINE_RECREATE_SEC "pipeline-recreate-sec"
#define CONFIG_GROUP_SOURCE_SGIE_BATCH_SIZE "sgie-batch-size"
GST_DEBUG_CATEGORY_EXTERN (APP_CFG_PARSER_CAT);
#define CHECK_ERROR(error) \
if (error) { \
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "%s", error->message); \
goto done; \
}
NvDsSourceConfig global_source_config;
static gboolean
parse_source_list (NvDsConfig * config, GKeyFile * key_file,
gchar * cfg_file_path)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
gsize num_strings;
keys = g_key_file_get_keys (key_file, CONFIG_GROUP_SOURCE_LIST, NULL, &error);
CHECK_ERROR (error);
for (key = keys; *key; key++) {
if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_NUM_SOURCE_BINS)) {
config->total_num_sources =
g_key_file_get_integer (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_NUM_SOURCE_BINS, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_URI_LIST)) {
config->uri_list =
g_key_file_get_string_list (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_URI_LIST, &num_strings, &error);
if (num_strings > MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
goto done;
}
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_SGIE_BATCH_SIZE)) {
config->sgie_batch_size =
g_key_file_get_integer (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_SGIE_BATCH_SIZE, &error);
CHECK_ERROR (error);
} else {
NVGSTDS_WARN_MSG_V ("Unknown key '%s' for group [%s]", *key,
CONFIG_GROUP_SOURCE_LIST);
}
}
if (g_key_file_has_key (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_URI_LIST, &error)) {
if (g_key_file_has_key (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_NUM_SOURCE_BINS, &error)) {
if (num_strings != config->total_num_sources) {
NVGSTDS_ERR_MSG_V ("Mismatch in URIs provided and num-source-bins.");
goto done;
}
} else {
config->total_num_sources = num_strings;
}
}
ret = TRUE;
done:
if (error) {
g_error_free (error);
}
if (keys) {
g_strfreev (keys);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
set_source_all_configs (NvDsConfig * config, gchar * cfg_file_path)
{
guint i = 0;
for (i = 0; i < config->total_num_sources; i++) {
config->multi_source_config[i] = global_source_config;
config->multi_source_config[i].camera_id = i;
if (config->uri_list) {
char *uri = config->uri_list[i];
if (g_str_has_prefix (config->uri_list[i], "file://")) {
config->multi_source_config[i].type = NV_DS_SOURCE_URI;
config->multi_source_config[i].uri = g_strdup (uri + 7);
config->multi_source_config[i].uri =
g_strdup_printf ("file://%s",
get_absolute_file_path (cfg_file_path,
config->multi_source_config[i].uri));
} else if (g_str_has_prefix (config->uri_list[i], "rtsp://")) {
config->multi_source_config[i].type = NV_DS_SOURCE_RTSP;
config->multi_source_config[i].uri = config->uri_list[i];
} else {
gchar *source_id_start_ptr = uri + 4;
gchar *source_id_end_ptr = NULL;
long camera_id =
g_ascii_strtoull (source_id_start_ptr, &source_id_end_ptr, 10);
if (source_id_start_ptr == source_id_end_ptr
|| *source_id_end_ptr != '\0') {
NVGSTDS_ERR_MSG_V
("Incorrect URI for camera source %s. FORMAT: <usb/csi>:<dev_node/sensor_id>",
uri);
return FALSE;
}
if (g_str_has_prefix (config->uri_list[i], "csi:")) {
config->multi_source_config[i].type = NV_DS_SOURCE_CAMERA_CSI;
config->multi_source_config[i].camera_csi_sensor_id = camera_id;
} else if (g_str_has_prefix (config->uri_list[i], "usb:")) {
config->multi_source_config[i].type = NV_DS_SOURCE_CAMERA_V4L2;
config->multi_source_config[i].camera_v4l2_dev_node = camera_id;
} else {
NVGSTDS_ERR_MSG_V ("URI %d (%s) not in proper format.", i,
config->uri_list[i]);
return FALSE;
}
}
}
}
return TRUE;
}
static gboolean
parse_tests (NvDsConfig *config, GKeyFile *key_file)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
keys = g_key_file_get_keys (key_file, CONFIG_GROUP_TESTS, NULL, &error);
CHECK_ERROR (error);
for (key = keys; *key; key++) {
if (!g_strcmp0 (*key, CONFIG_GROUP_TESTS_FILE_LOOP)) {
config->file_loop =
g_key_file_get_integer (key_file, CONFIG_GROUP_TESTS,
CONFIG_GROUP_TESTS_FILE_LOOP, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_TESTS_PIPELINE_RECREATE_SEC)) {
config->pipeline_recreate_sec =
g_key_file_get_integer (key_file, CONFIG_GROUP_TESTS,
CONFIG_GROUP_TESTS_PIPELINE_RECREATE_SEC, &error);
CHECK_ERROR (error);
} else {
NVGSTDS_WARN_MSG_V ("Unknown key '%s' for group [%s]", *key,
CONFIG_GROUP_TESTS);
}
}
ret = TRUE;
done:
if (error) {
g_error_free (error);
}
if (keys) {
g_strfreev (keys);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
parse_app (NvDsConfig *config, GKeyFile *key_file, gchar *cfg_file_path)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
keys = g_key_file_get_keys (key_file, CONFIG_GROUP_APP, NULL, &error);
CHECK_ERROR (error);
for (key = keys; *key; key++) {
if (!g_strcmp0 (*key, CONFIG_GROUP_APP_ENABLE_PERF_MEASUREMENT)) {
config->enable_perf_measurement =
g_key_file_get_integer (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_ENABLE_PERF_MEASUREMENT, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_PERF_MEASUREMENT_INTERVAL)) {
config->perf_measurement_interval_sec =
g_key_file_get_integer (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_PERF_MEASUREMENT_INTERVAL, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_GIE_OUTPUT_DIR)) {
config->bbox_dir_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_GIE_OUTPUT_DIR, &error));
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_GIE_TRACK_OUTPUT_DIR)) {
config->kitti_track_dir_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_GIE_TRACK_OUTPUT_DIR, &error));
CHECK_ERROR (error);
} else {
NVGSTDS_WARN_MSG_V ("Unknown key '%s' for group [%s]", *key,
CONFIG_GROUP_APP);
}
}
ret = TRUE;
done:
if (error) {
g_error_free (error);
}
if (keys) {
g_strfreev (keys);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
gboolean
parse_config_file (NvDsConfig *config, gchar *cfg_file_path)
{
GKeyFile *cfg_file = g_key_file_new ();
GError *error = NULL;
gboolean ret = FALSE;
gchar **groups = NULL;
gchar **group;
guint i, j;
config->source_list_enabled = FALSE;
if (!APP_CFG_PARSER_CAT) {
GST_DEBUG_CATEGORY_INIT (APP_CFG_PARSER_CAT, "NVDS_CFG_PARSER", 0, NULL);
}
if (!g_key_file_load_from_file (cfg_file, cfg_file_path, G_KEY_FILE_NONE,
&error)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to load uri file: %s",
error->message);
goto done;
}
if (g_key_file_has_group (cfg_file, CONFIG_GROUP_SOURCE_LIST)) {
if (!parse_source_list (config, cfg_file, cfg_file_path)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group",
CONFIG_GROUP_SOURCE_LIST);
goto done;
}
config->num_source_sub_bins = config->total_num_sources;
config->source_list_enabled = TRUE;
if (!g_key_file_has_group (cfg_file, CONFIG_GROUP_SOURCE_ALL)) {
NVGSTDS_ERR_MSG_V ("[source-attr-all] group not present.");
ret = FALSE;
goto done;
}
g_key_file_remove_group (cfg_file, CONFIG_GROUP_SOURCE_LIST, &error);
}
if (g_key_file_has_group (cfg_file, CONFIG_GROUP_SOURCE_ALL)) {
if (!parse_source (&global_source_config,
cfg_file, CONFIG_GROUP_SOURCE_ALL, cfg_file_path)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group",
CONFIG_GROUP_SOURCE_LIST);
goto done;
}
if (!set_source_all_configs (config, cfg_file_path)) {
ret = FALSE;
goto done;
}
g_key_file_remove_group (cfg_file, CONFIG_GROUP_SOURCE_ALL, &error);
}
groups = g_key_file_get_groups (cfg_file, NULL);
for (group = groups; *group; group++) {
gboolean parse_err = FALSE;
GST_CAT_DEBUG (APP_CFG_PARSER_CAT, "Parsing group: %s", *group);
if (!g_strcmp0 (*group, CONFIG_GROUP_APP)) {
parse_err = !parse_app (config, cfg_file, cfg_file_path);
}
if (!strncmp (*group, CONFIG_GROUP_SOURCE,
sizeof (CONFIG_GROUP_SOURCE) - 1)) {
if (config->num_source_sub_bins == MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
ret = FALSE;
goto done;
}
gchar *source_id_start_ptr = *group + strlen (CONFIG_GROUP_SOURCE);
gchar *source_id_end_ptr = NULL;
guint index =
g_ascii_strtoull (source_id_start_ptr, &source_id_end_ptr, 10);
if (source_id_start_ptr == source_id_end_ptr
|| *source_id_end_ptr != '\0') {
NVGSTDS_ERR_MSG_V
("Source group \"[%s]\" is not in the form \"[source<%%d>]\"",
*group);
ret = FALSE;
goto done;
}
guint source_id = 0;
if (config->source_list_enabled) {
if (index >= config->total_num_sources) {
NVGSTDS_ERR_MSG_V
("Invalid source group index %d, index cannot exceed %d", index,
config->total_num_sources);
ret = FALSE;
goto done;
}
source_id = index;
NVGSTDS_INFO_MSG_V ("Some parameters to be overwritten for group [%s]",
*group);
} else {
source_id = config->num_source_sub_bins;
}
parse_err = !parse_source (&config->multi_source_config[source_id],
cfg_file, *group, cfg_file_path);
if (config->source_list_enabled
&& config->multi_source_config[source_id].type ==
NV_DS_SOURCE_URI_MULTIPLE) {
NVGSTDS_ERR_MSG_V
("MultiURI support not available if [source-list] is provided");
ret = FALSE;
goto done;
}
if (config->multi_source_config[source_id].enable
&& !config->source_list_enabled) {
config->num_source_sub_bins++;
}
}
if (!g_strcmp0 (*group, CONFIG_GROUP_STREAMMUX)) {
parse_err = !parse_streammux (&config->streammux_config, cfg_file, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_OSD)) {
parse_err = !parse_osd (&config->osd_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_PREPROCESS)) {
parse_err = !parse_preprocess (&config->preprocess_config, cfg_file, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_PRIMARY_GIE)) {
parse_err =
!parse_gie (&config->primary_gie_config, cfg_file,
CONFIG_GROUP_PRIMARY_GIE, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_TRACKER)) {
parse_err = !parse_tracker (&config->tracker_config, cfg_file, cfg_file_path);
}
if (!strncmp (*group, CONFIG_GROUP_SECONDARY_GIE,
sizeof (CONFIG_GROUP_SECONDARY_GIE) - 1)) {
if (config->num_secondary_gie_sub_bins == MAX_SECONDARY_GIE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d secondary GIEs", MAX_SECONDARY_GIE_BINS);
ret = FALSE;
goto done;
}
parse_err =
!parse_gie (&config->secondary_gie_sub_bin_config[config->
num_secondary_gie_sub_bins],
cfg_file, *group, cfg_file_path);
if (config->secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins].enable){
config->num_secondary_gie_sub_bins++;
}
}
if (!strncmp (*group, CONFIG_GROUP_SINK, sizeof (CONFIG_GROUP_SINK) - 1)) {
if (config->num_sink_sub_bins == MAX_SINK_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sinks", MAX_SINK_BINS);
ret = FALSE;
goto done;
}
parse_err =
!parse_sink (&config->
sink_bin_sub_bin_config[config->num_sink_sub_bins], cfg_file, *group,
cfg_file_path);
if (config->
sink_bin_sub_bin_config[config->num_sink_sub_bins].enable){
config->num_sink_sub_bins++;
}
}
if (!strncmp (*group, CONFIG_GROUP_MSG_CONSUMER,
sizeof (CONFIG_GROUP_MSG_CONSUMER) - 1)) {
if (config->num_message_consumers == MAX_MESSAGE_CONSUMERS) {
NVGSTDS_ERR_MSG_V ("App supports max %d consumers", MAX_MESSAGE_CONSUMERS);
ret = FALSE;
goto done;
}
parse_err = !parse_msgconsumer (
&config->message_consumer_config[config->num_message_consumers],
cfg_file, *group, cfg_file_path);
if (config->message_consumer_config[config->num_message_consumers].enable) {
config->num_message_consumers++;
}
}
if (!g_strcmp0 (*group, CONFIG_GROUP_TILED_DISPLAY)) {
parse_err = !parse_tiled_display (&config->tiled_display_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_IMG_SAVE)) {
parse_err = !parse_image_save (&config->image_save_config , cfg_file, *group, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_DSANALYTICS)) {
parse_err = !parse_dsanalytics (&config->dsanalytics_config, cfg_file, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_DSEXAMPLE)) {
parse_err = !parse_dsexample (&config->dsexample_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_MSG_CONVERTER)) {
parse_err = !parse_msgconv (&config->msg_conv_config, cfg_file, *group, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_TESTS)) {
parse_err = !parse_tests (config, cfg_file);
}
if (parse_err) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group", *group);
goto done;
}
}
/* Updating batch size when source list is enabled */
if (config->source_list_enabled == TRUE) {
/* For streammux and pgie, batch size is set to number of sources */
config->streammux_config.batch_size = config->num_source_sub_bins;
config->primary_gie_config.batch_size = config->num_source_sub_bins;
if (config->sgie_batch_size != 0) {
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
config->secondary_gie_sub_bin_config[i].batch_size = config->sgie_batch_size;
}
}
}
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
if (config->secondary_gie_sub_bin_config[i].unique_id ==
config->primary_gie_config.unique_id) {
NVGSTDS_ERR_MSG_V ("Non unique gie ids found");
ret = FALSE;
goto done;
}
}
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
for (j = i + 1; j < config->num_secondary_gie_sub_bins; j++) {
if (config->secondary_gie_sub_bin_config[i].unique_id ==
config->secondary_gie_sub_bin_config[j].unique_id) {
NVGSTDS_ERR_MSG_V ("Non unique gie id %d found",
config->secondary_gie_sub_bin_config[i].unique_id);
ret = FALSE;
goto done;
}
}
}
for (i = 0; i < config->num_source_sub_bins; i++) {
if (config->multi_source_config[i].type == NV_DS_SOURCE_URI_MULTIPLE) {
if (config->multi_source_config[i].num_sources < 1) {
config->multi_source_config[i].num_sources = 1;
}
for (j = 1; j < config->multi_source_config[i].num_sources; j++) {
if (config->num_source_sub_bins == MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
ret = FALSE;
goto done;
}
memcpy (&config->multi_source_config[config->num_source_sub_bins],
&config->multi_source_config[i],
sizeof (config->multi_source_config[i]));
config->multi_source_config[config->num_source_sub_bins].type =
NV_DS_SOURCE_URI;
config->multi_source_config[config->num_source_sub_bins].uri =
g_strdup_printf (config->multi_source_config[config->
num_source_sub_bins].uri, j);
config->num_source_sub_bins++;
}
config->multi_source_config[i].type = NV_DS_SOURCE_URI;
config->multi_source_config[i].uri =
g_strdup_printf (config->multi_source_config[i].uri, 0);
}
}
ret = TRUE;
done:
if (cfg_file) {
g_key_file_free (cfg_file);
}
if (groups) {
g_strfreev (groups);
}
if (error) {
g_error_free (error);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}

Binary file not shown.

860
deepstream_app_main.c Executable file
View File

@ -0,0 +1,860 @@
/*
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "deepstream_app.h"
#include "deepstream_config_file_parser.h"
#include "nvds_version.h"
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#define MAX_INSTANCES 128
#define APP_TITLE "DeepStream"
#define DEFAULT_X_WINDOW_WIDTH 1920
#define DEFAULT_X_WINDOW_HEIGHT 1080
AppCtx *appCtx[MAX_INSTANCES];
static guint cintr = FALSE;
static GMainLoop *main_loop = NULL;
static gchar **cfg_files = NULL;
static gchar **input_uris = NULL;
static gboolean print_version = FALSE;
static gboolean show_bbox_text = FALSE;
static gboolean print_dependencies_version = FALSE;
static gboolean quit = FALSE;
static gint return_value = 0;
static guint num_instances;
static guint num_input_uris;
static GMutex fps_lock;
static gdouble fps[MAX_SOURCE_BINS];
static gdouble fps_avg[MAX_SOURCE_BINS];
static Display *display = NULL;
static Window windows[MAX_INSTANCES] = { 0 };
static GThread *x_event_thread = NULL;
static GMutex disp_lock;
static guint rrow, rcol, rcfg;
static gboolean rrowsel = FALSE, selecting = FALSE;
GST_DEBUG_CATEGORY (NVDS_APP);
GOptionEntry entries[] = {
{"version", 'v', 0, G_OPTION_ARG_NONE, &print_version,
"Print DeepStreamSDK version", NULL}
,
{"tiledtext", 't', 0, G_OPTION_ARG_NONE, &show_bbox_text,
"Display Bounding box labels in tiled mode", NULL}
,
{"version-all", 0, 0, G_OPTION_ARG_NONE, &print_dependencies_version,
"Print DeepStreamSDK and dependencies version", NULL}
,
{"cfg-file", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &cfg_files,
"Set the config file", NULL}
,
{"input-uri", 'i', 0, G_OPTION_ARG_FILENAME_ARRAY, &input_uris,
"Set the input uri (file://stream or rtsp://stream)", NULL}
,
{NULL}
,
};
/**
* Callback function to be called once all inferences (Primary + Secondary)
* are done. This is opportunity to modify content of the metadata.
* e.g. Here Person is being replaced with Man/Woman and corresponding counts
* are being maintained. It should be modified according to network classes
* or can be removed altogether if not required.
*/
static void
all_bbox_generated (AppCtx * appCtx, GstBuffer * buf,
NvDsBatchMeta * batch_meta, guint index)
{
guint num_male = 0;
guint num_female = 0;
guint num_objects[128];
memset (num_objects, 0, sizeof (num_objects));
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
l_obj = l_obj->next) {
NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
if (obj->unique_component_id ==
(gint) appCtx->config.primary_gie_config.unique_id) {
if (obj->class_id >= 0 && obj->class_id < 128) {
num_objects[obj->class_id]++;
}
if (appCtx->person_class_id > -1
&& obj->class_id == appCtx->person_class_id) {
if (strstr (obj->text_params.display_text, "Man")) {
str_replace (obj->text_params.display_text, "Man", "");
str_replace (obj->text_params.display_text, "Person", "Man");
num_male++;
} else if (strstr (obj->text_params.display_text, "Woman")) {
str_replace (obj->text_params.display_text, "Woman", "");
str_replace (obj->text_params.display_text, "Person", "Woman");
num_female++;
}
}
}
}
}
}
/**
* Function to handle program interrupt signal.
* It installs default handler after handling the interrupt.
*/
static void
_intr_handler (int signum)
{
struct sigaction action;
NVGSTDS_ERR_MSG_V ("User Interrupted.. \n");
memset (&action, 0, sizeof (action));
action.sa_handler = SIG_DFL;
sigaction (SIGINT, &action, NULL);
cintr = TRUE;
}
/**
* callback function to print the performance numbers of each stream.
*/
static void
perf_cb (gpointer context, NvDsAppPerfStruct * str)
{
static guint header_print_cnt = 0;
guint i;
AppCtx *appCtx = (AppCtx *) context;
guint numf = str->num_instances;
g_mutex_lock (&fps_lock);
for (i = 0; i < numf; i++) {
fps[i] = str->fps[i];
fps_avg[i] = str->fps_avg[i];
}
if (header_print_cnt % 20 == 0) {
g_print ("\n**PERF: ");
for (i = 0; i < numf; i++) {
g_print ("FPS %d (Avg)\t", i);
}
g_print ("\n");
header_print_cnt = 0;
}
header_print_cnt++;
if (num_instances > 1)
g_print ("PERF(%d): ", appCtx->index);
else
g_print ("**PERF: ");
for (i = 0; i < numf; i++) {
g_print ("%.2f (%.2f)\t", fps[i], fps_avg[i]);
}
g_print ("\n");
g_mutex_unlock (&fps_lock);
}
/**
* Loop function to check the status of interrupts.
* It comes out of loop if application got interrupted.
*/
static gboolean
check_for_interrupt (gpointer data)
{
if (quit) {
return FALSE;
}
if (cintr) {
cintr = FALSE;
quit = TRUE;
g_main_loop_quit (main_loop);
return FALSE;
}
return TRUE;
}
/*
* Function to install custom handler for program interrupt signal.
*/
static void
_intr_setup (void)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_handler = _intr_handler;
sigaction (SIGINT, &action, NULL);
}
static gboolean
kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select (STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
return FD_ISSET (STDIN_FILENO, &rdfs);
}
/*
* Function to enable / disable the canonical mode of terminal.
* In non canonical mode input is available immediately (without the user
* having to type a line-delimiter character).
*/
static void
changemode (int dir)
{
static struct termios oldt, newt;
if (dir == 1) {
tcgetattr (STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON);
tcsetattr (STDIN_FILENO, TCSANOW, &newt);
} else
tcsetattr (STDIN_FILENO, TCSANOW, &oldt);
}
static void
print_runtime_commands (void)
{
g_print ("\nRuntime commands:\n"
"\th: Print this help\n"
"\tq: Quit\n\n" "\tp: Pause\n" "\tr: Resume\n\n");
if (appCtx[0]->config.tiled_display_config.enable) {
g_print
("NOTE: To expand a source in the 2D tiled display and view object details,"
" left-click on the source.\n"
" To go back to the tiled display, right-click anywhere on the window.\n\n");
}
}
/**
* Loop function to check keyboard inputs and status of each pipeline.
*/
static gboolean
event_thread_func (gpointer arg)
{
guint i;
gboolean ret = TRUE;
// Check if all instances have quit
for (i = 0; i < num_instances; i++) {
if (!appCtx[i]->quit)
break;
}
if (i == num_instances) {
quit = TRUE;
g_main_loop_quit (main_loop);
return FALSE;
}
// Check for keyboard input
if (!kbhit ()) {
//continue;
return TRUE;
}
int c = fgetc (stdin);
g_print ("\n");
gint source_id;
GstElement *tiler = appCtx[rcfg]->pipeline.tiled_display_bin.tiler;
if (appCtx[rcfg]->config.tiled_display_config.enable)
{
g_object_get (G_OBJECT (tiler), "show-source", &source_id, NULL);
if (selecting) {
if (rrowsel == FALSE) {
if (c >= '0' && c <= '9') {
rrow = c - '0';
if (rrow < appCtx[rcfg]->config.tiled_display_config.rows){
g_print ("--selecting source row %d--\n", rrow);
rrowsel = TRUE;
}else{
g_print ("--selected source row %d out of bound, reenter\n", rrow);
}
}
} else {
if (c >= '0' && c <= '9') {
unsigned int tile_num_columns = appCtx[rcfg]->config.tiled_display_config.columns;
rcol = c - '0';
if (rcol < tile_num_columns){
selecting = FALSE;
rrowsel = FALSE;
source_id = tile_num_columns * rrow + rcol;
g_print ("--selecting source col %d sou=%d--\n", rcol, source_id);
if (source_id >= (gint) appCtx[rcfg]->config.num_source_sub_bins) {
source_id = -1;
} else {
appCtx[rcfg]->show_bbox_text = TRUE;
appCtx[rcfg]->active_source_index = source_id;
g_object_set (G_OBJECT (tiler), "show-source", source_id, NULL);
}
}else{
g_print ("--selected source col %d out of bound, reenter\n", rcol);
}
}
}
}
}
switch (c) {
case 'h':
print_runtime_commands ();
break;
case 'p':
for (i = 0; i < num_instances; i++)
pause_pipeline (appCtx[i]);
break;
case 'r':
for (i = 0; i < num_instances; i++)
resume_pipeline (appCtx[i]);
break;
case 'q':
quit = TRUE;
g_main_loop_quit (main_loop);
ret = FALSE;
break;
case 'c':
if (appCtx[rcfg]->config.tiled_display_config.enable && selecting == FALSE && source_id == -1) {
g_print ("--selecting config file --\n");
c = fgetc (stdin);
if (c >= '0' && c <= '9') {
rcfg = c - '0';
if (rcfg < num_instances) {
g_print ("--selecting config %d--\n", rcfg);
} else {
g_print ("--selected config file %d out of bound, reenter\n", rcfg);
rcfg = 0;
}
}
}
break;
case 'z':
if (appCtx[rcfg]->config.tiled_display_config.enable && source_id == -1 && selecting == FALSE) {
g_print ("--selecting source --\n");
selecting = TRUE;
} else {
if (!show_bbox_text)
appCtx[rcfg]->show_bbox_text = FALSE;
g_object_set (G_OBJECT (tiler), "show-source", -1, NULL);
appCtx[rcfg]->active_source_index = -1;
selecting = FALSE;
rcfg = 0;
g_print ("--tiled mode --\n");
}
break;
default:
break;
}
return ret;
}
static int
get_source_id_from_coordinates (float x_rel, float y_rel, AppCtx *appCtx)
{
int tile_num_rows = appCtx->config.tiled_display_config.rows;
int tile_num_columns = appCtx->config.tiled_display_config.columns;
int source_id = (int) (x_rel * tile_num_columns);
source_id += ((int) (y_rel * tile_num_rows)) * tile_num_columns;
/* Don't allow clicks on empty tiles. */
if (source_id >= (gint) appCtx->config.num_source_sub_bins)
source_id = -1;
return source_id;
}
/**
* Thread to monitor X window events.
*/
static gpointer
nvds_x_event_thread (gpointer data)
{
g_mutex_lock (&disp_lock);
while (display) {
XEvent e;
guint index;
while (XPending (display)) {
XNextEvent (display, &e);
switch (e.type) {
case ButtonPress:
{
XWindowAttributes win_attr;
XButtonEvent ev = e.xbutton;
gint source_id;
GstElement *tiler;
XGetWindowAttributes (display, ev.window, &win_attr);
for (index = 0; index < MAX_INSTANCES; index++)
if (ev.window == windows[index])
break;
tiler = appCtx[index]->pipeline.tiled_display_bin.tiler;
g_object_get (G_OBJECT (tiler), "show-source", &source_id, NULL);
if (ev.button == Button1 && source_id == -1) {
source_id =
get_source_id_from_coordinates (ev.x * 1.0 / win_attr.width,
ev.y * 1.0 / win_attr.height, appCtx[index]);
if (source_id > -1) {
g_object_set (G_OBJECT (tiler), "show-source", source_id, NULL);
appCtx[index]->active_source_index = source_id;
appCtx[index]->show_bbox_text = TRUE;
}
} else if (ev.button == Button3) {
g_object_set (G_OBJECT (tiler), "show-source", -1, NULL);
appCtx[index]->active_source_index = -1;
if (!show_bbox_text)
appCtx[index]->show_bbox_text = FALSE;
}
}
break;
case KeyRelease:
case KeyPress:
{
KeySym p, r, q;
guint i;
p = XKeysymToKeycode (display, XK_P);
r = XKeysymToKeycode (display, XK_R);
q = XKeysymToKeycode (display, XK_Q);
if (e.xkey.keycode == p) {
for (i = 0; i < num_instances; i++)
pause_pipeline (appCtx[i]);
break;
}
if (e.xkey.keycode == r) {
for (i = 0; i < num_instances; i++)
resume_pipeline (appCtx[i]);
break;
}
if (e.xkey.keycode == q) {
quit = TRUE;
g_main_loop_quit (main_loop);
}
}
break;
case ClientMessage:
{
Atom wm_delete;
for (index = 0; index < MAX_INSTANCES; index++)
if (e.xclient.window == windows[index])
break;
wm_delete = XInternAtom (display, "WM_DELETE_WINDOW", 1);
if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
quit = TRUE;
g_main_loop_quit (main_loop);
}
}
break;
}
}
g_mutex_unlock (&disp_lock);
g_usleep (G_USEC_PER_SEC / 20);
g_mutex_lock (&disp_lock);
}
g_mutex_unlock (&disp_lock);
return NULL;
}
/**
* callback function to add application specific metadata.
* Here it demonstrates how to display the URI of source in addition to
* the text generated after inference.
*/
static gboolean
overlay_graphics (AppCtx * appCtx, GstBuffer * buf,
NvDsBatchMeta * batch_meta, guint index)
{
int srcIndex = appCtx->active_source_index;
if (srcIndex == -1)
return TRUE;
NvDsFrameLatencyInfo *latency_info = NULL;
NvDsDisplayMeta *display_meta =
nvds_acquire_display_meta_from_pool (batch_meta);
display_meta->num_labels = 1;
display_meta->text_params[0].display_text = g_strdup_printf ("Source: %s",
appCtx->config.multi_source_config[srcIndex].uri);
display_meta->text_params[0].y_offset = 20;
display_meta->text_params[0].x_offset = 20;
display_meta->text_params[0].font_params.font_color = (NvOSD_ColorParams) {
0, 1, 0, 1};
display_meta->text_params[0].font_params.font_size =
appCtx->config.osd_config.text_size * 1.5;
display_meta->text_params[0].font_params.font_name = "Serif";
display_meta->text_params[0].set_bg_clr = 1;
display_meta->text_params[0].text_bg_clr = (NvOSD_ColorParams) {
0, 0, 0, 1.0};
if(nvds_enable_latency_measurement) {
g_mutex_lock (&appCtx->latency_lock);
latency_info = &appCtx->latency_info[index];
display_meta->num_labels++;
display_meta->text_params[1].display_text = g_strdup_printf ("Latency: %lf",
latency_info->latency);
g_mutex_unlock (&appCtx->latency_lock);
display_meta->text_params[1].y_offset = (display_meta->text_params[0].y_offset * 2 )+
display_meta->text_params[0].font_params.font_size;
display_meta->text_params[1].x_offset = 20;
display_meta->text_params[1].font_params.font_color = (NvOSD_ColorParams) {
0, 1, 0, 1};
display_meta->text_params[1].font_params.font_size =
appCtx->config.osd_config.text_size * 1.5;
display_meta->text_params[1].font_params.font_name = "Arial";
display_meta->text_params[1].set_bg_clr = 1;
display_meta->text_params[1].text_bg_clr = (NvOSD_ColorParams) {
0, 0, 0, 1.0};
}
nvds_add_display_meta_to_frame (nvds_get_nth_frame_meta (batch_meta->
frame_meta_list, 0), display_meta);
return TRUE;
}
static gboolean
recreate_pipeline_thread_func (gpointer arg)
{
guint i;
gboolean ret = TRUE;
AppCtx *appCtx = (AppCtx *) arg;
g_print ("Destroy pipeline\n");
destroy_pipeline (appCtx);
g_print ("Recreate pipeline\n");
if (!create_pipeline (appCtx, NULL,
all_bbox_generated, perf_cb, overlay_graphics)) {
NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
return_value = -1;
return FALSE;
}
if (gst_element_set_state (appCtx->pipeline.pipeline,
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
NVGSTDS_ERR_MSG_V ("Failed to set pipeline to PAUSED");
return_value = -1;
return FALSE;
}
for (i = 0; i < appCtx->config.num_sink_sub_bins; i++) {
if (!GST_IS_VIDEO_OVERLAY (appCtx->pipeline.instance_bins[0].sink_bin.
sub_bins[i].sink)) {
continue;
}
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (appCtx->pipeline.
instance_bins[0].sink_bin.sub_bins[i].sink),
(gulong) windows[appCtx->index]);
gst_video_overlay_expose (GST_VIDEO_OVERLAY (appCtx->pipeline.
instance_bins[0].sink_bin.sub_bins[i].sink));
}
if (gst_element_set_state (appCtx->pipeline.pipeline,
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
g_print ("\ncan't set pipeline to playing state.\n");
return_value = -1;
return FALSE;
}
return ret;
}
int
main (int argc, char *argv[])
{
GOptionContext *ctx = NULL;
GOptionGroup *group = NULL;
GError *error = NULL;
guint i;
ctx = g_option_context_new ("Nvidia DeepStream Demo");
group = g_option_group_new ("abc", NULL, NULL, NULL, NULL);
g_option_group_add_entries (group, entries);
g_option_context_set_main_group (ctx, group);
g_option_context_add_group (ctx, gst_init_get_option_group ());
GST_DEBUG_CATEGORY_INIT (NVDS_APP, "NVDS_APP", 0, NULL);
if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
NVGSTDS_ERR_MSG_V ("%s", error->message);
return -1;
}
if (print_version) {
g_print ("deepstream-app version %d.%d.%d\n",
NVDS_APP_VERSION_MAJOR, NVDS_APP_VERSION_MINOR, NVDS_APP_VERSION_MICRO);
nvds_version_print ();
return 0;
}
if (print_dependencies_version) {
g_print ("deepstream-app version %d.%d.%d\n",
NVDS_APP_VERSION_MAJOR, NVDS_APP_VERSION_MINOR, NVDS_APP_VERSION_MICRO);
nvds_version_print ();
nvds_dependencies_version_print ();
return 0;
}
if (cfg_files) {
num_instances = g_strv_length (cfg_files);
}
if (input_uris) {
num_input_uris = g_strv_length (input_uris);
}
if (!cfg_files || num_instances == 0) {
NVGSTDS_ERR_MSG_V ("Specify config file with -c option");
return_value = -1;
goto done;
}
for (i = 0; i < num_instances; i++) {
appCtx[i] = (AppCtx*) g_malloc0 (sizeof (AppCtx));
appCtx[i]->person_class_id = -1;
appCtx[i]->car_class_id = -1;
appCtx[i]->index = i;
appCtx[i]->active_source_index = -1;
if (show_bbox_text) {
appCtx[i]->show_bbox_text = TRUE;
}
if (input_uris && input_uris[i]) {
appCtx[i]->config.multi_source_config[0].uri =
g_strdup_printf ("%s", input_uris[i]);
g_free (input_uris[i]);
}
if (!parse_config_file (&appCtx[i]->config, cfg_files[i])) {
NVGSTDS_ERR_MSG_V ("Failed to parse config file '%s'", cfg_files[i]);
appCtx[i]->return_value = -1;
goto done;
}
}
for (i = 0; i < num_instances; i++) {
if (!create_pipeline (appCtx[i], NULL,
all_bbox_generated, perf_cb, overlay_graphics)) {
NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
return_value = -1;
goto done;
}
}
main_loop = g_main_loop_new (NULL, FALSE);
_intr_setup ();
g_timeout_add (400, check_for_interrupt, NULL);
g_mutex_init (&disp_lock);
display = XOpenDisplay (NULL);
for (i = 0; i < num_instances; i++) {
guint j;
if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
NVGSTDS_ERR_MSG_V ("Failed to set pipeline to PAUSED");
return_value = -1;
goto done;
}
for (j = 0; j < appCtx[i]->config.num_sink_sub_bins; j++) {
XTextProperty xproperty;
gchar *title;
guint width, height;
XSizeHints hints = {0};
if (!GST_IS_VIDEO_OVERLAY (appCtx[i]->pipeline.instance_bins[0].
sink_bin.sub_bins[j].sink)) {
continue;
}
if (!display) {
NVGSTDS_ERR_MSG_V ("Could not open X Display");
return_value = -1;
goto done;
}
if (appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.width)
width =
appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.width;
else
width = appCtx[i]->config.tiled_display_config.width;
if (appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.height)
height =
appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.height;
else
height = appCtx[i]->config.tiled_display_config.height;
width = (width) ? width : DEFAULT_X_WINDOW_WIDTH;
height = (height) ? height : DEFAULT_X_WINDOW_HEIGHT;
hints.flags = PPosition | PSize;
hints.x = appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.offset_x;
hints.y = appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.offset_y;
hints.width = width;
hints.height = height;
windows[i] =
XCreateSimpleWindow (display, RootWindow (display,
DefaultScreen (display)), hints.x, hints.y, width, height, 2,
0x00000000, 0x00000000);
XSetNormalHints(display, windows[i], &hints);
if (num_instances > 1)
title = g_strdup_printf (APP_TITLE "-%d", i);
else
title = g_strdup (APP_TITLE);
if (XStringListToTextProperty ((char **) &title, 1, &xproperty) != 0) {
XSetWMName (display, windows[i], &xproperty);
XFree (xproperty.value);
}
XSetWindowAttributes attr = { 0 };
if ((appCtx[i]->config.tiled_display_config.enable &&
appCtx[i]->config.tiled_display_config.rows *
appCtx[i]->config.tiled_display_config.columns == 1) ||
(appCtx[i]->config.tiled_display_config.enable == 0)) {
attr.event_mask = KeyPress;
} else if (appCtx[i]->config.tiled_display_config.enable) {
attr.event_mask = ButtonPress | KeyRelease;
}
XChangeWindowAttributes (display, windows[i], CWEventMask, &attr);
Atom wmDeleteMessage = XInternAtom (display, "WM_DELETE_WINDOW", False);
if (wmDeleteMessage != None) {
XSetWMProtocols (display, windows[i], &wmDeleteMessage, 1);
}
XMapRaised (display, windows[i]);
XSync (display, 1); //discard the events for now
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (appCtx
[i]->pipeline.instance_bins[0].sink_bin.sub_bins[j].sink),
(gulong) windows[i]);
gst_video_overlay_expose (GST_VIDEO_OVERLAY (appCtx[i]->
pipeline.instance_bins[0].sink_bin.sub_bins[j].sink));
if (!x_event_thread)
x_event_thread = g_thread_new ("nvds-window-event-thread",
nvds_x_event_thread, NULL);
}
}
/* Dont try to set playing state if error is observed */
if (return_value != -1) {
for (i = 0; i < num_instances; i++) {
if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
g_print ("\ncan't set pipeline to playing state.\n");
return_value = -1;
goto done;
}
if (appCtx[i]->config.pipeline_recreate_sec)
g_timeout_add_seconds (appCtx[i]->config.pipeline_recreate_sec,
recreate_pipeline_thread_func, appCtx[i]);
}
}
print_runtime_commands ();
changemode (1);
g_timeout_add (40, event_thread_func, NULL);
g_main_loop_run (main_loop);
changemode (0);
done:
g_print ("Quitting\n");
for (i = 0; i < num_instances; i++) {
if (appCtx[i]->return_value == -1)
return_value = -1;
destroy_pipeline (appCtx[i]);
g_mutex_lock (&disp_lock);
if (windows[i])
XDestroyWindow (display, windows[i]);
windows[i] = 0;
g_mutex_unlock (&disp_lock);
g_free (appCtx[i]);
}
g_mutex_lock (&disp_lock);
if (display)
XCloseDisplay (display);
display = NULL;
g_mutex_unlock (&disp_lock);
g_mutex_clear (&disp_lock);
if (main_loop) {
g_main_loop_unref (main_loop);
}
if (ctx) {
g_option_context_free (ctx);
}
if (return_value == 0) {
g_print ("App run successful\n");
} else {
g_print ("App run failed\n");
}
gst_deinit ();
return return_value;
}

BIN
deepstream_app_main.o Normal file

Binary file not shown.

8223
httplib.h Normal file

File diff suppressed because it is too large Load Diff

24665
json.hpp Normal file

File diff suppressed because it is too large Load Diff