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