/* * Copyright (c) 2018-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. */ #include "deepstream_app.h" #include "deepstream_config_file_parser.h" #include "nvds_version.h" #include #include #include #include #include #include #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_files = 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_files; 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); int start_new_pipeline(); 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-file", 'i', 0, G_OPTION_ARG_FILENAME_ARRAY, &input_files, "Set the input file", 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; 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 (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 (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; } int main () { GOptionContext *ctx = NULL; GOptionGroup *group = NULL; GError *error = NULL; guint i; gint argc = 3; gchar** argv = new gchar*[3]; for(int i = 0; i < 3; i++) { argv[i]= new gchar[1024]; } strcpy(argv[0], "deepstream-app"); strcpy(argv[1], "-c"); //strcpy(argv[2], "/home/js/EndosAssist/models/deepstream_app_config_yoloV4_w.txt"); strcpy(argv[2], "/opt/nvidia/deepstream/deepstream-5.1/sources/objectDetector_Yolo/deepstream_app_config_yoloV3.txt"); // strcpy(argv[3], "/home/js/endoscopy_AI_0630/EndosAssist/opt/nvidia/deepstream/deepstream-5.1/sources/yolov4_c/deepstream_app_config.txt"); std::cout << argv[2]<< std::endl; 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; } num_instances = 1; std::cout << "config file path:" <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_files && input_files[i]) { appCtx[i]->config.multi_source_config[0].uri = g_strdup_printf ("file://%s", input_files[i]); g_free (input_files[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; } if (!appCtx[i]->config.tiled_display_config.enable) continue; 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 && appCtx[i]->config.num_source_sub_bins == 1)) { attr.event_mask = KeyPress; } else { 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; } } } 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 (); start_new_pipeline(); return return_value; } int start_new_pipeline() { GOptionContext *ctx = NULL; GOptionGroup *group = NULL; GError *error = NULL; guint i; ctx = g_option_context_new ("Nvidia DeepStream Demo"); num_instances = 1; 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; } if (!appCtx[i]->config.tiled_display_config.enable) continue; 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 && appCtx[i]->config.num_source_sub_bins == 1)) { attr.event_mask = KeyPress; } else { 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; } } } 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; }