· 4 years ago · Aug 31, 2021, 12:10 PM
1/*
2 * Copyright (c) 2018-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
23#include <gst/gst.h>
24#include <string.h>
25#include <math.h>
26#include <stdlib.h>
27
28#include "deepstream_app.h"
29#define PGIE_CLASS_ID_VEHICLE 0
30#define PGIE_CLASS_ID_PERSON 2
31#define PRIMARY_DETECTOR_UID 1
32#define SECONDARY_DETECTOR_UID 4
33#define SECONDARY_CLASSIFIER_UID 5
34#define SGIE_CLASS_ID_LPD 0
35#define MAX_DISPLAY_LEN 64
36static guint batch_num = 0;
37static guint demux_batch_num = 0;
38
39#define MAX_DISPLAY_LEN 64
40
41#define MEASURE_ENABLE 1
42
43#define PGIE_CLASS_ID_VEHICLE 0
44#define PGIE_CLASS_ID_PERSON 2
45
46#define SGIE_CLASS_ID_LPD 0
47
48/* The muxer output resolution must be set if the input streams will be of
49 * different resolution. The muxer will scale all the input frames to this
50 * resolution. */
51#define MUXER_OUTPUT_WIDTH 1280
52#define MUXER_OUTPUT_HEIGHT 720
53
54/* Muxer batch formation timeout, for e.g. 40 millisec. Should ideally be set
55 * based on the fastest source's framerate. */
56#define MUXER_BATCH_TIMEOUT_USEC 4000000
57
58#define CONFIG_GROUP_TRACKER "tracker"
59#define CONFIG_GROUP_TRACKER_WIDTH "tracker-width"
60#define CONFIG_GROUP_TRACKER_HEIGHT "tracker-height"
61#define CONFIG_GROUP_TRACKER_LL_CONFIG_FILE "ll-config-file"
62#define CONFIG_GROUP_TRACKER_LL_LIB_FILE "ll-lib-file"
63#define CONFIG_GROUP_TRACKER_ENABLE_BATCH_PROCESS "enable-batch-process"
64#define CONFIG_GPU_ID "gpu-id"
65
66gint frame_number = 0;
67gint total_plate_number = 0;
68gchar pgie_classes_str[4][32] = { "Vehicle", "TwoWheeler", "Person",
69 "Roadsign"
70};
71
72typedef struct _perf_measure{
73 GstClockTime pre_time;
74 GstClockTime total_time;
75 guint count;
76}perf_measure;
77
78GST_DEBUG_CATEGORY_EXTERN (NVDS_APP);
79
80GQuark _dsmeta_quark;
81
82#define CEIL(a,b) ((a + b - 1) / b)
83
84/**
85 * @brief Add the (nvmsgconv->nvmsgbroker) sink-bin to the
86 * overall DS pipeline (if any configured) and link the same to
87 * common_elements.tee (This tee connects
88 * the common analytics path to Tiler/display-sink and
89 * to configured broker sink if any)
90 * NOTE: This API shall return TRUE if there are no
91 * broker sinks to add to pipeline
92 *
93 * @param appCtx [IN]
94 * @return TRUE if succussful; FALSE otherwise
95 */
96static gboolean add_and_link_broker_sink (AppCtx * appCtx);
97
98/**
99 * @brief Checks if there are any [sink] groups
100 * configured for source_id=provided source_id
101 * NOTE: source_id key and this API is valid only when we
102 * disable [tiler] and thus use demuxer for individual
103 * stream out
104 * @param config [IN] The DS Pipeline configuration struct
105 * @param source_id [IN] Source ID for which a specific [sink]
106 * group is searched for
107 */
108static gboolean is_sink_available_for_source_id(NvDsConfig *config, guint source_id);
109
110/**
111 * callback function to receive messages from components
112 * in the pipeline.
113 */
114static gboolean
115bus_callback (GstBus * bus, GstMessage * message, gpointer data)
116{
117 AppCtx *appCtx = (AppCtx *) data;
118 GST_CAT_DEBUG (NVDS_APP,
119 "Received message on bus: source %s, msg_type %s",
120 GST_MESSAGE_SRC_NAME (message), GST_MESSAGE_TYPE_NAME (message));
121 switch (GST_MESSAGE_TYPE (message)) {
122 case GST_MESSAGE_INFO:{
123 GError *error = NULL;
124 gchar *debuginfo = NULL;
125 gst_message_parse_info (message, &error, &debuginfo);
126 g_printerr ("INFO from %s: %s\n",
127 GST_OBJECT_NAME (message->src), error->message);
128 if (debuginfo) {
129 g_printerr ("Debug info: %s\n", debuginfo);
130 }
131 g_error_free (error);
132 g_free (debuginfo);
133 break;
134 }
135 case GST_MESSAGE_WARNING:{
136 GError *error = NULL;
137 gchar *debuginfo = NULL;
138 gst_message_parse_warning (message, &error, &debuginfo);
139 g_printerr ("WARNING from %s: %s\n",
140 GST_OBJECT_NAME (message->src), error->message);
141 if (debuginfo) {
142 g_printerr ("Debug info: %s\n", debuginfo);
143 }
144 g_error_free (error);
145 g_free (debuginfo);
146 break;
147 }
148 case GST_MESSAGE_ERROR:{
149 GError *error = NULL;
150 gchar *debuginfo = NULL;
151 guint i = 0;
152 gst_message_parse_error (message, &error, &debuginfo);
153 g_printerr ("ERROR from %s: %s\n",
154 GST_OBJECT_NAME (message->src), error->message);
155 if (debuginfo) {
156 g_printerr ("Debug info: %s\n", debuginfo);
157 }
158
159 NvDsSrcParentBin *bin = &appCtx->pipeline.multi_src_bin;
160 GstElement *msg_src_elem = (GstElement *) GST_MESSAGE_SRC (message);
161 gboolean bin_found = FALSE;
162 /* Find the source bin which generated the error. */
163 while (msg_src_elem && !bin_found) {
164 for (i = 0; i < bin->num_bins && !bin_found; i++) {
165 if (bin->sub_bins[i].src_elem == msg_src_elem ||
166 bin->sub_bins[i].bin == msg_src_elem) {
167 bin_found = TRUE;
168 break;
169 }
170 }
171 msg_src_elem = GST_ELEMENT_PARENT (msg_src_elem);
172 }
173
174 if ((i != bin->num_bins) &&
175 (appCtx->config.multi_source_config[0].type == NV_DS_SOURCE_RTSP)) {
176 // Error from one of RTSP source.
177 NvDsSrcBin *subBin = &bin->sub_bins[i];
178
179 if (!subBin->reconfiguring ||
180 g_strrstr(debuginfo, "500 (Internal Server Error)")) {
181 subBin->reconfiguring = TRUE;
182 g_timeout_add (0, reset_source_pipeline, subBin);
183 }
184 g_error_free (error);
185 g_free (debuginfo);
186 return TRUE;
187 }
188
189 if (appCtx->config.multi_source_config[0].type == NV_DS_SOURCE_CAMERA_V4L2) {
190 if (g_strrstr(debuginfo, "reason not-negotiated (-4)")) {
191 NVGSTDS_INFO_MSG_V ("incorrect camera parameters provided, please provide supported resolution and frame rate\n");
192 }
193
194 if (g_strrstr(debuginfo, "Buffer pool activation failed")) {
195 NVGSTDS_INFO_MSG_V ("usb bandwidth might be saturated\n");
196 }
197 }
198
199 g_error_free (error);
200 g_free (debuginfo);
201 appCtx->return_value = -1;
202 appCtx->quit = TRUE;
203 break;
204 }
205 case GST_MESSAGE_STATE_CHANGED:{
206 GstState oldstate, newstate;
207 gst_message_parse_state_changed (message, &oldstate, &newstate, NULL);
208 if (GST_ELEMENT (GST_MESSAGE_SRC (message)) == appCtx->pipeline.pipeline) {
209 switch (newstate) {
210 case GST_STATE_PLAYING:
211 NVGSTDS_INFO_MSG_V ("Pipeline running\n");
212 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->
213 pipeline.pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
214 "ds-app-playing");
215 break;
216 case GST_STATE_PAUSED:
217 if (oldstate == GST_STATE_PLAYING) {
218 NVGSTDS_INFO_MSG_V ("Pipeline paused\n");
219 }
220 break;
221 case GST_STATE_READY:
222 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->pipeline.
223 pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "ds-app-ready");
224 if (oldstate == GST_STATE_NULL) {
225 NVGSTDS_INFO_MSG_V ("Pipeline ready\n");
226 } else {
227 NVGSTDS_INFO_MSG_V ("Pipeline stopped\n");
228 }
229 break;
230 case GST_STATE_NULL:
231 g_mutex_lock (&appCtx->app_lock);
232 g_cond_broadcast (&appCtx->app_cond);
233 g_mutex_unlock (&appCtx->app_lock);
234 break;
235 default:
236 break;
237 }
238 }
239 break;
240 }
241 case GST_MESSAGE_EOS:{
242 /*
243 * In normal scenario, this would use g_main_loop_quit() to exit the
244 * loop and release the resources. Since this application might be
245 * running multiple pipelines through configuration files, it should wait
246 * till all pipelines are done.
247 */
248 NVGSTDS_INFO_MSG_V ("Received EOS. Exiting ...\n");
249 appCtx->quit = TRUE;
250 return FALSE;
251 break;
252 }
253 default:
254 break;
255 }
256 return TRUE;
257}
258
259/**
260 * Function to dump bounding box data in kitti format. For this to work,
261 * property "gie-kitti-output-dir" must be set in configuration file.
262 * Data of different sources and frames is dumped in separate file.
263 */
264static void
265write_kitti_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
266{
267 gchar bbox_file[1024] = { 0 };
268 FILE *bbox_params_dump_file = NULL;
269
270 if (!appCtx->config.bbox_dir_path)
271 return;
272
273 for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
274 l_frame = l_frame->next) {
275 NvDsFrameMeta *frame_meta = l_frame->data;
276 guint stream_id = frame_meta->pad_index;
277 g_snprintf (bbox_file, sizeof (bbox_file) - 1,
278 "%s/%02u_%03u_%06lu.txt", appCtx->config.bbox_dir_path,
279 appCtx->index, stream_id, (gulong) frame_meta->frame_num);
280 bbox_params_dump_file = fopen (bbox_file, "w");
281 if (!bbox_params_dump_file)
282 continue;
283
284 for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
285 l_obj = l_obj->next) {
286 NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
287 float left = obj->rect_params.left;
288 float top = obj->rect_params.top;
289 float right = left + obj->rect_params.width;
290 float bottom = top + obj->rect_params.height;
291 // Here confidence stores detection confidence, since dump gie output
292 // is before tracker plugin
293 float confidence = obj->confidence;
294 fprintf (bbox_params_dump_file,
295 "%s 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f\n",
296 obj->obj_label, left, top, right, bottom, confidence);
297 }
298 fclose (bbox_params_dump_file);
299 }
300}
301
302/**
303 * Function to dump past frame objs in kitti format.
304 */
305static void
306write_kitti_past_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
307{
308 if (!appCtx->config.kitti_track_dir_path)
309 return;
310
311 // dump past frame tracked objects appending current frame objects
312 gchar bbox_file[1024] = { 0 };
313 FILE *bbox_params_dump_file = NULL;
314
315 NvDsPastFrameObjBatch *pPastFrameObjBatch = NULL;
316 NvDsUserMetaList *bmeta_list = NULL;
317 NvDsUserMeta *user_meta = NULL;
318 for(bmeta_list=batch_meta->batch_user_meta_list; bmeta_list!=NULL; bmeta_list=bmeta_list->next){
319 user_meta = (NvDsUserMeta *)bmeta_list->data;
320 if(user_meta && user_meta->base_meta.meta_type==NVDS_TRACKER_PAST_FRAME_META){
321 pPastFrameObjBatch = (NvDsPastFrameObjBatch *) (user_meta->user_meta_data);
322 for (uint si=0; si < pPastFrameObjBatch->numFilled; si++){
323 NvDsPastFrameObjStream *objStream = (pPastFrameObjBatch->list) + si;
324 guint stream_id = (guint)(objStream->streamID);
325 for (uint li=0; li<objStream->numFilled; li++){
326 NvDsPastFrameObjList *objList = (objStream->list) + li;
327 for (uint oi=0; oi<objList->numObj; oi++) {
328 NvDsPastFrameObj *obj = (objList->list) + oi;
329 g_snprintf (bbox_file, sizeof (bbox_file) - 1,
330 "%s/%02u_%03u_%06lu.txt", appCtx->config.kitti_track_dir_path,
331 appCtx->index, stream_id, (gulong) obj->frameNum);
332
333 float left = obj->tBbox.left;
334 float right = left + obj->tBbox.width;
335 float top = obj->tBbox.top;
336 float bottom = top + obj->tBbox.height;
337 // Past frame object confidence given by tracker
338 float confidence = obj->confidence;
339 bbox_params_dump_file = fopen (bbox_file, "a");
340 if (!bbox_params_dump_file){
341 continue;
342 }
343 fprintf(bbox_params_dump_file,
344 "%s %lu 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f\n",
345 objList->objLabel, objList->uniqueId, left, top, right, bottom, confidence);
346 fclose (bbox_params_dump_file);
347 }
348 }
349 }
350 }
351 }
352}
353
354/**
355 * Function to dump bounding box data in kitti format with tracking ID added.
356 * For this to work, property "kitti-track-output-dir" must be set in configuration file.
357 * Data of different sources and frames is dumped in separate file.
358 */
359static void
360write_kitti_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
361{
362 gchar bbox_file[1024] = { 0 };
363 FILE *bbox_params_dump_file = NULL;
364
365 if (!appCtx->config.kitti_track_dir_path)
366 return;
367
368 for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
369 l_frame = l_frame->next) {
370 NvDsFrameMeta *frame_meta = l_frame->data;
371 guint stream_id = frame_meta->pad_index;
372 g_snprintf (bbox_file, sizeof (bbox_file) - 1,
373 "%s/%02u_%03u_%06lu.txt", appCtx->config.kitti_track_dir_path,
374 appCtx->index, stream_id, (gulong) frame_meta->frame_num);
375 bbox_params_dump_file = fopen (bbox_file, "w");
376 if (!bbox_params_dump_file)
377 continue;
378
379 for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
380 l_obj = l_obj->next) {
381 NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
382 float left = obj->tracker_bbox_info.org_bbox_coords.left;
383 float top = obj->tracker_bbox_info.org_bbox_coords.top;
384 float right = left + obj->tracker_bbox_info.org_bbox_coords.width;
385 float bottom = top + obj->tracker_bbox_info.org_bbox_coords.height;
386 // Here confidence stores tracker confidence value for tracker output
387 float confidence = obj->tracker_confidence;
388 guint64 id = obj->object_id;
389 fprintf (bbox_params_dump_file,
390 "%s %lu 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f\n",
391 obj->obj_label, id, left, top, right, bottom, confidence);
392 }
393 fclose (bbox_params_dump_file);
394 }
395}
396
397static gint
398component_id_compare_func (gconstpointer a, gconstpointer b)
399{
400 NvDsClassifierMeta *cmetaa = (NvDsClassifierMeta *) a;
401 NvDsClassifierMeta *cmetab = (NvDsClassifierMeta *) b;
402
403 if (cmetaa->unique_component_id < cmetab->unique_component_id)
404 return -1;
405 if (cmetaa->unique_component_id > cmetab->unique_component_id)
406 return 1;
407 return 0;
408}
409
410/**
411 * Function to process the attached metadata. This is just for demonstration
412 * and can be removed if not required.
413 * Here it demonstrates to use bounding boxes of different color and size for
414 * different type / class of objects.
415 * It also demonstrates how to join the different labels(PGIE + SGIEs)
416 * of an object to form a single string.
417 */
418static void
419process_meta (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
420{
421 // For single source always display text either with demuxer or with tiler
422 if (!appCtx->config.tiled_display_config.enable ||
423 appCtx->config.num_source_sub_bins == 1) {
424 appCtx->show_bbox_text = 1;
425 }
426
427 for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
428 l_frame = l_frame->next) {
429 NvDsFrameMeta *frame_meta = l_frame->data;
430 for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
431 l_obj = l_obj->next) {
432 NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
433 gint class_index = obj->class_id;
434 NvDsGieConfig *gie_config = NULL;
435 gchar *str_ins_pos = NULL;
436
437 if (obj->unique_component_id ==
438 (gint) appCtx->config.primary_gie_config.unique_id) {
439 gie_config = &appCtx->config.primary_gie_config;
440 } else {
441 for (gint i = 0; i < (gint) appCtx->config.num_secondary_gie_sub_bins;
442 i++) {
443 gie_config = &appCtx->config.secondary_gie_sub_bin_config[i];
444 if (obj->unique_component_id == (gint) gie_config->unique_id) {
445 break;
446 }
447 gie_config = NULL;
448 }
449 }
450 g_free (obj->text_params.display_text);
451 obj->text_params.display_text = NULL;
452
453 if (gie_config != NULL) {
454 if (g_hash_table_contains (gie_config->bbox_border_color_table,
455 class_index + (gchar *) NULL)) {
456 obj->rect_params.border_color =
457 *((NvOSD_ColorParams *)
458 g_hash_table_lookup (gie_config->bbox_border_color_table,
459 class_index + (gchar *) NULL));
460 } else {
461 obj->rect_params.border_color = gie_config->bbox_border_color;
462 }
463 obj->rect_params.border_width = appCtx->config.osd_config.border_width;
464
465 if (g_hash_table_contains (gie_config->bbox_bg_color_table,
466 class_index + (gchar *) NULL)) {
467 obj->rect_params.has_bg_color = 1;
468 obj->rect_params.bg_color =
469 *((NvOSD_ColorParams *)
470 g_hash_table_lookup (gie_config->bbox_bg_color_table,
471 class_index + (gchar *) NULL));
472 } else {
473 obj->rect_params.has_bg_color = 0;
474 }
475 }
476
477 if (!appCtx->show_bbox_text)
478 continue;
479
480 obj->text_params.x_offset = obj->rect_params.left;
481 obj->text_params.y_offset = obj->rect_params.top - 30;
482 obj->text_params.font_params.font_color =
483 appCtx->config.osd_config.text_color;
484 obj->text_params.font_params.font_size =
485 appCtx->config.osd_config.text_size;
486 obj->text_params.font_params.font_name = appCtx->config.osd_config.font;
487 if (appCtx->config.osd_config.text_has_bg) {
488 obj->text_params.set_bg_clr = 1;
489 obj->text_params.text_bg_clr = appCtx->config.osd_config.text_bg_color;
490 }
491
492 obj->text_params.display_text = g_malloc (128);
493 obj->text_params.display_text[0] = '\0';
494 str_ins_pos = obj->text_params.display_text;
495
496 if (obj->obj_label[0] != '\0')
497 sprintf (str_ins_pos, "%s", obj->obj_label);
498 str_ins_pos += strlen (str_ins_pos);
499
500 if (obj->object_id != UNTRACKED_OBJECT_ID) {
501 /** object_id is a 64-bit sequential value;
502 * but considering the display aesthetic,
503 * trimming to lower 32-bits */
504 if (appCtx->config.tracker_config.display_tracking_id) {
505 guint64 const LOW_32_MASK = 0x00000000FFFFFFFF;
506 sprintf (str_ins_pos, " %lu", (obj->object_id & LOW_32_MASK));
507 str_ins_pos += strlen (str_ins_pos);
508 }
509 }
510
511 obj->classifier_meta_list =
512 g_list_sort (obj->classifier_meta_list, component_id_compare_func);
513 for (NvDsMetaList * l_class = obj->classifier_meta_list; l_class != NULL;
514 l_class = l_class->next) {
515 NvDsClassifierMeta *cmeta = (NvDsClassifierMeta *) l_class->data;
516 for (NvDsMetaList * l_label = cmeta->label_info_list; l_label != NULL;
517 l_label = l_label->next) {
518 NvDsLabelInfo *label = (NvDsLabelInfo *) l_label->data;
519 if (label->pResult_label) {
520 sprintf (str_ins_pos, " %s", label->pResult_label);
521 } else if (label->result_label[0] != '\0') {
522 sprintf (str_ins_pos, " %s", label->result_label);
523 }
524 str_ins_pos += strlen (str_ins_pos);
525 }
526
527 }
528 }
529 }
530}
531
532/**
533 * Function which processes the inferred buffer and its metadata.
534 * It also gives opportunity to attach application specific
535 * metadata (e.g. clock, analytics output etc.).
536 */
537static void
538process_buffer (GstBuffer * buf, AppCtx * appCtx, guint index)
539{
540 NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
541 if (!batch_meta) {
542 NVGSTDS_WARN_MSG_V ("Batch meta not found for buffer %p", buf);
543 return;
544 }
545 process_meta (appCtx, batch_meta);
546 //NvDsInstanceData *data = &appCtx->instance_data[index];
547 //guint i;
548
549 // data->frame_num++;
550
551 /* Opportunity to modify the processed metadata or do analytics based on
552 * type of object e.g. maintaining count of particular type of car.
553 */
554 if (appCtx->all_bbox_generated_cb) {
555 appCtx->all_bbox_generated_cb (appCtx, buf, batch_meta, index);
556 }
557 //data->bbox_list_size = 0;
558
559 /*
560 * callback to attach application specific additional metadata.
561 */
562 if (appCtx->overlay_graphics_cb) {
563 appCtx->overlay_graphics_cb (appCtx, buf, batch_meta, index);
564 }
565}
566
567/**
568 * Buffer probe function to get the results of primary infer.
569 * Here it demonstrates the use by dumping bounding box coordinates in
570 * kitti format.
571 */
572static GstPadProbeReturn
573gie_primary_processing_done_buf_prob (GstPad * pad, GstPadProbeInfo * info,
574 gpointer u_data)
575{
576 GstBuffer *buf = (GstBuffer *) info->data;
577 AppCtx *appCtx = (AppCtx *) u_data;
578 NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
579 if (!batch_meta) {
580 NVGSTDS_WARN_MSG_V ("Batch meta not found for buffer %p", buf);
581 return GST_PAD_PROBE_OK;
582 }
583
584 write_kitti_output (appCtx, batch_meta);
585
586 return GST_PAD_PROBE_OK;
587}
588
589/**
590 * Probe function to get results after all inferences(Primary + Secondary)
591 * are done. This will be just before OSD or sink (in case OSD is disabled).
592 */
593static GstPadProbeReturn
594gie_processing_done_buf_prob (GstPad * pad, GstPadProbeInfo * info,
595 gpointer u_data)
596{
597 GstBuffer *buf = (GstBuffer *) info->data;
598 NvDsInstanceBin *bin = (NvDsInstanceBin *) u_data;
599 guint index = bin->index;
600 AppCtx *appCtx = bin->appCtx;
601
602 if (gst_buffer_is_writable (buf))
603 process_buffer (buf, appCtx, index);
604 //return GST_PAD_PROBE_OK;
605
606 //GstBuffer *buf = (GstBuffer *) info->data;
607 NvDsObjectMeta *obj_meta = NULL;
608 guint vehicle_count = 0;
609 guint person_count = 0;
610 guint lp_count = 0;
611 guint label_i = 0;
612 NvDsMetaList * l_frame = NULL;
613 NvDsMetaList * l_obj = NULL;
614 NvDsMetaList * l_class = NULL;
615 NvDsMetaList * l_label = NULL;
616 NvDsDisplayMeta *display_meta = NULL;
617 NvDsClassifierMeta * class_meta = NULL;
618 NvDsLabelInfo * label_info = NULL;
619 GstClockTime now;
620 perf_measure * perf = (perf_measure *)(u_data);
621
622 NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
623
624 now = g_get_monotonic_time();
625
626 if (perf->pre_time == GST_CLOCK_TIME_NONE) {
627 perf->pre_time = now;
628 perf->total_time = GST_CLOCK_TIME_NONE;
629 } else {
630 if (perf->total_time == GST_CLOCK_TIME_NONE) {
631 perf->total_time = (now - perf->pre_time);
632 } else {
633 perf->total_time += (now - perf->pre_time);
634 }
635 perf->pre_time = now;
636 perf->count++;
637 }
638
639 for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
640 l_frame = l_frame->next) {
641 NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
642 int offset = 0;
643 if (!frame_meta)
644 continue;
645 for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
646 l_obj = l_obj->next) {
647 obj_meta = (NvDsObjectMeta *) (l_obj->data);
648
649 if (!obj_meta)
650 continue;
651
652 /* Check that the object has been detected by the primary detector
653 * and that the class id is that of vehicles/persons. */
654 if (obj_meta->unique_component_id == PRIMARY_DETECTOR_UID) {
655 if (obj_meta->class_id == PGIE_CLASS_ID_VEHICLE)
656 vehicle_count++;
657 if (obj_meta->class_id == PGIE_CLASS_ID_PERSON)
658 person_count++;
659 }
660
661 if (obj_meta->unique_component_id == SECONDARY_DETECTOR_UID) {
662 if (obj_meta->class_id == SGIE_CLASS_ID_LPD) {
663 lp_count++;
664 /* Print this info only when operating in secondary model. */
665 if (obj_meta->parent)
666 g_print ("License plate found for parent object %p (type=%s)\n",
667 obj_meta->parent, pgie_classes_str[obj_meta->parent->class_id]);
668
669 obj_meta->text_params.set_bg_clr = 1;
670 obj_meta->text_params.text_bg_clr.red = 0.0;
671 obj_meta->text_params.text_bg_clr.green = 0.0;
672 obj_meta->text_params.text_bg_clr.blue = 0.0;
673 obj_meta->text_params.text_bg_clr.alpha = 0.0;
674
675 obj_meta->text_params.font_params.font_color.red = 1.0;
676 obj_meta->text_params.font_params.font_color.green = 1.0;
677 obj_meta->text_params.font_params.font_color.blue = 0.0;
678 obj_meta->text_params.font_params.font_color.alpha = 1.0;
679 obj_meta->text_params.font_params.font_size = 12;
680 }
681 }
682
683 for (l_class = obj_meta->classifier_meta_list; l_class != NULL;
684 l_class = l_class->next) {
685 class_meta = (NvDsClassifierMeta *)(l_class->data);
686 if (!class_meta)
687 continue;
688 if (class_meta->unique_component_id == SECONDARY_CLASSIFIER_UID) {
689 for ( label_i = 0, l_label = class_meta->label_info_list;
690 label_i < class_meta->num_labels && l_label; label_i++,
691 l_label = l_label->next) {
692 label_info = (NvDsLabelInfo *)(l_label->data);
693 if (label_info) {
694 if (label_info->label_id == 0 && label_info->result_class_id == 1) {
695 g_print ("Plate License %s\n",label_info->result_label);
696 }
697 }
698 }
699 }
700 }
701 }
702
703 display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
704 NvOSD_TextParams *txt_params = &display_meta->text_params[0];
705 display_meta->num_labels = 1;
706 txt_params->display_text = (char*) g_malloc0 (MAX_DISPLAY_LEN);
707 offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN,
708 "Person = %d ", person_count);
709 offset += snprintf(txt_params->display_text + offset , MAX_DISPLAY_LEN,
710 "Vehicle = %d ", vehicle_count);
711
712 /* Now set the offsets where the string should appear */
713 txt_params->x_offset = 10;
714 txt_params->y_offset = 12;
715
716 /* Font , font-color and font-size */
717 char font_n[6];
718 snprintf(font_n, 6, "Serif");
719 txt_params->font_params.font_name = font_n;
720 txt_params->font_params.font_size = 10;
721 txt_params->font_params.font_color.red = 1.0;
722 txt_params->font_params.font_color.green = 1.0;
723 txt_params->font_params.font_color.blue = 1.0;
724 txt_params->font_params.font_color.alpha = 1.0;
725
726 /* Text background color */
727 txt_params->set_bg_clr = 1;
728 txt_params->text_bg_clr.red = 0.0;
729 txt_params->text_bg_clr.green = 0.0;
730 txt_params->text_bg_clr.blue = 0.0;
731 txt_params->text_bg_clr.alpha = 1.0;
732
733 nvds_add_display_meta_to_frame(frame_meta, display_meta);
734 }
735
736 // g_print ("Frame Number = %d Vehicle Count = %d Person Count = %d"
737// " License Plate Count = %d\n",
738// frame_number, vehicle_count, person_count,
739// lp_count);
740 frame_number++;
741 total_plate_number += lp_count;
742 return GST_PAD_PROBE_OK;
743
744
745}
746
747/**
748 * Buffer probe function after tracker.
749 */
750static GstPadProbeReturn
751analytics_done_buf_prob (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
752{
753 NvDsInstanceBin *bin = (NvDsInstanceBin *) u_data;
754 guint index = bin->index;
755 AppCtx *appCtx = bin->appCtx;
756 GstBuffer *buf = (GstBuffer *) info->data;
757 NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
758 if (!batch_meta) {
759 NVGSTDS_WARN_MSG_V ("Batch meta not found for buffer %p", buf);
760 return GST_PAD_PROBE_OK;
761 }
762
763 /*
764 * Output KITTI labels with tracking ID if configured to do so.
765 */
766 write_kitti_track_output(appCtx, batch_meta);
767 if (appCtx->config.tracker_config.enable_past_frame)
768 {
769 write_kitti_past_track_output (appCtx, batch_meta);
770 }
771 if (appCtx->bbox_generated_post_analytics_cb)
772 {
773 appCtx->bbox_generated_post_analytics_cb (appCtx, buf, batch_meta, index);
774 }
775 return GST_PAD_PROBE_OK;
776}
777
778static GstPadProbeReturn
779latency_measurement_buf_prob(GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
780{
781 AppCtx *appCtx = (AppCtx *) u_data;
782 guint i = 0, num_sources_in_batch = 0;
783 if(nvds_enable_latency_measurement)
784 {
785 GstBuffer *buf = (GstBuffer *) info->data;
786 NvDsFrameLatencyInfo *latency_info = NULL;
787 g_mutex_lock (&appCtx->latency_lock);
788 latency_info = appCtx->latency_info;
789 g_print("\n************BATCH-NUM = %d**************\n",batch_num);
790 num_sources_in_batch = nvds_measure_buffer_latency(buf, latency_info);
791
792 for(i = 0; i < num_sources_in_batch; i++)
793 {
794 g_print("Source id = %d Frame_num = %d Frame latency = %lf (ms) \n",
795 latency_info[i].source_id,
796 latency_info[i].frame_num,
797 latency_info[i].latency);
798 }
799 g_mutex_unlock (&appCtx->latency_lock);
800 batch_num++;
801 }
802
803 return GST_PAD_PROBE_OK;
804}
805
806static GstPadProbeReturn
807demux_latency_measurement_buf_prob(GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
808{
809 AppCtx *appCtx = (AppCtx *) u_data;
810 guint i = 0, num_sources_in_batch = 0;
811 if(nvds_enable_latency_measurement)
812 {
813 GstBuffer *buf = (GstBuffer *) info->data;
814 NvDsFrameLatencyInfo *latency_info = NULL;
815 g_mutex_lock (&appCtx->latency_lock);
816 latency_info = appCtx->latency_info;
817 g_print("\n************DEMUX BATCH-NUM = %d**************\n",demux_batch_num);
818 num_sources_in_batch = nvds_measure_buffer_latency(buf, latency_info);
819
820 for(i = 0; i < num_sources_in_batch; i++)
821 {
822 g_print("Source id = %d Frame_num = %d Frame latency = %lf (ms) \n",
823 latency_info[i].source_id,
824 latency_info[i].frame_num,
825 latency_info[i].latency);
826 }
827 g_mutex_unlock (&appCtx->latency_lock);
828 demux_batch_num++;
829 }
830
831 return GST_PAD_PROBE_OK;
832}
833
834static gboolean
835add_and_link_broker_sink (AppCtx * appCtx)
836{
837 NvDsConfig *config = &appCtx->config;
838 /** Only first instance_bin broker sink
839 * employed as there's only one analytics path for N sources
840 * NOTE: There shall be only one [sink] group
841 * with type=6 (NV_DS_SINK_MSG_CONV_BROKER)
842 * a) Multiple of them does not make sense as we have only
843 * one analytics pipe generating the data for broker sink
844 * b) If Multiple broker sinks are configured by the user
845 * in config file, only the first in the order of
846 * appearance will be considered
847 * and others shall be ignored
848 * c) Ideally it should be documented (or obvious) that:
849 * multiple [sink] groups with type=6 (NV_DS_SINK_MSG_CONV_BROKER)
850 * is invalid
851 */
852 NvDsInstanceBin *instance_bin = &appCtx->pipeline.instance_bins[0];
853 NvDsPipeline *pipeline = &appCtx->pipeline;
854
855 for (guint i = 0; i < config->num_sink_sub_bins; i++) {
856 if(config->sink_bin_sub_bin_config[i].type == NV_DS_SINK_MSG_CONV_BROKER)
857 {
858 if(!pipeline->common_elements.tee) {
859 NVGSTDS_ERR_MSG_V ("%s failed; broker added without analytics; check config file\n", __func__);
860 return FALSE;
861 }
862 /** add the broker sink bin to pipeline */
863 if(!gst_bin_add (GST_BIN (pipeline->pipeline), instance_bin->sink_bin.sub_bins[i].bin)) {
864 return FALSE;
865 }
866 /** link the broker sink bin to the common_elements tee
867 * (The tee after nvinfer -> tracker (optional) -> sgies (optional) block) */
868 if (!link_element_to_tee_src_pad (pipeline->common_elements.tee, instance_bin->sink_bin.sub_bins[i].bin)) {
869 return FALSE;
870 }
871 }
872 }
873 return TRUE;
874}
875
876static gboolean
877create_demux_pipeline (AppCtx * appCtx, guint index)
878{
879 gboolean ret = FALSE;
880 NvDsConfig *config = &appCtx->config;
881 NvDsInstanceBin *instance_bin = &appCtx->pipeline.demux_instance_bins[index];
882 GstElement *last_elem;
883 gchar elem_name[32];
884
885 instance_bin->index = index;
886 instance_bin->appCtx = appCtx;
887
888 g_snprintf (elem_name, 32, "processing_demux_bin_%d", index);
889 instance_bin->bin = gst_bin_new (elem_name);
890
891 if (!create_demux_sink_bin (config->num_sink_sub_bins,
892 config->sink_bin_sub_bin_config, &instance_bin->demux_sink_bin,
893 config->sink_bin_sub_bin_config[index].source_id)) {
894 goto done;
895 }
896
897 gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->demux_sink_bin.bin);
898 last_elem = instance_bin->demux_sink_bin.bin;
899
900 if (config->osd_config.enable) {
901 if (!create_osd_bin (&config->osd_config, &instance_bin->osd_bin)) {
902 goto done;
903 }
904
905 gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->osd_bin.bin);
906
907 NVGSTDS_LINK_ELEMENT (instance_bin->osd_bin.bin, last_elem);
908
909 last_elem = instance_bin->osd_bin.bin;
910 }
911
912 NVGSTDS_BIN_ADD_GHOST_PAD (instance_bin->bin, last_elem, "sink");
913 if (config->osd_config.enable) {
914 NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
915 instance_bin->osd_bin.nvosd, "sink",
916 gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
917
918
919
920 } else {
921 NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
922 instance_bin->demux_sink_bin.bin, "sink",
923 gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
924 }
925
926 ret = TRUE;
927done:
928 if (!ret) {
929 NVGSTDS_ERR_MSG_V ("%s failed", __func__);
930 }
931 return ret;
932}
933
934/**
935 * Function to add components to pipeline which are dependent on number
936 * of streams. These components work on single buffer. If tiling is being
937 * used then single instance will be created otherwise < N > such instances
938 * will be created for < N > streams
939 */
940static gboolean
941create_processing_instance (AppCtx * appCtx, guint index)
942{
943 gboolean ret = FALSE;
944 NvDsConfig *config = &appCtx->config;
945 NvDsInstanceBin *instance_bin = &appCtx->pipeline.instance_bins[index];
946 GstElement *last_elem;
947 gchar elem_name[32];
948
949 instance_bin->index = index;
950 instance_bin->appCtx = appCtx;
951
952 g_snprintf (elem_name, 32, "processing_bin_%d", index);
953 instance_bin->bin = gst_bin_new (elem_name);
954
955 if (!create_sink_bin (config->num_sink_sub_bins,
956 config->sink_bin_sub_bin_config, &instance_bin->sink_bin, index)) {
957 goto done;
958 }
959
960 gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->sink_bin.bin);
961 last_elem = instance_bin->sink_bin.bin;
962
963 if (config->osd_config.enable) {
964 if (!create_osd_bin (&config->osd_config, &instance_bin->osd_bin)) {
965 goto done;
966 }
967
968 gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->osd_bin.bin);
969
970 NVGSTDS_LINK_ELEMENT (instance_bin->osd_bin.bin, last_elem);
971
972 last_elem = instance_bin->osd_bin.bin;
973 }
974
975 NVGSTDS_BIN_ADD_GHOST_PAD (instance_bin->bin, last_elem, "sink");
976 if (config->osd_config.enable) {
977 NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
978 instance_bin->osd_bin.nvosd, "sink",
979 gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
980 } else {
981 NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
982 instance_bin->sink_bin.bin, "sink",
983 gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
984 }
985
986 ret = TRUE;
987done:
988 if (!ret) {
989 NVGSTDS_ERR_MSG_V ("%s failed", __func__);
990 }
991 return ret;
992}
993
994/**
995 * Function to create common elements(Primary infer, tracker, secondary infer)
996 * of the pipeline. These components operate on muxed data from all the
997 * streams. So they are independent of number of streams in the pipeline.
998 */
999static gboolean
1000create_common_elements (NvDsConfig * config, NvDsPipeline * pipeline,
1001 GstElement ** sink_elem, GstElement ** src_elem,
1002 bbox_generated_callback bbox_generated_post_analytics_cb)
1003{
1004 gboolean ret = FALSE;
1005 *sink_elem = *src_elem = NULL;
1006
1007 if (config->primary_gie_config.enable) {
1008 if (config->num_secondary_gie_sub_bins > 0) {
1009 if (!create_secondary_gie_bin (config->num_secondary_gie_sub_bins,
1010 config->primary_gie_config.unique_id,
1011 config->secondary_gie_sub_bin_config,
1012 &pipeline->common_elements.secondary_gie_bin)) {
1013 goto done;
1014 }
1015 gst_bin_add (GST_BIN (pipeline->pipeline),
1016 pipeline->common_elements.secondary_gie_bin.bin);
1017 if (!*src_elem) {
1018 *src_elem = pipeline->common_elements.secondary_gie_bin.bin;
1019 }
1020 if (*sink_elem) {
1021 NVGSTDS_LINK_ELEMENT (pipeline->common_elements.secondary_gie_bin.bin,
1022 *sink_elem);
1023 }
1024 *sink_elem = pipeline->common_elements.secondary_gie_bin.bin;
1025 }
1026 }
1027
1028 if (config->dsanalytics_config.enable) {
1029 // Create dsanalytics element bin and set properties
1030 if (!create_dsanalytics_bin (&config->dsanalytics_config,
1031 &pipeline->common_elements.dsanalytics_bin)) {
1032 g_print ("creating dsanalytics bin failed\n");
1033 goto done;
1034 }
1035 // Add dsanalytics bin to instance bin
1036 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->common_elements.dsanalytics_bin.bin);
1037
1038 if (!*src_elem) {
1039 *src_elem = pipeline->common_elements.dsanalytics_bin.bin;
1040 }
1041 if (*sink_elem) {
1042 NVGSTDS_LINK_ELEMENT (pipeline->common_elements.dsanalytics_bin.bin,
1043 *sink_elem);
1044 }
1045 // Set this bin as the last element
1046 *sink_elem = pipeline->common_elements.dsanalytics_bin.bin;
1047 }
1048
1049 if (config->tracker_config.enable) {
1050 if (!create_tracking_bin (&config->tracker_config,
1051 &pipeline->common_elements.tracker_bin)) {
1052 g_print ("creating tracker bin failed\n");
1053 goto done;
1054 }
1055 gst_bin_add (GST_BIN (pipeline->pipeline),
1056 pipeline->common_elements.tracker_bin.bin);
1057 if (!*src_elem) {
1058 *src_elem = pipeline->common_elements.tracker_bin.bin;
1059 }
1060 if (*sink_elem) {
1061 NVGSTDS_LINK_ELEMENT (pipeline->common_elements.tracker_bin.bin,
1062 *sink_elem);
1063 }
1064 *sink_elem = pipeline->common_elements.tracker_bin.bin;
1065 }
1066
1067 if (config->primary_gie_config.enable) {
1068 if (!create_primary_gie_bin (&config->primary_gie_config,
1069 &pipeline->common_elements.primary_gie_bin)) {
1070 goto done;
1071 }
1072 gst_bin_add (GST_BIN (pipeline->pipeline),
1073 pipeline->common_elements.primary_gie_bin.bin);
1074 if (*sink_elem) {
1075 NVGSTDS_LINK_ELEMENT (pipeline->common_elements.primary_gie_bin.bin,
1076 *sink_elem);
1077 }
1078 *sink_elem = pipeline->common_elements.primary_gie_bin.bin;
1079 if (!*src_elem) {
1080 *src_elem = pipeline->common_elements.primary_gie_bin.bin;
1081 }
1082 NVGSTDS_ELEM_ADD_PROBE (pipeline->common_elements.
1083 primary_bbox_buffer_probe_id,
1084 pipeline->common_elements.primary_gie_bin.bin, "src",
1085 gie_primary_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
1086 pipeline->common_elements.appCtx);
1087 }
1088
1089 if(*src_elem) {
1090 NVGSTDS_ELEM_ADD_PROBE (pipeline->common_elements.
1091 primary_bbox_buffer_probe_id,
1092 *src_elem, "src",
1093 analytics_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
1094 &pipeline->common_elements);
1095
1096 /* Add common message converter */
1097 if (config->msg_conv_config.enable) {
1098 NvDsSinkMsgConvBrokerConfig *convConfig = &config->msg_conv_config;
1099 pipeline->common_elements.msg_conv = gst_element_factory_make (NVDS_ELEM_MSG_CONV, "common_msg_conv");
1100 if (!pipeline->common_elements.msg_conv) {
1101 NVGSTDS_ERR_MSG_V ("Failed to create element 'common_msg_conv'");
1102 goto done;
1103 }
1104
1105 g_object_set (G_OBJECT (pipeline->common_elements.msg_conv),
1106 "config", convConfig->config_file_path,
1107 "msg2p-lib", (convConfig->conv_msg2p_lib ? convConfig->conv_msg2p_lib : "null"),
1108 "payload-type", convConfig->conv_payload_type,
1109 "comp-id", convConfig->conv_comp_id,
1110 "debug-payload-dir", convConfig->debug_payload_dir,
1111 "multiple-payloads", convConfig->multiple_payloads, NULL);
1112
1113 gst_bin_add (GST_BIN (pipeline->pipeline),
1114 pipeline->common_elements.msg_conv);
1115
1116 NVGSTDS_LINK_ELEMENT (*src_elem, pipeline->common_elements.msg_conv);
1117 *src_elem = pipeline->common_elements.msg_conv;
1118 }
1119 pipeline->common_elements.tee = gst_element_factory_make (NVDS_ELEM_TEE, "common_analytics_tee");
1120 if (!pipeline->common_elements.tee) {
1121 NVGSTDS_ERR_MSG_V ("Failed to create element 'common_analytics_tee'");
1122 goto done;
1123 }
1124
1125 gst_bin_add (GST_BIN (pipeline->pipeline),
1126 pipeline->common_elements.tee);
1127
1128 NVGSTDS_LINK_ELEMENT (*src_elem, pipeline->common_elements.tee);
1129 *src_elem = pipeline->common_elements.tee;
1130 }
1131
1132 ret = TRUE;
1133done:
1134 return ret;
1135}
1136
1137static gboolean is_sink_available_for_source_id(NvDsConfig *config, guint source_id) {
1138 for (guint j = 0; j < config->num_sink_sub_bins; j++) {
1139 if (config->sink_bin_sub_bin_config[j].enable &&
1140 config->sink_bin_sub_bin_config[j].source_id == source_id &&
1141 config->sink_bin_sub_bin_config[j].link_to_demux == FALSE) {
1142 return TRUE;
1143 }
1144 }
1145 return FALSE;
1146}
1147
1148
1149
1150static gchar *
1151get_absolute_file_path (gchar *cfg_file_path, gchar *file_path)
1152{
1153 gchar abs_cfg_path[PATH_MAX + 1];
1154 gchar *abs_file_path;
1155 gchar *delim;
1156
1157 if (file_path && file_path[0] == '/') {
1158 return file_path;
1159 }
1160
1161 if (!realpath (cfg_file_path, abs_cfg_path)) {
1162 g_free (file_path);
1163 return NULL;
1164 }
1165
1166 /* Return absolute path of config file if file_path is NULL. */
1167 if (!file_path) {
1168 abs_file_path = g_strdup (abs_cfg_path);
1169 return abs_file_path;
1170 }
1171
1172 delim = g_strrstr (abs_cfg_path, "/");
1173 *(delim + 1) = '\0';
1174
1175 abs_file_path = g_strconcat (abs_cfg_path, file_path, NULL);
1176 g_free (file_path);
1177
1178 return abs_file_path;
1179}
1180
1181
1182static GstPadProbeReturn
1183osd_sink_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info,
1184 gpointer u_data)
1185{
1186 GstBuffer *buf = (GstBuffer *) info->data;
1187 NvDsObjectMeta *obj_meta = NULL;
1188 guint vehicle_count = 0;
1189 guint person_count = 0;
1190 guint lp_count = 0;
1191 guint label_i = 0;
1192 NvDsMetaList * l_frame = NULL;
1193 NvDsMetaList * l_obj = NULL;
1194 NvDsMetaList * l_class = NULL;
1195 NvDsMetaList * l_label = NULL;
1196 NvDsDisplayMeta *display_meta = NULL;
1197 NvDsClassifierMeta * class_meta = NULL;
1198 NvDsLabelInfo * label_info = NULL;
1199 GstClockTime now;
1200 perf_measure * perf = (perf_measure *)(u_data);
1201
1202 NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
1203
1204 now = g_get_monotonic_time();
1205
1206 if (perf->pre_time == GST_CLOCK_TIME_NONE) {
1207 perf->pre_time = now;
1208 perf->total_time = GST_CLOCK_TIME_NONE;
1209 } else {
1210 if (perf->total_time == GST_CLOCK_TIME_NONE) {
1211 perf->total_time = (now - perf->pre_time);
1212 } else {
1213 perf->total_time += (now - perf->pre_time);
1214 }
1215 perf->pre_time = now;
1216 perf->count++;
1217 }
1218
1219 for (l_frame = batch_meta->frame_meta_list; l_frame != NULL;
1220 l_frame = l_frame->next) {
1221 NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) (l_frame->data);
1222 int offset = 0;
1223 if (!frame_meta)
1224 continue;
1225 for (l_obj = frame_meta->obj_meta_list; l_obj != NULL;
1226 l_obj = l_obj->next) {
1227 obj_meta = (NvDsObjectMeta *) (l_obj->data);
1228
1229 if (!obj_meta)
1230 continue;
1231
1232 /* Check that the object has been detected by the primary detector
1233 * and that the class id is that of vehicles/persons. */
1234 if (obj_meta->unique_component_id == PRIMARY_DETECTOR_UID) {
1235 if (obj_meta->class_id == PGIE_CLASS_ID_VEHICLE)
1236 vehicle_count++;
1237 if (obj_meta->class_id == PGIE_CLASS_ID_PERSON)
1238 person_count++;
1239 }
1240
1241 if (obj_meta->unique_component_id == SECONDARY_DETECTOR_UID) {
1242 if (obj_meta->class_id == SGIE_CLASS_ID_LPD) {
1243 lp_count++;
1244 /* Print this info only when operating in secondary model. */
1245 if (obj_meta->parent)
1246 g_print ("License plate found for parent object %p (type=%s)\n",
1247 obj_meta->parent, pgie_classes_str[obj_meta->parent->class_id]);
1248
1249 obj_meta->text_params.set_bg_clr = 1;
1250 obj_meta->text_params.text_bg_clr.red = 0.0;
1251 obj_meta->text_params.text_bg_clr.green = 0.0;
1252 obj_meta->text_params.text_bg_clr.blue = 0.0;
1253 obj_meta->text_params.text_bg_clr.alpha = 0.0;
1254
1255 obj_meta->text_params.font_params.font_color.red = 1.0;
1256 obj_meta->text_params.font_params.font_color.green = 1.0;
1257 obj_meta->text_params.font_params.font_color.blue = 0.0;
1258 obj_meta->text_params.font_params.font_color.alpha = 1.0;
1259 obj_meta->text_params.font_params.font_size = 12;
1260 }
1261 }
1262
1263 for (l_class = obj_meta->classifier_meta_list; l_class != NULL;
1264 l_class = l_class->next) {
1265 class_meta = (NvDsClassifierMeta *)(l_class->data);
1266 if (!class_meta)
1267 continue;
1268 if (class_meta->unique_component_id == SECONDARY_CLASSIFIER_UID) {
1269 for ( label_i = 0, l_label = class_meta->label_info_list;
1270 label_i < class_meta->num_labels && l_label; label_i++,
1271 l_label = l_label->next) {
1272 label_info = (NvDsLabelInfo *)(l_label->data);
1273 if (label_info) {
1274 if (label_info->label_id == 0 && label_info->result_class_id == 1) {
1275 g_print ("Plate License %s\n",label_info->result_label);
1276 }
1277 }
1278 }
1279 }
1280 }
1281 }
1282
1283 display_meta = nvds_acquire_display_meta_from_pool(batch_meta);
1284 NvOSD_TextParams *txt_params = &display_meta->text_params[0];
1285 display_meta->num_labels = 1;
1286 txt_params->display_text = (char*) g_malloc0 (MAX_DISPLAY_LEN);
1287 offset = snprintf(txt_params->display_text, MAX_DISPLAY_LEN,
1288 "Person = %d ", person_count);
1289 offset += snprintf(txt_params->display_text + offset , MAX_DISPLAY_LEN,
1290 "Vehicle = %d ", vehicle_count);
1291
1292 /* Now set the offsets where the string should appear */
1293 txt_params->x_offset = 10;
1294 txt_params->y_offset = 12;
1295
1296 /* Font , font-color and font-size */
1297 char font_n[6];
1298 snprintf(font_n, 6, "Serif");
1299 txt_params->font_params.font_name = font_n;
1300 txt_params->font_params.font_size = 10;
1301 txt_params->font_params.font_color.red = 1.0;
1302 txt_params->font_params.font_color.green = 1.0;
1303 txt_params->font_params.font_color.blue = 1.0;
1304 txt_params->font_params.font_color.alpha = 1.0;
1305
1306 /* Text background color */
1307 txt_params->set_bg_clr = 1;
1308 txt_params->text_bg_clr.red = 0.0;
1309 txt_params->text_bg_clr.green = 0.0;
1310 txt_params->text_bg_clr.blue = 0.0;
1311 txt_params->text_bg_clr.alpha = 1.0;
1312
1313 nvds_add_display_meta_to_frame(frame_meta, display_meta);
1314 }
1315
1316 g_print ("Frame Number = %d Vehicle Count = %d Person Count = %d"
1317 " License Plate Count = %d\n",
1318 frame_number, vehicle_count, person_count,
1319 lp_count);
1320 frame_number++;
1321 total_plate_number += lp_count;
1322 return GST_PAD_PROBE_OK;
1323}
1324
1325
1326/**
1327 * Main function to create the pipeline.
1328 */
1329gboolean
1330create_pipeline (AppCtx * appCtx,
1331 bbox_generated_callback bbox_generated_post_analytics_cb,
1332 bbox_generated_callback all_bbox_generated_cb, perf_callback perf_cb,
1333 overlay_graphics_callback overlay_graphics_cb)
1334{
1335 gboolean ret = FALSE;
1336 NvDsPipeline *pipeline = &appCtx->pipeline;
1337 NvDsConfig *config = &appCtx->config;
1338 GstBus *bus;
1339 GstElement *last_elem;
1340 GstElement *tmp_elem1;
1341 GstElement *tmp_elem2;
1342 guint i;
1343 GstPad *fps_pad;
1344 gulong latency_probe_id;
1345
1346 _dsmeta_quark = g_quark_from_static_string (NVDS_META_STRING);
1347
1348 appCtx->all_bbox_generated_cb = all_bbox_generated_cb;
1349 appCtx->bbox_generated_post_analytics_cb = bbox_generated_post_analytics_cb;
1350 appCtx->overlay_graphics_cb = overlay_graphics_cb;
1351
1352 if (config->osd_config.num_out_buffers < 8) {
1353 config->osd_config.num_out_buffers = 8;
1354 }
1355
1356 pipeline->pipeline = gst_pipeline_new ("pipeline");
1357 if (!pipeline->pipeline) {
1358 NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
1359 goto done;
1360 }
1361
1362 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline->pipeline));
1363 pipeline->bus_id = gst_bus_add_watch (bus, bus_callback, appCtx);
1364 gst_object_unref (bus);
1365
1366 if (config->file_loop) {
1367 /* Let each source bin know it needs to loop. */
1368 guint i;
1369 for (i = 0; i < config->num_source_sub_bins; i++)
1370 config->multi_source_config[i].loop = TRUE;
1371 }
1372
1373 for (guint i = 0; i < config->num_sink_sub_bins; i++) {
1374 NvDsSinkSubBinConfig *sink_config = &config->sink_bin_sub_bin_config[i];
1375 switch (sink_config->type) {
1376 case NV_DS_SINK_FAKE:
1377 case NV_DS_SINK_RENDER_EGL:
1378 case NV_DS_SINK_RENDER_OVERLAY:
1379 /* Set the "qos" property of sink, if not explicitly specified in the
1380 config. */
1381 if (!sink_config->render_config.qos_value_specified) {
1382 sink_config->render_config.qos = FALSE;
1383 }
1384 default:
1385 break;
1386 }
1387 }
1388 /*
1389 * Add muxer and < N > source components to the pipeline based
1390 * on the settings in configuration file.
1391 */
1392 if (!create_multi_source_bin (config->num_source_sub_bins,
1393 config->multi_source_config, &pipeline->multi_src_bin))
1394 goto done;
1395 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->multi_src_bin.bin);
1396
1397
1398 if (config->streammux_config.is_parsed){
1399 if(!set_streammux_properties (&config->streammux_config,
1400 pipeline->multi_src_bin.streammux)){
1401 NVGSTDS_WARN_MSG_V("Failed to set streammux properties");
1402 }
1403 }
1404
1405
1406 if(appCtx->latency_info == NULL)
1407 {
1408 appCtx->latency_info = (NvDsFrameLatencyInfo *)
1409 calloc(1, config->streammux_config.batch_size *
1410 sizeof(NvDsFrameLatencyInfo));
1411 }
1412
1413 /** a tee after the tiler which shall be connected to sink(s) */
1414 pipeline->tiler_tee = gst_element_factory_make (NVDS_ELEM_TEE, "tiler_tee");
1415 if (!pipeline->tiler_tee) {
1416 NVGSTDS_ERR_MSG_V ("Failed to create element 'tiler_tee'");
1417 goto done;
1418 }
1419 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->tiler_tee);
1420
1421 /** Tiler + Demux in Parallel Use-Case */
1422 if (config->tiled_display_config.enable == NV_DS_TILED_DISPLAY_ENABLE_WITH_PARALLEL_DEMUX)
1423 {
1424 pipeline->demuxer =
1425 gst_element_factory_make (NVDS_ELEM_STREAM_DEMUX, "demuxer");
1426 if (!pipeline->demuxer) {
1427 NVGSTDS_ERR_MSG_V ("Failed to create element 'demuxer'");
1428 goto done;
1429 }
1430 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->demuxer);
1431
1432 /** NOTE:
1433 * demux output is supported for only one source
1434 * If multiple [sink] groups are configured with
1435 * link_to_demux=1, only the first [sink]
1436 * shall be constructed for all occurences of
1437 * [sink] groups with link_to_demux=1
1438 */
1439 {
1440 gchar pad_name[16];
1441 GstPad *demux_src_pad;
1442
1443 i = 0;
1444 if (!create_demux_pipeline (appCtx, i)) {
1445 goto done;
1446 }
1447
1448 for (i=0; i < config->num_sink_sub_bins; i++)
1449 {
1450 if (config->sink_bin_sub_bin_config[i].link_to_demux == TRUE)
1451 {
1452 g_snprintf (pad_name, 16, "src_%02d", config->sink_bin_sub_bin_config[i].source_id);
1453 break;
1454 }
1455 }
1456
1457 if (i >= config->num_sink_sub_bins)
1458 {
1459 g_print ("\n\nError : sink for demux (use link-to-demux-only property) is not provided in the config file\n\n");
1460 goto done;
1461 }
1462
1463 i = 0;
1464
1465 gst_bin_add (GST_BIN (pipeline->pipeline),
1466 pipeline->demux_instance_bins[i].bin);
1467
1468 demux_src_pad = gst_element_get_request_pad (pipeline->demuxer, pad_name);
1469 NVGSTDS_LINK_ELEMENT_FULL (pipeline->demuxer, pad_name,
1470 pipeline->demux_instance_bins[i].bin, "sink");
1471 gst_object_unref (demux_src_pad);
1472
1473 NVGSTDS_ELEM_ADD_PROBE(latency_probe_id,
1474 appCtx->pipeline.demux_instance_bins[i].demux_sink_bin.bin,
1475 "sink",
1476 demux_latency_measurement_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
1477 appCtx);
1478 latency_probe_id = latency_probe_id;
1479 }
1480
1481 last_elem = pipeline->demuxer;
1482 link_element_to_tee_src_pad (pipeline->tiler_tee, last_elem);
1483 last_elem = pipeline->tiler_tee;
1484 }
1485
1486 if (config->tiled_display_config.enable) {
1487
1488 /* Tiler will generate a single composited buffer for all sources. So need
1489 * to create only one processing instance. */
1490 if (!create_processing_instance (appCtx, 0)) {
1491 goto done;
1492 }
1493 // create and add tiling component to pipeline.
1494 if (config->tiled_display_config.columns *
1495 config->tiled_display_config.rows < config->num_source_sub_bins) {
1496 if (config->tiled_display_config.columns == 0) {
1497 config->tiled_display_config.columns =
1498 (guint) (sqrt (config->num_source_sub_bins) + 0.5);
1499 }
1500 config->tiled_display_config.rows =
1501 (guint) ceil (1.0 * config->num_source_sub_bins /
1502 config->tiled_display_config.columns);
1503 NVGSTDS_WARN_MSG_V
1504 ("Num of Tiles less than number of sources, readjusting to "
1505 "%u rows, %u columns", config->tiled_display_config.rows,
1506 config->tiled_display_config.columns);
1507 }
1508
1509 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->instance_bins[0].bin);
1510 last_elem = pipeline->instance_bins[0].bin;
1511
1512 if (!create_tiled_display_bin (&config->tiled_display_config,
1513 &pipeline->tiled_display_bin)) {
1514 goto done;
1515 }
1516 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->tiled_display_bin.bin);
1517 NVGSTDS_LINK_ELEMENT (pipeline->tiled_display_bin.bin, last_elem);
1518 last_elem = pipeline->tiled_display_bin.bin;
1519
1520 link_element_to_tee_src_pad (pipeline->tiler_tee, pipeline->tiled_display_bin.bin);
1521 last_elem = pipeline->tiler_tee;
1522
1523 NVGSTDS_ELEM_ADD_PROBE (latency_probe_id,
1524 pipeline->instance_bins->sink_bin.sub_bins[0].sink, "sink",
1525 latency_measurement_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
1526 appCtx);
1527 latency_probe_id = latency_probe_id;
1528 }
1529 else
1530 {
1531 /*
1532 * Create demuxer only if tiled display is disabled.
1533 */
1534 pipeline->demuxer =
1535 gst_element_factory_make (NVDS_ELEM_STREAM_DEMUX, "demuxer");
1536 if (!pipeline->demuxer) {
1537 NVGSTDS_ERR_MSG_V ("Failed to create element 'demuxer'");
1538 goto done;
1539 }
1540 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->demuxer);
1541
1542 for (i = 0; i < config->num_source_sub_bins; i++)
1543 {
1544 gchar pad_name[16];
1545 GstPad *demux_src_pad;
1546
1547 /* Check if any sink has been configured to render/encode output for
1548 * source index `i`. The processing instance for that source will be
1549 * created only if atleast one sink has been configured as such.
1550 */
1551 if (!is_sink_available_for_source_id(config, i))
1552 continue;
1553
1554 if (!create_processing_instance(appCtx, i))
1555 {
1556 goto done;
1557 }
1558 gst_bin_add(GST_BIN(pipeline->pipeline),
1559 pipeline->instance_bins[i].bin);
1560
1561 g_snprintf(pad_name, 16, "src_%02d", i);
1562 demux_src_pad = gst_element_get_request_pad(pipeline->demuxer, pad_name);
1563 NVGSTDS_LINK_ELEMENT_FULL(pipeline->demuxer, pad_name,
1564 pipeline->instance_bins[i].bin, "sink");
1565 gst_object_unref(demux_src_pad);
1566
1567 for (int k = 0; k < MAX_SINK_BINS;k++) {
1568 if(pipeline->instance_bins[i].sink_bin.sub_bins[k].sink){
1569 NVGSTDS_ELEM_ADD_PROBE(latency_probe_id,
1570 pipeline->instance_bins[i].sink_bin.sub_bins[k].sink, "sink",
1571 latency_measurement_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
1572 appCtx);
1573 break;
1574 }
1575 }
1576
1577 latency_probe_id = latency_probe_id;
1578 }
1579 last_elem = pipeline->demuxer;
1580 }
1581
1582 if (config->tiled_display_config.enable == NV_DS_TILED_DISPLAY_DISABLE) {
1583 fps_pad = gst_element_get_static_pad (pipeline->demuxer, "sink");
1584 }
1585 else {
1586 fps_pad = gst_element_get_static_pad (pipeline->tiled_display_bin.bin, "sink");
1587 }
1588
1589 pipeline->common_elements.appCtx = appCtx;
1590 // Decide where in the pipeline the element should be added and add only if
1591 // enabled
1592 if (config->dsexample_config.enable) {
1593 // Create dsexample element bin and set properties
1594 if (!create_dsexample_bin (&config->dsexample_config,
1595 &pipeline->dsexample_bin)) {
1596 goto done;
1597 }
1598 // Add dsexample bin to instance bin
1599 gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->dsexample_bin.bin);
1600
1601 // Link this bin to the last element in the bin
1602 NVGSTDS_LINK_ELEMENT (pipeline->dsexample_bin.bin, last_elem);
1603
1604 // Set this bin as the last element
1605 last_elem = pipeline->dsexample_bin.bin;
1606 }
1607 // create and add common components to pipeline.
1608 if (!create_common_elements (config, pipeline, &tmp_elem1, &tmp_elem2,
1609 bbox_generated_post_analytics_cb)) {
1610 goto done;
1611 }
1612
1613 if(!add_and_link_broker_sink(appCtx)) {
1614 goto done;
1615 }
1616
1617 if (tmp_elem2) {
1618 NVGSTDS_LINK_ELEMENT (tmp_elem2, last_elem);
1619 last_elem = tmp_elem1;
1620 }
1621
1622 NVGSTDS_LINK_ELEMENT (pipeline->multi_src_bin.bin, last_elem);
1623
1624 // enable performance measurement and add call back function to receive
1625 // performance data.
1626 if (config->enable_perf_measurement) {
1627 appCtx->perf_struct.context = appCtx;
1628 enable_perf_measurement (&appCtx->perf_struct, fps_pad,
1629 pipeline->multi_src_bin.num_bins,
1630 config->perf_measurement_interval_sec,
1631 config->multi_source_config[0].dewarper_config.num_surfaces_per_frame,
1632 perf_cb);
1633 }
1634
1635 latency_probe_id = latency_probe_id;
1636
1637 if (config->num_message_consumers) {
1638 for (i = 0; i < config->num_message_consumers; i++) {
1639 appCtx->c2d_ctx[i] = start_cloud_to_device_messaging (
1640 &config->message_consumer_config[i], NULL,
1641 &appCtx->pipeline.multi_src_bin);
1642 if (appCtx->c2d_ctx[i] == NULL) {
1643 NVGSTDS_ERR_MSG_V ("Failed to create message consumer");
1644 goto done;
1645 }
1646 }
1647 }
1648
1649 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->pipeline.pipeline),
1650 GST_DEBUG_GRAPH_SHOW_ALL, "ds-app-null");
1651
1652 g_mutex_init (&appCtx->app_lock);
1653 g_cond_init (&appCtx->app_cond);
1654 g_mutex_init (&appCtx->latency_lock);
1655
1656 ret = TRUE;
1657done:
1658 if (!ret) {
1659 NVGSTDS_ERR_MSG_V ("%s failed", __func__);
1660 }
1661 return ret;
1662}
1663
1664/**
1665 * Function to destroy pipeline and release the resources, probes etc.
1666 */
1667void
1668destroy_pipeline (AppCtx * appCtx)
1669{
1670 gint64 end_time;
1671 NvDsConfig *config = &appCtx->config;
1672 guint i;
1673 GstBus *bus = NULL;
1674
1675 end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND;
1676
1677 if (!appCtx)
1678 return;
1679
1680 if (appCtx->pipeline.demuxer) {
1681 gst_pad_send_event (gst_element_get_static_pad (appCtx->pipeline.demuxer,
1682 "sink"), gst_event_new_eos ());
1683 } else if (appCtx->pipeline.instance_bins[0].sink_bin.bin) {
1684 gst_pad_send_event (gst_element_get_static_pad (appCtx->
1685 pipeline.instance_bins[0].sink_bin.bin, "sink"),
1686 gst_event_new_eos ());
1687 }
1688
1689 g_usleep (100000);
1690
1691 g_mutex_lock (&appCtx->app_lock);
1692 if (appCtx->pipeline.pipeline) {
1693 destroy_smart_record_bin (&appCtx->pipeline.multi_src_bin);
1694 bus = gst_pipeline_get_bus (GST_PIPELINE (appCtx->pipeline.pipeline));
1695
1696 while (TRUE) {
1697 GstMessage *message = gst_bus_pop (bus);
1698 if (message == NULL)
1699 break;
1700 else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR)
1701 bus_callback (bus, message, appCtx);
1702 else
1703 gst_message_unref (message);
1704 }
1705 gst_element_set_state (appCtx->pipeline.pipeline, GST_STATE_NULL);
1706 }
1707 g_cond_wait_until (&appCtx->app_cond, &appCtx->app_lock, end_time);
1708 g_mutex_unlock (&appCtx->app_lock);
1709
1710 for (i = 0; i < appCtx->config.num_source_sub_bins; i++) {
1711 NvDsInstanceBin *bin = &appCtx->pipeline.instance_bins[i];
1712 if (config->osd_config.enable) {
1713 NVGSTDS_ELEM_REMOVE_PROBE (bin->all_bbox_buffer_probe_id,
1714 bin->osd_bin.nvosd, "sink");
1715 } else {
1716 NVGSTDS_ELEM_REMOVE_PROBE (bin->all_bbox_buffer_probe_id,
1717 bin->sink_bin.bin, "sink");
1718 }
1719
1720 if (config->primary_gie_config.enable) {
1721 NVGSTDS_ELEM_REMOVE_PROBE (bin->primary_bbox_buffer_probe_id,
1722 bin->primary_gie_bin.bin, "src");
1723 }
1724
1725 }
1726 if(appCtx->latency_info == NULL)
1727 {
1728 free(appCtx->latency_info);
1729 appCtx->latency_info = NULL;
1730 }
1731
1732 destroy_sink_bin ();
1733 g_mutex_clear(&appCtx->latency_lock);
1734
1735 if (appCtx->pipeline.pipeline) {
1736 bus = gst_pipeline_get_bus (GST_PIPELINE (appCtx->pipeline.pipeline));
1737 gst_bus_remove_watch (bus);
1738 gst_object_unref (bus);
1739 gst_object_unref (appCtx->pipeline.pipeline);
1740 }
1741
1742 if (config->num_message_consumers) {
1743 for (i = 0; i < config->num_message_consumers; i++) {
1744 if (appCtx->c2d_ctx[i])
1745 stop_cloud_to_device_messaging (appCtx->c2d_ctx[i]);
1746 }
1747 }
1748}
1749
1750gboolean
1751pause_pipeline (AppCtx * appCtx)
1752{
1753 GstState cur;
1754 GstState pending;
1755 GstStateChangeReturn ret;
1756 GstClockTime timeout = 5 * GST_SECOND / 1000;
1757
1758 ret =
1759 gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
1760 timeout);
1761
1762 if (ret == GST_STATE_CHANGE_ASYNC) {
1763 return FALSE;
1764 }
1765
1766 if (cur == GST_STATE_PAUSED) {
1767 return TRUE;
1768 } else if (cur == GST_STATE_PLAYING) {
1769 gst_element_set_state (appCtx->pipeline.pipeline, GST_STATE_PAUSED);
1770 gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
1771 GST_CLOCK_TIME_NONE);
1772 pause_perf_measurement (&appCtx->perf_struct);
1773 return TRUE;
1774 } else {
1775 return FALSE;
1776 }
1777}
1778
1779gboolean
1780resume_pipeline (AppCtx * appCtx)
1781{
1782 GstState cur;
1783 GstState pending;
1784 GstStateChangeReturn ret;
1785 GstClockTime timeout = 5 * GST_SECOND / 1000;
1786
1787 ret =
1788 gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
1789 timeout);
1790
1791 if (ret == GST_STATE_CHANGE_ASYNC) {
1792 return FALSE;
1793 }
1794
1795 if (cur == GST_STATE_PLAYING) {
1796 return TRUE;
1797 } else if (cur == GST_STATE_PAUSED) {
1798 gst_element_set_state (appCtx->pipeline.pipeline, GST_STATE_PLAYING);
1799 gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
1800 GST_CLOCK_TIME_NONE);
1801 resume_perf_measurement (&appCtx->perf_struct);
1802 return TRUE;
1803 } else {
1804 return FALSE;
1805 }
1806}