Commit 24460ef5 authored by das-qa's avatar das-qa
Browse files

Add python demo: deepstream-test1, deepstream-test2, deepstream-test3 and deepstream-test4

parent 77e9fc8c
/*
* SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include <linux/limits.h> /* For PATH_MAX */
#include <stdio.h>
#include <string.h>
#include "deepstream_common.h"
#include "deepstream_secondary_gie.h"
#define SECONDARY_GIE_CONFIG_KEY "secondary-gie-config"
#define SECONDARY_GIE_BIN_KEY "secondary-gie-bin"
#define GET_FILE_PATH(path) ((path) + (((path) && strstr ((path), "file://")) ? 7 : 0))
/**
* Wait for all secondary inferences to complete the processing and then send
* the processed buffer to downstream.
* This is way of synchronization between all secondary infers and sending
* buffer once meta data from all secondary infer components got attached.
* This is needed because all secondary infers process same buffer in parallel.
*/
static GstPadProbeReturn
wait_queue_buf_probe (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
NvDsSecondaryGieBin *bin = (NvDsSecondaryGieBin *) u_data;
if (info->type & GST_PAD_PROBE_TYPE_EVENT_BOTH) {
GstEvent *event = (GstEvent *) info->data;
if (event->type == GST_EVENT_EOS) {
return GST_PAD_PROBE_OK;
}
}
if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
g_mutex_lock (&bin->wait_lock);
while (GST_OBJECT_REFCOUNT_VALUE (GST_BUFFER (info->data)) > 1
&& !bin->stop && !bin->flush) {
gint64 end_time;
end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND / 1000;
g_cond_wait_until (&bin->wait_cond, &bin->wait_lock, end_time);
}
g_mutex_unlock (&bin->wait_lock);
}
return GST_PAD_PROBE_OK;
}
/**
* Probe function on sink pad of tee element. It is being used to
* capture EOS event. So that wait for all secondary to finish can be stopped.
* see ::wait_queue_buf_probe
*/
static GstPadProbeReturn
wait_queue_buf_probe1 (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
NvDsSecondaryGieBin *bin = (NvDsSecondaryGieBin *) u_data;
if (info->type & GST_PAD_PROBE_TYPE_EVENT_BOTH) {
GstEvent *event = (GstEvent *) info->data;
if (event->type == GST_EVENT_EOS) {
bin->stop = TRUE;
}
}
return GST_PAD_PROBE_OK;
}
static void
write_infer_output_to_file (GstBuffer * buf,
NvDsInferNetworkInfo * network_info, NvDsInferLayerInfo * layers_info,
guint num_layers, guint batch_size, gpointer user_data)
{
NvDsGieConfig *config = (NvDsGieConfig *) user_data;
guint i;
/* "gst_buffer_get_nvstream_meta()" can be called on the GstBuffer to get more
* information about the buffer.*/
for (i = 0; i < num_layers; i++) {
NvDsInferLayerInfo *info = &layers_info[i];
guint element_size = 0;
FILE *file;
gchar file_name[PATH_MAX];
gchar layer_name[128];
guint j;
switch (info->dataType) {
case FLOAT:
element_size = 4;
break;
case HALF:
element_size = 2;
break;
case INT32:
element_size = 4;
break;
case INT8:
element_size = 1;
break;
case INT64:
element_size = 8;
break;
}
g_strlcpy (layer_name, info->layerName, 128);
for (j = 0; layer_name[j] != '\0'; j++) {
layer_name[j] = (layer_name[j] == '/') ? '_' : layer_name[j];
}
g_snprintf (file_name, PATH_MAX,
"%s/%s_batch%010lu_batchsize%02d.bin",
config->raw_output_directory, layer_name,
config->file_write_frame_num, batch_size);
file_name[PATH_MAX - 1] = '\0';
file = fopen (file_name, "w");
if (!file) {
g_printerr ("Could not open file '%s' for writing:%s\n",
file_name, strerror (errno));
continue;
}
fwrite (info->buffer, element_size,
info->inferDims.numElements * batch_size, file);
fclose (file);
}
config->file_write_frame_num++;
}
/**
* Create secondary infer sub bin and sets properties mentioned
* in configuration file.
*/
static gboolean
create_secondary_gie (NvDsGieConfig * configs1,
NvDsSecondaryGieBinSubBin * subbins1, GstBin * bin, guint index)
{
gboolean ret = FALSE;
gchar elem_name[50];
gchar operate_on_class_str[128];
gint i;
NvDsGieConfig *config = &configs1[index];
NvDsSecondaryGieBinSubBin *subbin = &subbins1[index];
gst_nvinfer_raw_output_generated_callback out_callback =
write_infer_output_to_file;
if (!subbin->create) {
return TRUE;
}
g_snprintf (elem_name, sizeof (elem_name), "secondary_gie_%d_queue", index);
if (subbin->parent_index == -1 ||
subbins1[subbin->parent_index].num_children > 1) {
subbin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!subbin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (bin, subbin->queue);
}
g_snprintf (elem_name, sizeof (elem_name), "secondary_gie_%d", index);
subbin->secondary_gie = gst_element_factory_make (NVDS_ELEM_SGIE, elem_name);
switch (config->plugin_type) {
case NV_DS_GIE_PLUGIN_INFER:
subbin->secondary_gie =
gst_element_factory_make (NVDS_ELEM_SGIE, elem_name);
break;
case NV_DS_GIE_PLUGIN_INFER_SERVER:
subbin->secondary_gie =
gst_element_factory_make (NVDS_ELEM_INFER_SERVER, elem_name);
break;
default:
NVGSTDS_ERR_MSG_V ("Failed to create %s on unknown plugin_type",
elem_name);
goto done;
}
if (!subbin->secondary_gie) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
operate_on_class_str[0] = '\0';
for (i = 0; i < config->num_operate_on_class_ids; i++) {
g_snprintf (operate_on_class_str + strlen (operate_on_class_str),
sizeof (operate_on_class_str) -
strlen (operate_on_class_str) - 1, "%d:",
config->list_operate_on_class_ids[i]);
}
g_object_set (G_OBJECT (subbin->secondary_gie),
"config-file-path", GET_FILE_PATH (config->config_file_path),
"process-mode", 2, NULL);
g_object_set (G_OBJECT (subbin->secondary_gie),
"input-tensor-meta", config->input_tensor_meta, NULL);
if (config->num_operate_on_class_ids != 0)
g_object_set (G_OBJECT (subbin->secondary_gie),
"infer-on-class-ids", operate_on_class_str, NULL);
if (config->is_batch_size_set)
g_object_set (G_OBJECT (subbin->secondary_gie),
"batch-size", config->batch_size, NULL);
if (config->is_interval_set)
g_object_set (G_OBJECT (subbin->secondary_gie),
"interval", config->interval, NULL);
if (config->is_unique_id_set)
g_object_set (G_OBJECT (subbin->secondary_gie),
"unique-id", config->unique_id, NULL);
if (config->is_operate_on_gie_id_set)
g_object_set (G_OBJECT (subbin->secondary_gie),
"infer-on-gie-id", config->operate_on_gie_id, NULL);
if (config->raw_output_directory) {
g_object_set (G_OBJECT (subbin->secondary_gie),
"raw-output-generated-callback", out_callback,
"raw-output-generated-userdata", config, NULL);
}
if (config->is_gpu_id_set &&
NV_DS_GIE_PLUGIN_INFER_SERVER == config->plugin_type) {
NVGSTDS_WARN_MSG_V
("gpu-id: %u in sgie: %s is ignored, only accept nvinferserver config",
config->gpu_id, elem_name);
}
if (NV_DS_GIE_PLUGIN_INFER == config->plugin_type) {
if (config->model_engine_file_path) {
g_object_set (G_OBJECT (subbin->secondary_gie), "model-engine-file",
GET_FILE_PATH (config->model_engine_file_path), NULL);
}
if (config->is_gpu_id_set)
g_object_set (G_OBJECT (subbin->secondary_gie),
"gpu-id", config->gpu_id, NULL);
}
gst_bin_add (bin, subbin->secondary_gie);
if (subbin->num_children == 0) {
g_snprintf (elem_name, sizeof (elem_name), "secondary_gie_%d_sink", index);
subbin->sink =
gst_element_factory_make (NVDS_ELEM_SINK_FAKESINK, elem_name);
if (!subbin->sink) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (bin, subbin->sink);
g_object_set (G_OBJECT (subbin->sink), "async", FALSE, "sync", FALSE,
"enable-last-sample", FALSE, NULL);
}
if (subbin->num_children > 1) {
g_snprintf (elem_name, sizeof (elem_name), "secondary_gie_%d_tee", index);
subbin->tee = gst_element_factory_make (NVDS_ELEM_TEE, elem_name);
if (!subbin->tee) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (bin, subbin->tee);
}
if (subbin->queue) {
NVGSTDS_LINK_ELEMENT (subbin->queue, subbin->secondary_gie);
}
if (subbin->sink) {
NVGSTDS_LINK_ELEMENT (subbin->secondary_gie, subbin->sink);
}
if (subbin->tee) {
NVGSTDS_LINK_ELEMENT (subbin->secondary_gie, subbin->tee);
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/**
* This decides if secondary infer sub bin should be created or not.
* Decision is based on following criteria.
* 1) It is enabled in configuration file.
* 2) operate_on_gie_id should match the provided unique id of primary infer.
* 3) If third or more level of inference is created then operate_on_gie_id
* and unique_id should be created in such a way that third infer operate
* on second's output and second operate on primary's output and so on.
*/
static gboolean
should_create_secondary_gie (NvDsGieConfig * config_array, guint num_configs,
NvDsSecondaryGieBinSubBin * bins, guint index, gint primary_gie_id)
{
NvDsGieConfig *config = &config_array[index];
guint i;
if (!config->enable) {
return FALSE;
}
if (bins[index].create) {
return TRUE;
}
if (config->operate_on_gie_id == primary_gie_id) {
bins[index].create = TRUE;
bins[index].parent_index = -1;
return TRUE;
}
for (i = 0; i < num_configs; i++) {
if (config->operate_on_gie_id == (gint) config_array[i].unique_id) {
if (should_create_secondary_gie (config_array, num_configs, bins, i,
primary_gie_id)) {
bins[index].create = TRUE;
bins[index].parent_index = i;
bins[i].num_children++;
return TRUE;
}
break;
}
}
return FALSE;
}
gboolean
create_secondary_gie_bin (guint num_secondary_gie, guint primary_gie_unique_id,
NvDsGieConfig * config_array, NvDsSecondaryGieBin * bin)
{
gboolean ret = FALSE;
guint i;
GstPad *pad;
bin->bin = gst_bin_new ("secondary_gie_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'secondary_gie_bin'");
goto done;
}
bin->tee = gst_element_factory_make (NVDS_ELEM_TEE, "secondary_gie_bin_tee");
if (!bin->tee) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'secondary_gie_bin_tee'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->tee);
bin->queue =
gst_element_factory_make (NVDS_ELEM_QUEUE, "secondary_gie_bin_queue");
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'secondary_gie_bin_queue'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->queue);
pad = gst_element_get_static_pad (bin->queue, "src");
bin->wait_for_sgie_process_buf_probe_id = gst_pad_add_probe (pad,
(GstPadProbeType) (GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_EVENT_BOTH), wait_queue_buf_probe, bin, NULL);
gst_object_unref (pad);
pad = gst_element_get_static_pad (bin->tee, "sink");
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_EVENT_BOTH, wait_queue_buf_probe1, bin, NULL);
gst_object_unref (pad);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->tee, "sink");
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "src");
if (!link_element_to_tee_src_pad (bin->tee, bin->queue)) {
goto done;
}
for (i = 0; i < num_secondary_gie; i++) {
should_create_secondary_gie (config_array, num_secondary_gie,
bin->sub_bins, i, primary_gie_unique_id);
}
for (i = 0; i < num_secondary_gie; i++) {
if (bin->sub_bins[i].create) {
if (!create_secondary_gie (config_array, bin->sub_bins,
GST_BIN (bin->bin), i)) {
goto done;
}
}
}
for (i = 0; i < num_secondary_gie; i++) {
if (bin->sub_bins[i].create) {
if (bin->sub_bins[i].parent_index == -1) {
link_element_to_tee_src_pad (bin->tee, bin->sub_bins[i].queue);
} else {
if (bin->sub_bins[bin->sub_bins[i].parent_index].tee) {
link_element_to_tee_src_pad (bin->sub_bins[bin->sub_bins[i].
parent_index].tee, bin->sub_bins[i].queue);
} else {
NVGSTDS_LINK_ELEMENT (bin->sub_bins[bin->sub_bins[i].parent_index].
secondary_gie, bin->sub_bins[i].secondary_gie);
}
}
}
}
g_mutex_init (&bin->wait_lock);
g_cond_init (&bin->wait_cond);
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
void
destroy_secondary_gie_bin (NvDsSecondaryGieBin * bin)
{
if (bin->queue && bin->wait_for_sgie_process_buf_probe_id) {
GstPad *pad = gst_element_get_static_pad (bin->queue, "src");
gst_pad_remove_probe (pad, bin->wait_for_sgie_process_buf_probe_id);
gst_object_unref (pad);
}
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include <linux/limits.h> /* For PATH_MAX */
#include <stdio.h>
#include <string.h>
#include "deepstream_common.h"
#include "deepstream_secondary_preprocess.h"
/**
* Wait for all secondary preprocess to complete the processing and then send
* the processed buffer to downstream.
* This is way of synchronization between all secondary preprocess and sending
* buffer once meta data from all secondary infer components got attached.
* This is needed because all secondary preprocess process same buffer in parallel.
*/
static GstPadProbeReturn
wait_queue_buf_probe (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
NvDsSecondaryPreProcessBin *bin = (NvDsSecondaryPreProcessBin *) u_data;
if (info->type & GST_PAD_PROBE_TYPE_EVENT_BOTH) {
GstEvent *event = (GstEvent *) info->data;
if (event->type == GST_EVENT_EOS) {
return GST_PAD_PROBE_OK;
}
}
if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
g_mutex_lock (&bin->wait_lock);
while (GST_OBJECT_REFCOUNT_VALUE (GST_BUFFER (info->data)) > 1
&& !bin->stop && !bin->flush) {
gint64 end_time;
end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND / 1000;
g_cond_wait_until (&bin->wait_cond, &bin->wait_lock, end_time);
}
g_mutex_unlock (&bin->wait_lock);
}
return GST_PAD_PROBE_OK;
}
/**
* Probe function on sink pad of tee element. It is being used to
* capture EOS event. So that wait for all secondary to finish can be stopped.
* see ::wait_queue_buf_probe
*/
static GstPadProbeReturn
wait_queue_buf_probe1 (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
NvDsSecondaryPreProcessBin *bin = (NvDsSecondaryPreProcessBin *) u_data;
if (info->type & GST_PAD_PROBE_TYPE_EVENT_BOTH) {
GstEvent *event = (GstEvent *) info->data;
if (event->type == GST_EVENT_EOS) {
bin->stop = TRUE;
}
}
return GST_PAD_PROBE_OK;
}
/**
* Create secondary preprocess sub bin and sets properties mentioned
* in configuration file.
*/
static gboolean
create_secondary_preprocess (NvDsPreProcessConfig * configs1,
NvDsSecondaryPreProcessBinSubBin * subbins1, GstBin * bin, guint index)
{
gboolean ret = FALSE;
gchar elem_name[50];
NvDsPreProcessConfig *config = &configs1[index];
NvDsSecondaryPreProcessBinSubBin *subbin = &subbins1[index];
if (!subbin->create) {
return TRUE;
}
g_snprintf (elem_name, sizeof (elem_name), "secondary_preprocess_%d_queue",
index);
if (subbin->parent_index == -1 ||
subbins1[subbin->parent_index].num_children > 1) {
subbin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!subbin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (bin, subbin->queue);
}
g_snprintf (elem_name, sizeof (elem_name), "secondary_preprocess_%d", index);
subbin->secondary_preprocess =
gst_element_factory_make (NVDS_ELEM_SECONDARY_PREPROCESS, elem_name);
if (!subbin->secondary_preprocess) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (subbin->secondary_preprocess),
"config-file", config->config_file_path, NULL);
if (config->is_operate_on_gie_id_set) {
g_object_set (G_OBJECT (subbin->secondary_preprocess), "operate-on-gie-id",
config->operate_on_gie_id, NULL);
}
gst_bin_add (bin, subbin->secondary_preprocess);
if (subbin->num_children == 0) {
g_snprintf (elem_name, sizeof (elem_name), "secondary_preprocess_%d_sink",
index);
subbin->sink =
gst_element_factory_make (NVDS_ELEM_SINK_FAKESINK, elem_name);
if (!subbin->sink) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (bin, subbin->sink);
g_object_set (G_OBJECT (subbin->sink), "async", FALSE, "sync", FALSE,
"enable-last-sample", FALSE, NULL);
}
if (subbin->num_children > 1) {
g_snprintf (elem_name, sizeof (elem_name), "secondary_preprocess_%d_tee",
index);
subbin->tee = gst_element_factory_make (NVDS_ELEM_TEE, elem_name);
if (!subbin->tee) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (bin, subbin->tee);
}
if (subbin->queue) {
NVGSTDS_LINK_ELEMENT (subbin->queue, subbin->secondary_preprocess);
}
if (subbin->sink) {
NVGSTDS_LINK_ELEMENT (subbin->secondary_preprocess, subbin->sink);
}
if (subbin->tee) {
NVGSTDS_LINK_ELEMENT (subbin->secondary_preprocess, subbin->tee);
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/**
* This decides if secondary infer sub bin should be created or not.
* Decision is based on following criteria.
* 1) It is enabled in configuration file.
* 2) operate_on_gie_id should match the provided unique id of primary infer.
* 3) If third or more level of inference is created then operate_on_gie_id
* and unique_id should be created in such a way that third infer operate
* on second's output and second operate on primary's output and so on.
*/
static gboolean
should_create_secondary_preprocess (NvDsPreProcessConfig * config_array,
guint num_configs, NvDsSecondaryPreProcessBinSubBin * bins, guint index,
gint primary_gie_id)
{
NvDsPreProcessConfig *config = &config_array[index];
if (!config->enable) {
return FALSE;
}
if (bins[index].create) {
return TRUE;
}
if (config->operate_on_gie_id == primary_gie_id) {
bins[index].create = TRUE;
bins[index].parent_index = -1;
return TRUE;
}
return FALSE;
}
// Create bin, add queue and the element, link all elements and ghost pads,
// Set the element properties from the parsed config
gboolean
create_secondary_preprocess_bin (guint num_secondary_preprocess,
guint primary_gie_unique_id, NvDsPreProcessConfig * config_array,
NvDsSecondaryPreProcessBin * bin)
{
gboolean ret = FALSE;
guint i;
GstPad *pad;
bin->bin = gst_bin_new ("secondary_preprocess_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create 'secondary_preprocess_bin'");
goto done;
}
bin->tee =
gst_element_factory_make (NVDS_ELEM_TEE, "secondary_preprocess_bin_tee");
if (!bin->tee) {
NVGSTDS_ERR_MSG_V
("Failed to create element 'secondary_preprocess_bin_tee'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->tee);
bin->queue =
gst_element_factory_make (NVDS_ELEM_QUEUE, "secondary_preprocess_queue");
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create 'secondary_preprocess_queue'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->queue);
pad = gst_element_get_static_pad (bin->queue, "src");
bin->wait_for_secondary_preprocess_process_buf_probe_id =
gst_pad_add_probe (pad,
(GstPadProbeType) (GST_PAD_PROBE_TYPE_BUFFER |
GST_PAD_PROBE_TYPE_EVENT_BOTH), wait_queue_buf_probe, bin, NULL);
gst_object_unref (pad);
pad = gst_element_get_static_pad (bin->tee, "sink");
gst_pad_add_probe (pad,
GST_PAD_PROBE_TYPE_EVENT_BOTH, wait_queue_buf_probe1, bin, NULL);
gst_object_unref (pad);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->tee, "sink");
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "src");
if (!link_element_to_tee_src_pad (bin->tee, bin->queue)) {
goto done;
}
for (i = 0; i < num_secondary_preprocess; i++) {
should_create_secondary_preprocess (config_array, num_secondary_preprocess,
bin->sub_bins, i, primary_gie_unique_id);
}
for (i = 0; i < num_secondary_preprocess; i++) {
if (bin->sub_bins[i].create) {
if (!create_secondary_preprocess (config_array, bin->sub_bins,
GST_BIN (bin->bin), i)) {
goto done;
}
}
}
for (i = 0; i < num_secondary_preprocess; i++) {
if (bin->sub_bins[i].create) {
if (bin->sub_bins[i].parent_index == -1) {
link_element_to_tee_src_pad (bin->tee, bin->sub_bins[i].queue);
} else {
if (bin->sub_bins[bin->sub_bins[i].parent_index].tee) {
link_element_to_tee_src_pad (bin->sub_bins[bin->sub_bins[i].
parent_index].tee, bin->sub_bins[i].queue);
} else {
NVGSTDS_LINK_ELEMENT (bin->sub_bins[bin->sub_bins[i].parent_index].
secondary_preprocess, bin->sub_bins[i].secondary_preprocess);
}
}
}
}
g_mutex_init (&bin->wait_lock);
g_cond_init (&bin->wait_cond);
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
void
destroy_secondary_preprocess_bin (NvDsSecondaryPreProcessBin * bin)
{
if (bin->queue && bin->wait_for_secondary_preprocess_process_buf_probe_id) {
GstPad *pad = gst_element_get_static_pad (bin->queue, "src");
gst_pad_remove_probe (pad,
bin->wait_for_secondary_preprocess_process_buf_probe_id);
gst_object_unref (pad);
}
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include "deepstream_common.h"
#include "deepstream_segvisual.h"
#define SEG_OUTPUT_WIDTH 1280
#define SEG_OUTPUT_HEIGHT 720
gboolean
create_segvisual_bin (NvDsSegVisualConfig * config, NvDsSegVisualBin * bin)
{
gboolean ret = FALSE;
bin->bin = gst_bin_new ("segvisual_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create 'segvisual_bin'");
goto done;
}
bin->nvvidconv = gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, "segvisual_conv");
if (!bin->nvvidconv) {
NVGSTDS_ERR_MSG_V ("Failed to create 'segvisual_conv'");
goto done;
}
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, "segvisual_queue");
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create 'segvisual_queue'");
goto done;
}
bin->conv_queue =
gst_element_factory_make (NVDS_ELEM_QUEUE, "segvisual_conv_queue");
if (!bin->conv_queue) {
NVGSTDS_ERR_MSG_V ("Failed to create 'segvisual_conv_queue'");
goto done;
}
bin->nvsegvisual = gst_element_factory_make (NVDS_ELEM_SEGVISUAL, "nvsegvisual0");
if (!bin->nvsegvisual) {
NVGSTDS_ERR_MSG_V ("Failed to create 'nvsegvisual0'");
goto done;
}
gst_bin_add_many (GST_BIN (bin->bin), bin->queue,
bin->nvvidconv, bin->conv_queue, bin->nvsegvisual, NULL);
g_object_set (G_OBJECT (bin->nvvidconv), "gpu-id", config->gpu_id, NULL);
g_object_set (G_OBJECT (bin->nvvidconv), "nvbuf-memory-type",
config->nvbuf_memory_type, NULL);
if(config->width != 0 && config->height != 0){
g_object_set (G_OBJECT (bin->nvsegvisual), "width", config->width, "height",
config->height, NULL);
} else {
g_object_set (G_OBJECT (bin->nvsegvisual), "width", SEG_OUTPUT_WIDTH, "height",
SEG_OUTPUT_HEIGHT, NULL);
}
g_object_set (G_OBJECT (bin->nvsegvisual), "gpu-id", config->gpu_id, NULL);
g_object_set (G_OBJECT (bin->nvsegvisual), "batch-size", config->max_batch_size, NULL);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->nvvidconv);
NVGSTDS_LINK_ELEMENT (bin->nvvidconv, bin->conv_queue);
NVGSTDS_LINK_ELEMENT (bin->conv_queue, bin->nvsegvisual);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->nvsegvisual, "src");
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "deepstream_common.h"
#include "deepstream_sinks.h"
#include <gst/rtsp-server/rtsp-server.h>
#include <cuda_runtime_api.h>
static guint uid = 0;
static GstRTSPServer *server[MAX_SINK_BINS];
static guint server_count = 0;
static GMutex server_cnt_lock;
GST_DEBUG_CATEGORY_EXTERN (NVDS_APP);
/**
* Function to create sink bin for Display / Fakesink.
*/
static gboolean
create_render_bin (NvDsSinkRenderConfig * config, NvDsSinkBinSubBin * bin)
{
gboolean ret = FALSE;
gchar elem_name[50];
GstElement *connect_to;
GstCaps *caps = NULL;
uid++;
struct cudaDeviceProp prop;
cudaGetDeviceProperties (&prop, config->gpu_id);
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin%d", uid);
bin->bin = gst_bin_new (elem_name);
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_sink%d", uid);
switch (config->type) {
#ifndef IS_TEGRA
case NV_DS_SINK_RENDER_EGL:
GST_CAT_INFO (NVDS_APP, "NVvideo renderer\n");
bin->sink = gst_element_factory_make (NVDS_ELEM_SINK_EGL, elem_name);
g_object_set (G_OBJECT (bin->sink), "window-x", config->offset_x,
"window-y", config->offset_y, "window-width", config->width,
"window-height", config->height, NULL);
g_object_set (G_OBJECT (bin->sink), "enable-last-sample", FALSE, NULL);
break;
#endif
case NV_DS_SINK_RENDER_DRM:
#ifndef IS_TEGRA
NVGSTDS_ERR_MSG_V ("nvdrmvideosink is only supported for Jetson");
return FALSE;
#endif
GST_CAT_INFO (NVDS_APP, "NVvideo renderer\n");
bin->sink = gst_element_factory_make (NVDS_ELEM_SINK_DRM, elem_name);
if ((gint) config->color_range > -1) {
g_object_set (G_OBJECT (bin->sink), "color-range", config->color_range,
NULL);
}
g_object_set (G_OBJECT (bin->sink), "conn-id", config->conn_id, NULL);
g_object_set (G_OBJECT (bin->sink), "plane-id", config->plane_id, NULL);
if ((gint) config->set_mode > -1) {
g_object_set (G_OBJECT (bin->sink), "set-mode", config->set_mode, NULL);
}
break;
#ifdef IS_TEGRA
case NV_DS_SINK_RENDER_3D:
GST_CAT_INFO (NVDS_APP, "NVvideo renderer\n");
bin->sink = gst_element_factory_make (NVDS_ELEM_SINK_3D, elem_name);
g_object_set (G_OBJECT (bin->sink), "window-x", config->offset_x,
"window-y", config->offset_y, "window-width", config->width,
"window-height", config->height, NULL);
g_object_set (G_OBJECT (bin->sink), "enable-last-sample", FALSE, NULL);
break;
#endif
case NV_DS_SINK_FAKE:
bin->sink = gst_element_factory_make (NVDS_ELEM_SINK_FAKESINK, elem_name);
g_object_set (G_OBJECT (bin->sink), "enable-last-sample", FALSE, NULL);
break;
default:
return FALSE;
}
if (!bin->sink) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (bin->sink), "sync", config->sync, "max-lateness", -1,
"async", FALSE, "qos", config->qos, NULL);
if (!prop.integrated) {
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_cap_filter%d",
uid);
bin->cap_filter =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER, elem_name);
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->cap_filter);
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_transform%d", uid);
#ifndef IS_TEGRA
if (config->type == NV_DS_SINK_RENDER_EGL) {
if (prop.integrated) {
bin->transform =
gst_element_factory_make (NVDS_ELEM_EGLTRANSFORM, elem_name);
} else {
bin->transform =
gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, elem_name);
}
if (!bin->transform) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->transform);
if (!prop.integrated) {
caps = gst_caps_new_empty_simple ("video/x-raw");
GstCapsFeatures *feature = NULL;
feature = gst_caps_features_new (MEMORY_FEATURES, NULL);
gst_caps_set_features (caps, 0, feature);
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
g_object_set (G_OBJECT (bin->transform), "gpu-id", config->gpu_id, NULL);
g_object_set (G_OBJECT (bin->transform), "nvbuf-memory-type",
config->nvbuf_memory_type, NULL);
}
}
#endif
g_snprintf (elem_name, sizeof (elem_name), "render_queue%d", uid);
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
gst_bin_add_many (GST_BIN (bin->bin), bin->queue, bin->sink, NULL);
connect_to = bin->sink;
if (bin->cap_filter) {
NVGSTDS_LINK_ELEMENT (bin->cap_filter, connect_to);
connect_to = bin->cap_filter;
}
if (bin->transform) {
NVGSTDS_LINK_ELEMENT (bin->transform, connect_to);
connect_to = bin->transform;
}
NVGSTDS_LINK_ELEMENT (bin->queue, connect_to);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
ret = TRUE;
done:
if (caps) {
gst_caps_unref (caps);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static void
broker_queue_overrun (GstElement * sink_queue, gpointer user_data)
{
(void) sink_queue;
(void) user_data;
NVGSTDS_WARN_MSG_V ("nvmsgbroker queue overrun; Older Message Buffer "
"Dropped; Network bandwidth might be insufficient\n");
}
/**
* Function to create sink bin to generate meta-msg, convert to json based on
* a schema and send over msgbroker.
*/
static gboolean
create_msg_conv_broker_bin (NvDsSinkMsgConvBrokerConfig * config,
NvDsSinkBinSubBin * bin)
{
/** Create the subbin: -> q -> msgconv -> msgbroker bin */
gboolean ret = FALSE;
gchar elem_name[50];
uid++;
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin%d", uid);
bin->bin = gst_bin_new (elem_name);
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_queue%d", uid);
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
/** set threshold on queue to avoid pipeline choke when broker is stuck on network
* leaky=2 (2): downstream - Leaky on downstream (old buffers) */
g_object_set (G_OBJECT (bin->queue), "leaky", 2, NULL);
g_object_set (G_OBJECT (bin->queue), "max-size-buffers", 20, NULL);
g_signal_connect (G_OBJECT (bin->queue), "overrun",
G_CALLBACK (broker_queue_overrun), bin);
/* Create msg converter to generate payload from buffer metadata */
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_transform%d", uid);
if (config->disable_msgconv) {
bin->transform = gst_element_factory_make ("queue", elem_name);
} else {
bin->transform = gst_element_factory_make (NVDS_ELEM_MSG_CONV, elem_name);
}
if (!bin->transform) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
if (!config->disable_msgconv)
g_object_set (G_OBJECT (bin->transform), "config", config->config_file_path,
"msg2p-lib", (config->conv_msg2p_lib ? config->conv_msg2p_lib : NULL),
"payload-type", config->conv_payload_type,
"comp-id", config->conv_comp_id,
"debug-payload-dir", config->debug_payload_dir,
"multiple-payloads", config->multiple_payloads,
"msg2p-newapi", config->conv_msg2p_new_api,
"frame-interval", config->conv_frame_interval,
"dummy-payload", config->conv_dummy_payload, NULL);
/* Create msg broker to send payload to server */
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_sink%d", uid);
bin->sink = gst_element_factory_make (NVDS_ELEM_MSG_BROKER, elem_name);
if (!bin->sink) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (bin->sink), "proto-lib", config->proto_lib,
"conn-str", config->conn_str,
"topic", config->topic,
"sync", config->sync, "async", FALSE,
"config", config->broker_config_file_path,
"comp-id", config->broker_comp_id, "new-api", config->new_api,
"sleep-time", config->broker_sleep_time, NULL);
gst_bin_add_many (GST_BIN (bin->bin),
bin->queue, bin->transform, bin->sink, NULL);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->transform);
NVGSTDS_LINK_ELEMENT (bin->transform, bin->sink);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/**
* Probe function to drop upstream "GST_QUERY_SEEKING" query from h264parse element.
* This is a WAR to avoid memory leaks from h264parse element
*/
static GstPadProbeReturn
seek_query_drop_prob (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
if (GST_PAD_PROBE_INFO_TYPE (info) & GST_PAD_PROBE_TYPE_QUERY_UPSTREAM) {
GstQuery *query = GST_PAD_PROBE_INFO_QUERY (info);
if (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING) {
return GST_PAD_PROBE_DROP;
}
}
return GST_PAD_PROBE_OK;
}
/**
* Function to create sink bin to generate encoded output.
*/
static gboolean
create_encode_file_bin (NvDsSinkEncoderConfig * config, NvDsSinkBinSubBin * bin)
{
GstCaps *caps = NULL;
gboolean ret = FALSE;
gchar elem_name[50];
int probe_id = 0;
gulong bitrate = config->bitrate;
guint profile = config->profile;
const gchar * latency = g_getenv("NVDS_ENABLE_LATENCY_MEASUREMENT");
uid++;
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin%d", uid);
bin->bin = gst_bin_new (elem_name);
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_queue%d", uid);
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_transform%d", uid);
bin->transform = gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, elem_name);
if (!bin->transform) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (bin->transform), "compute-hw", config->compute_hw, NULL);
#if defined(__aarch64__) && !defined(AARCH64_IS_SBSA)
/* For Jetson, with copy-hw=1 and memory-type=nvbuf-mem-surface-array,
cudaMemcopy fail is observed. This is a WAR till root cause is fixed */
g_object_set (G_OBJECT (bin->transform), "copy-hw", 2, NULL);
#endif
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_cap_filter%d", uid);
bin->cap_filter = gst_element_factory_make (NVDS_ELEM_CAPS_FILTER, elem_name);
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_encoder%d", uid);
switch (config->codec) {
case NV_DS_ENCODER_H264:
if (config->enc_type == NV_DS_ENCODER_TYPE_SW) {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H264_SW, elem_name);
} else {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H264_HW, elem_name);
if (!bin->encoder) {
NVGSTDS_INFO_MSG_V("Could not create HW encoder. Falling back to SW encoder");
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H264_SW, elem_name);
config->enc_type = NV_DS_ENCODER_TYPE_SW;
}
}
break;
case NV_DS_ENCODER_H265:
if (config->enc_type == NV_DS_ENCODER_TYPE_SW) {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H265_SW, elem_name);
} else {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H265_HW, elem_name);
if (!bin->encoder) {
NVGSTDS_INFO_MSG_V("Could not create HW encoder. Falling back to SW encoder");
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H265_SW, elem_name);
config->enc_type = NV_DS_ENCODER_TYPE_SW;
}
}
break;
case NV_DS_ENCODER_MPEG4:
bin->encoder = gst_element_factory_make (NVDS_ELEM_ENC_MPEG4, elem_name);
break;
default:
goto done;
}
if (!bin->encoder) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
if (config->codec == NV_DS_ENCODER_MPEG4
|| config->enc_type == NV_DS_ENCODER_TYPE_SW)
caps = gst_caps_from_string ("video/x-raw, format=I420");
else
caps = gst_caps_from_string ("video/x-raw(memory:NVMM), format=I420");
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
NVGSTDS_ELEM_ADD_PROBE (probe_id,
bin->encoder, "sink",
seek_query_drop_prob, GST_PAD_PROBE_TYPE_QUERY_UPSTREAM, bin);
probe_id = probe_id;
if (config->codec == NV_DS_ENCODER_MPEG4)
config->enc_type = NV_DS_ENCODER_TYPE_SW;
struct cudaDeviceProp prop;
cudaGetDeviceProperties (&prop, config->gpu_id);
if (config->copy_meta == 1) {
g_object_set (G_OBJECT (bin->encoder), "copy-meta", TRUE, NULL);
}
if (config->enc_type == NV_DS_ENCODER_TYPE_HW) {
switch (config->output_io_mode) {
case NV_DS_ENCODER_OUTPUT_IO_MODE_MMAP:
default:
g_object_set (G_OBJECT (bin->encoder), "output-io-mode",
NV_DS_ENCODER_OUTPUT_IO_MODE_MMAP, NULL);
break;
case NV_DS_ENCODER_OUTPUT_IO_MODE_DMABUF_IMPORT:
g_object_set (G_OBJECT (bin->encoder), "output-io-mode",
NV_DS_ENCODER_OUTPUT_IO_MODE_DMABUF_IMPORT, NULL);
break;
}
}
if (config->enc_type == NV_DS_ENCODER_TYPE_HW) {
g_object_set (G_OBJECT (bin->encoder), "profile", profile, NULL);
g_object_set (G_OBJECT (bin->encoder), "iframeinterval",
config->iframeinterval, NULL);
g_object_set (G_OBJECT (bin->encoder), "bitrate", bitrate, NULL);
g_object_set (G_OBJECT (bin->encoder), "gpu-id", config->gpu_id, NULL);
} else {
if (config->codec == NV_DS_ENCODER_MPEG4)
g_object_set (G_OBJECT (bin->encoder), "bitrate", bitrate, NULL);
else {
//bitrate is in kbits/sec for software encoder x264enc and x265enc
g_object_set (G_OBJECT (bin->encoder), "bitrate", bitrate / 1000, NULL);
g_object_set (G_OBJECT (bin->encoder), "speed-preset", config->sw_preset, NULL);
}
}
switch (config->codec) {
case NV_DS_ENCODER_H264:
bin->codecparse = gst_element_factory_make ("h264parse", "h264-parser");
break;
case NV_DS_ENCODER_H265:
bin->codecparse = gst_element_factory_make ("h265parse", "h265-parser");
break;
case NV_DS_ENCODER_MPEG4:
bin->codecparse =
gst_element_factory_make ("mpeg4videoparse", "mpeg4-parser");
break;
default:
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_mux%d", uid);
//disabling the mux when latency measurement logs are enabled
if (latency) {
bin->mux = gst_element_factory_make (NVDS_ELEM_IDENTITY, elem_name);
}
else {
switch (config->container) {
case NV_DS_CONTAINER_MP4:
bin->mux = gst_element_factory_make (NVDS_ELEM_MUX_MP4, elem_name);
break;
case NV_DS_CONTAINER_MKV:
bin->mux = gst_element_factory_make (NVDS_ELEM_MKV, elem_name);
break;
default:
goto done;
}
}
if (!bin->mux) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_sink%d", uid);
bin->sink = gst_element_factory_make (NVDS_ELEM_SINK_FILE, elem_name);
if (!bin->sink) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (bin->sink), "location", config->output_file_path,
"sync", config->sync, "async", FALSE, NULL);
g_object_set (G_OBJECT (bin->transform), "gpu-id", config->gpu_id, NULL);
gst_bin_add_many (GST_BIN (bin->bin), bin->queue,
bin->transform, bin->codecparse, bin->cap_filter,
bin->encoder, bin->mux, bin->sink, NULL);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->transform);
NVGSTDS_LINK_ELEMENT (bin->transform, bin->cap_filter);
NVGSTDS_LINK_ELEMENT (bin->cap_filter, bin->encoder);
NVGSTDS_LINK_ELEMENT (bin->encoder, bin->codecparse);
NVGSTDS_LINK_ELEMENT (bin->codecparse, bin->mux);
NVGSTDS_LINK_ELEMENT (bin->mux, bin->sink);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
ret = TRUE;
done:
if (caps) {
gst_caps_unref (caps);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
start_rtsp_streaming (guint rtsp_port_num, guint updsink_port_num,
NvDsEncoderType enctype, guint64 udp_buffer_size)
{
GstRTSPMountPoints *mounts;
GstRTSPMediaFactory *factory;
char udpsrc_pipeline[512];
char port_num_Str[64] = { 0 };
char *encoder_name;
if (enctype == NV_DS_ENCODER_H264) {
encoder_name = "H264";
} else if (enctype == NV_DS_ENCODER_H265) {
encoder_name = "H265";
} else {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
return FALSE;
}
if (udp_buffer_size == 0)
udp_buffer_size = 512 * 1024;
sprintf (udpsrc_pipeline,
"( udpsrc name=pay0 port=%d buffer-size=%lu caps=\"application/x-rtp, media=video, "
"clock-rate=90000, encoding-name=%s, payload=96 \" )",
updsink_port_num, udp_buffer_size, encoder_name);
sprintf (port_num_Str, "%d", rtsp_port_num);
g_mutex_lock (&server_cnt_lock);
server[server_count] = gst_rtsp_server_new ();
g_object_set (server[server_count], "service", port_num_Str, NULL);
mounts = gst_rtsp_server_get_mount_points (server[server_count]);
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_shared (factory, TRUE);
gst_rtsp_media_factory_set_launch (factory, udpsrc_pipeline);
gst_rtsp_mount_points_add_factory (mounts, "/ds-test", factory);
g_object_unref (mounts);
gst_rtsp_server_attach (server[server_count], NULL);
server_count++;
g_mutex_unlock (&server_cnt_lock);
g_print
("\n *** DeepStream: Launched RTSP Streaming at rtsp://localhost:%d/ds-test ***\n\n",
rtsp_port_num);
return TRUE;
}
static gboolean
create_udpsink_bin (NvDsSinkEncoderConfig * config, NvDsSinkBinSubBin * bin)
{
GstCaps *caps = NULL;
gboolean ret = FALSE;
gchar elem_name[50];
gchar encode_name[50];
gchar rtppay_name[50];
int probe_id = 0;
//guint rtsp_port_num = g_rtsp_port_num++;
uid++;
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin%d", uid);
bin->bin = gst_bin_new (elem_name);
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_queue%d", uid);
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_transform%d", uid);
bin->transform = gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, elem_name);
if (!bin->transform) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (bin->transform), "compute-hw", config->compute_hw, NULL);
#if defined(__aarch64__) && !defined(AARCH64_IS_SBSA)
/* For Jetson, with copy-hw=1 and memory-type=nvbuf-mem-surface-array,
cudaMemcopy fail is observed. This is a WAR till root cause is fixed */
g_object_set (G_OBJECT (bin->transform), "copy-hw", 2, NULL);
#endif
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_cap_filter%d", uid);
bin->cap_filter = gst_element_factory_make (NVDS_ELEM_CAPS_FILTER, elem_name);
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (encode_name, sizeof (encode_name), "sink_sub_bin_encoder%d", uid);
g_snprintf (rtppay_name, sizeof (rtppay_name), "sink_sub_bin_rtppay%d", uid);
switch (config->codec) {
case NV_DS_ENCODER_H264:
bin->codecparse = gst_element_factory_make ("h264parse", "h264-parser");
g_object_set (G_OBJECT (bin->codecparse), "config-interval", -1, NULL);
bin->rtppay = gst_element_factory_make ("rtph264pay", rtppay_name);
if (config->enc_type == NV_DS_ENCODER_TYPE_SW) {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H264_SW, encode_name);
} else {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H264_HW, encode_name);
if (!bin->encoder) {
NVGSTDS_INFO_MSG_V("Could not create HW encoder. Falling back to SW encoder");
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H264_SW, encode_name);
config->enc_type = NV_DS_ENCODER_TYPE_SW;
}
}
break;
case NV_DS_ENCODER_H265:
bin->codecparse = gst_element_factory_make ("h265parse", "h265-parser");
g_object_set (G_OBJECT (bin->codecparse), "config-interval", -1, NULL);
bin->rtppay = gst_element_factory_make ("rtph265pay", rtppay_name);
if (config->enc_type == NV_DS_ENCODER_TYPE_SW) {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H265_SW, encode_name);
} else {
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H265_HW, encode_name);
if (!bin->encoder) {
NVGSTDS_INFO_MSG_V("Could not create HW encoder. Falling back to SW encoder");
bin->encoder =
gst_element_factory_make (NVDS_ELEM_ENC_H265_SW, encode_name);
config->enc_type = NV_DS_ENCODER_TYPE_SW;
}
}
break;
default:
goto done;
}
if (!bin->encoder) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", encode_name);
goto done;
}
if (config->enc_type == NV_DS_ENCODER_TYPE_SW)
caps = gst_caps_from_string ("video/x-raw, format=I420");
else
caps = gst_caps_from_string ("video/x-raw(memory:NVMM), format=I420");
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
NVGSTDS_ELEM_ADD_PROBE (probe_id,
bin->encoder, "sink",
seek_query_drop_prob, GST_PAD_PROBE_TYPE_QUERY_UPSTREAM, bin);
probe_id = probe_id;
if (!bin->rtppay) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", rtppay_name);
goto done;
}
if (config->enc_type == NV_DS_ENCODER_TYPE_SW) {
//bitrate is in kbits/sec for software encoder x264enc and x265enc
g_object_set (G_OBJECT (bin->encoder), "bitrate", config->bitrate / 1000,
NULL);
} else {
g_object_set (G_OBJECT (bin->encoder), "bitrate", config->bitrate, NULL);
g_object_set (G_OBJECT (bin->encoder), "profile", config->profile, NULL);
g_object_set (G_OBJECT (bin->encoder), "iframeinterval",
config->iframeinterval, NULL);
}
struct cudaDeviceProp prop;
cudaGetDeviceProperties (&prop, config->gpu_id);
if (prop.integrated) {
if (config->enc_type == NV_DS_ENCODER_TYPE_HW) {
g_object_set (G_OBJECT (bin->encoder), "preset-level", 1, NULL);
g_object_set (G_OBJECT (bin->encoder), "insert-sps-pps", 1, NULL);
g_object_set (G_OBJECT (bin->encoder), "gpu-id", config->gpu_id, NULL);
}
} else {
g_object_set (G_OBJECT (bin->transform), "gpu-id", config->gpu_id, NULL);
}
g_snprintf (elem_name, sizeof (elem_name), "sink_sub_bin_udpsink%d", uid);
bin->sink = gst_element_factory_make ("udpsink", elem_name);
if (!bin->sink) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_object_set (G_OBJECT (bin->sink), "host", "127.0.0.1", "port",
config->udp_port, "async", FALSE, "sync", config->sync, NULL);
gst_bin_add_many (GST_BIN (bin->bin),
bin->queue, bin->cap_filter, bin->transform,
bin->encoder, bin->codecparse, bin->rtppay, bin->sink, NULL);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->transform);
NVGSTDS_LINK_ELEMENT (bin->transform, bin->cap_filter);
NVGSTDS_LINK_ELEMENT (bin->cap_filter, bin->encoder);
NVGSTDS_LINK_ELEMENT (bin->encoder, bin->codecparse);
NVGSTDS_LINK_ELEMENT (bin->codecparse, bin->rtppay);
NVGSTDS_LINK_ELEMENT (bin->rtppay, bin->sink);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
ret = TRUE;
ret =
start_rtsp_streaming (config->rtsp_port, config->udp_port, config->codec,
config->udp_buffer_size);
if (ret != TRUE) {
g_print ("%s: start_rtsp_straming function failed\n", __func__);
}
done:
if (caps) {
gst_caps_unref (caps);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
gboolean
create_sink_bin (guint num_sub_bins, NvDsSinkSubBinConfig * config_array,
NvDsSinkBin * bin, guint index)
{
gboolean ret = FALSE;
guint i;
bin->bin = gst_bin_new ("sink_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'sink_bin'");
goto done;
}
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, "sink_bin_queue");
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'sink_bin_queue'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->queue);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
bin->tee = gst_element_factory_make (NVDS_ELEM_TEE, "sink_bin_tee");
if (!bin->tee) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'sink_bin_tee'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->tee);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->tee);
g_object_set (G_OBJECT (bin->tee), "allow-not-linked", TRUE, NULL);
for (i = 0; i < num_sub_bins; i++) {
if (!config_array[i].enable) {
continue;
}
if (config_array[i].source_id != index) {
continue;
}
if (config_array[i].link_to_demux) {
continue;
}
switch (config_array[i].type) {
#ifndef IS_TEGRA
case NV_DS_SINK_RENDER_EGL:
#else
case NV_DS_SINK_RENDER_3D:
#endif
case NV_DS_SINK_RENDER_DRM:
case NV_DS_SINK_FAKE:
config_array[i].render_config.type = config_array[i].type;
config_array[i].render_config.sync = config_array[i].sync;
if (!create_render_bin (&config_array[i].render_config,
&bin->sub_bins[i]))
goto done;
break;
case NV_DS_SINK_ENCODE_FILE:
config_array[i].encoder_config.sync = config_array[i].sync;
if (!create_encode_file_bin (&config_array[i].encoder_config,
&bin->sub_bins[i]))
goto done;
break;
case NV_DS_SINK_UDPSINK:
config_array[i].encoder_config.sync = config_array[i].sync;
if (!create_udpsink_bin (&config_array[i].encoder_config,
&bin->sub_bins[i]))
goto done;
break;
case NV_DS_SINK_MSG_CONV_BROKER:
config_array[i].msg_conv_broker_config.sync = config_array[i].sync;
if (!create_msg_conv_broker_bin (&config_array[i].
msg_conv_broker_config, &bin->sub_bins[i]))
goto done;
break;
default:
goto done;
}
if (config_array[i].type != NV_DS_SINK_MSG_CONV_BROKER) {
gst_bin_add (GST_BIN (bin->bin), bin->sub_bins[i].bin);
if (!link_element_to_tee_src_pad (bin->tee, bin->sub_bins[i].bin)) {
goto done;
}
}
bin->num_bins++;
}
if (bin->num_bins == 0) {
NvDsSinkRenderConfig config;
config.type = NV_DS_SINK_FAKE;
if (!create_render_bin (&config, &bin->sub_bins[0]))
goto done;
gst_bin_add (GST_BIN (bin->bin), bin->sub_bins[0].bin);
if (!link_element_to_tee_src_pad (bin->tee, bin->sub_bins[0].bin)) {
goto done;
}
bin->num_bins = 1;
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
gboolean
create_demux_sink_bin (guint num_sub_bins, NvDsSinkSubBinConfig * config_array,
NvDsSinkBin * bin, guint index)
{
gboolean ret = FALSE;
guint i;
bin->bin = gst_bin_new ("sink_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'sink_bin'");
goto done;
}
bin->queue = gst_element_factory_make (NVDS_ELEM_QUEUE, "sink_bin_queue");
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'sink_bin_queue'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->queue);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
bin->tee = gst_element_factory_make (NVDS_ELEM_TEE, "sink_bin_tee");
if (!bin->tee) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'sink_bin_tee'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->tee);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->tee);
for (i = 0; i < num_sub_bins; i++) {
if (!config_array[i].enable) {
continue;
}
if (!config_array[i].link_to_demux) {
continue;
}
switch (config_array[i].type) {
#ifndef IS_TEGRA
case NV_DS_SINK_RENDER_EGL:
#else
case NV_DS_SINK_RENDER_3D:
#endif
case NV_DS_SINK_RENDER_DRM:
case NV_DS_SINK_FAKE:
config_array[i].render_config.type = config_array[i].type;
config_array[i].render_config.sync = config_array[i].sync;
if (!create_render_bin (&config_array[i].render_config,
&bin->sub_bins[i]))
goto done;
break;
case NV_DS_SINK_ENCODE_FILE:
config_array[i].encoder_config.sync = config_array[i].sync;
if (!create_encode_file_bin (&config_array[i].encoder_config,
&bin->sub_bins[i]))
goto done;
break;
case NV_DS_SINK_UDPSINK:
if (!create_udpsink_bin (&config_array[i].encoder_config,
&bin->sub_bins[i]))
goto done;
break;
case NV_DS_SINK_MSG_CONV_BROKER:
config_array[i].msg_conv_broker_config.sync = config_array[i].sync;
if (!create_msg_conv_broker_bin (&config_array[i].
msg_conv_broker_config, &bin->sub_bins[i]))
goto done;
break;
default:
goto done;
}
if (config_array[i].type != NV_DS_SINK_MSG_CONV_BROKER) {
gst_bin_add (GST_BIN (bin->bin), bin->sub_bins[i].bin);
if (!link_element_to_tee_src_pad (bin->tee, bin->sub_bins[i].bin)) {
goto done;
}
}
bin->num_bins++;
}
if (bin->num_bins == 0) {
NvDsSinkRenderConfig config;
config.type = NV_DS_SINK_FAKE;
if (!create_render_bin (&config, &bin->sub_bins[0]))
goto done;
gst_bin_add (GST_BIN (bin->bin), bin->sub_bins[0].bin);
if (!link_element_to_tee_src_pad (bin->tee, bin->sub_bins[0].bin)) {
goto done;
}
bin->num_bins = 1;
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static GstRTSPFilterResult
client_filter (GstRTSPServer * server, GstRTSPClient * client,
gpointer user_data)
{
return GST_RTSP_FILTER_REMOVE;
}
void
destroy_sink_bin ()
{
GstRTSPMountPoints *mounts;
GstRTSPSessionPool *pool;
guint i = 0;
for (i = 0; i < server_count; i++) {
mounts = gst_rtsp_server_get_mount_points (server[i]);
gst_rtsp_mount_points_remove_factory (mounts, "/ds-test");
g_object_unref (mounts);
gst_rtsp_server_client_filter (server[i], client_filter, NULL);
pool = gst_rtsp_server_get_session_pool (server[i]);
gst_rtsp_session_pool_cleanup (pool);
g_object_unref (pool);
}
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include <string.h>
#include <stdio.h>
#include "gstnvdsmeta.h"
#include "deepstream_common.h"
#include "deepstream_sources.h"
#include "deepstream_dewarper.h"
#include <gst/rtp/gstrtcpbuffer.h>
#include <gst/rtsp/gstrtsptransport.h>
#include <cuda_runtime_api.h>
#include "nvdsgstutils.h"
#include "gst-nvdssr.h"
#include "gst-nvevent.h"
#define SRC_CONFIG_KEY "src_config"
#define SOURCE_RESET_INTERVAL_SEC 60
GST_DEBUG_CATEGORY_EXTERN (NVDS_APP);
GST_DEBUG_CATEGORY_EXTERN (APP_CFG_PARSER_CAT);
static gboolean install_mux_eosmonitor_probe = FALSE;
static gboolean
set_camera_csi_params (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
g_object_set (G_OBJECT (bin->src_elem), "sensor-id",
config->camera_csi_sensor_id, NULL);
GST_CAT_DEBUG (NVDS_APP, "Setting csi camera params successful");
return TRUE;
}
static gboolean
set_camera_v4l2_params (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
gchar device[64];
g_snprintf (device, sizeof (device), "/dev/video%d",
config->camera_v4l2_dev_node);
g_object_set (G_OBJECT (bin->src_elem), "device", device, NULL);
GST_CAT_DEBUG (NVDS_APP, "Setting v4l2 camera params successful");
return TRUE;
}
static gboolean
create_camera_source_bin (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
GstCaps *caps = NULL, *caps1 = NULL, *convertCaps = NULL;
gboolean ret = FALSE;
switch (config->type) {
case NV_DS_SOURCE_CAMERA_CSI:
bin->src_elem =
gst_element_factory_make (NVDS_ELEM_SRC_CAMERA_CSI, "src_elem");
break;
case NV_DS_SOURCE_CAMERA_V4L2:
bin->src_elem =
gst_element_factory_make (NVDS_ELEM_SRC_CAMERA_V4L2, "src_elem");
bin->cap_filter1 =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER, "src_cap_filter1");
if (!bin->cap_filter1) {
NVGSTDS_ERR_MSG_V ("Could not create 'src_cap_filter1'");
goto done;
}
caps1 = gst_caps_new_simple ("video/x-raw",
"width", G_TYPE_INT, config->source_width, "height", G_TYPE_INT,
config->source_height, "framerate", GST_TYPE_FRACTION,
config->source_fps_n, config->source_fps_d, NULL);
break;
default:
NVGSTDS_ERR_MSG_V ("Unsupported source type");
goto done;
}
if (!bin->src_elem) {
NVGSTDS_ERR_MSG_V ("Could not create 'src_elem'");
goto done;
}
bin->cap_filter =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER, "src_cap_filter");
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Could not create 'src_cap_filter'");
goto done;
}
if (config->video_format) {
caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
config->video_format, "width", G_TYPE_INT, config->source_width,
"height", G_TYPE_INT, config->source_height, "framerate",
GST_TYPE_FRACTION, config->source_fps_n, config->source_fps_d,
NULL);
} else {
caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, "NV12",
"width", G_TYPE_INT, config->source_width, "height", G_TYPE_INT,
config->source_height, "framerate", GST_TYPE_FRACTION,
config->source_fps_n, config->source_fps_d, NULL);
}
if (config->type == NV_DS_SOURCE_CAMERA_CSI) {
GstCapsFeatures *feature = NULL;
feature = gst_caps_features_new ("memory:NVMM", NULL);
gst_caps_set_features (caps, 0, feature);
}
struct cudaDeviceProp prop;
cudaGetDeviceProperties (&prop, config->gpu_id);
if (config->type == NV_DS_SOURCE_CAMERA_V4L2) {
GstElement *nvvidconv2;
GstCapsFeatures *feature = NULL;
//Check based on igpu/dgpu instead of x86/aarch64
GstElement *nvvidconv1 = NULL;
if (!prop.integrated) {
nvvidconv1 = gst_element_factory_make ("videoconvert", "nvvidconv1");
if (!nvvidconv1) {
NVGSTDS_ERR_MSG_V ("Failed to create 'nvvidconv1'");
goto done;
}
}
feature = gst_caps_features_new ("memory:NVMM", NULL);
gst_caps_set_features (caps, 0, feature);
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
g_object_set (G_OBJECT (bin->cap_filter1), "caps", caps1, NULL);
nvvidconv2 = gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, "nvvidconv2");
if (!nvvidconv2) {
NVGSTDS_ERR_MSG_V ("Failed to create 'nvvidconv2'");
goto done;
}
g_object_set (G_OBJECT (nvvidconv2), "gpu-id", config->gpu_id,
"nvbuf-memory-type", config->nvbuf_memory_type, NULL);
if (!prop.integrated) {
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem, bin->cap_filter1,
nvvidconv1, nvvidconv2, bin->cap_filter, NULL);
} else {
#if defined(__aarch64__) && !defined(AARCH64_IS_SBSA)
/* For Jetson, with copy-hw=1 and memory-type=nvbuf-mem-surface-array,
cudaMemcopy fail is observed. This is a WAR till root cause is fixed */
if (config->type == NV_DS_SOURCE_CAMERA_V4L2) {
g_object_set (G_OBJECT (nvvidconv2), "copy-hw", 2, NULL);
} else {
if (config->nvvideoconvert_copy_hw) {
g_object_set(G_OBJECT(nvvidconv2), "copy-hw", config->nvvideoconvert_copy_hw, NULL);
}
}
#endif
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem, bin->cap_filter1,
nvvidconv2, bin->cap_filter, NULL);
}
NVGSTDS_LINK_ELEMENT (bin->src_elem, bin->cap_filter1);
if (!prop.integrated) {
NVGSTDS_LINK_ELEMENT (bin->cap_filter1, nvvidconv1);
NVGSTDS_LINK_ELEMENT (nvvidconv1, nvvidconv2);
} else {
NVGSTDS_LINK_ELEMENT (bin->cap_filter1, nvvidconv2);
}
NVGSTDS_LINK_ELEMENT (nvvidconv2, bin->cap_filter);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter, "src");
} else {
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem, bin->cap_filter, NULL);
NVGSTDS_LINK_ELEMENT (bin->src_elem, bin->cap_filter);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter, "src");
}
switch (config->type) {
case NV_DS_SOURCE_CAMERA_CSI:
if (!set_camera_csi_params (config, bin)) {
NVGSTDS_ERR_MSG_V ("Could not set CSI camera properties");
goto done;
}
break;
case NV_DS_SOURCE_CAMERA_V4L2:
if (!set_camera_v4l2_params (config, bin)) {
NVGSTDS_ERR_MSG_V ("Could not set V4L2 camera properties");
goto done;
}
break;
default:
NVGSTDS_ERR_MSG_V ("Unsupported source type");
goto done;
}
ret = TRUE;
GST_CAT_DEBUG (NVDS_APP, "Created camera source bin successfully");
done:
if (caps)
gst_caps_unref (caps);
if (convertCaps)
gst_caps_unref (convertCaps);
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static void
cb_newpad (GstElement * decodebin, GstPad * pad, gpointer data)
{
GstCaps *caps = gst_pad_query_caps (pad, NULL);
const GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
if (!strncmp (name, "video", 5)) {
NvDsSrcBin *bin = (NvDsSrcBin *) data;
GstPad *sinkpad = gst_element_get_static_pad (bin->tee, "sink");
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) {
NVGSTDS_ERR_MSG_V ("Failed to link decodebin to pipeline");
} else {
NvDsSourceConfig *config =
(NvDsSourceConfig *) g_object_get_data (G_OBJECT (bin->cap_filter),
SRC_CONFIG_KEY);
gst_structure_get_int (str, "width", &config->source_width);
gst_structure_get_int (str, "height", &config->source_height);
gst_structure_get_fraction (str, "framerate", &config->source_fps_n,
&config->source_fps_d);
GST_CAT_DEBUG (NVDS_APP, "Decodebin linked to pipeline");
}
gst_object_unref (sinkpad);
} else if (g_str_has_prefix (name, "audio/x-raw")) {
NvDsSrcBin *bin = (NvDsSrcBin *) data;
/** skip linking if we did not prepare for audio */
if (!bin->audio_converter) {
return;
}
GstPad *sinkpad = gst_element_get_static_pad (bin->audio_converter, "sink");
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
NVGSTDS_ERR_MSG_V ("Failed to link decodebin to pipeline");
gst_object_unref (sinkpad);
}
}
static void
cb_newpad_audio (GstElement * decodebin, GstPad * pad, gpointer data)
{
GstCaps *caps = gst_pad_query_caps (pad, NULL);
const GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
if (g_str_has_prefix (name, "audio/x-raw")) {
NvDsSrcBin *bin = (NvDsSrcBin *) data;
GstPad *sinkpad = gst_element_get_static_pad (bin->audio_converter, "sink");
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
NVGSTDS_ERR_MSG_V ("Failed to link decodebin to pipeline");
gst_object_unref (sinkpad);
} else if (!strncmp (name, "video", 5)) {
/** connect video to fakesink and ignore the same */
NvDsSrcBin *bin = (NvDsSrcBin *) data;
bin->fakesink = gst_element_factory_make ("fakesink", "src_fakesink");
if (!bin->fakesink) {
NVGSTDS_ERR_MSG_V ("Could not create 'src_fakesink' for video path");
return;
}
g_object_set (G_OBJECT (bin->fakesink), "sync", FALSE, "async", FALSE,
NULL);
gst_bin_add_many (GST_BIN (bin->bin), bin->fakesink, NULL);
GstPad *sinkpad = gst_element_get_static_pad (bin->fakesink, "sink");
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK)
NVGSTDS_ERR_MSG_V ("Failed to link decodebin to pipeline");
gst_object_unref (sinkpad);
}
gst_caps_unref (caps);
}
static void
cb_sourcesetup (GstElement * object, GstElement * arg0, gpointer data)
{
NvDsSrcBin *bin = (NvDsSrcBin *) data;
if (g_object_class_find_property (G_OBJECT_GET_CLASS (arg0), "latency")) {
g_object_set (G_OBJECT (arg0), "latency", bin->latency, NULL);
}
if (bin->udp_buffer_size &&
g_object_class_find_property (G_OBJECT_GET_CLASS (arg0),
"udp-buffer-size")) {
g_object_set (G_OBJECT (arg0), "udp-buffer-size", bin->udp_buffer_size,
NULL);
}
}
/*
* Function to seek the source stream to start.
* It is required to play the stream in loop.
*/
static gboolean
seek_decode (gpointer data)
{
NvDsSrcBin *bin = (NvDsSrcBin *) data;
gboolean ret = TRUE;
gst_element_set_state (bin->bin, GST_STATE_PAUSED);
ret = gst_element_seek (bin->bin, 1.0, GST_FORMAT_TIME,
(GstSeekFlags) (GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH),
GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
if (!ret)
GST_WARNING ("Error in seeking pipeline");
gst_element_set_state (bin->bin, GST_STATE_PLAYING);
return FALSE;
}
/**
* Probe function to drop certain events to support custom
* logic of looping of each source stream.
*/
static GstPadProbeReturn
restart_stream_buf_prob (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
GstEvent *event = GST_EVENT (info->data);
NvDsSrcBin *bin = (NvDsSrcBin *) u_data;
if ((info->type & GST_PAD_PROBE_TYPE_BUFFER)) {
GST_BUFFER_PTS (GST_BUFFER (info->data)) += bin->prev_accumulated_base;
}
if ((info->type & GST_PAD_PROBE_TYPE_EVENT_BOTH)) {
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
g_timeout_add (1, seek_decode, bin);
}
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
GstSegment *segment = NULL;
gst_event_parse_segment (event, (const GstSegment **) &segment);
segment->base = bin->accumulated_base;
bin->prev_accumulated_base = bin->accumulated_base;
bin->accumulated_base += segment->stop;
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
/* QOS events from downstream sink elements cause decoder to drop
* frames after looping the file since the timestamps reset to 0.
* We should drop the QOS events since we have custom logic for
* looping individual sources. */
case GST_EVENT_QOS:
case GST_EVENT_SEGMENT:
case GST_EVENT_FLUSH_START:
case GST_EVENT_FLUSH_STOP:
return GST_PAD_PROBE_DROP;
default:
break;
}
}
return GST_PAD_PROBE_OK;
}
static void
decodebin_child_added (GstChildProxy * child_proxy, GObject * object,
gchar * name, gpointer user_data)
{
NvDsSrcBin *bin = (NvDsSrcBin *) user_data;
NvDsSourceConfig *config = bin->config;
struct cudaDeviceProp prop = {0};
cudaGetDeviceProperties (&prop, config->gpu_id);
if (cudaGetDeviceProperties (&prop,
config->gpu_id) != cudaSuccess) {
NVGSTDS_ERR_MSG_V ("Failed to get properties for GPU %d", config->gpu_id);
}
if (g_strrstr (name, "decodebin") == name) {
g_signal_connect (G_OBJECT (object), "child-added",
G_CALLBACK (decodebin_child_added), user_data);
}
if ((g_strrstr (name, "h264parse") == name) ||
(g_strrstr (name, "h265parse") == name)) {
g_object_set (object, "config-interval", -1, NULL);
}
if (g_strrstr (name, "fakesink") == name) {
g_object_set (object, "enable-last-sample", FALSE, NULL);
}
if (g_strrstr (name, "nvcuvid") == name) {
g_object_set (object, "gpu-id", config->gpu_id, NULL);
g_object_set (G_OBJECT (object), "cuda-memory-type",
config->cuda_memory_type, NULL);
g_object_set (object, "source-id", config->camera_id, NULL);
g_object_set (object, "num-decode-surfaces", config->num_decode_surfaces,
NULL);
if (config->Intra_decode)
g_object_set (object, "Intra-decode", config->Intra_decode, NULL);
}
if (g_strstr_len (name, -1, "omx") == name) {
if (config->Intra_decode)
g_object_set (object, "skip-frames", 2, NULL);
g_object_set (object, "disable-dvfs", TRUE, NULL);
}
if (g_strstr_len (name, -1, "nvv4l2decoder") == name) {
if (config->low_latency_mode)
g_object_set (object, "low-latency-mode", TRUE, NULL);
if (config->Intra_decode)
g_object_set (object, "skip-frames", 2, NULL);
#ifdef __aarch64__
if (g_object_class_find_property (G_OBJECT_GET_CLASS (object),
"enable-max-performance")) {
g_object_set (object, "enable-max-performance", TRUE, NULL);
}
#endif
if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), "gpu-id")) {
g_object_set (object, "gpu-id", config->gpu_id, NULL);
}
if (g_object_class_find_property (G_OBJECT_GET_CLASS (object),
"cudadec-memtype")) {
g_object_set (G_OBJECT (object), "cudadec-memtype",
config->cuda_memory_type, NULL);
}
g_object_set (object, "drop-frame-interval", config->drop_frame_interval,
NULL);
/* extract-sei-type5-data is a valid parameter for nvv4l2decoder
on x86 and ARM_SBSA */
if (!prop.integrated) {
g_object_set (object, "extract-sei-type5-data", config->extract_sei_type5_data,
NULL);
}
g_object_set (object, "num-extra-surfaces", config->num_extra_surfaces,
NULL);
/* Seek only if file is the source. */
if (config->loop && g_strstr_len (config->uri, -1, "file:/") == config->uri) {
NVGSTDS_ELEM_ADD_PROBE (bin->src_buffer_probe, GST_ELEMENT (object),
"sink", restart_stream_buf_prob,
(GstPadProbeType) (GST_PAD_PROBE_TYPE_EVENT_BOTH |
GST_PAD_PROBE_TYPE_EVENT_FLUSH | GST_PAD_PROBE_TYPE_BUFFER), bin);
}
}
done:
return;
}
static void
cb_newpad2 (GstElement * decodebin, GstPad * pad, gpointer data)
{
GstCaps *caps = gst_pad_query_caps (pad, NULL);
const GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
if (!strncmp (name, "video", 5)) {
NvDsSrcBin *bin = (NvDsSrcBin *) data;
GstPad *sinkpad = gst_element_get_static_pad (bin->cap_filter, "sink");
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) {
NVGSTDS_ERR_MSG_V ("Failed to link decodebin to pipeline");
} else {
NvDsSourceConfig *config =
(NvDsSourceConfig *) g_object_get_data (G_OBJECT (bin->cap_filter),
SRC_CONFIG_KEY);
gst_structure_get_int (str, "width", &config->source_width);
gst_structure_get_int (str, "height", &config->source_height);
gst_structure_get_fraction (str, "framerate", &config->source_fps_n,
&config->source_fps_d);
GST_CAT_DEBUG (NVDS_APP, "Decodebin linked to pipeline");
}
gst_object_unref (sinkpad);
}
gst_caps_unref (caps);
}
static void
cb_newpad3 (GstElement * decodebin, GstPad * pad, gpointer data)
{
GstCaps *caps = gst_pad_query_caps (pad, NULL);
const GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (str);
if (g_strrstr (name, "x-rtp")) {
NvDsSrcBin *bin = (NvDsSrcBin *) data;
GstPad *sinkpad = gst_element_get_static_pad (bin->depay, "sink");
if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) {
NVGSTDS_ERR_MSG_V ("Failed to link depay loader to rtsp src");
}
gst_object_unref (sinkpad);
}
gst_caps_unref (caps);
}
/* Returning FALSE from this callback will make rtspsrc ignore the stream.
* Ignore audio and add the proper depay element based on codec. */
static gboolean
cb_rtspsrc_select_stream (GstElement * rtspsrc, guint num, GstCaps * caps,
gpointer user_data)
{
GstStructure *str = gst_caps_get_structure (caps, 0);
const gchar *media = gst_structure_get_string (str, "media");
const gchar *encoding_name = gst_structure_get_string (str, "encoding-name");
gchar elem_name[50];
NvDsSrcBin *bin = (NvDsSrcBin *) user_data;
gboolean ret = FALSE;
gboolean is_video = (!g_strcmp0 (media, "video"));
if (!is_video)
return FALSE;
/* Create and add depay element only if it is not created yet. */
if (!bin->depay) {
g_snprintf (elem_name, sizeof (elem_name), "depay_elem%d", bin->bin_id);
/* Add the proper depay element based on codec. */
if (!g_strcmp0 (encoding_name, "H264")) {
bin->depay = gst_element_factory_make ("rtph264depay", elem_name);
g_snprintf (elem_name, sizeof (elem_name), "h264parse_elem%d",
bin->bin_id);
bin->parser = gst_element_factory_make ("h264parse", elem_name);
} else if (!g_strcmp0 (encoding_name, "H265")) {
bin->depay = gst_element_factory_make ("rtph265depay", elem_name);
g_snprintf (elem_name, sizeof (elem_name), "h265parse_elem%d",
bin->bin_id);
bin->parser = gst_element_factory_make ("h265parse", elem_name);
} else {
NVGSTDS_WARN_MSG_V ("%s not supported", encoding_name);
return FALSE;
}
if (!bin->depay) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
return FALSE;
}
gst_bin_add_many (GST_BIN (bin->bin), bin->depay, bin->parser, NULL);
NVGSTDS_LINK_ELEMENT (bin->depay, bin->parser);
NVGSTDS_LINK_ELEMENT (bin->parser, bin->tee_rtsp_pre_decode);
if (!gst_element_sync_state_with_parent (bin->depay)) {
NVGSTDS_ERR_MSG_V ("'%s' failed to sync state with parent", elem_name);
return FALSE;
}
gst_element_sync_state_with_parent (bin->parser);
}
ret = TRUE;
done:
return ret;
}
void
destroy_smart_record_bin (gpointer data)
{
unsigned int i = 0;
NvDsSrcBin *src_bin;
NvDsSrcParentBin *pbin = (NvDsSrcParentBin *) data;
g_return_if_fail (pbin);
for (i = 0; i < pbin->num_bins; i++) {
src_bin = &pbin->sub_bins[i];
if (src_bin && src_bin->recordCtx)
NvDsSRDestroy ((NvDsSRContext *) src_bin->recordCtx);
}
pbin->num_bins = 0;
}
static gpointer
smart_record_callback (NvDsSRRecordingInfo * info, gpointer userData)
{
static GMutex mutex;
FILE *logfile = NULL;
g_return_val_if_fail (info, NULL);
g_mutex_lock (&mutex);
logfile = fopen ("smart_record.log", "a");
if (logfile) {
fprintf (logfile, "%d:%d:%d:%ldms:%s:%s\n",
info->sessionId, info->width, info->height, info->duration,
info->dirpath, info->filename);
fclose (logfile);
} else {
g_print ("Error in opeing smart record log file\n");
}
g_mutex_unlock (&mutex);
return NULL;
}
/**
* Function called at regular interval to start and stop video recording.
* This is dummy implementation to show the usages of smart record APIs.
* startTime and Duration can be adjusted as per usecase.
*/
static gboolean
smart_record_event_generator (gpointer data)
{
NvDsSRSessionId sessId = 0;
NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
guint startTime = 7;
guint duration = 8;
if (src_bin->config->smart_rec_duration >= 0)
duration = src_bin->config->smart_rec_duration;
if (src_bin->config->smart_rec_start_time >= 0)
startTime = src_bin->config->smart_rec_start_time;
if (src_bin->recordCtx && !src_bin->reconfiguring) {
NvDsSRContext *ctx = (NvDsSRContext *) src_bin->recordCtx;
if (ctx->recordOn) {
NvDsSRStop (ctx, 0);
} else {
NvDsSRStart (ctx, &sessId, startTime, duration, NULL);
}
}
return TRUE;
}
static void
check_rtsp_reconnection_attempts (NvDsSrcBin * src_bin)
{
gboolean remove_probe = TRUE;
guint i = 0;
for (i = 0; i < src_bin->parent_bin->num_bins; i++) {
if (src_bin->parent_bin->sub_bins[i].config->type != NV_DS_SOURCE_RTSP)
continue;
if (src_bin->parent_bin->sub_bins[i].have_eos &&
(src_bin->parent_bin->sub_bins[i].rtsp_reconnect_interval_sec == 0 ||
src_bin->parent_bin->sub_bins[i].rtsp_reconnect_attempts == 0)) {
remove_probe = FALSE;
break;
}
if (src_bin->parent_bin->sub_bins[i].num_rtsp_reconnects <=
src_bin->parent_bin->sub_bins[i].rtsp_reconnect_attempts) {
if (src_bin->parent_bin->sub_bins[i].rtsp_reconnect_interval_sec ||
!src_bin->parent_bin->sub_bins[i].have_eos) {
remove_probe = FALSE;
break;
}
}
}
if (remove_probe) {
GstElement *pipeline =
GST_ELEMENT_PARENT (GST_ELEMENT_PARENT (src_bin->bin));
NVGSTDS_ELEM_REMOVE_PROBE (src_bin->parent_bin->
nvstreammux_eosmonitor_probe, src_bin->parent_bin->streammux, "src");
GST_ELEMENT_ERROR (pipeline, STREAM, FAILED,
("Reconnection attempts exceeded for all sources or EOS received."
" Stopping pipeline"), (NULL));
}
}
/**
* Function called at regular interval to check if NV_DS_SOURCE_RTSP type
* source in the pipeline is down / disconnected. This function try to
* reconnect the source by resetting that source pipeline.
*/
static gboolean
watch_source_status (gpointer data)
{
NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
struct timeval current_time;
gettimeofday (&current_time, NULL);
static struct timeval last_reset_time_global = { 0, 0 };
gdouble time_diff_msec_since_last_reset =
1000.0 * (current_time.tv_sec - last_reset_time_global.tv_sec) +
(current_time.tv_usec - last_reset_time_global.tv_usec) / 1000.0;
if (src_bin->reconfiguring) {
guint time_since_last_reconnect_sec =
current_time.tv_sec - src_bin->last_reconnect_time.tv_sec;
if (time_since_last_reconnect_sec >= SOURCE_RESET_INTERVAL_SEC) {
if (time_diff_msec_since_last_reset > 3000) {
if (src_bin->rtsp_reconnect_attempts == -1 ||
++src_bin->num_rtsp_reconnects <=
src_bin->rtsp_reconnect_attempts) {
last_reset_time_global = current_time;
// source is still not up, reconfigure it again.
reset_source_pipeline (src_bin);
} else {
GST_ELEMENT_WARNING (src_bin->bin, STREAM, FAILED,
("Number of RTSP reconnect attempts exceeded, stopping source: %d",
src_bin->source_id), (NULL));
check_rtsp_reconnection_attempts (src_bin);
GstElement *send_event_element = NULL;
if (src_bin->dewarper_bin.bin != NULL) {
send_event_element = src_bin->dewarper_bin.bin;
} else {
send_event_element = src_bin->cap_filter1;
}
gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_flush_start ());
gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_flush_stop (TRUE));
if (!gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_eos ())) {
GST_ERROR_OBJECT (send_event_element,
"Interrupted, Reconnection event not sent");
}
if (gst_element_set_state (src_bin->bin,
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
GST_ERROR_OBJECT (src_bin->bin, "Can't set source bin to NULL");
}
return FALSE;
}
}
}
} else {
gint time_since_last_buf_sec = 0;
g_mutex_lock (&src_bin->bin_lock);
if (src_bin->last_buffer_time.tv_sec != 0) {
time_since_last_buf_sec =
current_time.tv_sec - src_bin->last_buffer_time.tv_sec;
}
g_mutex_unlock (&src_bin->bin_lock);
// Reset source bin if no buffers are received in the last
// `rtsp_reconnect_interval_sec` seconds.
if (src_bin->rtsp_reconnect_interval_sec > 0 &&
time_since_last_buf_sec >= src_bin->rtsp_reconnect_interval_sec) {
if (time_diff_msec_since_last_reset > 3000) {
if (src_bin->rtsp_reconnect_attempts == -1 ||
++src_bin->num_rtsp_reconnects <=
src_bin->rtsp_reconnect_attempts) {
last_reset_time_global = current_time;
NVGSTDS_WARN_MSG_V
("No data from source %d since last %u sec. Trying reconnection",
src_bin->bin_id, time_since_last_buf_sec);
reset_source_pipeline (src_bin);
} else {
GST_ELEMENT_WARNING (src_bin->bin, STREAM, FAILED,
("Number of RTSP reconnect attempts exceeded, stopping source: %d",
src_bin->source_id), (NULL));
check_rtsp_reconnection_attempts (src_bin);
GstElement *send_event_element = NULL;
if (src_bin->dewarper_bin.bin != NULL) {
send_event_element = src_bin->dewarper_bin.bin;
} else {
send_event_element = src_bin->cap_filter1;
}
gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_flush_start ());
gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_flush_stop (TRUE));
if (!gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_eos ())) {
GST_ERROR_OBJECT (send_event_element,
"Interrupted, Reconnection event not sent");
}
if (gst_element_set_state (src_bin->bin,
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
GST_ERROR_OBJECT (src_bin->bin, "Can't set source bin to NULL");
}
return FALSE;
}
}
}
}
return TRUE;
}
/**
* Function called at regular interval when source bin is
* changing state async. This function watches the state of
* the source bin and sets it to PLAYING if the state of source
* bin stops at PAUSED when changing state ASYNC.
*/
static gboolean
watch_source_async_state_change (gpointer data)
{
NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
GstState state = GST_STATE_NULL, pending = GST_STATE_NULL;
GstStateChangeReturn ret;
ret = gst_element_get_state (src_bin->bin, &state, &pending, 0);
GST_CAT_DEBUG (NVDS_APP, "Bin %d %p: state:%s pending:%s ret:%s",
src_bin->bin_id, src_bin, gst_element_state_get_name (state),
gst_element_state_get_name (pending),
gst_element_state_change_return_get_name (ret));
// Bin is still changing state ASYNC. Wait for some more time.
if (ret == GST_STATE_CHANGE_ASYNC)
return TRUE;
// Bin state change failed / failed to get state
if (ret == GST_STATE_CHANGE_FAILURE) {
src_bin->async_state_watch_running = FALSE;
return FALSE;
}
// Bin successfully changed state to PLAYING. Stop watching state
if (state == GST_STATE_PLAYING) {
src_bin->reconfiguring = FALSE;
src_bin->async_state_watch_running = FALSE;
src_bin->num_rtsp_reconnects = 0;
return FALSE;
}
// Bin has stopped ASYNC state change but has not gone into
// PLAYING. Expliclity set state to PLAYING and keep watching
// state
gst_element_set_state (src_bin->bin, GST_STATE_PLAYING);
return TRUE;
}
/**
* Probe function to monitor data output from rtspsrc.
*/
static GstPadProbeReturn
rtspsrc_monitor_probe_func (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
NvDsSrcBin *bin = (NvDsSrcBin *) u_data;
if (info->type & GST_PAD_PROBE_TYPE_BUFFER) {
g_mutex_lock (&bin->bin_lock);
gettimeofday (&bin->last_buffer_time, NULL);
bin->have_eos = FALSE;
g_mutex_unlock (&bin->bin_lock);
}
if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
if (GST_EVENT_TYPE (info->data) == GST_EVENT_EOS) {
bin->have_eos = TRUE;
check_rtsp_reconnection_attempts (bin);
}
}
return GST_PAD_PROBE_OK;
}
/**
* Probe function to drop EOS events from nvstreammux when RTSP sources
* are being used so that app does not quit from EOS in case of RTSP
* connection errors and tries to reconnect.
*/
static GstPadProbeReturn
nvstreammux_eosmonitor_probe_func (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
GstEvent *event = (GstEvent *) info->data;
if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
return GST_PAD_PROBE_DROP;
}
return GST_PAD_PROBE_OK;
}
static gboolean
create_rtsp_src_bin (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
NvDsSRContext *ctx = NULL;
gboolean ret = FALSE;
gchar elem_name[50];
bin->config = config;
GstCaps *caps = NULL;
GstCapsFeatures *feature = NULL;
bin->latency = config->latency;
bin->udp_buffer_size = config->udp_buffer_size;
bin->rtsp_reconnect_interval_sec = config->rtsp_reconnect_interval_sec;
bin->rtsp_reconnect_attempts = config->rtsp_reconnect_attempts;
bin->num_rtsp_reconnects = 0;
g_snprintf (elem_name, sizeof (elem_name), "src_elem%d", bin->bin_id);
bin->src_elem = gst_element_factory_make ("rtspsrc", elem_name);
if (!bin->src_elem) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_signal_connect (G_OBJECT (bin->src_elem), "select-stream",
G_CALLBACK (cb_rtspsrc_select_stream), bin);
if (config->udp_buffer_size) {
g_object_set (G_OBJECT (bin->src_elem), "udp-buffer-size",
config->udp_buffer_size, NULL);
}
g_object_set (G_OBJECT (bin->src_elem), "location", config->uri, NULL);
g_object_set (G_OBJECT (bin->src_elem), "latency", config->latency, NULL);
g_object_set (G_OBJECT (bin->src_elem), "drop-on-latency", TRUE, NULL);
configure_source_for_ntp_sync (bin->src_elem);
// 0x4 for TCP and 0x7 for All (UDP/UDP-MCAST/TCP)
if ((config->select_rtp_protocol == GST_RTSP_LOWER_TRANS_TCP)
|| (config->select_rtp_protocol == (GST_RTSP_LOWER_TRANS_UDP |
GST_RTSP_LOWER_TRANS_UDP_MCAST | GST_RTSP_LOWER_TRANS_TCP))) {
g_object_set (G_OBJECT (bin->src_elem), "protocols",
config->select_rtp_protocol, NULL);
GST_DEBUG_OBJECT (bin->src_elem,
"RTP Protocol=0x%x (0x4=TCP and 0x7=UDP,TCP,UDPMCAST)----\n",
config->select_rtp_protocol);
}
g_signal_connect (G_OBJECT (bin->src_elem), "pad-added",
G_CALLBACK (cb_newpad3), bin);
g_snprintf (elem_name, sizeof (elem_name), "tee_rtsp_elem%d", bin->bin_id);
bin->tee_rtsp_pre_decode = gst_element_factory_make ("tee", elem_name);
if (!bin->tee_rtsp_pre_decode) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_snprintf (elem_name, sizeof (elem_name), "tee_rtsp_post_decode_elem%d",
bin->bin_id);
bin->tee_rtsp_post_decode = gst_element_factory_make ("tee", elem_name);
if (!bin->tee_rtsp_post_decode) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
if (config->smart_record) {
NvDsSRInitParams params = { 0 };
params.containerType = (NvDsSRContainerType) config->smart_rec_container;
if (config->file_prefix)
params.fileNamePrefix =
g_strdup_printf ("%s_%d", config->file_prefix, config->camera_id);
params.dirpath = config->dir_path;
params.cacheSize = config->smart_rec_cache_size;
params.defaultDuration = config->smart_rec_def_duration;
params.callback = smart_record_callback;
if (NvDsSRCreate (&ctx, &params) != NVDSSR_STATUS_OK) {
NVGSTDS_ERR_MSG_V ("Failed to create smart record bin");
g_free (params.fileNamePrefix);
goto done;
}
g_free (params.fileNamePrefix);
gst_bin_add (GST_BIN (bin->bin), ctx->recordbin);
bin->recordCtx = (gpointer) ctx;
}
g_snprintf (elem_name, sizeof (elem_name), "dec_que%d", bin->bin_id);
bin->dec_que = gst_element_factory_make ("queue", elem_name);
if (!bin->dec_que) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
if (bin->rtsp_reconnect_interval_sec > 0) {
NVGSTDS_ELEM_ADD_PROBE (bin->rtspsrc_monitor_probe, bin->dec_que,
"sink", rtspsrc_monitor_probe_func, GST_PAD_PROBE_TYPE_BUFFER, bin);
install_mux_eosmonitor_probe = TRUE;
} else {
NVGSTDS_ELEM_ADD_PROBE (bin->rtspsrc_monitor_probe, bin->dec_que,
"sink", rtspsrc_monitor_probe_func,
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, bin);
}
g_snprintf (elem_name, sizeof (elem_name), "decodebin_elem%d", bin->bin_id);
bin->decodebin = gst_element_factory_make ("decodebin", elem_name);
if (!bin->decodebin) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_signal_connect (G_OBJECT (bin->decodebin), "pad-added",
G_CALLBACK (cb_newpad2), bin);
g_signal_connect (G_OBJECT (bin->decodebin), "child-added",
G_CALLBACK (decodebin_child_added), bin);
g_snprintf (elem_name, sizeof (elem_name), "src_que%d", bin->bin_id);
bin->cap_filter = gst_element_factory_make (NVDS_ELEM_QUEUE, elem_name);
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
g_mutex_init (&bin->bin_lock);
if (config->dewarper_config.enable) {
if (!create_dewarper_bin (&config->dewarper_config, &bin->dewarper_bin)) {
g_print ("Failed to create dewarper bin \n");
goto done;
}
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem,
bin->tee_rtsp_pre_decode,
bin->dec_que,
bin->decodebin,
bin->cap_filter,
bin->tee_rtsp_post_decode, bin->dewarper_bin.bin, NULL);
} else {
g_snprintf (elem_name, sizeof (elem_name), "nvvidconv_elem%d", bin->bin_id);
bin->nvvidconv = gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, elem_name);
if (!bin->nvvidconv) {
NVGSTDS_ERR_MSG_V ("Could not create element 'nvvidconv_elem'");
goto done;
}
g_object_set (G_OBJECT (bin->nvvidconv), "gpu-id", config->gpu_id,
"nvbuf-memory-type", config->nvbuf_memory_type, NULL);
#if defined(__aarch64__) && !defined(AARCH64_IS_SBSA)
/* For Jetson, with copy-hw=1 and memory-type=nvbuf-mem-surface-array,
cudaMemcopy fail is observed. This is a WAR till root cause is fixed */
g_object_set (G_OBJECT (bin->nvvidconv), "copy-hw", 2, NULL);
#endif
if (config->video_format) {
caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, config->video_format, NULL);
} else {
caps = gst_caps_new_empty_simple ("video/x-raw");
}
feature = gst_caps_features_new ("memory:NVMM", NULL);
gst_caps_set_features (caps, 0, feature);
bin->cap_filter1 =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER,
"src_cap_filter_nvvidconv");
if (!bin->cap_filter1) {
NVGSTDS_ERR_MSG_V ("Could not create 'queue'");
goto done;
}
g_object_set (G_OBJECT (bin->cap_filter1), "caps", caps, NULL);
gst_caps_unref (caps);
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem,
bin->tee_rtsp_pre_decode,
bin->dec_que,
bin->decodebin,
bin->cap_filter,
bin->tee_rtsp_post_decode, bin->nvvidconv, bin->cap_filter1, NULL);
}
link_element_to_tee_src_pad (bin->tee_rtsp_pre_decode, bin->dec_que);
NVGSTDS_LINK_ELEMENT (bin->dec_que, bin->decodebin);
if (ctx)
link_element_to_tee_src_pad (bin->tee_rtsp_pre_decode, ctx->recordbin);
NVGSTDS_LINK_ELEMENT (bin->cap_filter, bin->tee_rtsp_post_decode);
if (config->dewarper_config.enable) {
link_element_to_tee_src_pad (bin->tee_rtsp_post_decode,
bin->dewarper_bin.bin);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->dewarper_bin.bin, "src");
} else {
link_element_to_tee_src_pad (bin->tee_rtsp_post_decode, bin->nvvidconv);
NVGSTDS_LINK_ELEMENT (bin->nvvidconv, bin->cap_filter1);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter1, "src");
}
ret = TRUE;
g_timeout_add (1000, watch_source_status, bin);
// Enable local start / stop events in addition to the one
// received from the server.
if (config->smart_record == 2) {
if (bin->config->smart_rec_interval)
g_timeout_add (bin->config->smart_rec_interval * 1000,
smart_record_event_generator, bin);
else
g_timeout_add (10000, smart_record_event_generator, bin);
}
GST_CAT_DEBUG (NVDS_APP,
"Decode bin created. Waiting for a new pad from decodebin to link");
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
create_audiodecode_src_bin (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
gboolean ret = FALSE;
guint const MAX_CAPS_LEN = 256;
gchar caps_audio_resampler[MAX_CAPS_LEN];
GstCaps *caps = NULL;
bin->config = config;
config->live_source = FALSE;
if (config->type == NV_DS_SOURCE_AUDIO_WAV) {
bin->src_elem =
gst_element_factory_make (NVDS_ELEM_SRC_MULTIFILE, "src_elem");
if (!bin->src_elem) {
NVGSTDS_ERR_MSG_V ("Could not create element 'src_elem'");
goto done;
}
g_object_set (G_OBJECT (bin->src_elem), "location", config->uri, NULL);
g_object_set (G_OBJECT (bin->src_elem), "loop", config->loop, NULL);
bin->decodebin =
gst_element_factory_make (NVDS_ELEM_WAVPARSE, "decodebin_elem");
if (!bin->decodebin) {
NVGSTDS_ERR_MSG_V ("Could not create element 'decodebin_elem'");
goto done;
}
g_object_set (G_OBJECT (bin->decodebin), "ignore-length", config->loop,
NULL);
} else if (config->type == NV_DS_SOURCE_ALSA_SRC) {
bin->src_elem = gst_element_factory_make (NVDS_ELEM_SRC_ALSA, "src_elem");
if (!bin->src_elem) {
NVGSTDS_ERR_MSG_V ("Could not create element 'src_elem'");
goto done;
}
if (config->alsa_device) {
g_object_set (G_OBJECT (bin->src_elem), "device", config->alsa_device,
NULL);
}
} else {
NVGSTDS_ERR_MSG_V ("Source Type (%d) not supported\n", config->type);
goto done;
}
bin->audio_converter =
gst_element_factory_make ("audioconvert", "audio-convert");
if (!bin->audio_converter) {
NVGSTDS_ERR_MSG_V ("Could not create 'audioconvert'");
goto done;
}
bin->audio_resample =
gst_element_factory_make ("audioresample", "audio-resample");
if (!bin->audio_resample) {
NVGSTDS_ERR_MSG_V ("Could not create 'audioresample'");
goto done;
}
bin->cap_filter =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER,
"src_cap_filter_audioresample");
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Could not create src_cap_filter_audioresample");
goto done;
}
if (snprintf (caps_audio_resampler, MAX_CAPS_LEN, "audio/x-raw, rate=%d",
config->input_audio_rate)
<= 0) {
NVGSTDS_ERR_MSG_V ("Could not create caps to force rate=%d",
config->input_audio_rate);
goto done;
}
caps = gst_caps_from_string (caps_audio_resampler);
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
gst_caps_unref (caps);
if (config->type == NV_DS_SOURCE_AUDIO_WAV) {
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem, bin->decodebin,
bin->audio_converter, bin->audio_resample, bin->cap_filter, NULL);
gst_element_link_many (bin->src_elem, bin->decodebin, bin->audio_converter,
bin->audio_resample, bin->cap_filter, NULL);
} else if (config->type == NV_DS_SOURCE_ALSA_SRC) {
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem,
bin->audio_converter, bin->audio_resample, bin->cap_filter, NULL);
gst_element_link_many (bin->src_elem, bin->audio_converter,
bin->audio_resample, bin->cap_filter, NULL);
}
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter, "src");
ret = TRUE;
GST_CAT_DEBUG (NVDS_APP,
"Decode bin created. Waiting for a new pad from decodebin to link");
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
create_uridecode_src_bin_audio (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
gboolean ret = FALSE;
guint const MAX_CAPS_LEN = 256;
gchar caps_audio_resampler[MAX_CAPS_LEN];
GstCaps *caps = NULL;
bin->config = config;
bin->src_elem = gst_element_factory_make (NVDS_ELEM_SRC_URI, "src_elem");
if (!bin->src_elem) {
NVGSTDS_ERR_MSG_V ("Could not create element 'src_elem'");
goto done;
}
bin->latency = config->latency;
bin->udp_buffer_size = config->udp_buffer_size;
if (g_strrstr (config->uri, "file:/")) {
config->live_source = FALSE;
}
if (g_strrstr (config->uri, "rtsp://") == config->uri) {
configure_source_for_ntp_sync (bin->src_elem);
}
g_object_set (G_OBJECT (bin->src_elem), "uri", config->uri, NULL);
g_signal_connect (G_OBJECT (bin->src_elem), "pad-added",
G_CALLBACK (cb_newpad_audio), bin);
bin->audio_converter =
gst_element_factory_make (NVDS_ELEM_AUDIO_CONV, "audioconv_elem");
if (!bin->audio_converter) {
NVGSTDS_ERR_MSG_V ("Could not create element audio_converter");
goto done;
}
bin->audio_resample =
gst_element_factory_make (NVDS_ELEM_AUDIO_RESAMPLER,
"audioresampler_elem");
if (!bin->audio_resample) {
NVGSTDS_ERR_MSG_V ("Could not create element audio_resample");
goto done;
}
bin->cap_filter =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER,
"src_cap_filter_audioresample");
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Could not create src_cap_filter_audioresample");
goto done;
}
if (snprintf (caps_audio_resampler, MAX_CAPS_LEN, "audio/x-raw, rate=%d",
config->input_audio_rate)
<= 0) {
NVGSTDS_ERR_MSG_V ("Could not create caps to force rate=%d",
config->input_audio_rate);
goto done;
}
caps = gst_caps_from_string (caps_audio_resampler);
g_object_set (G_OBJECT (bin->cap_filter), "caps", caps, NULL);
gst_caps_unref (caps);
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem,
bin->audio_converter, bin->audio_resample, bin->cap_filter, NULL);
NVGSTDS_LINK_ELEMENT (bin->audio_converter, bin->audio_resample);
NVGSTDS_LINK_ELEMENT (bin->audio_resample, bin->cap_filter);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter, "src");
ret = TRUE;
GST_CAT_DEBUG (NVDS_APP,
"Decode bin created. Waiting for a new pad from decodebin to link");
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
create_uridecode_src_bin (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
gboolean ret = FALSE;
GstCaps *caps = NULL;
GstCapsFeatures *feature = NULL;
bin->config = config;
bin->src_elem = gst_element_factory_make (NVDS_ELEM_SRC_URI, "src_elem");
if (!bin->src_elem) {
NVGSTDS_ERR_MSG_V ("Could not create element 'src_elem'");
goto done;
}
if (config->dewarper_config.enable) {
if (!create_dewarper_bin (&config->dewarper_config, &bin->dewarper_bin)) {
g_print ("Creating Dewarper bin failed \n");
goto done;
}
}
bin->latency = config->latency;
bin->udp_buffer_size = config->udp_buffer_size;
if (g_strrstr (config->uri, "file:/")) {
config->live_source = FALSE;
}
if (g_strrstr (config->uri, "rtsp://") == config->uri) {
configure_source_for_ntp_sync (bin->src_elem);
}
g_object_set (G_OBJECT (bin->src_elem), "uri", config->uri, NULL);
g_signal_connect (G_OBJECT (bin->src_elem), "pad-added",
G_CALLBACK (cb_newpad), bin);
g_signal_connect (G_OBJECT (bin->src_elem), "child-added",
G_CALLBACK (decodebin_child_added), bin);
g_signal_connect (G_OBJECT (bin->src_elem), "source-setup",
G_CALLBACK (cb_sourcesetup), bin);
bin->cap_filter = gst_element_factory_make (NVDS_ELEM_QUEUE, "queue");
if (!bin->cap_filter) {
NVGSTDS_ERR_MSG_V ("Could not create 'queue'");
goto done;
}
bin->nvvidconv =
gst_element_factory_make (NVDS_ELEM_VIDEO_CONV, "nvvidconv_elem");
if (!bin->nvvidconv) {
NVGSTDS_ERR_MSG_V ("Could not create element 'nvvidconv_elem'");
goto done;
}
g_object_set (G_OBJECT (bin->nvvidconv), "gpu-id", config->gpu_id,
"nvbuf-memory-type", config->nvbuf_memory_type, NULL);
#if defined(__aarch64__) && !defined(AARCH64_IS_SBSA)
/* For Jetson, with copy-hw=1 and memory-type=nvbuf-mem-surface-array,
cudaMemcopy fail is observed. This is a WAR till root cause is fixed */
g_object_set (G_OBJECT (bin->nvvidconv), "copy-hw", 2, NULL);
#endif
if (config->video_format) {
caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, config->video_format, NULL);
} else {
caps = gst_caps_new_empty_simple ("video/x-raw");
}
feature = gst_caps_features_new ("memory:NVMM", NULL);
gst_caps_set_features (caps, 0, feature);
bin->cap_filter1 =
gst_element_factory_make (NVDS_ELEM_CAPS_FILTER,
"src_cap_filter_nvvidconv");
if (!bin->cap_filter1) {
NVGSTDS_ERR_MSG_V ("Could not create 'queue'");
goto done;
}
g_object_set (G_OBJECT (bin->cap_filter1), "caps", caps, NULL);
gst_caps_unref (caps);
g_object_set_data (G_OBJECT (bin->cap_filter), SRC_CONFIG_KEY, config);
gst_bin_add_many (GST_BIN (bin->bin), bin->src_elem, bin->cap_filter,
bin->nvvidconv, bin->cap_filter1, NULL);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->cap_filter1, "src");
bin->fakesink = gst_element_factory_make ("fakesink", "src_fakesink");
if (!bin->fakesink) {
NVGSTDS_ERR_MSG_V ("Could not create 'src_fakesink'");
goto done;
}
bin->fakesink_queue = gst_element_factory_make ("queue", "fakequeue");
if (!bin->fakesink_queue) {
NVGSTDS_ERR_MSG_V ("Could not create 'fakequeue'");
goto done;
}
bin->tee = gst_element_factory_make ("tee", NULL);
if (!bin->tee) {
NVGSTDS_ERR_MSG_V ("Could not create 'tee'");
goto done;
}
gst_bin_add_many (GST_BIN (bin->bin), bin->fakesink, bin->tee,
bin->fakesink_queue, NULL);
NVGSTDS_LINK_ELEMENT (bin->fakesink_queue, bin->fakesink);
if (config->dewarper_config.enable) {
gst_bin_add_many (GST_BIN (bin->bin), bin->dewarper_bin.bin, NULL);
NVGSTDS_LINK_ELEMENT (bin->tee, bin->dewarper_bin.bin);
NVGSTDS_LINK_ELEMENT (bin->dewarper_bin.bin, bin->cap_filter);
} else {
link_element_to_tee_src_pad (bin->tee, bin->cap_filter);
}
NVGSTDS_LINK_ELEMENT (bin->cap_filter, bin->nvvidconv);
NVGSTDS_LINK_ELEMENT (bin->nvvidconv, bin->cap_filter1);
link_element_to_tee_src_pad (bin->tee, bin->fakesink_queue);
g_object_set (G_OBJECT (bin->fakesink), "sync", FALSE, "async", FALSE, NULL);
g_object_set (G_OBJECT (bin->fakesink), "enable-last-sample", FALSE, NULL);
ret = TRUE;
GST_CAT_DEBUG (NVDS_APP,
"Decode bin created. Waiting for a new pad from decodebin to link");
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
gboolean
create_audio_source_bin (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
static guint bin_cnt = 0;
gchar bin_name[64];
g_snprintf (bin_name, 64, "src_bin_%d", bin_cnt++);
bin->bin = gst_bin_new (bin_name);
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create 'src_bin'");
return FALSE;
}
if (!create_audiodecode_src_bin (config, bin)) {
return FALSE;
}
bin->live_source = config->live_source;
GST_CAT_DEBUG (NVDS_APP, "Source bin created");
return TRUE;
}
gboolean
create_source_bin (NvDsSourceConfig * config, NvDsSrcBin * bin)
{
static guint bin_cnt = 0;
gchar bin_name[64];
g_snprintf (bin_name, 64, "src_bin_%d", bin_cnt++);
bin->bin = gst_bin_new (bin_name);
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create 'src_bin'");
return FALSE;
}
switch (config->type) {
case NV_DS_SOURCE_CAMERA_V4L2:
if (!create_camera_source_bin (config, bin)) {
return FALSE;
}
break;
case NV_DS_SOURCE_URI:
if (!create_uridecode_src_bin (config, bin)) {
return FALSE;
}
bin->live_source = config->live_source;
break;
case NV_DS_SOURCE_RTSP:
if (!create_rtsp_src_bin (config, bin)) {
return FALSE;
}
break;
default:
NVGSTDS_ERR_MSG_V ("Source type not yet implemented!\n");
return FALSE;
}
GST_CAT_DEBUG (NVDS_APP, "Source bin created");
return TRUE;
}
gboolean
create_multi_source_bin (guint num_sub_bins, NvDsSourceConfig * configs,
NvDsSrcParentBin * bin)
{
gboolean ret = FALSE;
guint i = 0;
bin->reset_thread = NULL;
bin->bin = gst_bin_new ("multi_src_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'multi_src_bin'");
goto done;
}
g_object_set (bin->bin, "message-forward", TRUE, NULL);
bin->streammux =
gst_element_factory_make (NVDS_ELEM_STREAM_MUX, "src_bin_muxer");
if (!bin->streammux) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'src_bin_muxer'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->streammux);
for (i = 0; i < num_sub_bins; i++) {
if (!configs[i].enable) {
continue;
}
gchar elem_name[50];
g_snprintf (elem_name, sizeof (elem_name), "src_sub_bin%d", i);
bin->sub_bins[i].bin = gst_bin_new (elem_name);
if (!bin->sub_bins[i].bin) {
NVGSTDS_ERR_MSG_V ("Failed to create '%s'", elem_name);
goto done;
}
bin->sub_bins[i].bin_id = bin->sub_bins[i].source_id = i;
configs[i].live_source = TRUE;
bin->live_source = TRUE;
bin->sub_bins[i].eos_done = TRUE;
bin->sub_bins[i].reset_done = TRUE;
bin->sub_bins[i].parent_bin = bin;
switch (configs[i].type) {
case NV_DS_SOURCE_CAMERA_CSI:
case NV_DS_SOURCE_CAMERA_V4L2:
if (!create_camera_source_bin (&configs[i], &bin->sub_bins[i])) {
return FALSE;
}
break;
case NV_DS_SOURCE_URI:
if (!create_uridecode_src_bin (&configs[i], &bin->sub_bins[i])) {
return FALSE;
}
bin->live_source = configs[i].live_source;
break;
case NV_DS_SOURCE_RTSP:
if (!create_rtsp_src_bin (&configs[i], &bin->sub_bins[i])) {
return FALSE;
}
break;
case NV_DS_SOURCE_AUDIO_WAV:
if (!create_audio_source_bin (&configs[i], &bin->sub_bins[i])) {
return FALSE;
}
break;
case NV_DS_SOURCE_AUDIO_URI:
if (!create_uridecode_src_bin_audio (&configs[i], &bin->sub_bins[i])) {
return FALSE;
}
bin->live_source = configs->live_source;
break;
case NV_DS_SOURCE_ALSA_SRC:
if (!create_audio_source_bin (&configs[i], &bin->sub_bins[i])) {
return FALSE;
}
break;
default:
NVGSTDS_ERR_MSG_V ("Source type not yet implemented!\n");
return FALSE;
}
gst_bin_add (GST_BIN (bin->bin), bin->sub_bins[i].bin);
if (!link_element_to_streammux_sink_pad (bin->streammux,
bin->sub_bins[i].bin, i)) {
NVGSTDS_ERR_MSG_V ("source %d cannot be linked to mux's sink pad %p\n", i,
bin->streammux);
goto done;
}
bin->num_bins++;
}
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->streammux, "src");
if (install_mux_eosmonitor_probe) {
NVGSTDS_ELEM_ADD_PROBE (bin->nvstreammux_eosmonitor_probe, bin->streammux,
"src", nvstreammux_eosmonitor_probe_func,
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, bin);
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static void
set_properties_nvuribin (GstElement * element_, NvDsSourceConfig const *config)
{
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(element_)->elementfactory;
if (!g_strcmp0(GST_OBJECT_NAME(factory), "nvurisrcbin"))
g_object_set (element_, "uri", config->uri, NULL);
if (config->num_extra_surfaces)
g_object_set (element_, "num-extra-surfaces", config->num_extra_surfaces,
NULL);
if (config->gpu_id)
g_object_set (element_, "gpu-id", config->gpu_id, NULL);
g_object_set (element_, "cudadec-memtype", config->cuda_memory_type, NULL);
g_object_set (element_, "low-latency-mode", config->low_latency_mode, NULL);
if (config->drop_frame_interval)
g_object_set (element_, "drop-frame-interval", config->drop_frame_interval,
NULL);
if (config->extract_sei_type5_data)
g_object_set (element_, "extract-sei-type5-data", config->extract_sei_type5_data,
NULL);
if (config->select_rtp_protocol)
g_object_set (element_, "select-rtp-protocol", config->select_rtp_protocol,
NULL);
if (config->loop)
g_object_set (element_, "file-loop", config->loop, NULL);
if (config->smart_record)
g_object_set (element_, "smart-record", config->smart_record, NULL);
if (config->smart_rec_cache_size)
g_object_set (element_, "smart-rec-cache", config->smart_rec_cache_size,
NULL);
if (config->smart_rec_container)
g_object_set (element_, "smart-rec-container", config->smart_rec_container,
NULL);
if (config->smart_rec_def_duration)
g_object_set (element_, "smart-rec-default-duration",
config->smart_rec_def_duration, NULL);
if (config->rtsp_reconnect_interval_sec){
g_object_set (element_, "rtsp-reconnect-interval",
config->rtsp_reconnect_interval_sec, NULL);
g_object_set (element_, "rtsp-reconnect-attempts",
config->rtsp_reconnect_attempts, NULL);
}
if (config->latency)
g_object_set (element_, "latency", config->latency, NULL);
if (config->udp_buffer_size)
g_object_set (element_, "udp-buffer-size", config->udp_buffer_size, NULL);
}
gboolean
create_nvmultiurisrcbin_bin (guint num_sub_bins, NvDsSourceConfig * configs,
NvDsSrcParentBin * bin)
{
gboolean ret = FALSE;
guint i = 0;
bin->reset_thread = NULL;
bin->bin = gst_bin_new ("multiuri_src_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'multiuri_src_bin'");
goto done;
}
g_object_set (bin->bin, "message-forward", TRUE, NULL);
bin->nvmultiurisrcbin = bin->streammux =
gst_element_factory_make (NVDS_ELEM_NVMULTIURISRCBIN,
"src_nvmultiurisrcbin");
if (!bin->streammux) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'src_nvmultiurisrcbin'");
goto done;
}
gst_bin_add (GST_BIN (bin->bin), bin->streammux);
/** set properties for the nvurisrcbin if atleast one uri was provided */
for (i = 0; i < num_sub_bins; i++) {
if (!configs[i].enable) {
continue;
}
set_properties_nvuribin (bin->nvmultiurisrcbin, &configs[i]);
}
if (num_sub_bins == 0) {
set_properties_nvuribin (bin->nvmultiurisrcbin, configs);
}
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->streammux, "src");
if (install_mux_eosmonitor_probe) {
NVGSTDS_ELEM_ADD_PROBE (bin->nvstreammux_eosmonitor_probe, bin->streammux,
"src", nvstreammux_eosmonitor_probe_func,
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, bin);
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
gboolean
reset_source_pipeline (gpointer data)
{
NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
GstState state = GST_STATE_NULL, pending = GST_STATE_NULL;
GstStateChangeReturn ret;
g_mutex_lock (&src_bin->bin_lock);
gettimeofday (&src_bin->last_buffer_time, NULL);
gettimeofday (&src_bin->last_reconnect_time, NULL);
g_mutex_unlock (&src_bin->bin_lock);
GstElement *send_event_element = NULL;
if (src_bin->dewarper_bin.bin != NULL) {
send_event_element = src_bin->dewarper_bin.bin;
} else {
send_event_element = src_bin->cap_filter1;
}
gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_flush_start ());
gst_element_send_event (GST_ELEMENT (send_event_element),
gst_event_new_flush_stop (TRUE));
if (gst_element_set_state (src_bin->bin,
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE) {
GST_ERROR_OBJECT (src_bin->bin, "Can't set source bin to NULL");
return FALSE;
}
NVGSTDS_INFO_MSG_V ("Resetting source %d", src_bin->bin_id);
GST_CAT_INFO (NVDS_APP, "Reset source pipeline %s %p\n,", __func__, src_bin);
if (!gst_element_sync_state_with_parent (src_bin->bin)) {
GST_ERROR_OBJECT (src_bin->bin, "Couldn't sync state with parent");
}
if (src_bin->parser != NULL) {
if (!gst_element_send_event (GST_ELEMENT (src_bin->parser),
gst_nvevent_new_stream_reset (0)))
GST_ERROR_OBJECT (src_bin->parser,
"Interrupted, Reconnection event not sent");
}
ret = gst_element_get_state (src_bin->bin, &state, &pending, 0);
GST_CAT_DEBUG (NVDS_APP, "Bin %d %p: state:%s pending:%s ret:%s",
src_bin->bin_id, src_bin, gst_element_state_get_name (state),
gst_element_state_get_name (pending),
gst_element_state_change_return_get_name (ret));
if (ret == GST_STATE_CHANGE_ASYNC || ret == GST_STATE_CHANGE_NO_PREROLL) {
if (!src_bin->async_state_watch_running)
g_timeout_add (20, watch_source_async_state_change, src_bin);
src_bin->async_state_watch_running = TRUE;
src_bin->reconfiguring = TRUE;
} else if (ret == GST_STATE_CHANGE_SUCCESS && state == GST_STATE_PLAYING) {
src_bin->reconfiguring = FALSE;
}
return FALSE;
}
gboolean
set_source_to_playing (gpointer data)
{
NvDsSrcBin *subBin = (NvDsSrcBin *) data;
if (subBin->reconfiguring) {
gst_element_set_state (subBin->bin, GST_STATE_PLAYING);
GST_CAT_INFO (NVDS_APP, "Reconfiguring %s %p\n,", __func__, subBin);
subBin->reconfiguring = FALSE;
}
return FALSE;
}
gpointer
reset_encodebin (gpointer data)
{
NvDsSrcBin *src_bin = (NvDsSrcBin *) data;
g_usleep (10000);
GST_CAT_INFO (NVDS_APP, "Reset called %s %p\n,", __func__, src_bin);
GST_CAT_INFO (NVDS_APP, "Reset setting null for sink %s %p\n,", __func__,
src_bin);
src_bin->reset_done = TRUE;
return NULL;
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include "deepstream_common.h"
#include "deepstream_streammux.h"
#include <string.h>
// Create bin, add queue and the element, link all elements and ghost pads,
// Set the element properties from the parsed config
gboolean
set_streammux_properties (NvDsStreammuxConfig * config, GstElement * element)
{
gboolean ret = FALSE;
const gchar *new_mux_str = g_getenv ("USE_NEW_NVSTREAMMUX");
gboolean use_new_mux = !g_strcmp0 (new_mux_str, "yes");
if (!use_new_mux) {
g_object_set (G_OBJECT (element), "gpu-id", config->gpu_id, NULL);
g_object_set (G_OBJECT (element), "nvbuf-memory-type",
config->nvbuf_memory_type, NULL);
g_object_set (G_OBJECT (element), "live-source", config->live_source, NULL);
g_object_set (G_OBJECT (element),
"batched-push-timeout", config->batched_push_timeout, NULL);
g_object_set (G_OBJECT (element), "compute-hw", config->compute_hw, NULL);
if (config->buffer_pool_size >= 4) {
g_object_set (G_OBJECT (element),
"buffer-pool-size", config->buffer_pool_size, NULL);
}
g_object_set (G_OBJECT (element), "enable-padding",
config->enable_padding, NULL);
if (config->pipeline_width && config->pipeline_height) {
g_object_set (G_OBJECT (element), "width", config->pipeline_width, NULL);
g_object_set (G_OBJECT (element), "height",
config->pipeline_height, NULL);
}
if (!config->use_nvmultiurisrcbin) {
g_object_set (G_OBJECT (element), "async-process",
config->async_process, NULL);
}
}
if (config->batch_size && !config->use_nvmultiurisrcbin) {
g_object_set (G_OBJECT (element), "batch-size", config->batch_size, NULL);
}
g_object_set (G_OBJECT (element), "attach-sys-ts",
config->attach_sys_ts_as_ntp, NULL);
if (config->config_file_path) {
g_object_set (G_OBJECT (element),
"config-file-path", GET_FILE_PATH (config->config_file_path), NULL);
}
g_object_set (G_OBJECT (element), "frame-duration",
config->frame_duration, NULL);
g_object_set (G_OBJECT (element), "frame-num-reset-on-stream-reset",
config->frame_num_reset_on_stream_reset, NULL);
g_object_set (G_OBJECT (element), "sync-inputs", config->sync_inputs, NULL);
g_object_set (G_OBJECT (element), "max-latency", config->max_latency, NULL);
g_object_set (G_OBJECT (element), "frame-num-reset-on-eos",
config->frame_num_reset_on_eos, NULL);
g_object_set (G_OBJECT (element), "drop-pipeline-eos", config->no_pipeline_eos,
NULL);
if (config->extract_sei_type5_data) {
g_object_set (G_OBJECT (element), "extract-sei-type5-data", config->extract_sei_type5_data,
NULL);
}
if (config->num_surface_per_frame > 1) {
g_object_set (G_OBJECT (element), "num-surfaces-per-frame",
config->num_surface_per_frame, NULL);
}
ret = TRUE;
return ret;
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include "deepstream_common.h"
#include "deepstream_tiled_display.h"
gboolean
create_tiled_display_bin (NvDsTiledDisplayConfig * config,
NvDsTiledDisplayBin * bin)
{
gboolean ret = FALSE;
bin->bin = gst_bin_new ("tiled_display_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create 'tiled_display_bin'");
goto done;
}
bin->queue =
gst_element_factory_make (NVDS_ELEM_QUEUE, "tiled_display_queue");
if (!bin->queue) {
NVGSTDS_ERR_MSG_V ("Failed to create 'tiled_display_queue'");
goto done;
}
if ((config->enable == 1) || (config->enable == 2))
bin->tiler =
gst_element_factory_make (NVDS_ELEM_TILER, "tiled_display_tiler");
else if (config->enable == 3)
bin->tiler =
gst_element_factory_make ("identity", "tiled_display_identity");
if (!bin->tiler) {
NVGSTDS_ERR_MSG_V ("Failed to create 'tiled_display_tiler'");
goto done;
}
if (config->width)
g_object_set (G_OBJECT (bin->tiler), "width", config->width, NULL);
if (config->height)
g_object_set (G_OBJECT (bin->tiler), "height", config->height, NULL);
if (config->rows)
g_object_set (G_OBJECT (bin->tiler), "rows", config->rows, NULL);
if (config->columns)
g_object_set (G_OBJECT (bin->tiler), "columns", config->columns, NULL);
if (config->buffer_pool_size)
g_object_set (G_OBJECT (bin->tiler), "buffer-pool-size",
config->buffer_pool_size, NULL);
if (config->square_seq_grid)
g_object_set (G_OBJECT (bin->tiler), "square-seq-grid", config->square_seq_grid, NULL);
#ifdef IS_TEGRA
if (config->compute_hw)
g_object_set (G_OBJECT (bin->tiler), "compute-hw", config->compute_hw,
NULL);
#endif
g_object_set (G_OBJECT (bin->tiler), "gpu-id", config->gpu_id,
"nvbuf-memory-type", config->nvbuf_memory_type, NULL);
gst_bin_add_many (GST_BIN (bin->bin), bin->queue, bin->tiler, NULL);
NVGSTDS_LINK_ELEMENT (bin->queue, bin->tiler);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->queue, "sink");
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->tiler, "src");
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: LicenseRef-NvidiaProprietary
*
* NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
* property and proprietary rights in and to this material, related
* documentation and any modifications thereto. Any use, reproduction,
* disclosure or distribution of this material and related documentation
* without an express license agreement from NVIDIA CORPORATION or
* its affiliates is strictly prohibited.
*/
#include "deepstream_common.h"
#include "deepstream_tracker.h"
GST_DEBUG_CATEGORY_EXTERN (NVDS_APP);
gboolean
create_tracking_bin (NvDsTrackerConfig * config, NvDsTrackerBin * bin)
{
gboolean ret = FALSE;
bin->bin = gst_bin_new ("tracking_bin");
if (!bin->bin) {
NVGSTDS_ERR_MSG_V ("Failed to create 'tracking_bin'");
goto done;
}
bin->tracker =
gst_element_factory_make (NVDS_ELEM_TRACKER, "tracking_tracker");
if (!bin->tracker) {
NVGSTDS_ERR_MSG_V ("Failed to create 'tracking_tracker'");
goto done;
}
g_object_set (G_OBJECT (bin->tracker), "tracker-width", config->width,
"tracker-height", config->height,
"gpu-id", config->gpu_id,
"ll-config-file", config->ll_config_file,
"ll-lib-file", config->ll_lib_file, NULL);
g_object_set (G_OBJECT (bin->tracker), "display-tracking-id",
config->display_tracking_id, NULL);
g_object_set (G_OBJECT (bin->tracker), "tracking-id-reset-mode",
config->tracking_id_reset_mode, NULL);
g_object_set (G_OBJECT (bin->tracker), "tracking-surface-type",
config->tracking_surface_type, NULL);
g_object_set (G_OBJECT (bin->tracker), "input-tensor-meta",
config->input_tensor_meta, NULL);
g_object_set (G_OBJECT (bin->tracker), "tensor-meta-gie-id",
config->input_tensor_gie_id, NULL);
g_object_set (G_OBJECT (bin->tracker), "compute-hw",
config->compute_hw, NULL);
g_object_set (G_OBJECT (bin->tracker), "user-meta-pool-size",
config->user_meta_pool_size, NULL);
g_object_set (G_OBJECT (bin->tracker), "sub-batches",
config->sub_batches, NULL);
g_object_set (G_OBJECT (bin->tracker), "sub-batch-err-recovery-trial-cnt",
config->sub_batch_err_recovery_trial_cnt, NULL);
gst_bin_add_many (GST_BIN (bin->bin), bin->tracker, NULL);
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->tracker, "sink");
NVGSTDS_BIN_ADD_GHOST_PAD (bin->bin, bin->tracker, "src");
ret = TRUE;
GST_CAT_DEBUG (NVDS_APP, "Tracker bin created successfully");
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2019-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
################################################################################
import time
from threading import Lock
start_time=time.time()
fps_mutex = Lock()
class GETFPS:
def __init__(self,stream_id):
global start_time
self.start_time=start_time
self.is_first=True
self.frame_count=0
self.stream_id=stream_id
def update_fps(self):
end_time = time.time()
if self.is_first:
self.start_time = end_time
self.is_first = False
else:
global fps_mutex
with fps_mutex:
self.frame_count = self.frame_count + 1
def get_fps(self):
end_time = time.time()
with fps_mutex:
stream_fps = float(self.frame_count/(end_time - self.start_time))
self.frame_count = 0
self.start_time = end_time
return round(stream_fps, 2)
def print_data(self):
print('frame_count=',self.frame_count)
print('start_time=',self.start_time)
class PERF_DATA:
def __init__(self, num_streams=1):
self.perf_dict = {}
self.all_stream_fps = {}
for i in range(num_streams):
self.all_stream_fps["stream{0}".format(i)]=GETFPS(i)
def perf_print_callback(self):
self.perf_dict = {stream_index:stream.get_fps() for (stream_index, stream) in self.all_stream_fps.items()}
print ("\n**PERF: ", self.perf_dict, "\n")
return True
def update_fps(self, stream_index):
self.all_stream_fps[stream_index].update_fps()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment