init
This commit is contained in:
commit
e07e24d3ca
77
Makefile
Executable file
77
Makefile
Executable 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
41
README
Executable 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
BIN
deepstream-app
Executable file
Binary file not shown.
1704
deepstream_app.c
Executable file
1704
deepstream_app.c
Executable file
File diff suppressed because it is too large
Load Diff
214
deepstream_app.h
Executable file
214
deepstream_app.h
Executable 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
BIN
deepstream_app.o
Normal file
Binary file not shown.
545
deepstream_app_config_parser.c
Executable file
545
deepstream_app_config_parser.c
Executable 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;
|
||||||
|
}
|
BIN
deepstream_app_config_parser.o
Normal file
BIN
deepstream_app_config_parser.o
Normal file
Binary file not shown.
860
deepstream_app_main.c
Executable file
860
deepstream_app_main.c
Executable 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
BIN
deepstream_app_main.o
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user