deepstream_test5_app_main.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  1. /*
  2. * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  17. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. #include <gst/gst.h>
  23. #include <glib.h>
  24. #include <stdio.h>
  25. #include <stdbool.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <sys/stat.h>
  29. #include <sys/time.h>
  30. #include <sys/timeb.h>
  31. #include <sys/types.h>
  32. #include <sys/inotify.h>
  33. #include <time.h>
  34. #include <unistd.h>
  35. #include <errno.h>
  36. #include "deepstream_app.h"
  37. #include "deepstream_config_file_parser.h"
  38. #include "nvds_version.h"
  39. #include <termios.h>
  40. #include <X11/Xlib.h>
  41. #include <X11/Xutil.h>
  42. #include "gstnvdsmeta.h"
  43. #include "nvdsmeta_schema.h"
  44. #include "deepstream_test5_app.h"
  45. /*Analytics header*/
  46. #include "analytics.h"
  47. #define MAX_DISPLAY_LEN (64)
  48. #define MAX_TIME_STAMP_LEN (64)
  49. #define STREAMMUX_BUFFER_POOL_SIZE (16)
  50. /** @{
  51. * Macro's below and corresponding code-blocks are used to demonstrate
  52. * nvmsgconv + Broker Metadata manipulation possibility
  53. */
  54. /**
  55. * IMPORTANT Note 1:
  56. * The code within the check for model_used == APP_CONFIG_ANALYTICS_RESNET_PGIE_3SGIE_TYPE_COLOR_MAKE
  57. * is applicable as sample demo code for
  58. * configs that use resnet PGIE model
  59. * with class ID's: {0, 1, 2, 3} for {CAR, BICYCLE, PERSON, ROADSIGN}
  60. * followed by optional Tracker + 3 X SGIEs (Vehicle-Type,Color,Make)
  61. * only!
  62. * Please comment out the code if using any other
  63. * custom PGIE + SGIE combinations
  64. * and use the code as reference to write your own
  65. * NvDsEventMsgMeta generation code in generate_event_msg_meta()
  66. * function
  67. */
  68. typedef enum
  69. {
  70. APP_CONFIG_ANALYTICS_MODELS_UNKNOWN = 0,
  71. APP_CONFIG_ANALYTICS_RESNET_PGIE_3SGIE_TYPE_COLOR_MAKE = 1,
  72. } AppConfigAnalyticsModel;
  73. #define RESNET10_PGIE_3SGIE_TYPE_COLOR_MAKECLASS_ID_CAR (0)
  74. #ifdef GENERATE_DUMMY_META_EXT
  75. #define RESNET10_PGIE_3SGIE_TYPE_COLOR_MAKECLASS_ID_PERSON (2)
  76. #endif
  77. /** @} */
  78. /* PERSON ID definition. */
  79. #define PERSON_ID 0
  80. #ifdef EN_DEBUG
  81. #define LOGD(...) printf(__VA_ARGS__)
  82. #else
  83. #define LOGD(...)
  84. #endif
  85. static TestAppCtx *testAppCtx;
  86. GST_DEBUG_CATEGORY (NVDS_APP);
  87. /** @{ imported from deepstream-app as is */
  88. #define MAX_INSTANCES 128
  89. #define APP_TITLE "DeepStreamTest5App"
  90. #define DEFAULT_X_WINDOW_WIDTH 1920
  91. #define DEFAULT_X_WINDOW_HEIGHT 1080
  92. AppCtx *appCtx[MAX_INSTANCES];
  93. static guint cintr = FALSE;
  94. static GMainLoop *main_loop = NULL;
  95. static gchar **cfg_files = NULL;
  96. static gchar **input_files = NULL;
  97. static gchar **override_cfg_file = NULL;
  98. static gboolean playback_utc = TRUE;
  99. static gboolean print_version = FALSE;
  100. static gboolean show_bbox_text = TRUE;
  101. static gboolean force_tcp = TRUE;
  102. static gboolean print_dependencies_version = FALSE;
  103. static gboolean quit = FALSE;
  104. static gint return_value = 0;
  105. static guint num_instances;
  106. static guint num_input_files;
  107. static GMutex fps_lock;
  108. static gdouble fps[MAX_SOURCE_BINS];
  109. static gdouble fps_avg[MAX_SOURCE_BINS];
  110. static Display *display = NULL;
  111. static Window windows[MAX_INSTANCES] = { 0 };
  112. static GThread *x_event_thread = NULL;
  113. static GMutex disp_lock;
  114. static guint rrow, rcol, rcfg;
  115. static gboolean rrowsel = FALSE, selecting = FALSE;
  116. static AppConfigAnalyticsModel model_used = APP_CONFIG_ANALYTICS_MODELS_UNKNOWN;
  117. /** @} imported from deepstream-app as is */
  118. GOptionEntry entries[] = {
  119. {"version", 'v', 0, G_OPTION_ARG_NONE, &print_version,
  120. "Print DeepStreamSDK version", NULL}
  121. ,
  122. {"tiledtext", 't', 0, G_OPTION_ARG_NONE, &show_bbox_text,
  123. "Display Bounding box labels in tiled mode", NULL}
  124. ,
  125. {"version-all", 0, 0, G_OPTION_ARG_NONE, &print_dependencies_version,
  126. "Print DeepStreamSDK and dependencies version", NULL}
  127. ,
  128. {"cfg-file", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &cfg_files,
  129. "Set the config file", NULL}
  130. ,
  131. {"override-cfg-file", 'o', 0, G_OPTION_ARG_FILENAME_ARRAY, &override_cfg_file,
  132. "Set the override config file, used for on-the-fly model update feature",
  133. NULL}
  134. ,
  135. {"input-file", 'i', 0, G_OPTION_ARG_FILENAME_ARRAY, &input_files,
  136. "Set the input file", NULL}
  137. ,
  138. {"playback-utc", 'p', 0, G_OPTION_ARG_INT, &playback_utc,
  139. "Playback utc; default=true (base UTC from file/rtsp URL); =false (base UTC from file-URL or RTCP Sender Report)",
  140. NULL}
  141. ,
  142. {"pgie-model-used", 'm', 0, G_OPTION_ARG_INT, &model_used,
  143. "PGIE Model used; {0 - Unknown [DEFAULT]}, {1: Resnet 4-class [Car, Bicycle, Person, Roadsign]}",
  144. NULL}
  145. ,
  146. {"no-force-tcp", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &force_tcp,
  147. "Do not force TCP for RTP transport", NULL}
  148. ,
  149. {NULL}
  150. ,
  151. };
  152. static void
  153. generate_ts_rfc3339 (char *buf, int buf_size)
  154. {
  155. time_t tloc;
  156. struct tm tm_log;
  157. struct timespec ts;
  158. char strmsec[6]; //.nnnZ\0
  159. clock_gettime (CLOCK_REALTIME, &ts);
  160. memcpy (&tloc, (void *) (&ts.tv_sec), sizeof (time_t));
  161. gmtime_r (&tloc, &tm_log);
  162. strftime (buf, buf_size, "%Y-%m-%dT%H:%M:%S", &tm_log);
  163. int ms = ts.tv_nsec / 1000000;
  164. g_snprintf (strmsec, sizeof (strmsec), ".%.3dZ", ms);
  165. strncat (buf, strmsec, buf_size);
  166. }
  167. static GstClockTime
  168. generate_ts_rfc3339_from_ts (char *buf, int buf_size, GstClockTime ts,
  169. gchar * src_uri, gint stream_id)
  170. {
  171. time_t tloc;
  172. struct tm tm_log;
  173. char strmsec[6]; //.nnnZ\0
  174. int ms;
  175. GstClockTime ts_generated;
  176. if (playback_utc
  177. || (appCtx[0]->config.multi_source_config[stream_id].type !=
  178. NV_DS_SOURCE_RTSP)) {
  179. if (testAppCtx->streams[stream_id].meta_number == 0) {
  180. testAppCtx->streams[stream_id].timespec_first_frame =
  181. extract_utc_from_uri (src_uri);
  182. memcpy (&tloc,
  183. (void *) (&testAppCtx->streams[stream_id].timespec_first_frame.
  184. tv_sec), sizeof (time_t));
  185. ms = testAppCtx->streams[stream_id].timespec_first_frame.tv_nsec /
  186. 1000000;
  187. testAppCtx->streams[stream_id].gst_ts_first_frame = ts;
  188. ts_generated =
  189. GST_TIMESPEC_TO_TIME (testAppCtx->streams[stream_id].
  190. timespec_first_frame);
  191. if (ts_generated == 0) {
  192. g_print
  193. ("WARNING; playback mode used with URI [%s] not conforming to timestamp format;"
  194. " check README; using system-time\n", src_uri);
  195. clock_gettime (CLOCK_REALTIME,
  196. &testAppCtx->streams[stream_id].timespec_first_frame);
  197. ts_generated =
  198. GST_TIMESPEC_TO_TIME (testAppCtx->streams[stream_id].
  199. timespec_first_frame);
  200. }
  201. } else {
  202. GstClockTime ts_current =
  203. GST_TIMESPEC_TO_TIME (testAppCtx->
  204. streams[stream_id].timespec_first_frame) + (ts -
  205. testAppCtx->streams[stream_id].gst_ts_first_frame);
  206. struct timespec timespec_current;
  207. GST_TIME_TO_TIMESPEC (ts_current, timespec_current);
  208. memcpy (&tloc, (void *) (&timespec_current.tv_sec), sizeof (time_t));
  209. ms = timespec_current.tv_nsec / 1000000;
  210. ts_generated = ts_current;
  211. }
  212. } else {
  213. /** ts itself is UTC Time in ns */
  214. struct timespec timespec_current;
  215. GST_TIME_TO_TIMESPEC (ts, timespec_current);
  216. memcpy (&tloc, (void *) (&timespec_current.tv_sec), sizeof (time_t));
  217. ms = timespec_current.tv_nsec / 1000000;
  218. ts_generated = ts;
  219. }
  220. gmtime_r (&tloc, &tm_log);
  221. strftime (buf, buf_size, "%Y-%m-%dT%H:%M:%S", &tm_log);
  222. g_snprintf (strmsec, sizeof (strmsec), ".%.3dZ", ms);
  223. strncat (buf, strmsec, buf_size);
  224. LOGD ("ts=%s\n", buf);
  225. return ts_generated;
  226. }
  227. static gpointer
  228. meta_copy_func (gpointer data, gpointer user_data)
  229. {
  230. NvDsUserMeta *user_meta = (NvDsUserMeta *) data;
  231. NvDsEventMsgMeta *srcMeta = (NvDsEventMsgMeta *) user_meta->user_meta_data;
  232. NvDsEventMsgMeta *dstMeta = NULL;
  233. dstMeta = (NvDsEventMsgMeta *)g_memdup ((gpointer)srcMeta, sizeof(NvDsEventMsgMeta));
  234. dstMeta->extMsg = (gpointer)g_memdup(srcMeta->extMsg, sizeof(srcMeta->extMsgSize));
  235. if (srcMeta->ts)
  236. dstMeta->ts = g_strdup (srcMeta->ts);
  237. if (srcMeta->objSignature.size > 0) {
  238. dstMeta->objSignature.signature = g_memdup (srcMeta->objSignature.signature,
  239. srcMeta->objSignature.size);
  240. dstMeta->objSignature.size = srcMeta->objSignature.size;
  241. }
  242. if (srcMeta->objectId) {
  243. dstMeta->objectId = g_strdup (srcMeta->objectId);
  244. }
  245. /*
  246. if (srcMeta->sensorStr) {
  247. dstMeta->sensorStr = g_strdup (srcMeta->sensorStr);
  248. }
  249. */
  250. if (srcMeta->extMsgSize > 0) {
  251. if (srcMeta->objType == NVDS_OBJECT_TYPE_PERSON) {
  252. NvDsPersonObject *srcObj = (NvDsPersonObject *) srcMeta->extMsg;
  253. NvDsPersonObject *obj =
  254. (NvDsPersonObject *) g_malloc0 (sizeof (NvDsPersonObject));
  255. obj->age = srcObj->age;
  256. if (srcObj->gender)
  257. obj->gender = g_strdup (srcObj->gender);
  258. if (srcObj->cap)
  259. obj->cap = g_strdup (srcObj->cap);
  260. if (srcObj->hair)
  261. obj->hair = g_strdup (srcObj->hair);
  262. if (srcObj->apparel)
  263. obj->apparel = g_strdup (srcObj->apparel);
  264. dstMeta->extMsg = obj;
  265. dstMeta->extMsgSize = sizeof (NvDsPersonObject);
  266. }
  267. }
  268. g_print(" %s %d source id: %d, Enter: %d, Exit: %d\n",__func__,__LINE__, dstMeta->source_id, dstMeta->lccum_cnt_entry, dstMeta->lccum_cnt_exit);
  269. return dstMeta;
  270. }
  271. static void
  272. meta_free_func (gpointer data, gpointer user_data)
  273. {
  274. NvDsUserMeta *user_meta = (NvDsUserMeta *) data;
  275. NvDsEventMsgMeta *srcMeta = (NvDsEventMsgMeta *) user_meta->user_meta_data;
  276. user_meta->user_meta_data = NULL;
  277. if (srcMeta->ts) {
  278. g_free (srcMeta->ts);
  279. }
  280. if (srcMeta->objSignature.size > 0) {
  281. g_free (srcMeta->objSignature.signature);
  282. srcMeta->objSignature.size = 0;
  283. }
  284. if (srcMeta->objectId) {
  285. g_free (srcMeta->objectId);
  286. }
  287. if (srcMeta->sensorStr) {
  288. g_free (srcMeta->sensorStr);
  289. }
  290. if (srcMeta->extMsgSize > 0) {
  291. if (srcMeta->objType == NVDS_OBJECT_TYPE_VEHICLE) {
  292. NvDsVehicleObject *obj = (NvDsVehicleObject *) srcMeta->extMsg;
  293. if (obj->type)
  294. g_free (obj->type);
  295. if (obj->color)
  296. g_free (obj->color);
  297. if (obj->make)
  298. g_free (obj->make);
  299. if (obj->model)
  300. g_free (obj->model);
  301. if (obj->license)
  302. g_free (obj->license);
  303. if (obj->region)
  304. g_free (obj->region);
  305. } else if (srcMeta->objType == NVDS_OBJECT_TYPE_PERSON) {
  306. NvDsPersonObject *obj = (NvDsPersonObject *) srcMeta->extMsg;
  307. if (obj->gender)
  308. g_free (obj->gender);
  309. if (obj->cap)
  310. g_free (obj->cap);
  311. if (obj->hair)
  312. g_free (obj->hair);
  313. if (obj->apparel)
  314. g_free (obj->apparel);
  315. }
  316. g_free (srcMeta->extMsg);
  317. srcMeta->extMsg = NULL;
  318. srcMeta->extMsgSize = 0;
  319. }
  320. g_free (srcMeta);
  321. }
  322. #ifdef GENERATE_DUMMY_META_EXT
  323. #endif /**< GENERATE_DUMMY_META_EXT */
  324. static void
  325. generate_event_msg_meta (gpointer data, gint class_id, gboolean useTs,
  326. GstClockTime ts, gchar * src_uri, gint stream_id, guint sensor_id,
  327. AnalyticsUserMeta * obj_params, float scaleW, float scaleH,
  328. NvDsFrameMeta * frame_meta)
  329. {
  330. NvDsEventMsgMeta *meta = (NvDsEventMsgMeta *) data;
  331. GstClockTime ts_generated = 0;
  332. meta->objType = NVDS_OBJECT_TYPE_UNKNOWN; /**< object unknown */
  333. meta->frameId = frame_meta->frame_num;
  334. meta->ts = (gchar *) g_malloc0 (MAX_TIME_STAMP_LEN + 1);
  335. meta->objectId = (gchar *) g_malloc0 (MAX_LABEL_SIZE);
  336. //strncpy (meta->objectId, obj_params->obj_label, MAX_LABEL_SIZE);
  337. /** INFO: This API is called once for every 30 frames (now) */
  338. if (useTs && src_uri) {
  339. ts_generated =
  340. generate_ts_rfc3339_from_ts (meta->ts, MAX_TIME_STAMP_LEN, ts, src_uri,
  341. stream_id);
  342. } else {
  343. generate_ts_rfc3339 (meta->ts, MAX_TIME_STAMP_LEN);
  344. }
  345. /** tracking ID */
  346. meta->trackingId = class_id;
  347. (void) ts_generated;
  348. meta->type = NVDS_EVENT_ENTRY;
  349. meta->objType = NVDS_OBJECT_TYPE_PERSON;
  350. meta->objClassId = PERSON_ID;
  351. meta->occupancy = obj_params->lccum_cnt;
  352. meta->lccum_cnt_entry = obj_params->lcc_cnt_entry;
  353. meta->lccum_cnt_exit = obj_params->lcc_cnt_exit ;
  354. meta->source_id = obj_params->source_id;
  355. // g_print("source id: %d, Enter: %d, Exit: %d\n", meta->source_id, meta->lccum_cnt_entry, meta->lccum_cnt_exit);
  356. }
  357. /*
  358. * Access analytics data.
  359. */
  360. void analytics_custom_parse_nvdsanalytics_meta_data (NvDsMetaList *l_user, AnalyticsUserMeta *data);
  361. /**
  362. * Callback function to be called once all inferences (Primary + Secondary)
  363. * are done. This is opportunity to modify content of the metadata.
  364. * e.g. Here Person is being replaced with Man/Woman and corresponding counts
  365. * are being maintained. It should be modified according to network classes
  366. * or can be removed altogether if not required.
  367. */
  368. static void
  369. bbox_generated_probe_after_analytics (AppCtx * appCtx, GstBuffer * buf,
  370. NvDsBatchMeta * batch_meta, guint index)
  371. {
  372. NvDsObjectMeta *obj_meta = NULL;
  373. GstClockTime buffer_pts = 0;
  374. guint32 stream_id = 0;
  375. if (!appCtx->config.dsanalytics_config.enable){
  376. g_print ("Unable to get nvdsanalytics src pad\n");
  377. return;
  378. }
  379. for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
  380. l_frame = l_frame->next) {
  381. NvDsFrameMeta *frame_meta = l_frame->data;
  382. stream_id = frame_meta->source_id;
  383. GstClockTime buf_ntp_time = 0;
  384. if (playback_utc == FALSE) {
  385. /** Calculate the buffer-NTP-time
  386. * derived from this stream's RTCP Sender Report here:
  387. */
  388. StreamSourceInfo *src_stream = &testAppCtx->streams[stream_id];
  389. buf_ntp_time = frame_meta->ntp_timestamp;
  390. if (buf_ntp_time < src_stream->last_ntp_time) {
  391. NVGSTDS_WARN_MSG_V ("Source %d: NTP timestamps are backward in time."
  392. " Current: %lu previous: %lu", stream_id, buf_ntp_time,
  393. src_stream->last_ntp_time);
  394. }
  395. src_stream->last_ntp_time = buf_ntp_time;
  396. }
  397. GList *l;
  398. NvDsMetaList *l_analyticsuser;
  399. l_analyticsuser = frame_meta->frame_user_meta_list;
  400. AnalyticsUserMeta *user_data =
  401. (AnalyticsUserMeta *) g_malloc0(sizeof(AnalyticsUserMeta));
  402. if (l_analyticsuser != NULL) {
  403. analytics_custom_parse_nvdsanalytics_meta_data(l_analyticsuser, user_data);
  404. }
  405. user_data->source_id = stream_id;
  406. // l_analyticsuser = l_analyticsuser->next;
  407. /* Code from test5 application */
  408. for (l = frame_meta->obj_meta_list; l != NULL; l = l->next) {
  409. /* Now using above information we need to form a text that should
  410. * be displayed on top of the bounding box, so lets form it here. */
  411. obj_meta = (NvDsObjectMeta *) (l->data);
  412. {
  413. /**
  414. * Enable only if this callback is after tiler
  415. * NOTE: Scaling back code-commented
  416. * now that bbox_generated_probe_after_analytics() is post analytics
  417. * (say pgie, tracker or sgie)
  418. * and before tiler, no plugin shall scale metadata and will be
  419. * corresponding to the nvstreammux resolution
  420. */
  421. float scaleW = 0;
  422. float scaleH = 0;
  423. /* Frequency of messages to be send will be based on use case.
  424. * Here message is being sent for first object every 30 frames.
  425. */
  426. buffer_pts = frame_meta->buf_pts;
  427. if (!appCtx->config.streammux_config.pipeline_width
  428. || !appCtx->config.streammux_config.pipeline_height) {
  429. g_print ("invalid pipeline params\n");
  430. return;
  431. }
  432. LOGD ("stream %d==%d [%d X %d]\n", frame_meta->source_id,
  433. frame_meta->pad_index, frame_meta->source_frame_width,
  434. frame_meta->source_frame_height);
  435. scaleW =
  436. (float) frame_meta->source_frame_width /
  437. appCtx->config.streammux_config.pipeline_width;
  438. scaleH =
  439. (float) frame_meta->source_frame_height /
  440. appCtx->config.streammux_config.pipeline_height;
  441. if (playback_utc == FALSE) {
  442. /** Use the buffer-NTP-time derived from this stream's RTCP Sender
  443. * Report here:
  444. */
  445. buffer_pts = buf_ntp_time;
  446. }
  447. /** Generate NvDsEventMsgMeta for every object */
  448. NvDsEventMsgMeta *msg_meta =
  449. (NvDsEventMsgMeta *) g_malloc0 (sizeof (NvDsEventMsgMeta));
  450. generate_event_msg_meta (msg_meta, PERSON_ID, TRUE,
  451. /**< useTs NOTE: Pass FALSE for files without base-timestamp in URI */
  452. buffer_pts,
  453. appCtx->config.multi_source_config[stream_id].uri, stream_id,
  454. appCtx->config.multi_source_config[stream_id].camera_id,
  455. user_data, scaleW, scaleH, frame_meta);
  456. testAppCtx->streams[stream_id].meta_number++;
  457. NvDsUserMeta *user_event_meta =
  458. nvds_acquire_user_meta_from_pool (batch_meta);
  459. if (user_event_meta) {
  460. /*
  461. * Since generated event metadata has custom objects for
  462. * Vehicle / Person which are allocated dynamically, we are
  463. * setting copy and free function to handle those fields when
  464. * metadata copy happens between two components.
  465. */
  466. user_event_meta->user_meta_data = (void *) msg_meta;
  467. user_event_meta->base_meta.batch_meta = batch_meta;
  468. user_event_meta->base_meta.meta_type = NVDS_EVENT_MSG_META;
  469. user_event_meta->base_meta.copy_func =
  470. (NvDsMetaCopyFunc) meta_copy_func;
  471. user_event_meta->base_meta.release_func =
  472. (NvDsMetaReleaseFunc) meta_free_func;
  473. nvds_add_user_meta_to_frame (frame_meta, user_event_meta);
  474. } else {
  475. g_print ("Error in attaching event meta to buffer\n");
  476. }
  477. }
  478. }
  479. testAppCtx->streams[stream_id].frameCount++;
  480. g_free(user_data);
  481. }
  482. }
  483. /** @{ imported from deepstream-app as is */
  484. /**
  485. * Function to handle program interrupt signal.
  486. * It installs default handler after handling the interrupt.
  487. */
  488. static void
  489. _intr_handler (int signum)
  490. {
  491. struct sigaction action;
  492. NVGSTDS_ERR_MSG_V ("User Interrupted.. \n");
  493. memset (&action, 0, sizeof (action));
  494. action.sa_handler = SIG_DFL;
  495. sigaction (SIGINT, &action, NULL);
  496. cintr = TRUE;
  497. }
  498. /**
  499. * callback function to print the performance numbers of each stream.
  500. */
  501. static void
  502. perf_cb (gpointer context, NvDsAppPerfStruct * str)
  503. {
  504. static guint header_print_cnt = 0;
  505. guint i;
  506. AppCtx *appCtx = (AppCtx *) context;
  507. guint numf = str->num_instances;
  508. g_mutex_lock (&fps_lock);
  509. for (i = 0; i < numf; i++) {
  510. fps[i] = str->fps[i];
  511. fps_avg[i] = str->fps_avg[i];
  512. }
  513. if (header_print_cnt % 20 == 0) {
  514. g_print ("\n**PERF: ");
  515. for (i = 0; i < numf; i++) {
  516. g_print ("FPS %d (Avg)\t", i);
  517. }
  518. g_print ("\n");
  519. header_print_cnt = 0;
  520. }
  521. header_print_cnt++;
  522. time_t t = time (NULL);
  523. struct tm *tm = localtime (&t);
  524. printf ("%s", asctime (tm));
  525. if (num_instances > 1)
  526. g_print ("PERF(%d): ", appCtx->index);
  527. else
  528. g_print ("**PERF: ");
  529. for (i = 0; i < numf; i++) {
  530. g_print ("%.2f (%.2f)\t", fps[i], fps_avg[i]);
  531. }
  532. g_print ("\n");
  533. g_mutex_unlock (&fps_lock);
  534. }
  535. /**
  536. * Loop function to check the status of interrupts.
  537. * It comes out of loop if application got interrupted.
  538. */
  539. static gboolean
  540. check_for_interrupt (gpointer data)
  541. {
  542. if (quit) {
  543. return FALSE;
  544. }
  545. if (cintr) {
  546. cintr = FALSE;
  547. quit = TRUE;
  548. g_main_loop_quit (main_loop);
  549. return FALSE;
  550. }
  551. return TRUE;
  552. }
  553. /*
  554. * Function to install custom handler for program interrupt signal.
  555. */
  556. static void
  557. _intr_setup (void)
  558. {
  559. struct sigaction action;
  560. memset (&action, 0, sizeof (action));
  561. action.sa_handler = _intr_handler;
  562. sigaction (SIGINT, &action, NULL);
  563. }
  564. static gboolean
  565. kbhit (void)
  566. {
  567. struct timeval tv;
  568. fd_set rdfs;
  569. tv.tv_sec = 0;
  570. tv.tv_usec = 0;
  571. FD_ZERO (&rdfs);
  572. FD_SET (STDIN_FILENO, &rdfs);
  573. select (STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
  574. return FD_ISSET (STDIN_FILENO, &rdfs);
  575. }
  576. /*
  577. * Function to enable / disable the canonical mode of terminal.
  578. * In non canonical mode input is available immediately (without the user
  579. * having to type a line-delimiter character).
  580. */
  581. static void
  582. changemode (int dir)
  583. {
  584. static struct termios oldt, newt;
  585. if (dir == 1) {
  586. tcgetattr (STDIN_FILENO, &oldt);
  587. newt = oldt;
  588. newt.c_lflag &= ~(ICANON);
  589. tcsetattr (STDIN_FILENO, TCSANOW, &newt);
  590. } else
  591. tcsetattr (STDIN_FILENO, TCSANOW, &oldt);
  592. }
  593. static void
  594. print_runtime_commands (void)
  595. {
  596. g_print ("\nRuntime commands:\n"
  597. "\th: Print this help\n"
  598. "\tq: Quit\n\n" "\tp: Pause\n" "\tr: Resume\n\n");
  599. if (appCtx[0]->config.tiled_display_config.enable) {
  600. g_print
  601. ("NOTE: To expand a source in the 2D tiled display and view object details,"
  602. " left-click on the source.\n"
  603. " To go back to the tiled display, right-click anywhere on the window.\n\n");
  604. }
  605. }
  606. /**
  607. * Loop function to check keyboard inputs and status of each pipeline.
  608. */
  609. static gboolean
  610. event_thread_func (gpointer arg)
  611. {
  612. guint i;
  613. gboolean ret = TRUE;
  614. // Check if all instances have quit
  615. for (i = 0; i < num_instances; i++) {
  616. if (!appCtx[i]->quit)
  617. break;
  618. }
  619. if (i == num_instances) {
  620. quit = TRUE;
  621. g_main_loop_quit (main_loop);
  622. return FALSE;
  623. }
  624. // Check for keyboard input
  625. if (!kbhit ()) {
  626. //continue;
  627. return TRUE;
  628. }
  629. int c = fgetc (stdin);
  630. g_print ("\n");
  631. gint source_id;
  632. GstElement *tiler = appCtx[rcfg]->pipeline.tiled_display_bin.tiler;
  633. g_object_get (G_OBJECT (tiler), "show-source", &source_id, NULL);
  634. if (selecting) {
  635. if (rrowsel == FALSE) {
  636. if (c >= '0' && c <= '9') {
  637. rrow = c - '0';
  638. g_print ("--selecting source row %d--\n", rrow);
  639. rrowsel = TRUE;
  640. }
  641. } else {
  642. if (c >= '0' && c <= '9') {
  643. int tile_num_columns = appCtx[rcfg]->config.tiled_display_config.columns;
  644. rcol = c - '0';
  645. selecting = FALSE;
  646. rrowsel = FALSE;
  647. source_id = tile_num_columns * rrow + rcol;
  648. g_print ("--selecting source col %d sou=%d--\n", rcol, source_id);
  649. if (source_id >= (gint) appCtx[rcfg]->config.num_source_sub_bins) {
  650. source_id = -1;
  651. } else {
  652. appCtx[rcfg]->show_bbox_text = TRUE;
  653. appCtx[rcfg]->active_source_index = source_id;
  654. g_object_set (G_OBJECT (tiler), "show-source", source_id, NULL);
  655. }
  656. }
  657. }
  658. }
  659. switch (c) {
  660. case 'h':
  661. print_runtime_commands ();
  662. break;
  663. case 'p':
  664. for (i = 0; i < num_instances; i++)
  665. pause_pipeline (appCtx[i]);
  666. break;
  667. case 'r':
  668. for (i = 0; i < num_instances; i++)
  669. resume_pipeline (appCtx[i]);
  670. break;
  671. case 'q':
  672. quit = TRUE;
  673. g_main_loop_quit (main_loop);
  674. ret = FALSE;
  675. break;
  676. case 'c':
  677. if (selecting == FALSE && source_id == -1) {
  678. g_print("--selecting config file --\n");
  679. c = fgetc(stdin);
  680. if (c >= '0' && c <= '9') {
  681. rcfg = c - '0';
  682. if (rcfg < num_instances) {
  683. g_print("--selecting config %d--\n", rcfg);
  684. } else {
  685. g_print("--selected config file %d out of bound, reenter\n", rcfg);
  686. rcfg = 0;
  687. }
  688. }
  689. }
  690. break;
  691. case 'z':
  692. if (source_id == -1 && selecting == FALSE) {
  693. g_print ("--selecting source --\n");
  694. selecting = TRUE;
  695. } else {
  696. if (!show_bbox_text) {
  697. GstElement *nvosd = appCtx[rcfg]->pipeline.instance_bins[0].osd_bin.nvosd;
  698. g_object_set (G_OBJECT (nvosd), "display-text", FALSE, NULL);
  699. g_object_set (G_OBJECT (tiler), "show-source", -1, NULL);
  700. }
  701. appCtx[rcfg]->active_source_index = -1;
  702. selecting = FALSE;
  703. rcfg = 0;
  704. g_print("--tiled mode --\n");
  705. }
  706. break;
  707. default:
  708. break;
  709. }
  710. return ret;
  711. }
  712. static int
  713. get_source_id_from_coordinates (float x_rel, float y_rel, AppCtx *appCtx)
  714. {
  715. int tile_num_rows = appCtx->config.tiled_display_config.rows;
  716. int tile_num_columns = appCtx->config.tiled_display_config.columns;
  717. int source_id = (int) (x_rel * tile_num_columns);
  718. source_id += ((int) (y_rel * tile_num_rows)) * tile_num_columns;
  719. /* Don't allow clicks on empty tiles. */
  720. if (source_id >= (gint) appCtx->config.num_source_sub_bins)
  721. source_id = -1;
  722. return source_id;
  723. }
  724. /**
  725. * Thread to monitor X window events.
  726. */
  727. static gpointer
  728. nvds_x_event_thread (gpointer data)
  729. {
  730. g_mutex_lock (&disp_lock);
  731. while (display) {
  732. XEvent e;
  733. guint index;
  734. while (XPending (display)) {
  735. XNextEvent (display, &e);
  736. switch (e.type) {
  737. case ButtonPress:
  738. {
  739. XWindowAttributes win_attr;
  740. XButtonEvent ev = e.xbutton;
  741. gint source_id;
  742. GstElement *tiler;
  743. XGetWindowAttributes (display, ev.window, &win_attr);
  744. for (index = 0; index < MAX_INSTANCES; index++)
  745. if (ev.window == windows[index])
  746. break;
  747. tiler = appCtx[index]->pipeline.tiled_display_bin.tiler;
  748. g_object_get (G_OBJECT (tiler), "show-source", &source_id, NULL);
  749. if (ev.button == Button1 && source_id == -1) {
  750. source_id =
  751. get_source_id_from_coordinates (ev.x * 1.0 / win_attr.width,
  752. ev.y * 1.0 / win_attr.height, appCtx[index]);
  753. if (source_id > -1) {
  754. g_object_set (G_OBJECT (tiler), "show-source", source_id, NULL);
  755. appCtx[index]->active_source_index = source_id;
  756. appCtx[index]->show_bbox_text = TRUE;
  757. GstElement *nvosd = appCtx[index]->pipeline.instance_bins[0].osd_bin.nvosd;
  758. g_object_set (G_OBJECT (nvosd), "display-text", TRUE, NULL);
  759. }
  760. } else if (ev.button == Button3) {
  761. g_object_set (G_OBJECT (tiler), "show-source", -1, NULL);
  762. appCtx[index]->active_source_index = -1;
  763. if (!show_bbox_text) {
  764. appCtx[index]->show_bbox_text = FALSE;
  765. GstElement *nvosd = appCtx[index]->pipeline.instance_bins[0].osd_bin.nvosd;
  766. g_object_set (G_OBJECT (nvosd), "display-text", FALSE, NULL);
  767. }
  768. }
  769. }
  770. break;
  771. case KeyRelease:
  772. {
  773. KeySym p, r, q;
  774. guint i;
  775. p = XKeysymToKeycode (display, XK_P);
  776. r = XKeysymToKeycode (display, XK_R);
  777. q = XKeysymToKeycode (display, XK_Q);
  778. if (e.xkey.keycode == p) {
  779. for (i = 0; i < num_instances; i++)
  780. pause_pipeline (appCtx[i]);
  781. break;
  782. }
  783. if (e.xkey.keycode == r) {
  784. for (i = 0; i < num_instances; i++)
  785. resume_pipeline (appCtx[i]);
  786. break;
  787. }
  788. if (e.xkey.keycode == q) {
  789. quit = TRUE;
  790. g_main_loop_quit (main_loop);
  791. }
  792. }
  793. break;
  794. case ClientMessage:
  795. {
  796. Atom wm_delete;
  797. for (index = 0; index < MAX_INSTANCES; index++)
  798. if (e.xclient.window == windows[index])
  799. break;
  800. wm_delete = XInternAtom (display, "WM_DELETE_WINDOW", 1);
  801. if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
  802. quit = TRUE;
  803. g_main_loop_quit (main_loop);
  804. }
  805. }
  806. break;
  807. }
  808. }
  809. g_mutex_unlock (&disp_lock);
  810. g_usleep (G_USEC_PER_SEC / 20);
  811. g_mutex_lock (&disp_lock);
  812. }
  813. g_mutex_unlock (&disp_lock);
  814. return NULL;
  815. }
  816. /**
  817. * callback function to add application specific metadata.
  818. * Here it demonstrates how to display the URI of source in addition to
  819. * the text generated after inference.
  820. */
  821. static gboolean
  822. overlay_graphics (AppCtx * appCtx, GstBuffer * buf,
  823. NvDsBatchMeta * batch_meta, guint index)
  824. {
  825. return TRUE;
  826. }
  827. /** @} imported from deepstream-app as is */
  828. int
  829. main (int argc, char *argv[])
  830. {
  831. testAppCtx = (TestAppCtx *) g_malloc0 (sizeof (TestAppCtx));
  832. GOptionContext *ctx = NULL;
  833. GOptionGroup *group = NULL;
  834. GError *error = NULL;
  835. guint i;
  836. ctx = g_option_context_new ("Nvidia DeepStream Test5");
  837. group = g_option_group_new ("abc", NULL, NULL, NULL, NULL);
  838. g_option_group_add_entries (group, entries);
  839. g_option_context_set_main_group (ctx, group);
  840. g_option_context_add_group (ctx, gst_init_get_option_group ());
  841. GST_DEBUG_CATEGORY_INIT (NVDS_APP, "NVDS_APP", 0, NULL);
  842. if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
  843. NVGSTDS_ERR_MSG_V ("%s", error->message);
  844. g_print ("%s",g_option_context_get_help (ctx, TRUE, NULL));
  845. return -1;
  846. }
  847. if (print_version) {
  848. g_print ("deepstream-test5-app version %d.%d.%d\n",
  849. NVDS_APP_VERSION_MAJOR, NVDS_APP_VERSION_MINOR, NVDS_APP_VERSION_MICRO);
  850. return 0;
  851. }
  852. if (print_dependencies_version) {
  853. g_print ("deepstream-test5-app version %d.%d.%d\n",
  854. NVDS_APP_VERSION_MAJOR, NVDS_APP_VERSION_MINOR, NVDS_APP_VERSION_MICRO);
  855. return 0;
  856. }
  857. if (cfg_files) {
  858. num_instances = g_strv_length (cfg_files);
  859. }
  860. if (input_files) {
  861. num_input_files = g_strv_length (input_files);
  862. }
  863. if (!cfg_files || num_instances == 0) {
  864. NVGSTDS_ERR_MSG_V ("Specify config file with -c option");
  865. return_value = -1;
  866. goto done;
  867. }
  868. for (i = 0; i < num_instances; i++) {
  869. appCtx[i] = (AppCtx *) g_malloc0 (sizeof (AppCtx));
  870. appCtx[i]->person_class_id = -1;
  871. appCtx[i]->car_class_id = -1;
  872. appCtx[i]->index = i;
  873. appCtx[i]->active_source_index = -1;
  874. if (show_bbox_text) {
  875. appCtx[i]->show_bbox_text = TRUE;
  876. }
  877. if (input_files && input_files[i]) {
  878. appCtx[i]->config.multi_source_config[0].uri =
  879. g_strdup_printf ("file://%s", input_files[i]);
  880. g_free (input_files[i]);
  881. }
  882. if (!parse_config_file (&appCtx[i]->config, cfg_files[i])) {
  883. NVGSTDS_ERR_MSG_V ("Failed to parse config file '%s'", cfg_files[i]);
  884. appCtx[i]->return_value = -1;
  885. goto done;
  886. }
  887. if (override_cfg_file && override_cfg_file[i]) {
  888. if (!g_file_test (override_cfg_file[i],
  889. G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))
  890. {
  891. g_print ("Override file %s does not exist, quitting...\n",
  892. override_cfg_file[i]);
  893. appCtx[i]->return_value = -1;
  894. goto done;
  895. }
  896. }
  897. }
  898. for (i = 0; i < num_instances; i++) {
  899. for (guint j = 0; j < appCtx[i]->config.num_source_sub_bins; j++) {
  900. /** Force the source (applicable only if RTSP)
  901. * to use TCP for RTP/RTCP channels.
  902. * forcing TCP to avoid problems with UDP port usage from within docker-
  903. * container.
  904. * The UDP RTCP channel when run within docker had issues receiving
  905. * RTCP Sender Reports from server
  906. */
  907. if (force_tcp)
  908. appCtx[i]->config.multi_source_config[j].select_rtp_protocol = 0x04;
  909. }
  910. if (!create_pipeline (appCtx[i], bbox_generated_probe_after_analytics,
  911. NULL, perf_cb, overlay_graphics)) {
  912. NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
  913. return_value = -1;
  914. goto done;
  915. }
  916. /* if (appCtx[i]->config.dsanalytics_config.enable){
  917. GstPad *src_pad = NULL;
  918. GstElement *nvdsanalytics = appCtx[i]->pipeline.common_elements.dsanalytics_bin.elem_dsanalytics;
  919. src_pad = gst_element_get_static_pad (nvdsanalytics, "src");
  920. if (!src_pad)
  921. g_print ("Unable to get nvdsanalytics src pad\n");
  922. else
  923. {
  924. gst_pad_add_probe (src_pad, GST_PAD_PROBE_TYPE_BUFFER,
  925. nvdsanalytics_src_pad_buffer_probe, NULL, NULL);
  926. gst_object_unref (src_pad);
  927. }
  928. }*/
  929. /** Now add probe to RTPSession plugin src pad */
  930. for (guint j = 0; j < appCtx[i]->pipeline.multi_src_bin.num_bins; j++) {
  931. testAppCtx->streams[j].id = j;
  932. }
  933. /** In test5 app, as we could have several sources connected
  934. * for a typical IoT use-case, raising the nvstreammux's
  935. * buffer-pool-size to 16 */
  936. g_object_set (appCtx[i]->pipeline.multi_src_bin.streammux,
  937. "buffer-pool-size", STREAMMUX_BUFFER_POOL_SIZE, NULL);
  938. }
  939. main_loop = g_main_loop_new (NULL, FALSE);
  940. _intr_setup ();
  941. g_timeout_add (400, check_for_interrupt, NULL);
  942. g_mutex_init (&disp_lock);
  943. display = XOpenDisplay (NULL);
  944. for (i = 0; i < num_instances; i++) {
  945. guint j;
  946. if (!show_bbox_text) {
  947. GstElement *nvosd = appCtx[i]->pipeline.instance_bins[0].osd_bin.nvosd;
  948. g_object_set(G_OBJECT(nvosd), "display-text", FALSE, NULL);
  949. }
  950. if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
  951. GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
  952. NVGSTDS_ERR_MSG_V ("Failed to set pipeline to PAUSED");
  953. return_value = -1;
  954. goto done;
  955. }
  956. if (!appCtx[i]->config.tiled_display_config.enable)
  957. continue;
  958. for (j = 0; j < appCtx[i]->config.num_sink_sub_bins; j++) {
  959. XTextProperty xproperty;
  960. gchar *title;
  961. guint width, height;
  962. XSizeHints hints = {0};
  963. if (!GST_IS_VIDEO_OVERLAY (appCtx[i]->pipeline.instance_bins[0].sink_bin.
  964. sub_bins[j].sink)) {
  965. continue;
  966. }
  967. if (!display) {
  968. NVGSTDS_ERR_MSG_V ("Could not open X Display");
  969. return_value = -1;
  970. goto done;
  971. }
  972. if (appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.width)
  973. width =
  974. appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.width;
  975. else
  976. width = appCtx[i]->config.tiled_display_config.width;
  977. if (appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.height)
  978. height =
  979. appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.height;
  980. else
  981. height = appCtx[i]->config.tiled_display_config.height;
  982. width = (width) ? width : DEFAULT_X_WINDOW_WIDTH;
  983. height = (height) ? height : DEFAULT_X_WINDOW_HEIGHT;
  984. hints.flags = PPosition | PSize;
  985. hints.x = appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.offset_x;
  986. hints.y = appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.offset_y;
  987. hints.width = width;
  988. hints.height = height;
  989. windows[i] =
  990. XCreateSimpleWindow (display, RootWindow (display,
  991. DefaultScreen (display)), hints.x, hints.y, width, height, 2,
  992. 0x00000000, 0x00000000);
  993. XSetNormalHints(display, windows[i], &hints);
  994. if (num_instances > 1)
  995. title = g_strdup_printf (APP_TITLE "-%d", i);
  996. else
  997. title = g_strdup (APP_TITLE);
  998. if (XStringListToTextProperty ((char **) &title, 1, &xproperty) != 0) {
  999. XSetWMName (display, windows[i], &xproperty);
  1000. XFree (xproperty.value);
  1001. }
  1002. XSetWindowAttributes attr = { 0 };
  1003. if ((appCtx[i]->config.tiled_display_config.enable &&
  1004. appCtx[i]->config.tiled_display_config.rows *
  1005. appCtx[i]->config.tiled_display_config.columns == 1) ||
  1006. (appCtx[i]->config.tiled_display_config.enable == 0 &&
  1007. appCtx[i]->config.num_source_sub_bins == 1)) {
  1008. } else {
  1009. attr.event_mask = ButtonPress | KeyRelease;
  1010. }
  1011. XChangeWindowAttributes (display, windows[i], CWEventMask, &attr);
  1012. Atom wmDeleteMessage = XInternAtom (display, "WM_DELETE_WINDOW", False);
  1013. if (wmDeleteMessage != None) {
  1014. XSetWMProtocols (display, windows[i], &wmDeleteMessage, 1);
  1015. }
  1016. XMapRaised (display, windows[i]);
  1017. XSync (display, 1); //discard the events for now
  1018. gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (appCtx
  1019. [i]->pipeline.instance_bins[0].sink_bin.sub_bins[j].sink),
  1020. (gulong) windows[i]);
  1021. gst_video_overlay_expose (GST_VIDEO_OVERLAY (appCtx[i]->pipeline.
  1022. instance_bins[0].sink_bin.sub_bins[j].sink));
  1023. if (!x_event_thread)
  1024. x_event_thread = g_thread_new ("nvds-window-event-thread",
  1025. nvds_x_event_thread, NULL);
  1026. }
  1027. }
  1028. /* Dont try to set playing state if error is observed */
  1029. if (return_value != -1) {
  1030. for (i = 0; i < num_instances; i++) {
  1031. if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
  1032. GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
  1033. g_print ("\ncan't set pipeline to playing state.\n");
  1034. return_value = -1;
  1035. goto done;
  1036. }
  1037. }
  1038. }
  1039. print_runtime_commands ();
  1040. changemode (1);
  1041. g_timeout_add (40, event_thread_func, NULL);
  1042. g_main_loop_run (main_loop);
  1043. changemode (0);
  1044. done:
  1045. g_print ("Quitting\n");
  1046. for (i = 0; i < num_instances; i++) {
  1047. if (appCtx[i] == NULL)
  1048. continue;
  1049. if (appCtx[i]->return_value == -1)
  1050. return_value = -1;
  1051. destroy_pipeline (appCtx[i]);
  1052. g_mutex_lock (&disp_lock);
  1053. if (windows[i])
  1054. XDestroyWindow (display, windows[i]);
  1055. windows[i] = 0;
  1056. g_mutex_unlock (&disp_lock);
  1057. g_free (appCtx[i]);
  1058. }
  1059. g_mutex_lock (&disp_lock);
  1060. if (display)
  1061. XCloseDisplay (display);
  1062. display = NULL;
  1063. g_mutex_unlock (&disp_lock);
  1064. g_mutex_clear (&disp_lock);
  1065. if (main_loop) {
  1066. g_main_loop_unref (main_loop);
  1067. }
  1068. if (ctx) {
  1069. g_option_context_free (ctx);
  1070. }
  1071. if (return_value == 0) {
  1072. g_print ("App run successful\n");
  1073. } else {
  1074. g_print ("App run failed\n");
  1075. }
  1076. gst_deinit ();
  1077. return return_value;
  1078. g_free (testAppCtx);
  1079. return 0;
  1080. }
  1081. static gchar *
  1082. get_first_result_label (NvDsClassifierMeta * classifierMeta)
  1083. {
  1084. GList *n;
  1085. for (n = classifierMeta->label_info_list; n != NULL; n = n->next) {
  1086. NvDsLabelInfo *labelInfo = (NvDsLabelInfo *) (n->data);
  1087. if (labelInfo->result_label[0] != '\0') {
  1088. return g_strdup (labelInfo->result_label);
  1089. }
  1090. }
  1091. return NULL;
  1092. }