861 lines
25 KiB
C
Executable File
861 lines
25 KiB
C
Executable File
/*
|
|
* 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;
|
|
}
|