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) 2020-2023 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.
################################################################################
[message-broker]
#hostname=localhost
#port=6379
#streamsize=10000
#payloadkey=metadata
#consumergroup=mygroup
#consumername=myname
#share-connection = 1
#!/usr/bin/env python3
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Python port of sugon_apps/sample_apps/deepstream-test4/deepstream_test4_app.c
#
# Pipeline (与 C 非 YAML 模式一致):
# filesrc -> h264parse -> mach264dec -> nvstreammux -> pgie -> nvdsosd -> tee
# tee -> queue -> nvmsgconv -> nvmsgbroker
# tee -> queue -> fakesink
#
# 说明:
# - 默认 pgie 为 nvinferserver,配置 dstest4_pgie_nvinferserver_config.txt(与 C 默认一致)
# 环境变量 DEEPSTREAM_PGIE=nvinfer 时使用 nvinfer + dstest4_pgie_config.txt
# - C 中 YAML 整表解析 (nvds_yml_parser) 本脚本未实现;请使用命令行参数
# python3.10 deepstream_test_4.py -i /workspace/shared_docker/video/cr7_1920x1080.h264 -p /opt/deepstream/lib/libnvds_mqtt_proto.so --conn-str="127.0.0.1;1883;/server/fromArm"
################################################################################
from __future__ import annotations
import argparse
import os
import sys
sys.path.append("../")
import gi
gi.require_version("Gst", "1.0")
from gi.repository import Gst
import pyds
from common.utils import long_to_uint64
MAX_DISPLAY_LEN = 64
MAX_TIME_STAMP_LEN = 32
PGIE_CLASS_ID_VEHICLE = 0
PGIE_CLASS_ID_PERSON = 2
MUXER_OUTPUT_WIDTH = 1920
MUXER_OUTPUT_HEIGHT = 1080
MUXER_BATCH_TIMEOUT_USEC = 40000
MSCONV_CONFIG_FILE = "dstest4_msgconv_config.txt"
PGIE_CONFIG_NVINFERSERVER = "dstest4_pgie_nvinferserver_config.txt"
PGIE_CONFIG_NVINFER = "dstest4_pgie_config.txt"
pgie_classes_str = ["Vehicle", "TwoWheeler", "Person", "Roadsign"]
DEFAULT_DECODER = os.environ.get("DEEPSTREAM_DECODER", "mach264dec")
# 仅用于规避少数环境下 EOS 后解释器退出阶段的段错误:
# export DEEPSTREAM_PYTHON_EXIT_HARD=1
FORCE_HARD_EXIT = os.environ.get("DEEPSTREAM_PYTHON_EXIT_HARD", "0") == "1"
# 运行期由 parse_args 设置,供 probe 使用
_frame_interval = 30
_msg2p_meta = 0
class _RunState:
def __init__(self):
self.pipeline = None
self.bus = None
self.is_running = False
self.osd_sink_pad = None
self.osd_probe_id = None
self._cleaned = False
def cleanup(self):
# Backtrace 显示 coredump 发生在 gst_pad_remove_probe() 的 GI closure free 阶段。
# 因此这里避免显式 remove_probe,让 pipeline 进入 NULL 时由 GStreamer 统一清理。
# 同时将 _cleaned 提前置位,防止 EOS/ERROR/异常路径重入 cleanup 引发重复释放。
if self._cleaned:
return
self._cleaned = True
self.is_running = False
self.osd_probe_id = None
self.osd_sink_pad = None
if self.bus is not None:
try:
self.bus.set_flushing(True)
except Exception:
pass
self.bus = None
if self.pipeline:
print("Stopping pipeline...")
self.pipeline.set_state(Gst.State.NULL)
self.pipeline.get_state(5 * Gst.SECOND)
print("Pipeline stopped")
self.pipeline = None
def handle_message(msg: Gst.Message, state: _RunState) -> None:
t = msg.type
if t == Gst.MessageType.EOS:
print("\nEnd of stream")
state.cleanup()
if FORCE_HARD_EXIT:
os._exit(0)
elif t == Gst.MessageType.ERROR:
err, debug = msg.parse_error()
print(f"Error: {err}")
if debug:
print(f"Debug: {debug}")
state.cleanup()
elif t == Gst.MessageType.WARNING:
err, debug = msg.parse_warning()
print(f"Warning: {err}")
if debug:
print(f"Debug: {debug}")
elif t == Gst.MessageType.STATE_CHANGED:
if state.pipeline and msg.src == state.pipeline:
o, n, _p = msg.parse_state_changed()
print(f"Pipeline state: {o.value_nick} -> {n.value_nick}")
def generate_vehicle_meta(data):
obj = pyds.NvDsVehicleObject.cast(data)
obj.type = "sedan"
obj.color = "blue"
obj.make = "Bugatti"
obj.model = "M"
obj.license = "XX1234"
obj.region = "CA"
return obj
def generate_person_meta(data):
obj = pyds.NvDsPersonObject.cast(data)
obj.age = 45
obj.cap = "none"
obj.hair = "black"
obj.gender = "male"
obj.apparel = "formal"
return obj
def generate_event_msg_meta(data, class_id):
meta = pyds.NvDsEventMsgMeta.cast(data)
meta.sensorId = 0
meta.placeId = 0
meta.moduleId = 0
meta.sensorStr = "sensor-0"
meta.ts = pyds.alloc_buffer(MAX_TIME_STAMP_LEN + 1)
pyds.generate_ts_rfc3339(meta.ts, MAX_TIME_STAMP_LEN)
if class_id == PGIE_CLASS_ID_VEHICLE:
meta.type = pyds.NvDsEventType.NVDS_EVENT_MOVING
meta.objType = pyds.NvDsObjectType.NVDS_OBJECT_TYPE_VEHICLE
meta.objClassId = PGIE_CLASS_ID_VEHICLE
obj = pyds.alloc_nvds_vehicle_object()
obj = generate_vehicle_meta(obj)
meta.extMsg = obj
meta.extMsgSize = sys.getsizeof(pyds.NvDsVehicleObject)
elif class_id == PGIE_CLASS_ID_PERSON:
meta.type = pyds.NvDsEventType.NVDS_EVENT_ENTRY
meta.objType = pyds.NvDsObjectType.NVDS_OBJECT_TYPE_PERSON
meta.objClassId = PGIE_CLASS_ID_PERSON
obj = pyds.alloc_nvds_person_object()
obj = generate_person_meta(obj)
meta.extMsg = obj
meta.extMsgSize = sys.getsizeof(pyds.NvDsPersonObject)
return meta
def osd_sink_pad_buffer_probe(pad, info, u_data):
"""与 C 中 osd_sink_pad_buffer_metadata_probe (msg2p_meta==0) 及 NVIDIA Python 样例对齐。"""
state = u_data
if state is not None and not state.is_running:
return Gst.PadProbeReturn.OK
global _frame_interval, _msg2p_meta
if _msg2p_meta != 0:
return Gst.PadProbeReturn.OK
obj_counter = {0: 0, 1: 0, 2: 0, 3: 0}
gst_buffer = info.get_buffer()
if not gst_buffer:
return Gst.PadProbeReturn.OK
batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
if not batch_meta:
return Gst.PadProbeReturn.OK
frame_number = 0
l_frame = batch_meta.frame_meta_list
while l_frame is not None:
try:
frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
except StopIteration:
break
is_first_object = True
frame_number = frame_meta.frame_num
l_obj = frame_meta.obj_meta_list
while l_obj is not None:
try:
obj_meta = pyds.NvDsObjectMeta.cast(l_obj.data)
except StopIteration:
break
txt_params = obj_meta.text_params
cid = obj_meta.class_id
if 0 <= cid < len(pgie_classes_str):
txt_params.display_text = pgie_classes_str[cid]
if cid in obj_counter:
obj_counter[cid] = obj_counter[cid] + 1
txt_params.font_params.font_name = "Serif"
txt_params.font_params.font_size = 10
txt_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0)
txt_params.set_bg_clr = 1
txt_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0)
if is_first_object and (frame_number % _frame_interval) == 0:
user_event_meta = pyds.nvds_acquire_user_meta_from_pool(batch_meta)
if user_event_meta:
msg_meta = pyds.alloc_nvds_event_msg_meta(user_event_meta)
msg_meta.bbox.top = obj_meta.rect_params.top
msg_meta.bbox.left = obj_meta.rect_params.left
msg_meta.bbox.width = obj_meta.rect_params.width
msg_meta.bbox.height = obj_meta.rect_params.height
msg_meta.frameId = frame_number
msg_meta.trackingId = long_to_uint64(obj_meta.object_id)
msg_meta.confidence = obj_meta.confidence
msg_meta = generate_event_msg_meta(msg_meta, obj_meta.class_id)
user_event_meta.user_meta_data = msg_meta
user_event_meta.base_meta.meta_type = pyds.NvDsMetaType.NVDS_EVENT_MSG_META
pyds.nvds_add_user_meta_to_frame(frame_meta, user_event_meta)
is_first_object = False
try:
l_obj = l_obj.next
except StopIteration:
break
try:
l_frame = l_frame.next
except StopIteration:
break
print(
"Frame Number =",
frame_number,
"Vehicle Count =",
obj_counter.get(PGIE_CLASS_ID_VEHICLE, 0),
"Person Count =",
obj_counter.get(PGIE_CLASS_ID_PERSON, 0),
)
return Gst.PadProbeReturn.OK
def build_pipeline(args: argparse.Namespace) -> tuple[Gst.Pipeline, _RunState]:
use_nvinfer = os.environ.get("DEEPSTREAM_PGIE", "").lower() == "nvinfer"
if use_nvinfer:
pgie = Gst.ElementFactory.make("nvinfer", "primary-nvinference-engine")
pgie_cfg = PGIE_CONFIG_NVINFER
else:
pgie = Gst.ElementFactory.make("nvinferserver", "primary-nvinference-engine")
pgie_cfg = PGIE_CONFIG_NVINFERSERVER
pipeline = Gst.Pipeline.new("dstest4-pipeline")
source = Gst.ElementFactory.make("filesrc", "file-source")
h264parser = Gst.ElementFactory.make("h264parse", "h264-parser")
decoder = Gst.ElementFactory.make(DEFAULT_DECODER, "mach264dec-decoder")
streammux = Gst.ElementFactory.make("nvstreammux", "nvstreammux")
nvosd = Gst.ElementFactory.make("nvdsosd", "nv-onscreendisplay")
msgconv = Gst.ElementFactory.make("nvmsgconv", "nvmsg-converter")
msgbroker = Gst.ElementFactory.make("nvmsgbroker", "nvmsg-broker")
tee = Gst.ElementFactory.make("tee", "nvsink-tee")
queue1 = Gst.ElementFactory.make("queue", "nvtee-que1")
queue2 = Gst.ElementFactory.make("queue", "nvtee-que2")
sink = Gst.ElementFactory.make("fakesink", "nvvideo-renderer")
els = [
pipeline,
source,
h264parser,
decoder,
streammux,
pgie,
nvosd,
msgconv,
msgbroker,
tee,
queue1,
queue2,
sink,
]
if not all(els):
raise RuntimeError("One or more GStreamer elements could not be created")
source.set_property("location", args.input_file)
streammux.set_property("batch-size", 1)
streammux.set_property("width", MUXER_OUTPUT_WIDTH)
streammux.set_property("height", MUXER_OUTPUT_HEIGHT)
streammux.set_property("batched-push-timeout", MUXER_BATCH_TIMEOUT_USEC)
pgie.set_property("config-file-path", pgie_cfg)
msgconv.set_property("config", MSCONV_CONFIG_FILE)
msgconv.set_property("payload-type", args.schema_type)
try:
msgconv.set_property("msg2p-newapi", args.msg2p_meta)
except Exception:
pass
try:
msgconv.set_property("frame-interval", args.frame_interval)
except Exception:
pass
msgbroker.set_property("proto-lib", args.proto_lib)
msgbroker.set_property("conn-str", args.conn_str)
msgbroker.set_property("sync", False)
if args.cfg_file:
msgbroker.set_property("config", args.cfg_file)
if args.topic:
msgbroker.set_property("topic", args.topic)
sink.set_property("sync", True)
for e in (
source,
h264parser,
decoder,
streammux,
pgie,
nvosd,
tee,
queue1,
queue2,
msgconv,
msgbroker,
sink,
):
pipeline.add(e)
if not source.link(h264parser) or not h264parser.link(decoder):
raise RuntimeError("Failed to link decode chain")
sinkpad = streammux.request_pad_simple("sink_0")
srcpad = decoder.get_static_pad("src")
if not sinkpad or not srcpad:
raise RuntimeError("Failed to get mux/decoder pads")
if srcpad.link(sinkpad) != Gst.PadLinkReturn.OK:
raise RuntimeError("Failed to link decoder -> streammux")
# 与 C 一致:pgie 直连 nvdsosd(无 nvvideoconvert)
if not streammux.link(pgie) or not pgie.link(nvosd) or not nvosd.link(tee):
raise RuntimeError("Failed to link mux -> pgie -> osd -> tee")
if not queue1.link(msgconv) or not msgconv.link(msgbroker):
raise RuntimeError("Failed to link msg branch")
if not queue2.link(sink):
raise RuntimeError("Failed to link queue2 -> sink")
q1_sink = queue1.get_static_pad("sink")
q2_sink = queue2.get_static_pad("sink")
tee_msg_pad = tee.request_pad_simple("src_%u")
tee_render_pad = tee.request_pad_simple("src_%u")
if not all([q1_sink, q2_sink, tee_msg_pad, tee_render_pad]):
raise RuntimeError("Failed to get tee/queue pads")
if tee_msg_pad.link(q1_sink) != Gst.PadLinkReturn.OK:
raise RuntimeError("Failed to link tee -> queue1")
if tee_render_pad.link(q2_sink) != Gst.PadLinkReturn.OK:
raise RuntimeError("Failed to link tee -> queue2")
state = _RunState()
state.pipeline = pipeline
state.is_running = True
osdsinkpad = nvosd.get_static_pad("sink")
if osdsinkpad and args.msg2p_meta == 0:
state.osd_sink_pad = osdsinkpad
state.osd_probe_id = osdsinkpad.add_probe(
Gst.PadProbeType.BUFFER, osd_sink_pad_buffer_probe, state
)
elif not osdsinkpad:
sys.stderr.write("Warning: could not get nvdsosd sink pad\n")
return pipeline, state
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(
description="DeepStream test4 (Python) — message broker + inference"
)
p.add_argument(
"-i",
"--input-file",
required=True,
help="Input elementary H.264 file",
)
p.add_argument(
"-p",
"--proto-lib",
required=True,
help="Absolute path to nvmsgbroker proto adaptor .so",
)
p.add_argument(
"--conn-str",
default="localhost;2181;testTopic",
help="Broker connection string",
)
p.add_argument("-c", "--cfg-file", default=None, help="Adaptor config file for msgbroker")
p.add_argument("-t", "--topic", default=None, help="Message topic")
p.add_argument(
"-s",
"--schema-type",
type=int,
default=0,
help="nvmsgconv payload-type (0=full, 1=minimal, 2=protobuf per plugin)",
)
p.add_argument(
"--msg2p-meta",
type=int,
default=0,
choices=(0, 1),
help="0=event NVDS_EVENT_MSG_META (default), 1=custom blob (Python 仅实现 0)",
)
p.add_argument(
"--frame-interval",
type=int,
default=30,
help="Attach event meta every N frames (first object), default 30",
)
args = p.parse_args()
global _frame_interval, _msg2p_meta
_frame_interval = args.frame_interval
_msg2p_meta = args.msg2p_meta
if args.msg2p_meta != 0:
print("Note: msg2p-meta=1 (image/custom) path is not implemented; running without OSD probe.")
return args
def main() -> int:
args = parse_args()
Gst.init(None)
try:
pipeline, state = build_pipeline(args)
except Exception as e:
sys.stderr.write(f"Failed to build pipeline: {e}\n")
return 1
print(f"Now playing: {args.input_file}")
ret = pipeline.set_state(Gst.State.PLAYING)
if ret == Gst.StateChangeReturn.FAILURE:
sys.stderr.write("Failed to set PLAYING\n")
state.cleanup()
return 1
bus = pipeline.get_bus()
state.bus = bus
print("Running...")
try:
while state.is_running:
try:
msg = bus.timed_pop_filtered(
100 * Gst.MSECOND,
Gst.MessageType.EOS
| Gst.MessageType.ERROR
| Gst.MessageType.WARNING
| Gst.MessageType.STATE_CHANGED,
)
if msg:
handle_message(msg, state)
except KeyboardInterrupt:
print("\nInterrupted")
state.cleanup()
break
except Exception:
if not state._cleaned:
state.cleanup()
raise
return 0
if __name__ == "__main__":
sys.exit(main())
################################################################################
# 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.
################################################################################
source:
location: ../../../streams/cr7_1920x1080.h264
streammux:
batch-size: 1
batched-push-timeout: 40000
width: 1920
height: 1080
msgconv:
#If you want to send images, please set the "payload-type: 1" and "msg2p-newapi: 1"
payload-type: 1
msg2p-newapi: 1
frame-interval: 30
msgbroker:
proto-lib: /opt/deepstream/lib/libnvds_mqtt_proto.so
conn-str: 127.0.0.1;1883
topic: /server/fromArm
sync: 0
sink:
sync: 1
# Inference using nvinferserver:
primary-gie:
plugin-type: 1
config-file-path: yolov5_nvinferserver_config.txt
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2018-2019 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.
################################################################################
[sensor0]
enable=1
type=Camera
id=CAMERA_ID
location=45.293701447;-75.8303914499;48.1557479338
description="Entrance of Garage Right Lane"
coordinate=5.2;10.1;11.2
[place0]
enable=1
id=1
type=garage
name=XYZ
location=30.32;-40.55;100.0
coordinate=1.0;2.0;3.0
place-sub-field1=walsh
place-sub-field2=lane1
place-sub-field3=P2
[place1]
enable=1
id=1
type=garage
name=XYZ
location=28.47;47.46;1.53
coordinate=1.0;2.0;3.0
place-sub-field1="C-76-2"
place-sub-field2="LEV/EV/CP/ADA"
place-sub-field3=P2
[analytics0]
enable=1
id=XYZ
description="Vehicle Detection and License Plate Recognition"
source=OpenALR
version=1.0
\ No newline at end of file
################################################################################
# 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.
################################################################################
sensor0:
enable: 1
type: Camera
id: CAMERA_ID
location: 45.293701447;-75.8303914499;48.1557479338
description: "Entrance of Garage Right Lane"
coordinate: 5.2;10.1;11.2
place0:
enable: 1
id: 1
type: garage
name: XYZ
location: 30.32;-40.55;100.0
coordinate: 1.0;2.0;3.0
place-sub-field1: walsh
place-sub-field2: lane1
place-sub-field3: P2
place1:
enable: 1
id: 1
type: garage
name: XYZ
location: 28.47;47.46;1.53
coordinate: 1.0;2.0;3.0
place-sub-field1: "C-76-2"
place-sub-field2: "LEV/EV/CP/ADA"
place-sub-field3: P2
analytics0:
enable: 1
id: XYZ
description: "Vehicle Detection and License Plate Recognition"
source: OpenALR
version: 1.0
\ No newline at end of file
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 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.
################################################################################
# Following properties are mandatory when engine files are not specified:
# int8-calib-file(Only in INT8)
# Caffemodel mandatory properties: model-file, proto-file, output-blob-names
# UFF: uff-file, input-dims, uff-input-blob-name, output-blob-names
# ONNX: onnx-file
#
# Mandatory properties for detectors:
# num-detected-classes
#
# Optional properties for detectors:
# cluster-mode(Default=Group Rectangles), interval(Primary mode only, Default=0)
# custom-lib-path,
# parse-bbox-func-name
#
# Mandatory properties for classifiers:
# classifier-threshold, is-classifier
#
# Optional properties for classifiers:
# classifier-async-mode(Secondary mode only, Default=false)
#
# Optional properties in secondary mode:
# operate-on-gie-id(Default=0), operate-on-class-ids(Defaults to all classes),
# input-object-min-width, input-object-min-height, input-object-max-width,
# input-object-max-height
#
# Following properties are always recommended:
# batch-size(Default=1)
#
# Other optional properties:
# net-scale-factor(Default=1), network-mode(Default=0 i.e FP32),
# model-color-format(Default=0 i.e. RGB) model-engine-file, labelfile-path,
# mean-file, gie-unique-id(Default=0), offsets, process-mode (Default=1 i.e. primary),
# custom-lib-path, network-mode(Default=0 i.e FP32)
#
# The values in the config file are overridden by values set through GObject
# properties.
[property]
gpu-id=0
net-scale-factor=0.00392156862745098
onnx-file=../../../../samples/models/Primary_Detector/resnet18_trafficcamnet_pruned.onnx
model-engine-file=../../../../samples/models/Primary_Detector/resnet18_trafficcamnet_pruned.onnx_b1_gpu0_fp16.engine
labelfile-path=../../../../samples/models/Primary_Detector/labels.txt
int8-calib-file=../../../../samples/models/Primary_Detector/cal_trt.bin
batch-size=1
process-mode=1
model-color-format=0
## 0=FP32, 1=INT8, 2=FP16 mode
network-mode=2
num-detected-classes=4
interval=0
process-mode=1
model-color-format=0
gie-unique-id=1
#scaling-filter=0
#scaling-compute-hw=0
cluster-mode=2
[class-attrs-all]
pre-cluster-threshold=0.2
topk=20
nms-iou-threshold=0.5
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2018-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.
################################################################################
infer_config {
unique_id: 1
gpu_ids: [0]
max_batch_size: 30
backend {
inputs: [ {
name: "input_1:0"
}]
outputs: [
{name: "output_cov/Sigmoid:0"},
{name: "output_bbox/BiasAdd:0"}
]
triton {
model_name: "Primary_Detector"
version: 1
model_repo {
root: "../../../../sugon_samples/triton_model_repo"
strict_model_config: true
backend_dir: "/opt/deepstream/third_party/backends"
}
}
}
preprocess {
network_format: MEDIA_FORMAT_NONE
tensor_order: TENSOR_ORDER_LINEAR
tensor_name: "input_1:0"
maintain_aspect_ratio: 0
frame_scaling_hw: FRAME_SCALING_HW_DEFAULT
frame_scaling_filter: 1
normalize {
scale_factor: 0.00392156862745098
channel_offsets: [0, 0, 0]
}
}
postprocess {
labelfile_path: "../../../../sugon_samples/labels/Primary_Detector/labels.txt"
detection {
num_detected_classes: 4
per_class_params {
key: 0
value { pre_threshold: 0.4 }
}
nms {
confidence_threshold:0.2
topk:20
iou_threshold:0.5
}
}
}
extra {
copy_input_to_host_buffers: false
output_buffer_pool_size: 2
}
}
input_control {
process_mode: PROCESS_MODE_FULL_FRAME
operate_on_gie_id: -1
interval: 0
}
python_app_nvidia @ 9b27f02f
Subproject commit 9b27f02ffea46a3ded2ad26b3eea27ef3e2dfded
################################################################################
# SPDX-FileCopyrightText: Copyright (c) 2019-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.
################################################################################
CUDA_VER?=
ifeq ($(CUDA_VER),)
$(error "CUDA_VER is not set")
endif
APP:= deepstream-app
TARGET_DEVICE = $(shell gcc -dumpmachine | cut -f1 -d -)
NVDS_VERSION:=7.1
LIB_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/lib/
APP_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/bin/
ifeq ($(TARGET_DEVICE),aarch64)
CFLAGS:= -DPLATFORM_TEGRA
endif
SRCS:= $(wildcard *.c) $(wildcard *.cpp)
SRCS+= $(wildcard ../../apps-common/src/*.c)
SRCS+= $(wildcard ../../apps-common/src/deepstream-yaml/*.cpp)
INCS:= $(wildcard *.h)
PKGS:= gstreamer-1.0 gstreamer-video-1.0 x11 json-glib-1.0
OBJS:= $(SRCS:.c=.o)
OBJS:= $(OBJS:.cpp=.o)
CFLAGS+= -I./ -I../../apps-common/includes \
-I../../../includes -DDS_VERSION_MINOR=1 -DDS_VERSION_MAJOR=5 \
-I /usr/local/cuda-$(CUDA_VER)/include
LIBS:= -L/usr/local/cuda-$(CUDA_VER)/lib64/ -lcudart
LIBS+= -L$(LIB_INSTALL_DIR) -lnvdsgst_meta -lnvds_meta -lnvdsgst_helper -lnvdsgst_customhelper \
-lnvdsgst_smartrecord -lnvds_utils -lnvds_msgbroker -lm -lyaml-cpp \
-lcuda -lgstrtspserver-1.0 -ldl -Wl,-rpath,$(LIB_INSTALL_DIR)
CFLAGS+= $(shell pkg-config --cflags $(PKGS))
LIBS+= $(shell pkg-config --libs $(PKGS))
all: $(APP)
%.o: %.c $(INCS) Makefile
$(CC) -c -o $@ $(CFLAGS) $<
%.o: %.cpp $(INCS) Makefile
$(CXX) -c -o $@ $(CFLAGS) $<
$(APP): $(OBJS) Makefile
$(CXX) -o $(APP) $(OBJS) $(LIBS)
install: $(APP)
cp -rv $(APP) $(APP_INSTALL_DIR)
clean:
rm -rf $(OBJS) $(APP)
*****************************************************************************
* 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.
*****************************************************************************
*****************************************************************************
deepstream-app
README
*****************************************************************************
===============================================================================
1. Prerequisites:
===============================================================================
Follow these procedures to use the deepstream-app application for native
compilation.
You must have the following development packages installed
GStreamer-1.0
GStreamer-1.0 Base Plugins
GStreamer-1.0 gstrtspserver
X11 client-side library
Glib json library - json-glib-1.0
yaml-cpp
EGL graphics interface lib - libegl-mesa0
1. To install these packages, execute the following command:
sudo apt-get install libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev \
libgstrtspserver-1.0-dev libx11-dev libjson-glib-dev libyaml-cpp-dev \
libgbm1 libglapi-mesa libgles2-mesa-dev
Note:
Minimum installation version for libgbm1, libglapi-mesa and libgles2-mesa-dev
should be >= 23.0.4-0ubuntu1~22.04.1
===============================================================================
2. Purpose:
===============================================================================
This document shall describe about the sample deepstream application.
The application demonstrates, how to use multiple sources for use
cases like object detection, tracking, smart recording etc.
===============================================================================
3. To compile:
===============================================================================
$ Set CUDA_VER in the MakeFile as per platform.
For both Jetson & x86, CUDA_VER=12.6
$ sudo make (sudo not required in case of docker containers)
===============================================================================
4. Usage:
===============================================================================
Run the application by executing the command:
./deepstream-app -c <config-file>
Please refer "../../apps-common/includes/deepstream_config.h" to modify
application parameters like maximum number of sources etc.
NOTE:
1. Prerequisites to use nvdrmvideosink (Jetson only)
a. Ensure that X server is not running.
Command to stop X server:
$sudo service gdm stop
$sudo pkill -9 Xorg
b. If "Could not get EGL display connection" error is encountered,
use command $unset DISPLAY
c. Ensure that a display device is connected to the Jetson board.
/*
* 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 <gst/gst.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
#include "deepstream_app.h"
#define MAX_DISPLAY_LEN 64
static guint demux_batch_num = 0;
GST_DEBUG_CATEGORY_EXTERN (NVDS_APP);
GQuark _dsmeta_quark;
#define CEIL(a,b) ((a + b - 1) / b)
/**
* @brief Add the (nvmsgconv->nvmsgbroker) sink-bin to the
* overall DS pipeline (if any configured) and link the same to
* common_elements.tee (This tee connects
* the common analytics path to Tiler/display-sink and
* to configured broker sink if any)
* NOTE: This API shall return TRUE if there are no
* broker sinks to add to pipeline
*
* @param appCtx [IN]
* @return TRUE if succussful; FALSE otherwise
*/
static gboolean add_and_link_broker_sink (AppCtx * appCtx);
/**
* @brief Checks if there are any [sink] groups
* configured for source_id=provided source_id
* NOTE: source_id key and this API is valid only when we
* disable [tiler] and thus use demuxer for individual
* stream out
* @param config [IN] The DS Pipeline configuration struct
* @param source_id [IN] Source ID for which a specific [sink]
* group is searched for
*/
static gboolean is_sink_available_for_source_id (NvDsConfig * config,
guint source_id);
static NvDsSensorInfo* s_sensor_info_create(NvDsSensorInfo* sensor_info);
static void s_sensor_info_destroy(NvDsSensorInfo* sensor_info);
static NvDsSensorInfo* s_sensor_info_create(NvDsSensorInfo* sensor_info) {
NvDsSensorInfo* sensorInfoToHash = (NvDsSensorInfo*)g_malloc0(sizeof(NvDsSensorInfo));
*sensorInfoToHash = *sensor_info;
sensorInfoToHash->sensor_id = (gchar const*)g_strdup(sensor_info->sensor_id);
sensorInfoToHash->sensor_name = (gchar const*)g_strdup(sensor_info->sensor_name);
sensorInfoToHash->uri = (gchar const*)g_strdup(sensor_info->uri);
return sensorInfoToHash;
}
static void s_sensor_info_destroy(NvDsSensorInfo* sensor_info) {
if(!sensor_info)
return;
if(sensor_info->sensor_id) {
g_free((void*)sensor_info->sensor_id);
}
if(sensor_info->sensor_name) {
g_free((void*)sensor_info->sensor_name);
}
g_free(sensor_info);
}
static void s_sensor_info_callback_stream_added (AppCtx *appCtx, NvDsSensorInfo* sensorInfo) {
NvDsSensorInfo* sensorInfoToHash = s_sensor_info_create(sensorInfo);
/** save the sensor info into the hash map */
g_hash_table_insert (appCtx->sensorInfoHash, sensorInfo->source_id + (char *)NULL, sensorInfoToHash);
}
static void s_sensor_info_callback_stream_removed (AppCtx *appCtx, NvDsSensorInfo* sensorInfo) {
NvDsSensorInfo* sensorInfoFromHash = get_sensor_info(appCtx, sensorInfo->source_id);
/** remove the sensor info from the hash map */
if(sensorInfoFromHash) {
g_hash_table_remove(appCtx->sensorInfoHash, sensorInfo->source_id + (gchar*)NULL);
s_sensor_info_destroy(sensorInfoFromHash);
}
}
NvDsSensorInfo* get_sensor_info(AppCtx* appCtx, guint source_id) {
NvDsSensorInfo* sensorInfo = (NvDsSensorInfo*)g_hash_table_lookup(appCtx->sensorInfoHash,
source_id + (gchar*)NULL);
return sensorInfo;
}
/*Note: Below callbacks/functions defined for FPS logging,
* when nvmultiurisrcbin is being used*/
static NvDsFPSSensorInfo* s_fps_sensor_info_create(NvDsFPSSensorInfo* sensor_info);
NvDsFPSSensorInfo* get_fps_sensor_info(AppCtx* appCtx, guint source_id);
static void s_fps_sensor_info_destroy(NvDsFPSSensorInfo* sensor_info);
static NvDsFPSSensorInfo* s_fps_sensor_info_create(NvDsFPSSensorInfo* sensor_info) {
NvDsFPSSensorInfo* fpssensorInfoToHash = (NvDsFPSSensorInfo*)g_malloc0(sizeof(NvDsFPSSensorInfo));
*fpssensorInfoToHash = *sensor_info;
fpssensorInfoToHash->uri = (gchar const*)g_strdup(sensor_info->uri);
fpssensorInfoToHash->source_id = sensor_info->source_id;
fpssensorInfoToHash->sensor_id = (gchar const*)g_strdup(sensor_info->sensor_id);
fpssensorInfoToHash->sensor_name = (gchar const*)g_strdup(sensor_info->sensor_name);
return fpssensorInfoToHash;
}
static void s_fps_sensor_info_destroy(NvDsFPSSensorInfo* sensor_info) {
if(!sensor_info)
return;
if(sensor_info->sensor_id) {
g_free((void*)sensor_info->sensor_id);
}
if(sensor_info->sensor_name) {
g_free((void*)sensor_info->sensor_name);
}
if(sensor_info->uri) {
g_free((void*)sensor_info->uri);
}
g_free(sensor_info);
}
static void s_fps_sensor_info_callback_stream_added (AppCtx *appCtx, NvDsFPSSensorInfo* sensorInfo) {
NvDsFPSSensorInfo* fpssensorInfoToHash = s_fps_sensor_info_create(sensorInfo);
/** save the sensor info into the hash map */
g_hash_table_insert (appCtx->perf_struct.FPSInfoHash, GUINT_TO_POINTER(sensorInfo->source_id), fpssensorInfoToHash);
}
NvDsFPSSensorInfo* get_fps_sensor_info(AppCtx* appCtx, guint source_id) {
NvDsFPSSensorInfo* sensorInfo = (NvDsFPSSensorInfo*)g_hash_table_lookup(appCtx->perf_struct.FPSInfoHash,
GUINT_TO_POINTER(source_id));
return sensorInfo;
}
static void s_fps_sensor_info_callback_stream_removed (AppCtx *appCtx, NvDsFPSSensorInfo* sensorInfo) {
NvDsFPSSensorInfo* fpsensorInfoFromHash = get_fps_sensor_info(appCtx, sensorInfo->source_id);
/** remove the sensor info from the hash map */
if(fpsensorInfoFromHash) {
g_hash_table_remove(appCtx->perf_struct.FPSInfoHash, GUINT_TO_POINTER(sensorInfo->source_id));
s_fps_sensor_info_destroy(fpsensorInfoFromHash);
}
}
/**
* callback function to receive messages from components
* in the pipeline.
*/
static gboolean
bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
AppCtx *appCtx = (AppCtx *) data;
GST_CAT_DEBUG (NVDS_APP,
"Received message on bus: source %s, msg_type %s",
GST_MESSAGE_SRC_NAME (message), GST_MESSAGE_TYPE_NAME (message));
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_INFO:{
GError *error = NULL;
gchar *debuginfo = NULL;
gst_message_parse_info (message, &error, &debuginfo);
g_printerr ("INFO from %s: %s\n",
GST_OBJECT_NAME (message->src), error->message);
if (debuginfo) {
g_printerr ("Debug info: %s\n", debuginfo);
}
g_error_free (error);
g_free (debuginfo);
break;
}
case GST_MESSAGE_WARNING:{
GError *error = NULL;
gchar *debuginfo = NULL;
gst_message_parse_warning (message, &error, &debuginfo);
g_printerr ("WARNING from %s: %s\n",
GST_OBJECT_NAME (message->src), error->message);
if (debuginfo) {
g_printerr ("Debug info: %s\n", debuginfo);
}
g_error_free (error);
g_free (debuginfo);
break;
}
case GST_MESSAGE_ERROR:{
GError *error = NULL;
gchar *debuginfo = NULL;
const gchar *attempts_error =
"Reconnection attempts exceeded for all sources or EOS received.";
guint i = 0;
gst_message_parse_error (message, &error, &debuginfo);
if (strstr (error->message, attempts_error)) {
g_print
("Reconnection attempt exceeded or EOS received for all sources."
" Exiting.\n");
g_error_free (error);
g_free (debuginfo);
appCtx->return_value = 0;
appCtx->quit = TRUE;
return TRUE;
}
g_printerr ("ERROR from %s: %s\n",
GST_OBJECT_NAME (message->src), error->message);
if (debuginfo) {
g_printerr ("Debug info: %s\n", debuginfo);
}
NvDsSrcParentBin *bin = &appCtx->pipeline.multi_src_bin;
GstElement *msg_src_elem = (GstElement *) GST_MESSAGE_SRC (message);
gboolean bin_found = FALSE;
/* Find the source bin which generated the error. */
while (msg_src_elem && !bin_found) {
for (i = 0; i < bin->num_bins && !bin_found; i++) {
if (bin->sub_bins[i].src_elem == msg_src_elem ||
bin->sub_bins[i].bin == msg_src_elem) {
bin_found = TRUE;
break;
}
}
msg_src_elem = GST_ELEMENT_PARENT (msg_src_elem);
}
if ((i != bin->num_bins) &&
(appCtx->config.multi_source_config[0].type == NV_DS_SOURCE_RTSP)) {
// Error from one of RTSP source.
NvDsSrcBin *subBin = &bin->sub_bins[i];
if (!subBin->reconfiguring ||
g_strrstr (debuginfo, "500 (Internal Server Error)")) {
subBin->reconfiguring = TRUE;
g_timeout_add (0, reset_source_pipeline, subBin);
}
g_error_free (error);
g_free (debuginfo);
return TRUE;
}
if (appCtx->config.multi_source_config[0].type ==
NV_DS_SOURCE_CAMERA_V4L2) {
if (g_strrstr (debuginfo, "reason not-negotiated (-4)")) {
NVGSTDS_INFO_MSG_V
("incorrect camera parameters provided, please provide supported resolution and frame rate\n");
}
if (g_strrstr (debuginfo, "Buffer pool activation failed")) {
NVGSTDS_INFO_MSG_V ("usb bandwidth might be saturated\n");
}
}
g_error_free (error);
g_free (debuginfo);
appCtx->return_value = -1;
appCtx->quit = TRUE;
break;
}
case GST_MESSAGE_STATE_CHANGED:{
GstState oldstate, newstate;
gst_message_parse_state_changed (message, &oldstate, &newstate, NULL);
if (GST_ELEMENT (GST_MESSAGE_SRC (message)) == appCtx->pipeline.pipeline) {
switch (newstate) {
case GST_STATE_PLAYING:
NVGSTDS_INFO_MSG_V ("Pipeline running\n");
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->pipeline.
pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "ds-app-playing");
break;
case GST_STATE_PAUSED:
if (oldstate == GST_STATE_PLAYING) {
NVGSTDS_INFO_MSG_V ("Pipeline paused\n");
}
break;
case GST_STATE_READY:
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->
pipeline.pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"ds-app-ready");
if (oldstate == GST_STATE_NULL) {
NVGSTDS_INFO_MSG_V ("Pipeline ready\n");
} else {
NVGSTDS_INFO_MSG_V ("Pipeline stopped\n");
}
break;
case GST_STATE_NULL:
g_mutex_lock (&appCtx->app_lock);
g_cond_broadcast (&appCtx->app_cond);
g_mutex_unlock (&appCtx->app_lock);
break;
default:
break;
}
}
break;
}
case GST_MESSAGE_EOS:{
/*
* In normal scenario, this would use g_main_loop_quit() to exit the
* loop and release the resources. Since this application might be
* running multiple pipelines through configuration files, it should wait
* till all pipelines are done.
*/
if (appCtx->config.use_nvmultiurisrcbin) {
appCtx->eos_received = TRUE;
gboolean app_quit = TRUE;
for (int i = 0; i < MAX_SOURCE_BINS; i++) {
if (appCtx->config.multi_source_config[i].type == NV_DS_SOURCE_RTSP) {
if (appCtx->config.multi_source_config[i].rtsp_reconnect_attempt_exceeded != TRUE) {
app_quit = FALSE;
} else {
app_quit = TRUE;
}
}
}
if (app_quit) {
appCtx->quit = TRUE;
NVGSTDS_INFO_MSG_V ("Received EOS. Exiting ...\n");
return FALSE;
} else {
NVGSTDS_INFO_MSG_V ("Received EOS ...\n");
}
} else {
NVGSTDS_INFO_MSG_V ("Received EOS. Exiting ...\n");
appCtx->quit = TRUE;
return FALSE;
}
break;
}
case GST_MESSAGE_ELEMENT:{
if (gst_nvmessage_is_force_pipeline_eos (message)) {
gboolean app_quit = FALSE;
if (gst_nvmessage_parse_force_pipeline_eos (message, &app_quit)) {
if (app_quit)
appCtx->quit = TRUE;
}
}
if(gst_nvmessage_is_stream_add(message)) {
g_mutex_lock (&(appCtx->perf_struct).struct_lock);
appCtx->config.num_source_sub_bins++;
NvDsSensorInfo sensorInfo = {0};
gst_nvmessage_parse_stream_add(message, &sensorInfo);
g_print("new stream added [%d:%s:%s]\n\n\n\n", sensorInfo.source_id, sensorInfo.sensor_id, sensorInfo.sensor_name);
/** Callback */
s_sensor_info_callback_stream_added(appCtx, &sensorInfo);
gboolean is_rtsp = g_str_has_prefix (sensorInfo.uri, "rtsp://");
gboolean is_ipc = g_str_has_prefix (sensorInfo.uri, "ipc://");
appCtx->config.multi_source_config[sensorInfo.source_id].uri = g_strdup(sensorInfo.uri);
if (is_rtsp) {
appCtx->config.multi_source_config[sensorInfo.source_id].type = NV_DS_SOURCE_RTSP;
}
else if (is_ipc) {
appCtx->config.multi_source_config[sensorInfo.source_id].type = NV_DS_SOURCE_IPC;
}
else {
appCtx->config.multi_source_config[sensorInfo.source_id].type = NV_DS_SOURCE_URI;
}
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->
pipeline.pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"ds-app-added");
NvDsFPSSensorInfo fpssensorInfo = {0};
gst_nvmessage_parse_fps_stream_add(message, &fpssensorInfo);
s_fps_sensor_info_callback_stream_added(appCtx, &fpssensorInfo);
g_mutex_unlock (&(appCtx->perf_struct).struct_lock);
}
if(gst_nvmessage_is_stream_remove(message)) {
g_mutex_lock (&(appCtx->perf_struct).struct_lock);
appCtx->config.num_source_sub_bins--;
NvDsSensorInfo sensorInfo = {0};
gst_nvmessage_parse_stream_remove(message, &sensorInfo);
g_print("new stream removed [%d:%s]\n\n\n\n", sensorInfo.source_id, sensorInfo.sensor_id);
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->
pipeline.pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
"ds-app-removed");
/** Callback */
s_sensor_info_callback_stream_removed(appCtx, &sensorInfo);
NvDsFPSSensorInfo fpssensorInfo = {0};
gst_nvmessage_parse_fps_stream_remove(message, &fpssensorInfo);
s_fps_sensor_info_callback_stream_removed(appCtx, &fpssensorInfo);
g_mutex_unlock (&(appCtx->perf_struct).struct_lock);
}
if (gst_nvmessage_is_reconnect_attempt_exceeded (message)) {
NvDsRtspAttemptsInfo rtsp_info = {0};
gboolean rec_attempt_exceeded_for_all = TRUE;
if (gst_nvmessage_parse_reconnect_attempt_exceeded (message, &rtsp_info)) {
if (rtsp_info.attempt_exceeded) {
appCtx->config.multi_source_config[rtsp_info.source_id].rtsp_reconnect_attempt_exceeded =
rtsp_info.attempt_exceeded;
NVGSTDS_INFO_MSG_V ("rtsp reconnect attempt exceeded for source_id : %d\n",rtsp_info.source_id);
}
if (appCtx->eos_received) {
for (int i = 0; i < MAX_SOURCE_BINS; i++) {
if (appCtx->config.multi_source_config[i].type == NV_DS_SOURCE_RTSP) {
if (appCtx->config.multi_source_config[i].rtsp_reconnect_attempt_exceeded != TRUE) {
rec_attempt_exceeded_for_all = FALSE;
}
}
}
if (rec_attempt_exceeded_for_all) {
NVGSTDS_INFO_MSG_V ("Exiting ...\n");
appCtx->quit = TRUE;
return FALSE;
}
}
}
}
break;
}
default:
break;
}
return TRUE;
}
/**
* Function to dump bounding box data in kitti format. For this to work,
* property "gie-kitti-output-dir" must be set in configuration file.
* Data of different sources and frames is dumped in separate file.
*/
static void
write_kitti_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
gchar bbox_file[1024] = { 0 };
FILE *bbox_params_dump_file = NULL;
if (!appCtx->config.bbox_dir_path)
return;
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
guint stream_id = frame_meta->pad_index;
g_snprintf (bbox_file, sizeof (bbox_file) - 1,
"%s/%02u_%03u_%06lu.txt", appCtx->config.bbox_dir_path,
appCtx->index, stream_id, (gulong) frame_meta->frame_num);
bbox_params_dump_file = fopen (bbox_file, "w");
if (!bbox_params_dump_file)
continue;
for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
l_obj = l_obj->next) {
NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
float left = obj->rect_params.left;
float top = obj->rect_params.top;
float right = left + obj->rect_params.width;
float bottom = top + obj->rect_params.height;
// Here confidence stores detection confidence, since dump gie output
// is before tracker plugin
float confidence = obj->confidence;
fprintf (bbox_params_dump_file,
"%s 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f\n",
obj->obj_label, left, top, right, bottom, confidence);
}
fclose (bbox_params_dump_file);
}
}
/**
* Function to dump past frame objs in kitti format.
*/
static void
write_kitti_past_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
if (!appCtx->config.kitti_track_dir_path)
return;
// dump past frame tracked objects appending current frame objects
gchar bbox_file[1024] = { 0 };
FILE *bbox_params_dump_file = NULL;
NvDsTargetMiscDataBatch *pPastFrameObjBatch = NULL;
NvDsUserMetaList *bmeta_list = NULL;
NvDsUserMeta *user_meta = NULL;
for (bmeta_list = batch_meta->batch_user_meta_list; bmeta_list != NULL;
bmeta_list = bmeta_list->next) {
user_meta = (NvDsUserMeta *) bmeta_list->data;
if (user_meta
&& user_meta->base_meta.meta_type == NVDS_TRACKER_PAST_FRAME_META) {
pPastFrameObjBatch =
(NvDsTargetMiscDataBatch *) (user_meta->user_meta_data);
for (uint si = 0; si < pPastFrameObjBatch->numFilled; si++) {
NvDsTargetMiscDataStream *objStream = (pPastFrameObjBatch->list) + si;
guint stream_id = (guint) (objStream->streamID);
for (uint li = 0; li < objStream->numFilled; li++) {
NvDsTargetMiscDataObject *objList = (objStream->list) + li;
for (uint oi = 0; oi < objList->numObj; oi++) {
NvDsTargetMiscDataFrame *obj = (objList->list) + oi;
g_snprintf (bbox_file, sizeof (bbox_file) - 1,
"%s/%02u_%03u_%06lu.txt", appCtx->config.kitti_track_dir_path,
appCtx->index, stream_id, (gulong) obj->frameNum);
float left = obj->tBbox.left;
float right = left + obj->tBbox.width;
float top = obj->tBbox.top;
float bottom = top + obj->tBbox.height;
// Past frame object confidence given by tracker
float confidence = obj->confidence;
bbox_params_dump_file = fopen (bbox_file, "a");
if (!bbox_params_dump_file) {
continue;
}
fprintf (bbox_params_dump_file,
"%s %lu 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f\n",
objList->objLabel, objList->uniqueId, left, top, right, bottom,
confidence);
fclose (bbox_params_dump_file);
}
}
}
}
}
}
/**
* Function to dump bounding box data in kitti format with tracking ID added.
* For this to work, property "kitti-track-output-dir" must be set in configuration file.
* Data of different sources and frames is dumped in separate file.
*/
static void
write_kitti_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
gchar bbox_file[1024] = { 0 };
FILE *bbox_params_dump_file = NULL;
if (!appCtx->config.kitti_track_dir_path)
return;
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
guint stream_id = frame_meta->pad_index;
g_snprintf (bbox_file, sizeof (bbox_file) - 1,
"%s/%02u_%03u_%06lu.txt", appCtx->config.kitti_track_dir_path,
appCtx->index, stream_id, (gulong) frame_meta->frame_num);
bbox_params_dump_file = fopen (bbox_file, "w");
if (!bbox_params_dump_file)
continue;
for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
l_obj = l_obj->next) {
NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
float left = obj->tracker_bbox_info.org_bbox_coords.left;
float top = obj->tracker_bbox_info.org_bbox_coords.top;
float right = left + obj->tracker_bbox_info.org_bbox_coords.width;
float bottom = top + obj->tracker_bbox_info.org_bbox_coords.height;
// Here confidence stores tracker confidence value for tracker output
float confidence = obj->tracker_confidence;
guint64 id = obj->object_id;
bool write_proj_info = false;
float visibility = -1.0, x_img_foot = -1.0, y_img_foot = -1.0;
// Attach projected object info if stored in user meta
for (NvDsUserMetaList * l_obj_user = obj->obj_user_meta_list; l_obj_user != NULL;
l_obj_user = l_obj_user->next) {
NvDsUserMeta *user_meta = (NvDsUserMeta *) l_obj_user->data;
if (user_meta && user_meta->base_meta.meta_type == NVDS_OBJ_VISIBILITY
&& user_meta->user_meta_data) {
write_proj_info = true;
visibility = *((float *) (user_meta->user_meta_data));
} else if (user_meta && user_meta->base_meta.meta_type == NVDS_OBJ_IMAGE_FOOT_LOCATION
&& user_meta->user_meta_data) {
write_proj_info = true;
x_img_foot = ((float *) (user_meta->user_meta_data))[0];
y_img_foot = ((float *) (user_meta->user_meta_data))[1];
}
}
if (write_proj_info)
{
fprintf (bbox_params_dump_file,
"%s %lu 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f %f %f %f\n",
obj->obj_label, id, left, top, right, bottom, confidence, visibility, x_img_foot, y_img_foot);
}
else
{
fprintf (bbox_params_dump_file,
"%s %lu 0.0 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f\n",
obj->obj_label, id, left, top, right, bottom, confidence);
}
}
fclose (bbox_params_dump_file);
}
}
/**
* Function to dump object ReID embeddings to files when the tracker outputs
* ReID embeddings into user meta. For this to work, property "reid-track-output-dir"
* must be set in configuration file.
* Data of different sources and frames is dumped in separate file.
*/
static void
write_reid_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
if (!appCtx->config.reid_track_dir_path)
return;
gchar reid_file[1024] = { 0 };
FILE *reid_params_dump_file = NULL;
/** Save the reid embedding for each frame. */
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
/** Create dump file name. */
guint stream_id = frame_meta->pad_index;
g_snprintf (reid_file, sizeof (reid_file) - 1,
"%s/%02u_%03u_%06lu.txt", appCtx->config.reid_track_dir_path,
appCtx->index, stream_id, (gulong) frame_meta->frame_num);
reid_params_dump_file = fopen (reid_file, "w");
if (!reid_params_dump_file)
continue;
/** Save the reid embedding for each object. */
for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
l_obj = l_obj->next) {
NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
guint64 id = obj->object_id;
for (NvDsUserMetaList * l_obj_user = obj->obj_user_meta_list; l_obj_user != NULL;
l_obj_user = l_obj_user->next) {
/** Find the object's reid embedding index in user meta. */
NvDsUserMeta *user_meta = (NvDsUserMeta *) l_obj_user->data;
if (user_meta && user_meta->base_meta.meta_type == NVDS_TRACKER_OBJ_REID_META
&& user_meta->user_meta_data) {
NvDsObjReid *pReidObj = (NvDsObjReid *) (user_meta->user_meta_data);
if (pReidObj != NULL && pReidObj->ptr_host != NULL && pReidObj->featureSize > 0) {
fprintf (reid_params_dump_file, "%lu", id);
for (guint ele_i = 0; ele_i < pReidObj->featureSize; ele_i++) {
fprintf (reid_params_dump_file, " %f", pReidObj->ptr_host[ele_i]);
}
fprintf (reid_params_dump_file, "\n");
}
}
}
}
fclose (reid_params_dump_file);
}
}
/**
* Function to dump terminated object information to files when the tracker outputs
* terminated track info into user meta. For this to work, property "terminated_track_output_path"
* must be set in configuration file.
* Data of different sources and frames is dumped in separate file.
*/
static void
write_terminated_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
if (!appCtx->config.terminated_track_output_path)
return;
gchar term_file[1024] = { 0 };
FILE *term_params_dump_file = NULL;
/** Find batch terminted tensor in batch user meta. */
GList *pTerminatedTrackList = NULL; // list of pointers to NvDsTargetMiscDataBatch
NvDsTargetMiscDataBatch *pTerminatedTrackBatch = NULL;
for (NvDsUserMetaList *l_batch_user = batch_meta->batch_user_meta_list;
l_batch_user != NULL;
l_batch_user = l_batch_user->next
)
{
NvDsUserMeta *user_meta = (NvDsUserMeta *) l_batch_user->data;
if (user_meta && user_meta->base_meta.meta_type == NVDS_TRACKER_TERMINATED_LIST_META)
{
pTerminatedTrackBatch = (NvDsTargetMiscDataBatch *) (user_meta->user_meta_data);
pTerminatedTrackList = g_list_append (pTerminatedTrackList, pTerminatedTrackBatch);
}
}
if (!pTerminatedTrackList)
return;
/** Save the Terminated data for each frame. */
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next)
{
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
for (GList *l = pTerminatedTrackList; l != NULL; l = l->next)
{
pTerminatedTrackBatch = (NvDsTargetMiscDataBatch *) (l->data);
for (uint si = 0; si < pTerminatedTrackBatch->numFilled; si++)
{
NvDsTargetMiscDataStream *objStream = (pTerminatedTrackBatch->list) + si;
guint stream_id = (guint)(objStream->streamID);
if (frame_meta->pad_index != stream_id)
continue;
g_snprintf(term_file, sizeof(term_file) - 1,
"%s/%02u_%03u_%06lu.txt", appCtx->config.terminated_track_output_path,
appCtx->index, stream_id, (gulong)frame_meta->frame_num);
term_params_dump_file = fopen(term_file, "w");
if (!term_params_dump_file)
continue;
for (uint li = 0; li < objStream->numFilled; li++)
{
NvDsTargetMiscDataObject *objList = (objStream->list) + li;
fprintf(term_params_dump_file,
"Target: %ld,%d,%hu\n",
objList->uniqueId,
objList->classId,
stream_id);
for (uint oi = 0; oi < objList->numObj; oi++)
{
NvDsTargetMiscDataFrame *obj = (objList->list) + oi;
float left = obj->tBbox.left;
float right = left + obj->tBbox.width;
float top = obj->tBbox.top;
float bottom = top + obj->tBbox.height;
fprintf(term_params_dump_file,
"%u %lu %u 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f %d %f\n",
obj->frameNum, objList->uniqueId, objList->classId, left, top, right, bottom,
obj->confidence, obj->trackerState, obj->visibility);
}
}
fprintf(term_params_dump_file, "\n");
fclose(term_params_dump_file);
}
}
}
g_list_free(pTerminatedTrackList);
}
/**
* Function to dump terminated object information to files when the tracker outputs
* terminated track info into user meta. For this to work, property "terminated_track_output_path"
* must be set in configuration file.
* Data of different sources and frames is dumped in separate file.
*/
static void
write_shadow_track_output (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
if (!appCtx->config.shadow_track_output_path)
return;
gchar term_file[1024] = { 0 };
FILE *shadow_dump_file = NULL;
/** Find shadow tracked tensor in batch user meta. */
GList *pShadowTrackList = NULL; // list of pointers to NvDsTargetMiscDataBatch
NvDsTargetMiscDataBatch *pShadowTrackBatch = NULL;
for (NvDsUserMetaList *l_batch_user = batch_meta->batch_user_meta_list;
l_batch_user != NULL;
l_batch_user = l_batch_user->next
)
{
NvDsUserMeta *user_meta = (NvDsUserMeta *) l_batch_user->data;
if (user_meta && user_meta->base_meta.meta_type == NVDS_TRACKER_SHADOW_LIST_META)
{
// std::cout << "Found Shadow Data" << std::endl;
pShadowTrackBatch = (NvDsTargetMiscDataBatch *) (user_meta->user_meta_data);
pShadowTrackList = g_list_append (pShadowTrackList, pShadowTrackBatch);
}
}
if (!pShadowTrackList)
return;
/** Save the Terminated data for each frame. */
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next)
{
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
for (GList *l = pShadowTrackList; l != NULL; l = l->next)
{
pShadowTrackBatch = (NvDsTargetMiscDataBatch *) (l->data);
for (uint si = 0; si < pShadowTrackBatch->numFilled; si++)
{
NvDsTargetMiscDataStream *objStream = (pShadowTrackBatch->list) + si;
guint stream_id = (guint)(objStream->streamID);
if (frame_meta->pad_index != stream_id)
continue;
g_snprintf(term_file, sizeof(term_file) - 1,
"%s/%02u_%03u_%06lu.txt", appCtx->config.shadow_track_output_path,
appCtx->index, stream_id, (gulong)frame_meta->frame_num);
shadow_dump_file = fopen(term_file, "w");
if (!shadow_dump_file)
continue;
for (uint li = 0; li < objStream->numFilled; li++)
{
NvDsTargetMiscDataObject *objList = (objStream->list) + li;
if (objList->numObj > 0)
{
NvDsTargetMiscDataFrame *obj = (objList->list); // get first element only
float left = obj->tBbox.left;
float right = left + obj->tBbox.width;
float top = obj->tBbox.top;
float bottom = top + obj->tBbox.height;
fprintf(shadow_dump_file,
"%u %lu %u 0 0.0 %f %f %f %f 0.0 0.0 0.0 0.0 0.0 0.0 0.0 %f %d %f\n",
obj->frameNum, objList->uniqueId, objList->classId, left, top, right, bottom,
obj->confidence, obj->trackerState, obj->visibility);
}
}
fclose(shadow_dump_file);
}
}
}
g_list_free(pShadowTrackList);
}
static gint
component_id_compare_func (gconstpointer a, gconstpointer b)
{
NvDsClassifierMeta *cmetaa = (NvDsClassifierMeta *) a;
NvDsClassifierMeta *cmetab = (NvDsClassifierMeta *) b;
if (cmetaa->unique_component_id < cmetab->unique_component_id)
return -1;
if (cmetaa->unique_component_id > cmetab->unique_component_id)
return 1;
return 0;
}
/**
* Function to process the attached metadata. This is just for demonstration
* and can be removed if not required.
* Here it demonstrates to use bounding boxes of different color and size for
* different type / class of objects.
* It also demonstrates how to join the different labels(PGIE + SGIEs)
* of an object to form a single string.
*/
static void
process_meta (AppCtx * appCtx, NvDsBatchMeta * batch_meta)
{
// For single source always display text either with demuxer or with tiler
if (!appCtx->config.tiled_display_config.enable ||
appCtx->config.num_source_sub_bins == 1) {
appCtx->show_bbox_text = 1;
}
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *) l_frame->data;
for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
l_obj = l_obj->next) {
NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
gint class_index = obj->class_id;
NvDsGieConfig *gie_config = NULL;
gchar *str_ins_pos = NULL;
if (obj->unique_component_id ==
(gint) appCtx->config.primary_gie_config.unique_id) {
gie_config = &appCtx->config.primary_gie_config;
} else {
for (gint i = 0; i < (gint) appCtx->config.num_secondary_gie_sub_bins;
i++) {
gie_config = &appCtx->config.secondary_gie_sub_bin_config[i];
if (obj->unique_component_id == (gint) gie_config->unique_id) {
break;
}
gie_config = NULL;
}
}
g_free (obj->text_params.display_text);
obj->text_params.display_text = NULL;
if (gie_config != NULL) {
if (g_hash_table_contains (gie_config->bbox_border_color_table,
class_index + (gchar *) NULL)) {
obj->rect_params.border_color = *((NvOSD_ColorParams *)
g_hash_table_lookup (gie_config->bbox_border_color_table,
class_index + (gchar *) NULL));
} else {
obj->rect_params.border_color = gie_config->bbox_border_color;
}
obj->rect_params.border_width = appCtx->config.osd_config.border_width;
if (g_hash_table_contains (gie_config->bbox_bg_color_table,
class_index + (gchar *) NULL)) {
obj->rect_params.has_bg_color = 1;
obj->rect_params.bg_color = *((NvOSD_ColorParams *)
g_hash_table_lookup (gie_config->bbox_bg_color_table,
class_index + (gchar *) NULL));
} else {
obj->rect_params.has_bg_color = 0;
}
}
if (!appCtx->show_bbox_text)
continue;
obj->text_params.x_offset = obj->rect_params.left;
obj->text_params.y_offset = obj->rect_params.top - 30;
obj->text_params.font_params.font_color =
appCtx->config.osd_config.text_color;
obj->text_params.font_params.font_size =
appCtx->config.osd_config.text_size;
obj->text_params.font_params.font_name = appCtx->config.osd_config.font;
if (appCtx->config.osd_config.text_has_bg) {
obj->text_params.set_bg_clr = 1;
obj->text_params.text_bg_clr = appCtx->config.osd_config.text_bg_color;
}
obj->text_params.display_text = (char *) g_malloc (128);
obj->text_params.display_text[0] = '\0';
str_ins_pos = obj->text_params.display_text;
if (obj->obj_label[0] != '\0')
sprintf (str_ins_pos, "%s", obj->obj_label);
str_ins_pos += strlen (str_ins_pos);
if (obj->object_id != UNTRACKED_OBJECT_ID) {
/** object_id is a 64-bit sequential value;
* but considering the display aesthetic,
* trimming to lower 32-bits */
if (appCtx->config.tracker_config.display_tracking_id) {
guint64 const LOW_32_MASK = 0x00000000FFFFFFFF;
sprintf (str_ins_pos, " %lu", (obj->object_id & LOW_32_MASK));
str_ins_pos += strlen (str_ins_pos);
}
}
obj->classifier_meta_list =
g_list_sort (obj->classifier_meta_list, component_id_compare_func);
for (NvDsMetaList * l_class = obj->classifier_meta_list; l_class != NULL;
l_class = l_class->next) {
NvDsClassifierMeta *cmeta = (NvDsClassifierMeta *) l_class->data;
for (NvDsMetaList * l_label = cmeta->label_info_list; l_label != NULL;
l_label = l_label->next) {
NvDsLabelInfo *label = (NvDsLabelInfo *) l_label->data;
if (label->pResult_label) {
sprintf (str_ins_pos, " %s", label->pResult_label);
} else if (label->result_label[0] != '\0') {
sprintf (str_ins_pos, " %s", label->result_label);
}
str_ins_pos += strlen (str_ins_pos);
}
}
}
}
}
/**
* Function which processes the inferred buffer and its metadata.
* It also gives opportunity to attach application specific
* metadata (e.g. clock, analytics output etc.).
*/
static void
process_buffer (GstBuffer * buf, AppCtx * appCtx, guint index)
{
NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
if (!batch_meta) {
NVGSTDS_WARN_MSG_V ("Batch meta not found for buffer %p", buf);
return;
}
process_meta (appCtx, batch_meta);
//NvDsInstanceData *data = &appCtx->instance_data[index];
//guint i;
// data->frame_num++;
/* Opportunity to modify the processed metadata or do analytics based on
* type of object e.g. maintaining count of particular type of car.
*/
if (appCtx->all_bbox_generated_cb) {
appCtx->all_bbox_generated_cb (appCtx, buf, batch_meta, index);
}
//data->bbox_list_size = 0;
/*
* callback to attach application specific additional metadata.
*/
if (appCtx->overlay_graphics_cb) {
appCtx->overlay_graphics_cb (appCtx, buf, batch_meta, index);
}
}
/**
* Buffer probe function to get the results of primary infer.
* Here it demonstrates the use by dumping bounding box coordinates in
* kitti format.
*/
static GstPadProbeReturn
gie_primary_processing_done_buf_prob (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
GstBuffer *buf = (GstBuffer *) info->data;
AppCtx *appCtx = (AppCtx *) u_data;
NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
if (!batch_meta) {
NVGSTDS_WARN_MSG_V ("Batch meta not found for buffer %p", buf);
return GST_PAD_PROBE_OK;
}
write_kitti_output (appCtx, batch_meta);
return GST_PAD_PROBE_OK;
}
/**
* Probe function to get results after all inferences(Primary + Secondary)
* are done. This will be just before OSD or sink (in case OSD is disabled).
*/
static GstPadProbeReturn
gie_processing_done_buf_prob (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
GstBuffer *buf = (GstBuffer *) info->data;
NvDsInstanceBin *bin = (NvDsInstanceBin *) u_data;
guint index = bin->index;
AppCtx *appCtx = bin->appCtx;
if (gst_buffer_is_writable (buf))
process_buffer (buf, appCtx, index);
return GST_PAD_PROBE_OK;
}
/**
* Buffer probe function after tracker.
*/
static GstPadProbeReturn
analytics_done_buf_prob (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
NvDsInstanceBin *bin = (NvDsInstanceBin *) u_data;
guint index = bin->index;
AppCtx *appCtx = bin->appCtx;
GstBuffer *buf = (GstBuffer *) info->data;
NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta (buf);
if (!batch_meta) {
NVGSTDS_WARN_MSG_V ("Batch meta not found for buffer %p", buf);
return GST_PAD_PROBE_OK;
}
/*
* Output KITTI labels with tracking ID if configured to do so.
*/
write_kitti_track_output (appCtx, batch_meta);
write_kitti_past_track_output (appCtx, batch_meta);
write_reid_track_output(appCtx, batch_meta);
write_terminated_track_output(appCtx, batch_meta);
write_shadow_track_output(appCtx, batch_meta);
if (appCtx->bbox_generated_post_analytics_cb) {
appCtx->bbox_generated_post_analytics_cb (appCtx, buf, batch_meta, index);
}
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
latency_measurement_buf_prob (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
AppCtx *appCtx = (AppCtx *) u_data;
guint i = 0, num_sources_in_batch = 0;
if (nvds_enable_latency_measurement) {
GstBuffer *buf = (GstBuffer *) info->data;
NvDsFrameLatencyInfo *latency_info = NULL;
g_mutex_lock (&appCtx->latency_lock);
latency_info = appCtx->latency_info;
guint64 batch_num= GPOINTER_TO_SIZE(g_object_get_data(G_OBJECT(pad),"latency-batch-num"));
g_print("\n************BATCH-NUM = %lu**************\n",batch_num);
num_sources_in_batch = nvds_measure_buffer_latency (buf, latency_info);
for (i = 0; i < num_sources_in_batch; i++) {
g_print ("Source id = %d Frame_num = %d Frame latency = %lf (ms) \n",
latency_info[i].source_id,
latency_info[i].frame_num, latency_info[i].latency);
}
g_mutex_unlock (&appCtx->latency_lock);
g_object_set_data(G_OBJECT(pad),"latency-batch-num",GSIZE_TO_POINTER(batch_num+1));
}
return GST_PAD_PROBE_OK;
}
static GstPadProbeReturn
demux_latency_measurement_buf_prob (GstPad * pad, GstPadProbeInfo * info,
gpointer u_data)
{
AppCtx *appCtx = (AppCtx *) u_data;
guint i = 0, num_sources_in_batch = 0;
if (nvds_enable_latency_measurement) {
GstBuffer *buf = (GstBuffer *) info->data;
NvDsFrameLatencyInfo *latency_info = NULL;
g_mutex_lock (&appCtx->latency_lock);
latency_info = appCtx->latency_info;
g_print ("\n************DEMUX BATCH-NUM = %d**************\n",
demux_batch_num);
num_sources_in_batch = nvds_measure_buffer_latency (buf, latency_info);
for (i = 0; i < num_sources_in_batch; i++) {
g_print ("Source id = %d Frame_num = %d Frame latency = %lf (ms) \n",
latency_info[i].source_id,
latency_info[i].frame_num, latency_info[i].latency);
}
g_mutex_unlock (&appCtx->latency_lock);
demux_batch_num++;
}
return GST_PAD_PROBE_OK;
}
static gboolean
add_and_link_broker_sink (AppCtx * appCtx)
{
NvDsConfig *config = &appCtx->config;
/** Only first instance_bin broker sink
* employed as there's only one analytics path for N sources
* NOTE: There shall be only one [sink] group
* with type=6 (NV_DS_SINK_MSG_CONV_BROKER)
* a) Multiple of them does not make sense as we have only
* one analytics pipe generating the data for broker sink
* b) If Multiple broker sinks are configured by the user
* in config file, only the first in the order of
* appearance will be considered
* and others shall be ignored
* c) Ideally it should be documented (or obvious) that:
* multiple [sink] groups with type=6 (NV_DS_SINK_MSG_CONV_BROKER)
* is invalid
*/
NvDsInstanceBin *instance_bin = &appCtx->pipeline.instance_bins[0];
NvDsPipeline *pipeline = &appCtx->pipeline;
for (guint i = 0; i < config->num_sink_sub_bins; i++) {
if (config->sink_bin_sub_bin_config[i].type == NV_DS_SINK_MSG_CONV_BROKER) {
if (!pipeline->common_elements.tee) {
NVGSTDS_ERR_MSG_V
("%s failed; broker added without analytics; check config file\n",
__func__);
return FALSE;
}
/** add the broker sink bin to pipeline */
if (!gst_bin_add (GST_BIN (pipeline->pipeline),
instance_bin->sink_bin.sub_bins[i].bin)) {
return FALSE;
}
/** link the broker sink bin to the common_elements tee
* (The tee after nvinfer -> tracker (optional) -> sgies (optional) block) */
if (!link_element_to_tee_src_pad (pipeline->common_elements.tee,
instance_bin->sink_bin.sub_bins[i].bin)) {
return FALSE;
}
}
}
return TRUE;
}
static gboolean
create_demux_pipeline (AppCtx * appCtx, guint index)
{
gboolean ret = FALSE;
NvDsConfig *config = &appCtx->config;
NvDsInstanceBin *instance_bin = &appCtx->pipeline.demux_instance_bins[index];
GstElement *last_elem;
gchar elem_name[32];
instance_bin->index = index;
instance_bin->appCtx = appCtx;
g_snprintf (elem_name, 32, "processing_demux_bin_%d", index);
instance_bin->bin = gst_bin_new (elem_name);
if (!create_demux_sink_bin (config->num_sink_sub_bins,
config->sink_bin_sub_bin_config, &instance_bin->demux_sink_bin,
config->sink_bin_sub_bin_config[index].source_id)) {
goto done;
}
gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->demux_sink_bin.bin);
last_elem = instance_bin->demux_sink_bin.bin;
if (config->osd_config.enable) {
if (!create_osd_bin (&config->osd_config, &instance_bin->osd_bin)) {
goto done;
}
gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->osd_bin.bin);
NVGSTDS_LINK_ELEMENT (instance_bin->osd_bin.bin, last_elem);
last_elem = instance_bin->osd_bin.bin;
}
NVGSTDS_BIN_ADD_GHOST_PAD (instance_bin->bin, last_elem, "sink");
if (config->osd_config.enable) {
NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
instance_bin->osd_bin.nvosd, "sink",
gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
} else {
NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
instance_bin->demux_sink_bin.bin, "sink",
gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/**
* Function to add components to pipeline which are dependent on number
* of streams. These components work on single buffer. If tiling is being
* used then single instance will be created otherwise < N > such instances
* will be created for < N > streams
*/
static gboolean
create_processing_instance (AppCtx * appCtx, guint index)
{
gboolean ret = FALSE;
NvDsConfig *config = &appCtx->config;
NvDsInstanceBin *instance_bin = &appCtx->pipeline.instance_bins[index];
GstElement *last_elem;
gchar elem_name[32];
instance_bin->index = index;
instance_bin->appCtx = appCtx;
g_snprintf (elem_name, 32, "processing_bin_%d", index);
instance_bin->bin = gst_bin_new (elem_name);
if (!create_sink_bin (config->num_sink_sub_bins,
config->sink_bin_sub_bin_config, &instance_bin->sink_bin, index)) {
goto done;
}
gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->sink_bin.bin);
last_elem = instance_bin->sink_bin.bin;
if (config->osd_config.enable) {
if (!create_osd_bin (&config->osd_config, &instance_bin->osd_bin)) {
goto done;
}
gst_bin_add (GST_BIN (instance_bin->bin), instance_bin->osd_bin.bin);
NVGSTDS_LINK_ELEMENT (instance_bin->osd_bin.bin, last_elem);
last_elem = instance_bin->osd_bin.bin;
}
NVGSTDS_BIN_ADD_GHOST_PAD (instance_bin->bin, last_elem, "sink");
if (config->osd_config.enable) {
NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
instance_bin->osd_bin.nvosd, "sink",
gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
} else {
NVGSTDS_ELEM_ADD_PROBE (instance_bin->all_bbox_buffer_probe_id,
instance_bin->sink_bin.bin, "sink",
gie_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, instance_bin);
}
ret = TRUE;
done:
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/**
* Function to create common elements(Primary infer, tracker, secondary infer)
* of the pipeline. These components operate on muxed data from all the
* streams. So they are independent of number of streams in the pipeline.
*/
static gboolean
create_common_elements (NvDsConfig * config, NvDsPipeline * pipeline,
GstElement ** sink_elem, GstElement ** src_elem,
bbox_generated_callback bbox_generated_post_analytics_cb)
{
gboolean ret = FALSE;
*sink_elem = *src_elem = NULL;
if (config->segvisual_config.enable) {
if (!create_segvisual_bin (&config->segvisual_config,
&pipeline->common_elements.segvisual_bin)) {
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.segvisual_bin.bin);
if (!*src_elem) {
*src_elem = pipeline->common_elements.segvisual_bin.bin;
}
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.segvisual_bin.bin,
*sink_elem);
}
*sink_elem = pipeline->common_elements.segvisual_bin.bin;
}
if (config->primary_gie_config.enable) {
if (config->num_secondary_gie_sub_bins > 0) {
/** if using nvmultiurisrcbin, override batch-size config for sgie */
if (config->use_nvmultiurisrcbin) {
for (guint i = 0; i < config->num_secondary_gie_sub_bins; i++) {
config->secondary_gie_sub_bin_config[i].batch_size =
config->sgie_batch_size;
}
}
if (!create_secondary_gie_bin (config->num_secondary_gie_sub_bins,
config->primary_gie_config.unique_id,
config->secondary_gie_sub_bin_config,
&pipeline->common_elements.secondary_gie_bin)) {
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.secondary_gie_bin.bin);
if (!*src_elem) {
*src_elem = pipeline->common_elements.secondary_gie_bin.bin;
}
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.secondary_gie_bin.bin,
*sink_elem);
}
*sink_elem = pipeline->common_elements.secondary_gie_bin.bin;
}
}
if (config->primary_gie_config.enable) {
if (config->num_secondary_preprocess_sub_bins > 0) {
if (!create_secondary_preprocess_bin (config->
num_secondary_preprocess_sub_bins,
config->primary_gie_config.unique_id,
config->secondary_preprocess_sub_bin_config,
&pipeline->common_elements.secondary_preprocess_bin)) {
g_print ("creating secondary_preprocess bin failed\n");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.secondary_preprocess_bin.bin);
if (!*src_elem) {
*src_elem = pipeline->common_elements.secondary_preprocess_bin.bin;
}
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.
secondary_preprocess_bin.bin, *sink_elem);
}
*sink_elem = pipeline->common_elements.secondary_preprocess_bin.bin;
}
}
if (config->dsanalytics_config.enable) {
if (!create_dsanalytics_bin (&config->dsanalytics_config,
&pipeline->common_elements.dsanalytics_bin)) {
g_print ("creating dsanalytics bin failed\n");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.dsanalytics_bin.bin);
if (!*src_elem) {
*src_elem = pipeline->common_elements.dsanalytics_bin.bin;
}
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.dsanalytics_bin.bin,
*sink_elem);
}
*sink_elem = pipeline->common_elements.dsanalytics_bin.bin;
}
if (config->tracker_config.enable) {
if (!create_tracking_bin (&config->tracker_config,
&pipeline->common_elements.tracker_bin)) {
g_print ("creating tracker bin failed\n");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.tracker_bin.bin);
if (!*src_elem) {
*src_elem = pipeline->common_elements.tracker_bin.bin;
}
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.tracker_bin.bin,
*sink_elem);
}
*sink_elem = pipeline->common_elements.tracker_bin.bin;
}
if (config->primary_gie_config.enable) {
/** if using nvmultiurisrcbin, override batch-size config for pgie */
if (config->use_nvmultiurisrcbin) {
config->primary_gie_config.batch_size = config->max_batch_size;
}
if (!create_primary_gie_bin (&config->primary_gie_config,
&pipeline->common_elements.primary_gie_bin)) {
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.primary_gie_bin.bin);
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.primary_gie_bin.bin,
*sink_elem);
}
*sink_elem = pipeline->common_elements.primary_gie_bin.bin;
if (!*src_elem) {
*src_elem = pipeline->common_elements.primary_gie_bin.bin;
}
NVGSTDS_ELEM_ADD_PROBE (pipeline->
common_elements.primary_bbox_buffer_probe_id,
pipeline->common_elements.primary_gie_bin.bin, "src",
gie_primary_processing_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
pipeline->common_elements.appCtx);
}
if (config->preprocess_config.enable) {
if (!create_preprocess_bin (&config->preprocess_config,
&pipeline->common_elements.preprocess_bin)) {
g_print ("creating preprocess bin failed\n");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.preprocess_bin.bin);
if (!*src_elem) {
*src_elem = pipeline->common_elements.preprocess_bin.bin;
}
if (*sink_elem) {
NVGSTDS_LINK_ELEMENT (pipeline->common_elements.preprocess_bin.bin,
*sink_elem);
}
*sink_elem = pipeline->common_elements.preprocess_bin.bin;
}
if (*src_elem) {
NVGSTDS_ELEM_ADD_PROBE (pipeline->
common_elements.primary_bbox_buffer_probe_id, *src_elem, "src",
analytics_done_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
&pipeline->common_elements);
/* Add common message converter */
if (config->msg_conv_config.enable) {
NvDsSinkMsgConvBrokerConfig *convConfig = &config->msg_conv_config;
pipeline->common_elements.msg_conv =
gst_element_factory_make (NVDS_ELEM_MSG_CONV, "common_msg_conv");
if (!pipeline->common_elements.msg_conv) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'common_msg_conv'");
goto done;
}
g_object_set (G_OBJECT (pipeline->common_elements.msg_conv),
"config", convConfig->config_file_path,
"msg2p-lib",
(convConfig->conv_msg2p_lib ? convConfig->conv_msg2p_lib : "null"),
"payload-type", convConfig->conv_payload_type, "comp-id",
convConfig->conv_comp_id, "debug-payload-dir",
convConfig->debug_payload_dir, "multiple-payloads",
convConfig->multiple_payloads,"msg2p-newapi", convConfig->conv_msg2p_new_api,
"frame-interval", convConfig->conv_frame_interval, NULL);
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->common_elements.msg_conv);
NVGSTDS_LINK_ELEMENT (*src_elem, pipeline->common_elements.msg_conv);
*src_elem = pipeline->common_elements.msg_conv;
}
pipeline->common_elements.tee =
gst_element_factory_make (NVDS_ELEM_TEE, "common_analytics_tee");
if (!pipeline->common_elements.tee) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'common_analytics_tee'");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->common_elements.tee);
NVGSTDS_LINK_ELEMENT (*src_elem, pipeline->common_elements.tee);
*src_elem = pipeline->common_elements.tee;
}
ret = TRUE;
done:
return ret;
}
static gboolean
is_sink_available_for_source_id (NvDsConfig * config, guint source_id)
{
for (guint j = 0; j < config->num_sink_sub_bins; j++) {
if (config->sink_bin_sub_bin_config[j].enable &&
config->sink_bin_sub_bin_config[j].source_id == source_id &&
config->sink_bin_sub_bin_config[j].link_to_demux == FALSE) {
return TRUE;
}
}
return FALSE;
}
/**
* Main function to create the pipeline.
*/
gboolean
create_pipeline (AppCtx * appCtx,
bbox_generated_callback bbox_generated_post_analytics_cb,
bbox_generated_callback all_bbox_generated_cb, perf_callback perf_cb,
overlay_graphics_callback overlay_graphics_cb)
{
gboolean ret = FALSE;
NvDsPipeline *pipeline = &appCtx->pipeline;
NvDsConfig *config = &appCtx->config;
GstBus *bus;
GstElement *last_elem;
GstElement *tmp_elem1;
GstElement *tmp_elem2;
guint i;
GstPad *fps_pad = NULL;
gulong latency_probe_id;
_dsmeta_quark = g_quark_from_static_string (NVDS_META_STRING);
appCtx->all_bbox_generated_cb = all_bbox_generated_cb;
appCtx->bbox_generated_post_analytics_cb = bbox_generated_post_analytics_cb;
appCtx->overlay_graphics_cb = overlay_graphics_cb;
appCtx->sensorInfoHash = g_hash_table_new (NULL, NULL);
appCtx->perf_struct.FPSInfoHash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
if (config->osd_config.num_out_buffers < 8) {
config->osd_config.num_out_buffers = 8;
}
pipeline->pipeline = gst_pipeline_new ("pipeline");
if (!pipeline->pipeline) {
NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
goto done;
}
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline->pipeline));
pipeline->bus_id = gst_bus_add_watch (bus, bus_callback, appCtx);
gst_object_unref (bus);
if (config->file_loop) {
/* Let each source bin know it needs to loop. */
guint i;
for (i = 0; i < config->num_source_sub_bins; i++)
config->multi_source_config[i].loop = TRUE;
}
for (guint i = 0; i < config->num_sink_sub_bins; i++) {
NvDsSinkSubBinConfig *sink_config = &config->sink_bin_sub_bin_config[i];
switch (sink_config->type) {
case NV_DS_SINK_FAKE:
#ifndef IS_TEGRA
case NV_DS_SINK_RENDER_EGL:
#else
case NV_DS_SINK_RENDER_3D:
#endif
case NV_DS_SINK_RENDER_DRM:
/* Set the "qos" property of sink, if not explicitly specified in the
config. */
if (!sink_config->render_config.qos_value_specified) {
sink_config->render_config.qos = FALSE;
}
default:
break;
}
}
/*
* Add muxer and < N > source components to the pipeline based
* on the settings in configuration file.
*/
if (config->use_nvmultiurisrcbin) {
if (config->num_source_sub_bins >0){
if (!create_nvmultiurisrcbin_bin (config->num_source_sub_bins,
config->multi_source_config, &pipeline->multi_src_bin))
goto done;
}
else {
if (!config->source_attr_all_parsed) {
NVGSTDS_ERR_MSG_V("[source-attr-all] config group not set, needs to be configured");
goto done;
}
if (!create_nvmultiurisrcbin_bin (config->num_source_sub_bins,
&config->source_attr_all_config, &pipeline->multi_src_bin))
goto done;
//[source-list] added with num-source-bins=0; This means source-bin
//will be created and be waiting for source adds over REST API
//mark num-source-bins=1 as one source-bin is indeed created
config->num_source_sub_bins = 1;
}
/** set properties for nvmultiurisrcbin */
if (config->uri_list) {
gchar *uri_list_comma_sep = g_strjoinv (",", config->uri_list);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "uri-list",
uri_list_comma_sep, NULL);
g_free (uri_list_comma_sep);
}
if (config->sensor_id_list) {
gchar *uri_list_comma_sep = g_strjoinv (",", config->sensor_id_list);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "sensor-id-list",
uri_list_comma_sep, NULL);
g_free (uri_list_comma_sep);
}
if (config->sensor_name_list) {
gchar *uri_list_comma_sep = g_strjoinv (",", config->sensor_name_list);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "sensor-name-list",
uri_list_comma_sep, NULL);
g_free (uri_list_comma_sep);
}
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "max-batch-size",
config->max_batch_size, NULL);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "ip-address",
config->http_ip, NULL);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "port",
config->http_port, NULL);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "extract-sei-type5-data-dec",
config->extract_sei_type5_data, NULL);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "low-latency-mode",
config->low_latency_mode, NULL);
g_object_set (pipeline->multi_src_bin.nvmultiurisrcbin, "sei-uuid",
config->sei_uuid, NULL);
} else {
if (!create_multi_source_bin (config->num_source_sub_bins,
config->multi_source_config, &pipeline->multi_src_bin))
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->multi_src_bin.bin);
if (config->streammux_config.is_parsed) {
if (config->use_nvmultiurisrcbin) {
config->streammux_config.use_nvmultiurisrcbin = TRUE;
/** overriding mux_config.batch_size to max_batch_size */
config->streammux_config.batch_size = config->max_batch_size;
}
if (!set_streammux_properties (&config->streammux_config,
pipeline->multi_src_bin.streammux)) {
NVGSTDS_WARN_MSG_V ("Failed to set streammux properties");
}
}
if (appCtx->latency_info == NULL) {
appCtx->latency_info = (NvDsFrameLatencyInfo *)
calloc (1, config->streammux_config.batch_size *
sizeof (NvDsFrameLatencyInfo));
}
/** a tee after the tiler which shall be connected to sink(s) */
pipeline->tiler_tee = gst_element_factory_make (NVDS_ELEM_TEE, "tiler_tee");
if (!pipeline->tiler_tee) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'tiler_tee'");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->tiler_tee);
/** Tiler + Demux in Parallel Use-Case */
if (config->tiled_display_config.enable ==
NV_DS_TILED_DISPLAY_ENABLE_WITH_PARALLEL_DEMUX) {
pipeline->demuxer =
gst_element_factory_make (NVDS_ELEM_STREAM_DEMUX, "demuxer");
if (!pipeline->demuxer) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'demuxer'");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->demuxer);
/** NOTE:
* demux output is supported for only one source
* If multiple [sink] groups are configured with
* link_to_demux=1, only the first [sink]
* shall be constructed for all occurences of
* [sink] groups with link_to_demux=1
*/
{
gchar pad_name[16];
GstPad *demux_src_pad;
i = 0;
if (!create_demux_pipeline (appCtx, i)) {
goto done;
}
for (i = 0; i < config->num_sink_sub_bins; i++) {
if (config->sink_bin_sub_bin_config[i].link_to_demux == TRUE) {
g_snprintf (pad_name, 16, "src_%02d",
config->sink_bin_sub_bin_config[i].source_id);
break;
}
}
if (i >= config->num_sink_sub_bins) {
g_print
("\n\nError : sink for demux (use link-to-demux-only property) is not provided in the config file\n\n");
goto done;
}
i = 0;
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->demux_instance_bins[i].bin);
demux_src_pad = gst_element_request_pad_simple (pipeline->demuxer, pad_name);
NVGSTDS_LINK_ELEMENT_FULL (pipeline->demuxer, pad_name,
pipeline->demux_instance_bins[i].bin, "sink");
gst_object_unref (demux_src_pad);
NVGSTDS_ELEM_ADD_PROBE (latency_probe_id,
appCtx->pipeline.demux_instance_bins[i].demux_sink_bin.bin,
"sink",
demux_latency_measurement_buf_prob, GST_PAD_PROBE_TYPE_BUFFER,
appCtx);
latency_probe_id = latency_probe_id;
}
last_elem = pipeline->demuxer;
link_element_to_tee_src_pad (pipeline->tiler_tee, last_elem);
last_elem = pipeline->tiler_tee;
}
if (config->tiled_display_config.enable) {
/* Tiler will generate a single composited buffer for all sources. So need
* to create only one processing instance. */
if (!create_processing_instance (appCtx, 0)) {
goto done;
}
// create and add tiling component to pipeline.
if (config->tiled_display_config.columns *
config->tiled_display_config.rows < config->num_source_sub_bins) {
if (config->tiled_display_config.columns == 0) {
config->tiled_display_config.columns =
(guint) (sqrt (config->num_source_sub_bins) + 0.5);
}
config->tiled_display_config.rows =
(guint) ceil (1.0 * config->num_source_sub_bins /
config->tiled_display_config.columns);
NVGSTDS_WARN_MSG_V
("Num of Tiles less than number of sources, readjusting to "
"%u rows, %u columns", config->tiled_display_config.rows,
config->tiled_display_config.columns);
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->instance_bins[0].bin);
last_elem = pipeline->instance_bins[0].bin;
if (!create_tiled_display_bin (&config->tiled_display_config,
&pipeline->tiled_display_bin)) {
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->tiled_display_bin.bin);
NVGSTDS_LINK_ELEMENT (pipeline->tiled_display_bin.bin, last_elem);
last_elem = pipeline->tiled_display_bin.bin;
link_element_to_tee_src_pad (pipeline->tiler_tee,
pipeline->tiled_display_bin.bin);
last_elem = pipeline->tiler_tee;
NVGSTDS_ELEM_ADD_PROBE (latency_probe_id,
pipeline->instance_bins->sink_bin.sub_bins[0].sink, "sink",
latency_measurement_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, appCtx);
latency_probe_id = latency_probe_id;
} else {
/*
* Create demuxer only if tiled display is disabled.
*/
pipeline->demuxer =
gst_element_factory_make (NVDS_ELEM_STREAM_DEMUX, "demuxer");
if (!pipeline->demuxer) {
NVGSTDS_ERR_MSG_V ("Failed to create element 'demuxer'");
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->demuxer);
for (i = 0; i < config->num_source_sub_bins; i++) {
gchar pad_name[16];
GstPad *demux_src_pad;
/* Check if any sink has been configured to render/encode output for
* source index `i`. The processing instance for that source will be
* created only if atleast one sink has been configured as such.
*/
if (!is_sink_available_for_source_id (config, i))
continue;
if (!create_processing_instance (appCtx, i)) {
goto done;
}
gst_bin_add (GST_BIN (pipeline->pipeline),
pipeline->instance_bins[i].bin);
g_snprintf (pad_name, 16, "src_%02d", i);
demux_src_pad = gst_element_request_pad_simple (pipeline->demuxer, pad_name);
NVGSTDS_LINK_ELEMENT_FULL (pipeline->demuxer, pad_name,
pipeline->instance_bins[i].bin, "sink");
gst_object_unref (demux_src_pad);
for (int k = 0; k < MAX_SINK_BINS; k++) {
if (pipeline->instance_bins[i].sink_bin.sub_bins[k].sink) {
NVGSTDS_ELEM_ADD_PROBE (latency_probe_id,
pipeline->instance_bins[i].sink_bin.sub_bins[k].sink, "sink",
latency_measurement_buf_prob, GST_PAD_PROBE_TYPE_BUFFER, appCtx);
break;
}
}
latency_probe_id = latency_probe_id;
}
last_elem = pipeline->demuxer;
}
if (config->tiled_display_config.enable == NV_DS_TILED_DISPLAY_DISABLE) {
fps_pad = gst_element_get_static_pad (pipeline->demuxer, "sink");
} else {
fps_pad =
gst_element_get_static_pad (pipeline->tiled_display_bin.bin, "sink");
}
pipeline->common_elements.appCtx = appCtx;
// Decide where in the pipeline the element should be added and add only if
// enabled
if (config->dsexample_config.enable) {
// Create dsexample element bin and set properties
if (!create_dsexample_bin (&config->dsexample_config,
&pipeline->dsexample_bin)) {
goto done;
}
// Add dsexample bin to instance bin
gst_bin_add (GST_BIN (pipeline->pipeline), pipeline->dsexample_bin.bin);
// Link this bin to the last element in the bin
NVGSTDS_LINK_ELEMENT (pipeline->dsexample_bin.bin, last_elem);
// Set this bin as the last element
last_elem = pipeline->dsexample_bin.bin;
}
// create and add common components to pipeline.
if (!create_common_elements (config, pipeline, &tmp_elem1, &tmp_elem2,
bbox_generated_post_analytics_cb)) {
goto done;
}
if (!add_and_link_broker_sink (appCtx)) {
goto done;
}
if (tmp_elem2) {
NVGSTDS_LINK_ELEMENT (tmp_elem2, last_elem);
last_elem = tmp_elem1;
}
NVGSTDS_LINK_ELEMENT (pipeline->multi_src_bin.bin, last_elem);
// enable performance measurement and add call back function to receive
// performance data.
if (config->enable_perf_measurement) {
appCtx->perf_struct.context = appCtx;
if (config->use_nvmultiurisrcbin) {
appCtx->perf_struct.stream_name_display = config->stream_name_display;
appCtx->perf_struct.use_nvmultiurisrcbin = config->use_nvmultiurisrcbin;
enable_perf_measurement (&appCtx->perf_struct, fps_pad,
config->max_batch_size,
config->perf_measurement_interval_sec,
config->multi_source_config[0].dewarper_config.num_surfaces_per_frame,
perf_cb);
} else {
enable_perf_measurement (&appCtx->perf_struct, fps_pad,
pipeline->multi_src_bin.num_bins,
config->perf_measurement_interval_sec,
config->multi_source_config[0].dewarper_config.num_surfaces_per_frame,
perf_cb);
}
}
latency_probe_id = latency_probe_id;
if (config->num_message_consumers) {
for (i = 0; i < config->num_message_consumers; i++) {
appCtx->c2d_ctx[i] =
start_cloud_to_device_messaging (&config->message_consumer_config[i],
NULL, &appCtx->pipeline.multi_src_bin);
if (appCtx->c2d_ctx[i] == NULL) {
NVGSTDS_ERR_MSG_V ("Failed to create message consumer");
goto done;
}
}
}
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (appCtx->pipeline.pipeline),
GST_DEBUG_GRAPH_SHOW_ALL, "ds-app-null");
g_mutex_init (&appCtx->app_lock);
g_cond_init (&appCtx->app_cond);
g_mutex_init (&appCtx->latency_lock);
ret = TRUE;
done:
if (fps_pad)
gst_object_unref (fps_pad);
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/**
* Function to destroy pipeline and release the resources, probes etc.
*/
void
destroy_pipeline (AppCtx * appCtx)
{
gint64 end_time;
NvDsConfig *config = &appCtx->config;
guint i;
GstBus *bus = NULL;
end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND;
if (!appCtx)
return;
gst_element_send_event(appCtx->pipeline.pipeline, gst_event_new_eos());
sleep (1);
g_mutex_lock (&appCtx->app_lock);
if (appCtx->pipeline.pipeline) {
destroy_smart_record_bin (&appCtx->pipeline.multi_src_bin);
bus = gst_pipeline_get_bus (GST_PIPELINE (appCtx->pipeline.pipeline));
while (TRUE) {
GstMessage *message = gst_bus_pop (bus);
if (message == NULL || GST_MESSAGE_TYPE (message) == GST_MESSAGE_EOS)
break;
else if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR)
bus_callback (bus, message, appCtx);
else
gst_message_unref (message);
}
gst_object_unref (bus);
gst_element_set_state (appCtx->pipeline.pipeline, GST_STATE_NULL);
}
g_cond_wait_until (&appCtx->app_cond, &appCtx->app_lock, end_time);
g_mutex_unlock (&appCtx->app_lock);
for (i = 0; i < appCtx->config.num_source_sub_bins; i++) {
NvDsInstanceBin *bin = &appCtx->pipeline.instance_bins[i];
if (config->osd_config.enable) {
NVGSTDS_ELEM_REMOVE_PROBE (bin->all_bbox_buffer_probe_id,
bin->osd_bin.nvosd, "sink");
} else {
NVGSTDS_ELEM_REMOVE_PROBE (bin->all_bbox_buffer_probe_id,
bin->sink_bin.bin, "sink");
}
if (config->primary_gie_config.enable) {
NVGSTDS_ELEM_REMOVE_PROBE (bin->primary_bbox_buffer_probe_id,
bin->primary_gie_bin.bin, "src");
}
}
if (appCtx->latency_info == NULL) {
free (appCtx->latency_info);
appCtx->latency_info = NULL;
}
if (appCtx->sensorInfoHash) {
g_hash_table_destroy(appCtx->sensorInfoHash);
}
if (appCtx->perf_struct.FPSInfoHash) {
g_hash_table_destroy(appCtx->perf_struct.FPSInfoHash);
}
destroy_sink_bin ();
g_mutex_clear (&appCtx->latency_lock);
if (appCtx->pipeline.pipeline) {
bus = gst_pipeline_get_bus (GST_PIPELINE (appCtx->pipeline.pipeline));
gst_bus_remove_watch (bus);
gst_object_unref (bus);
gst_object_unref (appCtx->pipeline.pipeline);
appCtx->pipeline.pipeline = NULL;
pause_perf_measurement (&appCtx->perf_struct);
//for pipeline-recreate, reset rtsp srouce's depay, such as rtph264depay.
NvDsSrcParentBin *pbin = &appCtx->pipeline.multi_src_bin;
if(pbin){
NvDsSrcBin *src_bin;
for (i = 0; i < MAX_SOURCE_BINS; i++) {
src_bin = &pbin->sub_bins[i];
if (src_bin && src_bin->config
&& src_bin->config->type == NV_DS_SOURCE_RTSP){
src_bin->depay = NULL;
}
}
}
}
if (config->num_message_consumers) {
for (i = 0; i < config->num_message_consumers; i++) {
if (appCtx->c2d_ctx[i])
stop_cloud_to_device_messaging (appCtx->c2d_ctx[i]);
}
}
}
gboolean
pause_pipeline (AppCtx * appCtx)
{
GstState cur;
GstState pending;
GstStateChangeReturn ret;
GstClockTime timeout = 5 * GST_SECOND / 1000;
ret =
gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
timeout);
if (ret == GST_STATE_CHANGE_ASYNC) {
return FALSE;
}
if (cur == GST_STATE_PAUSED) {
return TRUE;
} else if (cur == GST_STATE_PLAYING) {
gst_element_set_state (appCtx->pipeline.pipeline, GST_STATE_PAUSED);
gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
GST_CLOCK_TIME_NONE);
pause_perf_measurement (&appCtx->perf_struct);
return TRUE;
} else {
return FALSE;
}
}
gboolean
resume_pipeline (AppCtx * appCtx)
{
GstState cur;
GstState pending;
GstStateChangeReturn ret;
GstClockTime timeout = 5 * GST_SECOND / 1000;
ret =
gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
timeout);
if (ret == GST_STATE_CHANGE_ASYNC) {
return FALSE;
}
if (cur == GST_STATE_PLAYING) {
return TRUE;
} else if (cur == GST_STATE_PAUSED) {
gst_element_set_state (appCtx->pipeline.pipeline, GST_STATE_PLAYING);
gst_element_get_state (appCtx->pipeline.pipeline, &cur, &pending,
GST_CLOCK_TIME_NONE);
resume_perf_measurement (&appCtx->perf_struct);
return TRUE;
} else {
return FALSE;
}
}
/*
* 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.
*/
#ifndef __NVGSTDS_APP_H__
#define __NVGSTDS_APP_H__
#include <gst/gst.h>
#include <stdio.h>
#include "deepstream_app_version.h"
#include "deepstream_common.h"
#include "deepstream_config.h"
#include "deepstream_osd.h"
#include "deepstream_segvisual.h"
#include "deepstream_perf.h"
#include "deepstream_preprocess.h"
#include "deepstream_primary_gie.h"
#include "deepstream_sinks.h"
#include "deepstream_sources.h"
#include "deepstream_streammux.h"
#include "deepstream_tiled_display.h"
#include "deepstream_dsanalytics.h"
#include "deepstream_dsexample.h"
#include "deepstream_tracker.h"
#include "deepstream_secondary_gie.h"
#include "deepstream_secondary_preprocess.h"
#include "deepstream_c2d_msg.h"
#include "deepstream_image_save.h"
#include "gst-nvdscustommessage.h"
#include "gst-nvdscommonconfig.h"
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct _AppCtx AppCtx;
typedef void (*bbox_generated_callback) (AppCtx *appCtx, GstBuffer *buf,
NvDsBatchMeta *batch_meta, guint index);
typedef gboolean (*overlay_graphics_callback) (AppCtx *appCtx, GstBuffer *buf,
NvDsBatchMeta *batch_meta, guint index);
typedef struct
{
guint index;
gulong all_bbox_buffer_probe_id;
gulong primary_bbox_buffer_probe_id;
gulong fps_buffer_probe_id;
GstElement *bin;
GstElement *tee;
GstElement *msg_conv;
NvDsPreProcessBin preprocess_bin;
NvDsPrimaryGieBin primary_gie_bin;
NvDsOSDBin osd_bin;
NvDsSegVisualBin segvisual_bin;
NvDsSecondaryGieBin secondary_gie_bin;
NvDsSecondaryPreProcessBin secondary_preprocess_bin;
NvDsTrackerBin tracker_bin;
NvDsSinkBin sink_bin;
NvDsSinkBin demux_sink_bin;
NvDsDsAnalyticsBin dsanalytics_bin;
NvDsDsExampleBin dsexample_bin;
AppCtx *appCtx;
} NvDsInstanceBin;
typedef struct
{
gulong primary_bbox_buffer_probe_id;
guint bus_id;
GstElement *pipeline;
NvDsSrcParentBin multi_src_bin;
NvDsInstanceBin instance_bins[MAX_SOURCE_BINS];
NvDsInstanceBin demux_instance_bins[MAX_SOURCE_BINS];
NvDsInstanceBin common_elements;
GstElement *tiler_tee;
NvDsTiledDisplayBin tiled_display_bin;
GstElement *demuxer;
NvDsDsExampleBin dsexample_bin;
AppCtx *appCtx;
} NvDsPipeline;
typedef struct
{
gboolean enable_perf_measurement;
gint file_loop;
gint pipeline_recreate_sec;
gboolean source_list_enabled;
guint total_num_sources;
guint num_source_sub_bins;
guint num_secondary_gie_sub_bins;
guint num_secondary_preprocess_sub_bins;
guint num_sink_sub_bins;
guint num_message_consumers;
guint perf_measurement_interval_sec;
guint sgie_batch_size;
gboolean extract_sei_type5_data;
gchar *sei_uuid;
gboolean low_latency_mode;
gchar *bbox_dir_path;
gchar *kitti_track_dir_path;
gchar *reid_track_dir_path;
gchar *terminated_track_output_path;
gchar *shadow_track_output_path;
gchar **uri_list;
gchar **sensor_id_list;
gchar **sensor_name_list;
NvDsSourceConfig multi_source_config[MAX_SOURCE_BINS];
NvDsStreammuxConfig streammux_config;
NvDsOSDConfig osd_config;
NvDsSegVisualConfig segvisual_config;
NvDsPreProcessConfig preprocess_config;
NvDsPreProcessConfig secondary_preprocess_sub_bin_config[MAX_SECONDARY_PREPROCESS_BINS];
NvDsGieConfig primary_gie_config;
NvDsTrackerConfig tracker_config;
NvDsGieConfig secondary_gie_sub_bin_config[MAX_SECONDARY_GIE_BINS];
NvDsSinkSubBinConfig sink_bin_sub_bin_config[MAX_SINK_BINS];
NvDsMsgConsumerConfig message_consumer_config[MAX_MESSAGE_CONSUMERS];
NvDsTiledDisplayConfig tiled_display_config;
NvDsDsAnalyticsConfig dsanalytics_config;
NvDsDsExampleConfig dsexample_config;
NvDsSinkMsgConvBrokerConfig msg_conv_config;
NvDsImageSave image_save_config;
/** To support nvmultiurisrcbin */
gboolean use_nvmultiurisrcbin;
gboolean stream_name_display;
guint max_batch_size;
gchar* http_ip;
gchar* http_port;
gboolean source_attr_all_parsed;
NvDsSourceConfig source_attr_all_config;
/** To set Global GPU ID for all the componenents at once if needed
* This will be used in case gpu_id prop is not set for a component
* if gpu_id prop is set for a component, global_gpu_id will be overridden by it */
gint global_gpu_id;
} NvDsConfig;
typedef struct
{
gulong frame_num;
} NvDsInstanceData;
struct _AppCtx
{
gboolean version;
gboolean cintr;
gboolean show_bbox_text;
gboolean seeking;
gboolean quit;
gint person_class_id;
gint car_class_id;
gint return_value;
guint index;
gint active_source_index;
GMutex app_lock;
GCond app_cond;
NvDsPipeline pipeline;
NvDsConfig config;
NvDsConfig override_config;
NvDsInstanceData instance_data[MAX_SOURCE_BINS];
NvDsC2DContext *c2d_ctx[MAX_MESSAGE_CONSUMERS];
NvDsAppPerfStructInt perf_struct;
bbox_generated_callback bbox_generated_post_analytics_cb;
bbox_generated_callback all_bbox_generated_cb;
overlay_graphics_callback overlay_graphics_cb;
NvDsFrameLatencyInfo *latency_info;
GMutex latency_lock;
GThread *ota_handler_thread;
guint ota_inotify_fd;
guint ota_watch_desc;
/** Hash table to save NvDsSensorInfo
* obtained with REST API stream/add, remove operations
* The key is souce_id */
GHashTable *sensorInfoHash;
gboolean eos_received;
};
/**
* @brief Create DS Anyalytics Pipeline per the appCtx
* configurations
* @param appCtx [IN/OUT] The application context
* providing the config info and where the
* pipeline resources are maintained
* @param bbox_generated_post_analytics_cb [IN] This callback
* shall be triggered after analytics
* (PGIE, Tracker or the last SGIE appearing
* in the pipeline)
* More info: create_common_elements()
* @param all_bbox_generated_cb [IN]
* @param perf_cb [IN]
* @param overlay_graphics_cb [IN]
*/
gboolean create_pipeline (AppCtx * appCtx,
bbox_generated_callback bbox_generated_post_analytics_cb,
bbox_generated_callback all_bbox_generated_cb,
perf_callback perf_cb,
overlay_graphics_callback overlay_graphics_cb);
gboolean pause_pipeline (AppCtx * appCtx);
gboolean resume_pipeline (AppCtx * appCtx);
gboolean seek_pipeline (AppCtx * appCtx, glong milliseconds, gboolean seek_is_relative);
void toggle_show_bbox_text (AppCtx * appCtx);
void destroy_pipeline (AppCtx * appCtx);
void restart_pipeline (AppCtx * appCtx);
/**
* Function to read properties from configuration file.
*
* @param[in] config pointer to @ref NvDsConfig
* @param[in] cfg_file_path path of configuration file.
*
* @return true if parsed successfully.
*/
gboolean
parse_config_file (NvDsConfig * config, gchar * cfg_file_path);
/**
* Function to read properties from YML configuration file.
*
* @param[in] config pointer to @ref NvDsConfig
* @param[in] cfg_file_path path of configuration file.
*
* @return true if parsed successfully.
*/
gboolean
parse_config_file_yaml (NvDsConfig * config, gchar * cfg_file_path);
/**
* Function to procure the NvDsSensorInfo for the source_id
* that was added using the nvmultiurisrcbin REST API
*
* @param[in] appCtx [IN/OUT] The application context
* providing the config info and where the
* pipeline resources are maintained
* @param[in] source_id [IN] The unique source_id found in NvDsFrameMeta
*
* @return [transfer-floating] The NvDsSensorInfo for the source_id
* that was added using the nvmultiurisrcbin REST API.
* Please note that the returned pointer
* will be valid only until the stream is removed.
*/
NvDsSensorInfo* get_sensor_info(AppCtx* appCtx, guint source_id);
#ifdef __cplusplus
}
#endif
#endif
/*
* 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 "deepstream_app.h"
#include "deepstream_config_file_parser.h"
#define CONFIG_GROUP_APP "application"
#define CONFIG_GROUP_APP_ENABLE_PERF_MEASUREMENT "enable-perf-measurement"
#define CONFIG_GROUP_APP_PERF_MEASUREMENT_INTERVAL "perf-measurement-interval-sec"
#define CONFIG_GROUP_APP_GIE_OUTPUT_DIR "gie-kitti-output-dir"
#define CONFIG_GROUP_APP_GIE_TRACK_OUTPUT_DIR "kitti-track-output-dir"
#define CONFIG_GROUP_APP_REID_TRACK_OUTPUT_DIR "reid-track-output-dir"
#define CONFIG_GROUP_APP_GLOBAL_GPU_ID "global-gpu-id"
#define CONFIG_GROUP_APP_TERMINATED_TRACK_OUTPUT_DIR "terminated-track-output-dir"
#define CONFIG_GROUP_APP_SHADOW_TRACK_OUTPUT_DIR "shadow-track-output-dir"
#define CONFIG_GROUP_TESTS "tests"
#define CONFIG_GROUP_TESTS_FILE_LOOP "file-loop"
#define CONFIG_GROUP_TESTS_PIPELINE_RECREATE_SEC "pipeline-recreate-sec"
#define CONFIG_GROUP_SOURCE_SGIE_BATCH_SIZE "sgie-batch-size"
GST_DEBUG_CATEGORY_EXTERN (APP_CFG_PARSER_CAT);
#define CHECK_ERROR(error) \
if (error) { \
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "%s", error->message); \
goto done; \
}
static gboolean
parse_source_list (NvDsConfig * config, GKeyFile * key_file,
gchar * cfg_file_path)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
gsize num_strings_uri = 0;
gsize num_strings_sensor_id = 0;
gsize num_strings_sensor_name = 0;
keys = g_key_file_get_keys (key_file, CONFIG_GROUP_SOURCE_LIST, NULL, &error);
CHECK_ERROR (error);
for (key = keys; *key; key++) {
if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_NUM_SOURCE_BINS)) {
config->total_num_sources =
g_key_file_get_integer (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_NUM_SOURCE_BINS, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_URI_LIST)) {
config->uri_list =
g_key_file_get_string_list (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_URI_LIST, &num_strings_uri, &error);
if (num_strings_uri > MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
goto done;
}
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_SENSOR_ID_LIST)) {
config->sensor_id_list =
g_key_file_get_string_list (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_SENSOR_ID_LIST, &num_strings_sensor_id, &error);
if (num_strings_sensor_id > MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
goto done;
}
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_SENSOR_NAME_LIST)) {
config->sensor_name_list =
g_key_file_get_string_list (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_SENSOR_NAME_LIST, &num_strings_sensor_name, &error);
if (num_strings_sensor_name > MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
goto done;
}
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_USE_NVMULTIURISRCBIN)) {
config->use_nvmultiurisrcbin =
g_key_file_get_boolean (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_USE_NVMULTIURISRCBIN, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_STREAM_NAME_DISPLAY)) {
config->stream_name_display =
g_key_file_get_boolean (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_STREAM_NAME_DISPLAY, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_HTTP_IP)) {
config->http_ip =
g_key_file_get_string (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_HTTP_IP, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_HTTP_PORT)) {
config->http_port =
g_key_file_get_string (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_HTTP_PORT, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_LIST_MAX_BATCH_SIZE)) {
config->max_batch_size =
g_key_file_get_integer (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_MAX_BATCH_SIZE, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_SOURCE_SGIE_BATCH_SIZE)) {
config->sgie_batch_size =
g_key_file_get_integer (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_SGIE_BATCH_SIZE, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0(*key, CONFIG_GROUP_SOURCE_EXTRACT_SEI_TYPE5_DATA)) {
config->extract_sei_type5_data =
g_key_file_get_integer(key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_EXTRACT_SEI_TYPE5_DATA, &error);
CHECK_ERROR(error);
} else if (!g_strcmp0(*key, CONFIG_GROUP_SOURCE_LIST_LOW_LATENCY_MODE)) {
config->low_latency_mode =
g_key_file_get_integer(key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_LOW_LATENCY_MODE, &error);
CHECK_ERROR(error);
} else if (!g_strcmp0(*key, CONFIG_GROUP_SOURCE_SEI_UUID)) {
config->sei_uuid =
g_key_file_get_string(key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_SEI_UUID, &error);
CHECK_ERROR(error);
}
else {
NVGSTDS_WARN_MSG_V ("Unknown key '%s' for group [%s]", *key,
CONFIG_GROUP_SOURCE_LIST);
}
}
if (g_key_file_has_key (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_URI_LIST, &error)) {
if (g_key_file_has_key (key_file, CONFIG_GROUP_SOURCE_LIST,
CONFIG_GROUP_SOURCE_LIST_NUM_SOURCE_BINS, &error)) {
if (num_strings_uri != config->total_num_sources) {
NVGSTDS_ERR_MSG_V ("Mismatch in URIs provided and num-source-bins.");
goto done;
}
if (num_strings_sensor_id != config->total_num_sources) {
NVGSTDS_ERR_MSG_V ("Mismatch in Sensor IDs provided and num-source-bins.");
goto done;
}
if (num_strings_sensor_name != config->total_num_sources) {
NVGSTDS_ERR_MSG_V ("Mismatch in Sensor Names provided and num-source-bins.");
goto done;
}
} else {
config->total_num_sources = num_strings_uri;
}
}
ret = TRUE;
done:
if (error) {
g_error_free (error);
}
if (keys) {
g_strfreev (keys);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
set_source_all_configs (NvDsConfig * config, gchar * cfg_file_path)
{
guint i = 0;
for (i = 0; i < config->total_num_sources; i++) {
config->multi_source_config[i] = config->source_attr_all_config;
config->multi_source_config[i].camera_id = i;
if (config->uri_list) {
char *uri = config->uri_list[i];
if (!uri) {
NVGSTDS_ERR_MSG_V ("uri %d entry of list is NULL, use valid uri separated by ';' with the source-list section", (i+1));
return FALSE;
}
if (g_str_has_prefix (config->uri_list[i], "file://")) {
config->multi_source_config[i].type = NV_DS_SOURCE_URI;
config->multi_source_config[i].uri = g_strdup (uri + 7);
config->multi_source_config[i].uri =
g_strdup_printf ("file://%s",
get_absolute_file_path (cfg_file_path,
config->multi_source_config[i].uri));
} else if (g_str_has_prefix (config->uri_list[i], "rtsp://")) {
config->multi_source_config[i].type = NV_DS_SOURCE_RTSP;
config->multi_source_config[i].uri = config->uri_list[i];
} else {
gchar *source_id_start_ptr = uri + 4;
gchar *source_id_end_ptr = NULL;
long camera_id =
g_ascii_strtoull (source_id_start_ptr, &source_id_end_ptr, 10);
if (source_id_start_ptr == source_id_end_ptr
|| *source_id_end_ptr != '\0') {
NVGSTDS_ERR_MSG_V
("Incorrect URI for camera source %s. FORMAT: <usb/csi>:<dev_node/sensor_id>",
uri);
return FALSE;
}
if (g_str_has_prefix (config->uri_list[i], "csi:")) {
config->multi_source_config[i].type = NV_DS_SOURCE_CAMERA_CSI;
config->multi_source_config[i].camera_csi_sensor_id = camera_id;
} else if (g_str_has_prefix (config->uri_list[i], "usb:")) {
config->multi_source_config[i].type = NV_DS_SOURCE_CAMERA_V4L2;
config->multi_source_config[i].camera_v4l2_dev_node = camera_id;
} else {
NVGSTDS_ERR_MSG_V ("URI %d (%s) not in proper format.", i,
config->uri_list[i]);
return FALSE;
}
}
}
}
return TRUE;
}
static gboolean
parse_tests (NvDsConfig * config, GKeyFile * key_file)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
keys = g_key_file_get_keys (key_file, CONFIG_GROUP_TESTS, NULL, &error);
CHECK_ERROR (error);
for (key = keys; *key; key++) {
if (!g_strcmp0 (*key, CONFIG_GROUP_TESTS_FILE_LOOP)) {
config->file_loop =
g_key_file_get_integer (key_file, CONFIG_GROUP_TESTS,
CONFIG_GROUP_TESTS_FILE_LOOP, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_TESTS_PIPELINE_RECREATE_SEC)) {
config->pipeline_recreate_sec =
g_key_file_get_integer (key_file, CONFIG_GROUP_TESTS,
CONFIG_GROUP_TESTS_PIPELINE_RECREATE_SEC, &error);
CHECK_ERROR (error);
} else {
NVGSTDS_WARN_MSG_V ("Unknown key '%s' for group [%s]", *key,
CONFIG_GROUP_TESTS);
}
}
ret = TRUE;
done:
if (error) {
g_error_free (error);
}
if (keys) {
g_strfreev (keys);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
static gboolean
parse_app (NvDsConfig * config, GKeyFile * key_file, gchar * cfg_file_path)
{
gboolean ret = FALSE;
gchar **keys = NULL;
gchar **key = NULL;
GError *error = NULL;
keys = g_key_file_get_keys (key_file, CONFIG_GROUP_APP, NULL, &error);
CHECK_ERROR (error);
for (key = keys; *key; key++) {
if (!g_strcmp0 (*key, CONFIG_GROUP_APP_ENABLE_PERF_MEASUREMENT)) {
config->enable_perf_measurement =
g_key_file_get_integer (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_ENABLE_PERF_MEASUREMENT, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_PERF_MEASUREMENT_INTERVAL)) {
config->perf_measurement_interval_sec =
g_key_file_get_integer (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_PERF_MEASUREMENT_INTERVAL, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_GIE_OUTPUT_DIR)) {
config->bbox_dir_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_GIE_OUTPUT_DIR, &error));
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_GIE_TRACK_OUTPUT_DIR)) {
config->kitti_track_dir_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_GIE_TRACK_OUTPUT_DIR, &error));
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_REID_TRACK_OUTPUT_DIR)) {
config->reid_track_dir_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_REID_TRACK_OUTPUT_DIR, &error));
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_GLOBAL_GPU_ID)) {
/** App Level GPU ID is set here if it is present in APP LEVEL config group
* if gpu_id prop is not set for any component, this global_gpu_id will be used */
config->global_gpu_id =
g_key_file_get_integer (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_GLOBAL_GPU_ID, &error);
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_TERMINATED_TRACK_OUTPUT_DIR)) {
config->terminated_track_output_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_TERMINATED_TRACK_OUTPUT_DIR, &error));
CHECK_ERROR (error);
} else if (!g_strcmp0 (*key, CONFIG_GROUP_APP_SHADOW_TRACK_OUTPUT_DIR)) {
config->shadow_track_output_path = get_absolute_file_path (cfg_file_path,
g_key_file_get_string (key_file, CONFIG_GROUP_APP,
CONFIG_GROUP_APP_SHADOW_TRACK_OUTPUT_DIR, &error));
CHECK_ERROR (error);
}
else {
NVGSTDS_WARN_MSG_V ("Unknown key '%s' for group [%s]", *key,
CONFIG_GROUP_APP);
}
}
ret = TRUE;
done:
if (error) {
g_error_free (error);
}
if (keys) {
g_strfreev (keys);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
gboolean
parse_config_file (NvDsConfig * config, gchar * cfg_file_path)
{
GKeyFile *cfg_file = g_key_file_new ();
GError *error = NULL;
gboolean ret = FALSE;
gchar **groups = NULL;
gchar **group;
guint i, j;
guint num_dewarper_source = 0;
config->source_list_enabled = FALSE;
config->source_attr_all_parsed = FALSE;
/** Initialize global gpu id to -1 */
config->global_gpu_id = -1;
if (!APP_CFG_PARSER_CAT) {
GST_DEBUG_CATEGORY_INIT (APP_CFG_PARSER_CAT, "NVDS_CFG_PARSER", 0, NULL);
}
if (!g_key_file_load_from_file (cfg_file, cfg_file_path, G_KEY_FILE_NONE,
&error)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to load uri file: %s",
error->message);
goto done;
}
/** App group parsing at top level to set global_gpu_id (if available)
* before any other group parsing */
if (g_key_file_has_group (cfg_file, CONFIG_GROUP_APP)) {
if (!parse_app (config, cfg_file, cfg_file_path)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group",
CONFIG_GROUP_APP);
goto done;
}
}
if (g_key_file_has_group (cfg_file, CONFIG_GROUP_SOURCE_LIST)) {
if (!parse_source_list (config, cfg_file, cfg_file_path)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group",
CONFIG_GROUP_SOURCE_LIST);
goto done;
}
config->num_source_sub_bins = config->total_num_sources;
config->source_list_enabled = TRUE;
if (!g_key_file_has_group (cfg_file, CONFIG_GROUP_SOURCE_ALL)) {
NVGSTDS_ERR_MSG_V ("[source-attr-all] group not present.");
ret = FALSE;
goto done;
}
g_key_file_remove_group (cfg_file, CONFIG_GROUP_SOURCE_LIST, &error);
}
if (g_key_file_has_group (cfg_file, CONFIG_GROUP_SOURCE_ALL)) {
/** set gpu_id for source component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->source_attr_all_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for source component is present,
* it will override the value set using global_gpu_id in parse_source function */
if (!parse_source (&config->source_attr_all_config,
cfg_file, (gchar*)CONFIG_GROUP_SOURCE_ALL, cfg_file_path)) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group",
CONFIG_GROUP_SOURCE_LIST);
goto done;
}
config->source_attr_all_parsed = TRUE;
if (!set_source_all_configs (config, cfg_file_path)) {
ret = FALSE;
goto done;
}
g_key_file_remove_group (cfg_file, CONFIG_GROUP_SOURCE_ALL, &error);
}
groups = g_key_file_get_groups (cfg_file, NULL);
for (group = groups; *group; group++) {
gboolean parse_err = FALSE;
GST_CAT_DEBUG (APP_CFG_PARSER_CAT, "Parsing group: %s", *group);
if (!strncmp (*group, CONFIG_GROUP_SOURCE,
sizeof (CONFIG_GROUP_SOURCE) - 1)) {
if (config->num_source_sub_bins == MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
ret = FALSE;
goto done;
}
gchar *source_id_start_ptr = *group + strlen (CONFIG_GROUP_SOURCE);
gchar *source_id_end_ptr = NULL;
guint index =
g_ascii_strtoull (source_id_start_ptr, &source_id_end_ptr, 10);
if (source_id_start_ptr == source_id_end_ptr
|| *source_id_end_ptr != '\0') {
NVGSTDS_ERR_MSG_V
("Source group \"[%s]\" is not in the form \"[source<%%d>]\"",
*group);
ret = FALSE;
goto done;
}
guint source_id = 0;
if (config->source_list_enabled) {
if (index >= config->total_num_sources) {
NVGSTDS_ERR_MSG_V
("Invalid source group index %d, index cannot exceed %d", index,
config->total_num_sources);
ret = FALSE;
goto done;
}
source_id = index;
NVGSTDS_INFO_MSG_V ("Some parameters to be overwritten for group [%s]",
*group);
} else {
source_id = config->num_source_sub_bins;
}
/** set gpu_id for source component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->multi_source_config[source_id].gpu_id = config->global_gpu_id;
}
/** if gpu_id for source component is present,
* it will override the value set using global_gpu_id in parse_source function */
parse_err = !parse_source (&config->multi_source_config[source_id],
cfg_file, *group, cfg_file_path);
if (config->source_list_enabled
&& config->multi_source_config[source_id].type ==
NV_DS_SOURCE_URI_MULTIPLE) {
NVGSTDS_ERR_MSG_V
("MultiURI support not available if [source-list] is provided");
ret = FALSE;
goto done;
}
if (config->multi_source_config[source_id].enable
&& !config->source_list_enabled) {
config->num_source_sub_bins++;
}
}
if (!g_strcmp0 (*group, CONFIG_GROUP_STREAMMUX)) {
/** set gpu_id for streammux component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->streammux_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for streammux component is present,
* it will override the value set using global_gpu_id in parse_streammux function */
parse_err =
!parse_streammux (&config->streammux_config, cfg_file, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_OSD)) {
/** set gpu_id for osd component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->osd_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for osd component is present,
* it will override the value set using global_gpu_id in parse_osd function */
parse_err = !parse_osd (&config->osd_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_SEGVISUAL)) {
/** set gpu_id for segvisual component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->segvisual_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for segvisual component is present,
* it will override the value set using global_gpu_id in parse_segvisual function */
parse_err = !parse_segvisual(&config->segvisual_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_PREPROCESS)) {
parse_err =
!parse_preprocess (&config->preprocess_config, cfg_file,
*group, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_PRIMARY_GIE)) {
/** set gpu_id for primary gie component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->primary_gie_config.gpu_id = config->global_gpu_id;
config->primary_gie_config.is_gpu_id_set = TRUE;
}
/** if gpu_id for primary gie component is present,
* it will override the value set using global_gpu_id in parse_gie function */
parse_err =
!parse_gie (&config->primary_gie_config, cfg_file,
(gchar*)CONFIG_GROUP_PRIMARY_GIE, cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_TRACKER)) {
/** set gpu_id for tracker component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->tracker_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for tracker component is present,
* it will override the value set using global_gpu_id in parse_tracker function */
parse_err =
!parse_tracker (&config->tracker_config, cfg_file, cfg_file_path);
}
if (!strncmp (*group, CONFIG_GROUP_SECONDARY_GIE,
sizeof (CONFIG_GROUP_SECONDARY_GIE) - 1)) {
if (config->num_secondary_gie_sub_bins == MAX_SECONDARY_GIE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d secondary GIEs",
MAX_SECONDARY_GIE_BINS);
ret = FALSE;
goto done;
}
/** set gpu_id for secondary gie component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins].gpu_id = config->global_gpu_id;
config->secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins].is_gpu_id_set = TRUE;
}
/** if gpu_id for secondary gie component is present,
* it will override the value set using global_gpu_id in parse_gie function */
parse_err =
!parse_gie (&config->
secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins],
cfg_file, *group, cfg_file_path);
if (config->secondary_gie_sub_bin_config[config->
num_secondary_gie_sub_bins].enable) {
config->num_secondary_gie_sub_bins++;
}
}
if (!strncmp (*group, CONFIG_GROUP_SECONDARY_PREPROCESS,
sizeof (CONFIG_GROUP_SECONDARY_PREPROCESS) - 1)) {
if (config->num_secondary_preprocess_sub_bins ==
MAX_SECONDARY_PREPROCESS_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d secondary PREPROCESSs",
MAX_SECONDARY_PREPROCESS_BINS);
ret = FALSE;
goto done;
}
parse_err =
!parse_preprocess (&config->
secondary_preprocess_sub_bin_config
[config->num_secondary_preprocess_sub_bins], cfg_file, *group,
cfg_file_path);
if (config->secondary_preprocess_sub_bin_config[config->
num_secondary_preprocess_sub_bins].enable) {
config->num_secondary_preprocess_sub_bins++;
}
}
if (!strncmp (*group, CONFIG_GROUP_SINK, sizeof (CONFIG_GROUP_SINK) - 1)) {
if (config->num_sink_sub_bins == MAX_SINK_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sinks", MAX_SINK_BINS);
ret = FALSE;
goto done;
}
/** set gpu_id for sink component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
GError *error = NULL;
if (g_key_file_get_integer (cfg_file, *group,
"enable", &error) == TRUE && error == NULL) {
config->sink_bin_sub_bin_config[config->num_sink_sub_bins].encoder_config.gpu_id = config->sink_bin_sub_bin_config[config->num_sink_sub_bins].render_config.gpu_id = config->global_gpu_id;
}
}
/** if gpu_id for sink component is present,
* it will override the value set using global_gpu_id in parse_sink function */
parse_err =
!parse_sink (&config->sink_bin_sub_bin_config[config->
num_sink_sub_bins], cfg_file, *group, cfg_file_path);
if (config->sink_bin_sub_bin_config[config->num_sink_sub_bins].enable) {
config->num_sink_sub_bins++;
}
}
if (!strncmp (*group, CONFIG_GROUP_MSG_CONSUMER,
sizeof (CONFIG_GROUP_MSG_CONSUMER) - 1)) {
if (config->num_message_consumers == MAX_MESSAGE_CONSUMERS) {
NVGSTDS_ERR_MSG_V ("App supports max %d consumers",
MAX_MESSAGE_CONSUMERS);
ret = FALSE;
goto done;
}
parse_err =
!parse_msgconsumer (&config->message_consumer_config[config->
num_message_consumers], cfg_file, *group, cfg_file_path);
if (config->message_consumer_config[config->num_message_consumers].enable) {
config->num_message_consumers++;
}
}
if (!g_strcmp0 (*group, CONFIG_GROUP_TILED_DISPLAY)) {
/** set gpu_id for tiled display component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->tiled_display_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for tiled display component is present,
* it will override the value set using global_gpu_id in parse_tiled_display function */
parse_err =
!parse_tiled_display (&config->tiled_display_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_IMG_SAVE)) {
/** set gpu_id for image save component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->image_save_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for image save component is present,
* it will override the value set using global_gpu_id in parse_image_save function */
parse_err =
!parse_image_save (&config->image_save_config, cfg_file, *group,
cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_DSANALYTICS)) {
parse_err =
!parse_dsanalytics (&config->dsanalytics_config, cfg_file,
cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_DSEXAMPLE)) {
/** set gpu_id for dsexample component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->dsexample_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for dsexample component is present,
* it will override the value set using global_gpu_id in parse_dsexample function */
parse_err = !parse_dsexample (&config->dsexample_config, cfg_file);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_MSG_CONVERTER)) {
parse_err =
!parse_msgconv (&config->msg_conv_config, cfg_file, *group,
cfg_file_path);
}
if (!g_strcmp0 (*group, CONFIG_GROUP_TESTS)) {
parse_err = !parse_tests (config, cfg_file);
}
if (!strncmp (*group, CONFIG_GROUP_DEWARPER, strlen(*group)-1)) {
guint source_id = 0;
{
gchar *source_id_start_ptr = *group + strlen (CONFIG_GROUP_DEWARPER);
gchar *source_id_end_ptr = NULL;
source_id = g_ascii_strtoull (source_id_start_ptr, &source_id_end_ptr, 10);
if (source_id_start_ptr == source_id_end_ptr || *source_id_end_ptr != '\0') {
NVGSTDS_ERR_MSG_V
("dewarper group \"[%s]\" is not in the form \"[dewarper<%%d>]\"", *group);
ret = FALSE;
goto done;}
}
/** set gpu_id for dewarper component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->multi_source_config[source_id].dewarper_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for dewarper component is present,
* it will override the value set using global_gpu_id in parse_dewarper function */
parse_err = !parse_dewarper (&config->multi_source_config[source_id].dewarper_config,
cfg_file, cfg_file_path, *group);
if(config->multi_source_config[source_id].dewarper_config.enable)
num_dewarper_source++;
if( num_dewarper_source > config->num_source_sub_bins) {
NVGSTDS_ERR_MSG_V ("Dewarper max numbers %u should be less than number of sources %u",
num_dewarper_source, config->num_source_sub_bins);
ret = FALSE;
goto done;
}
}
if (parse_err) {
GST_CAT_ERROR (APP_CFG_PARSER_CAT, "Failed to parse '%s' group", *group);
goto done;
}
}
/* Updating batch size when source list is enabled */
if (config->source_list_enabled == TRUE) {
/* For streammux and pgie, batch size is set to number of sources */
config->streammux_config.batch_size = config->num_source_sub_bins;
config->primary_gie_config.batch_size = config->num_source_sub_bins;
if (config->sgie_batch_size != 0) {
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
config->secondary_gie_sub_bin_config[i].batch_size =
config->sgie_batch_size;
}
}
}
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
if (config->secondary_gie_sub_bin_config[i].unique_id ==
config->primary_gie_config.unique_id) {
NVGSTDS_ERR_MSG_V ("Non unique gie ids found");
ret = FALSE;
goto done;
}
}
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
for (j = i + 1; j < config->num_secondary_gie_sub_bins; j++) {
if (config->secondary_gie_sub_bin_config[i].unique_id ==
config->secondary_gie_sub_bin_config[j].unique_id) {
NVGSTDS_ERR_MSG_V ("Non unique gie id %d found",
config->secondary_gie_sub_bin_config[i].unique_id);
ret = FALSE;
goto done;
}
}
}
for (i = 0; i < config->num_source_sub_bins; i++) {
if (config->multi_source_config[i].type == NV_DS_SOURCE_URI_MULTIPLE) {
if (config->multi_source_config[i].num_sources < 1) {
config->multi_source_config[i].num_sources = 1;
}
for (j = 1; j < config->multi_source_config[i].num_sources; j++) {
if (config->num_source_sub_bins == MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
ret = FALSE;
goto done;
}
memcpy (&config->multi_source_config[config->num_source_sub_bins],
&config->multi_source_config[i],
sizeof (config->multi_source_config[i]));
config->multi_source_config[config->num_source_sub_bins].type =
NV_DS_SOURCE_URI;
config->multi_source_config[config->num_source_sub_bins].uri =
g_strdup_printf (config->
multi_source_config[config->num_source_sub_bins].uri, j);
config->num_source_sub_bins++;
}
config->multi_source_config[i].type = NV_DS_SOURCE_URI;
config->multi_source_config[i].uri =
g_strdup_printf (config->multi_source_config[i].uri, 0);
}
}
ret = TRUE;
done:
if (cfg_file) {
g_key_file_free (cfg_file);
}
if (groups) {
g_strfreev (groups);
}
if (error) {
g_error_free (error);
}
if (!ret) {
NVGSTDS_ERR_MSG_V ("%s failed", __func__);
}
return ret;
}
/*
* SPDX-FileCopyrightText: Copyright (c) 2022-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>
#include <cstring>
#include "deepstream_app.h"
#include "deepstream_config_yaml.h"
#include <iostream>
#include <stdlib.h>
#include <fstream>
using std::cout;
using std::endl;
static gboolean
parse_tests_yaml (NvDsConfig *config, gchar *cfg_file_path)
{
gboolean ret = FALSE;
YAML::Node configyml = YAML::LoadFile(cfg_file_path);
for(YAML::const_iterator itr = configyml["tests"].begin();
itr != configyml["tests"].end(); ++itr)
{
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "file-loop") {
config->file_loop = itr->second.as<gint>();
} else {
cout << "Unknown key " << paramKey << " for group tests" << endl;
}
}
ret = TRUE;
if (!ret) {
cout << __func__ << " failed" << endl;
}
return ret;
}
static gboolean
parse_app_yaml (NvDsConfig *config, gchar *cfg_file_path)
{
gboolean ret = FALSE;
YAML::Node configyml = YAML::LoadFile(cfg_file_path);
for(YAML::const_iterator itr = configyml["application"].begin();
itr != configyml["application"].end(); ++itr)
{
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "enable-perf-measurement") {
config->enable_perf_measurement =
itr->second.as<gboolean>();
} else if (paramKey == "perf-measurement-interval-sec") {
config->perf_measurement_interval_sec =
itr->second.as<guint>();
} else if (paramKey == "gie-kitti-output-dir") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1023);
config->bbox_dir_path = (char*) malloc(sizeof(char) * 1024);
get_absolute_file_path_yaml (cfg_file_path, str, config->bbox_dir_path);
g_free(str);
} else if (paramKey == "kitti-track-output-dir") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1023);
config->kitti_track_dir_path = (char*) malloc(sizeof(char) * 1024);
get_absolute_file_path_yaml (cfg_file_path, str, config->kitti_track_dir_path);
g_free(str);
} else if (paramKey == "reid-track-output-dir") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1023);
config->reid_track_dir_path = (char*) malloc(sizeof(char) * 1024);
get_absolute_file_path_yaml (cfg_file_path, str, config->reid_track_dir_path);
g_free(str);
} else if (paramKey == "global-gpu-id") {
/** App Level GPU ID is set here if it is present in APP LEVEL config group
* if gpu_id prop is not set for any component, this global_gpu_id will be used */
config->global_gpu_id = itr->second.as<guint>();
} else if (paramKey == "terminated-track-output-dir") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1023);
config->terminated_track_output_path = (char*) malloc(sizeof(char) * 1024);
get_absolute_file_path_yaml (cfg_file_path, str, config->terminated_track_output_path);
g_free(str);
} else if (paramKey == "shadow-track-output-dir") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1023);
config->shadow_track_output_path = (char*) malloc(sizeof(char) * 1024);
get_absolute_file_path_yaml (cfg_file_path, str, config->shadow_track_output_path);
g_free(str);
}
else {
cout << "Unknown key " << paramKey << " for group application" << endl;
}
}
ret = TRUE;
if (!ret) {
cout << __func__ << " failed" << endl;
}
return ret;
}
static std::vector<std::string>
split_csv_entries (std::string input) {
std::vector<int> positions;
for (unsigned int i = 0; i < input.size(); i++) {
if (input[i] == ',')
positions.push_back(i);
}
std::vector<std::string> ret;
int prev = 0;
for (auto &j: positions) {
std::string temp = input.substr(prev, j - prev);
ret.push_back(temp);
prev = j + 1;
}
ret.push_back(input.substr(prev, input.size() - prev));
return ret;
}
gboolean
parse_config_file_yaml (NvDsConfig *config, gchar *cfg_file_path)
{
gboolean parse_err = false;
gboolean ret = FALSE;
YAML::Node configyml = YAML::LoadFile(cfg_file_path);
std::string source_str = "source";
std::string sink_str = "sink";
std::string sgie_str = "secondary-gie";
std::string msgcons_str = "message-consumer";
std::string dewarper_str = "dewarper";
config->source_list_enabled = FALSE;
/** Initialize global gpu id to -1 */
config->global_gpu_id = -1;
/** App group parsing at top level to set global_gpu_id (if available)
* before any other group parsing */
if (configyml["application"]) {
parse_err = !parse_app_yaml (config, cfg_file_path);
}
for(YAML::const_iterator itr = configyml.begin();
itr != configyml.end(); ++itr) {
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "source") {
if(configyml["source"]["csv-file-path"]) {
std::string csv_file_path = configyml["source"]["csv-file-path"].as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, csv_file_path.c_str(), 1023);
char *abs_csv_path = (char*) malloc(sizeof(char) * 1024);
get_absolute_file_path_yaml (cfg_file_path, str, abs_csv_path);
g_free(str);
std::ifstream inputFile (abs_csv_path);
if (!inputFile.is_open()) {
cout << "Couldn't open CSV file " << abs_csv_path << endl;
}
std::string line, temp;
/* Separating header field and inserting as strings into the vector.
*/
while(getline(inputFile, line)){
gboolean is_comment = false;
size_t space_count = 0;
for (char c : line) {
if (c != ' ' && c!='\t') {
if (c != '#') {
is_comment = false;
}
else
{
is_comment = true;
}
break;
}
else {
space_count++;
}
}
if(!is_comment && space_count<line.length())
break;
}
std::vector<std::string> headers = split_csv_entries(line);
/*Parsing each csv entry as an input source */
while(getline(inputFile, line)) {
std::vector<std::string> source_values = split_csv_entries(line);
if (config->num_source_sub_bins == MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
ret = FALSE;
goto done;
}
guint source_id = 0;
source_id = config->num_source_sub_bins;
/** set gpu_id for source component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->multi_source_config[source_id].gpu_id = config->global_gpu_id;
}
/** if gpu_id for source component is present,
* it will override the value set using global_gpu_id in parse_source_yaml function */
parse_err = !parse_source_yaml (&config->multi_source_config[source_id], headers, source_values, cfg_file_path);
if (config->multi_source_config[source_id].enable)
config->num_source_sub_bins++;
}
} else {
NVGSTDS_ERR_MSG_V ("CSV file not specified\n");
ret = FALSE;
goto done;
}
}
else if (paramKey == "streammux") {
/** set gpu_id for streammux component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->streammux_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for streammux component is present,
* it will override the value set using global_gpu_id in parse_streammux_yaml function */
parse_err = !parse_streammux_yaml(&config->streammux_config, cfg_file_path);
}
else if (paramKey == "osd") {
/** set gpu_id for osd component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->osd_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for osd component is present,
* it will override the value set using global_gpu_id in parse_osd_yaml function */
parse_err = !parse_osd_yaml(&config->osd_config, cfg_file_path);
}
else if (paramKey == "segvisual") {
/** set gpu_id for segvisual component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->segvisual_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for segvisual component is present,
* it will override the value set using global_gpu_id in parse_segvisual_yaml function */
parse_err = !parse_segvisual_yaml(&config->segvisual_config, cfg_file_path);
}
else if (paramKey == "pre-process") {
parse_err = !parse_preprocess_yaml(&config->preprocess_config, cfg_file_path);
}
else if (paramKey == "primary-gie") {
/** set gpu_id for primary gie component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->primary_gie_config.gpu_id = config->global_gpu_id;
config->primary_gie_config.is_gpu_id_set = TRUE;
}
/** if gpu_id for primary gie component is present,
* it will override the value set using global_gpu_id in parse_gie_yaml function */
parse_err = !parse_gie_yaml(&config->primary_gie_config, paramKey, cfg_file_path);
}
else if (paramKey == "tracker") {
/** set gpu_id for tracker component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->tracker_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for tracker component is present,
* it will override the value set using global_gpu_id in parse_tracker_yaml function */
parse_err = !parse_tracker_yaml(&config->tracker_config, cfg_file_path);
}
else if (paramKey.compare(0, sgie_str.size(), sgie_str) == 0) {
if (config->num_secondary_gie_sub_bins == MAX_SECONDARY_GIE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d secondary GIEs", MAX_SECONDARY_GIE_BINS);
ret = FALSE;
goto done;
}
/* set gpu_id for secondary gie component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins].gpu_id = config->global_gpu_id;
config->secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins].is_gpu_id_set = TRUE;
}
/** if gpu_id for secondary gie component is present,
* it will override the value set using global_gpu_id in parse_gie_yaml function */
parse_err =
!parse_gie_yaml (&config->secondary_gie_sub_bin_config[config->
num_secondary_gie_sub_bins],
paramKey, cfg_file_path);
if (config->secondary_gie_sub_bin_config[config->num_secondary_gie_sub_bins].enable){
config->num_secondary_gie_sub_bins++;
}
}
else if (paramKey.compare(0, sink_str.size(), sink_str) == 0) {
if (config->num_sink_sub_bins == MAX_SINK_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sinks", MAX_SINK_BINS);
ret = FALSE;
goto done;
}
/* set gpu_id for sink component using global_gpu_id(if available) */
if (config->global_gpu_id != -1 && configyml[paramKey]["enable"].as<gboolean>()) {
config->sink_bin_sub_bin_config[config->num_sink_sub_bins].encoder_config.gpu_id = config->sink_bin_sub_bin_config[config->num_sink_sub_bins].render_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for sink component is present,
* it will override the value set using global_gpu_id in parse_sink_yaml function */
parse_err =
!parse_sink_yaml (&config->
sink_bin_sub_bin_config[config->num_sink_sub_bins], paramKey, cfg_file_path);
if (config->
sink_bin_sub_bin_config[config->num_sink_sub_bins].enable) {
config->num_sink_sub_bins++;
}
}
else if (paramKey.compare(0, msgcons_str.size(), msgcons_str) == 0) {
if (config->num_message_consumers == MAX_MESSAGE_CONSUMERS) {
NVGSTDS_ERR_MSG_V ("App supports max %d consumers", MAX_MESSAGE_CONSUMERS);
ret = FALSE;
goto done;
}
parse_err = !parse_msgconsumer_yaml (
&config->message_consumer_config[config->num_message_consumers],
paramKey, cfg_file_path);
if (config->message_consumer_config[config->num_message_consumers].enable) {
config->num_message_consumers++;
}
}
else if (paramKey == "tiled-display") {
/* set gpu_id for tiled display component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->tiled_display_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for tiled display component is present,
* it will override the value set using global_gpu_id in parse_tiled_display_yaml function */
parse_err = !parse_tiled_display_yaml (&config->tiled_display_config, cfg_file_path);
}
else if (paramKey == "img-save") {
/** set gpu_id for image save component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->image_save_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for image save component is present,
* it will override the value set using global_gpu_id in parse_image_save_yaml function */
parse_err = !parse_image_save_yaml (&config->image_save_config , cfg_file_path);
}
else if (paramKey == "nvds-analytics") {
parse_err = !parse_dsanalytics_yaml (&config->dsanalytics_config, cfg_file_path);
}
else if (paramKey == "ds-example") {
/** set gpu_id for dsexample component using global_gpu_id(if available) */
if (config->global_gpu_id != -1) {
config->dsexample_config.gpu_id = config->global_gpu_id;
}
/** if gpu_id for dsexample component is present,
* it will override the value set using global_gpu_id in parse_dsexample_yaml function */
parse_err = !parse_dsexample_yaml (&config->dsexample_config, cfg_file_path);
}
else if (paramKey == "message-converter") {
parse_err = !parse_msgconv_yaml (&config->msg_conv_config, paramKey, cfg_file_path);
}
else if (paramKey == "tests") {
parse_err = !parse_tests_yaml (config, cfg_file_path);
}
else if (paramKey.compare(0, dewarper_str.size(), dewarper_str) == 0) {
size_t start = paramKey.find(dewarper_str);
int source_id = 0;
if(start != std::string::npos) {
std::string index_str = paramKey.substr(start+dewarper_str.length(), paramKey.length()-start-dewarper_str.length());
source_id = std::stoi(index_str);
parse_dewarper_yaml (&config->multi_source_config[source_id].dewarper_config, paramKey, cfg_file_path);
} else {
NVGSTDS_ERR_MSG_V ("Dewarper key is wrong ! ");
parse_err = true;
}
}
if (parse_err) {
cout << "failed parsing" << endl;
goto done;
}
}
/* Updating batch size when source list is enabled */
/* if (config->source_list_enabled == TRUE) {
// For streammux and pgie, batch size is set to number of sources
config->streammux_config.batch_size = config->num_source_sub_bins;
config->primary_gie_config.batch_size = config->num_source_sub_bins;
if (config->sgie_batch_size != 0) {
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
config->secondary_gie_sub_bin_config[i].batch_size = config->sgie_batch_size;
}
}
} */
unsigned int i, j;
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
if (config->secondary_gie_sub_bin_config[i].unique_id ==
config->primary_gie_config.unique_id) {
NVGSTDS_ERR_MSG_V ("Non unique gie ids found");
ret = FALSE;
goto done;
}
}
for (i = 0; i < config->num_secondary_gie_sub_bins; i++) {
for (j = i + 1; j < config->num_secondary_gie_sub_bins; j++) {
if (config->secondary_gie_sub_bin_config[i].unique_id ==
config->secondary_gie_sub_bin_config[j].unique_id) {
NVGSTDS_ERR_MSG_V ("Non unique gie id %d found",
config->secondary_gie_sub_bin_config[i].unique_id);
ret = FALSE;
goto done;
}
}
}
for (i = 0; i < config->num_source_sub_bins; i++) {
if (config->multi_source_config[i].type == NV_DS_SOURCE_URI_MULTIPLE) {
if (config->multi_source_config[i].num_sources < 1) {
config->multi_source_config[i].num_sources = 1;
}
for (j = 1; j < config->multi_source_config[i].num_sources; j++) {
if (config->num_source_sub_bins == MAX_SOURCE_BINS) {
NVGSTDS_ERR_MSG_V ("App supports max %d sources", MAX_SOURCE_BINS);
ret = FALSE;
goto done;
}
memcpy (&config->multi_source_config[config->num_source_sub_bins],
&config->multi_source_config[i],
sizeof (config->multi_source_config[i]));
config->multi_source_config[config->num_source_sub_bins].type =
NV_DS_SOURCE_URI;
config->multi_source_config[config->num_source_sub_bins].uri =
g_strdup_printf (config->multi_source_config[config->
num_source_sub_bins].uri, j);
config->num_source_sub_bins++;
}
config->multi_source_config[i].type = NV_DS_SOURCE_URI;
config->multi_source_config[i].uri =
g_strdup_printf (config->multi_source_config[i].uri, 0);
}
}
ret = TRUE;
done:
if (!ret) {
cout << __func__ << " failed" << endl;
}
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_app.h"
#include "deepstream_config_file_parser.h"
#include <cuda_runtime_api.h>
#include "nvds_version.h"
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#define MAX_INSTANCES 128
#define APP_TITLE "DeepStream"
#define DEFAULT_X_WINDOW_WIDTH 1920
#define DEFAULT_X_WINDOW_HEIGHT 1080
AppCtx *appCtx[MAX_INSTANCES];
static guint cintr = FALSE;
static GMainLoop *main_loop = NULL;
static gchar **cfg_files = NULL;
static gchar **input_uris = NULL;
static gboolean print_version = FALSE;
static gboolean show_bbox_text = FALSE;
static gboolean print_dependencies_version = FALSE;
static gboolean quit = FALSE;
static gint return_value = 0;
static guint num_instances;
static guint num_input_uris;
static GMutex fps_lock;
static gdouble fps[MAX_SOURCE_BINS];
static gdouble fps_avg[MAX_SOURCE_BINS];
static Display *display = NULL;
static Window windows[MAX_INSTANCES] = { 0 };
static GThread *x_event_thread = NULL;
static GMutex disp_lock;
static guint rrow, rcol, rcfg;
static gboolean rrowsel = FALSE, selecting = FALSE;
GST_DEBUG_CATEGORY (NVDS_APP);
GOptionEntry entries[] = {
{"version", 'v', 0, G_OPTION_ARG_NONE, &print_version,
"Print DeepStreamSDK version", NULL}
,
{"tiledtext", 't', 0, G_OPTION_ARG_NONE, &show_bbox_text,
"Display Bounding box labels in tiled mode", NULL}
,
{"version-all", 0, 0, G_OPTION_ARG_NONE, &print_dependencies_version,
"Print DeepStreamSDK and dependencies version", NULL}
,
{"cfg-file", 'c', 0, G_OPTION_ARG_FILENAME_ARRAY, &cfg_files,
"Set the config file", NULL}
,
{"input-uri", 'i', 0, G_OPTION_ARG_FILENAME_ARRAY, &input_uris,
"Set the input uri (file://stream or rtsp://stream)", NULL}
,
{NULL}
,
};
/**
* Callback function to be called once all inferences (Primary + Secondary)
* are done. This is opportunity to modify content of the metadata.
* e.g. Here Person is being replaced with Man/Woman and corresponding counts
* are being maintained. It should be modified according to network classes
* or can be removed altogether if not required.
*/
static void
all_bbox_generated (AppCtx * appCtx, GstBuffer * buf,
NvDsBatchMeta * batch_meta, guint index)
{
guint num_male = 0;
guint num_female = 0;
guint num_objects[128];
memset (num_objects, 0, sizeof (num_objects));
for (NvDsMetaList * l_frame = batch_meta->frame_meta_list; l_frame != NULL;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = l_frame->data;
for (NvDsMetaList * l_obj = frame_meta->obj_meta_list; l_obj != NULL;
l_obj = l_obj->next) {
NvDsObjectMeta *obj = (NvDsObjectMeta *) l_obj->data;
if (obj->unique_component_id ==
(gint) appCtx->config.primary_gie_config.unique_id) {
if (obj->class_id >= 0 && obj->class_id < 128) {
num_objects[obj->class_id]++;
}
if (appCtx->person_class_id > -1
&& obj->class_id == appCtx->person_class_id) {
if (strstr (obj->text_params.display_text, "Man")) {
str_replace (obj->text_params.display_text, "Man", "");
str_replace (obj->text_params.display_text, "Person", "Man");
num_male++;
} else if (strstr (obj->text_params.display_text, "Woman")) {
str_replace (obj->text_params.display_text, "Woman", "");
str_replace (obj->text_params.display_text, "Person", "Woman");
num_female++;
}
}
}
}
}
}
/**
* Function to handle program interrupt signal.
* It installs default handler after handling the interrupt.
*/
static void
_intr_handler (int signum)
{
struct sigaction action;
NVGSTDS_ERR_MSG_V ("User Interrupted.. \n");
memset (&action, 0, sizeof (action));
action.sa_handler = SIG_DFL;
sigaction (SIGINT, &action, NULL);
cintr = TRUE;
}
/**
* callback function to print the performance numbers of each stream.
*/
static void
perf_cb (gpointer context, NvDsAppPerfStruct * str)
{
static guint header_print_cnt = 0;
guint i;
AppCtx *appCtx = (AppCtx *) context;
guint numf = str->num_instances;
g_mutex_lock (&fps_lock);
for (i = 0; i < numf; i++) {
fps[i] = str->fps[i];
fps_avg[i] = str->fps_avg[i];
}
if (header_print_cnt % 20 == 0) {
g_print ("\n**PERF: ");
for (i = 0; i < numf; i++) {
g_print ("FPS %d (Avg)\t", i);
}
g_print ("\n");
header_print_cnt = 0;
}
header_print_cnt++;
if (num_instances > 1)
g_print ("PERF(%d): ", appCtx->index);
else
g_print ("**PERF: ");
for (i = 0; i < numf; i++) {
g_print ("%.2f (%.2f)\t", fps[i], fps_avg[i]);
}
g_print ("\n");
g_mutex_unlock (&fps_lock);
}
/**
* Loop function to check the status of interrupts.
* It comes out of loop if application got interrupted.
*/
static gboolean
check_for_interrupt (gpointer data)
{
if (quit) {
return FALSE;
}
if (cintr) {
cintr = FALSE;
quit = TRUE;
g_main_loop_quit (main_loop);
return FALSE;
}
return TRUE;
}
/*
* Function to install custom handler for program interrupt signal.
*/
static void
_intr_setup (void)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_handler = _intr_handler;
sigaction (SIGINT, &action, NULL);
}
static gboolean
kbhit (void)
{
struct timeval tv;
fd_set rdfs;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&rdfs);
FD_SET (STDIN_FILENO, &rdfs);
select (STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
return FD_ISSET (STDIN_FILENO, &rdfs);
}
/*
* Function to enable / disable the canonical mode of terminal.
* In non canonical mode input is available immediately (without the user
* having to type a line-delimiter character).
*/
static void
changemode (int dir)
{
static struct termios oldt, newt;
if (dir == 1) {
tcgetattr (STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON);
tcsetattr (STDIN_FILENO, TCSANOW, &newt);
} else
tcsetattr (STDIN_FILENO, TCSANOW, &oldt);
}
static void
print_runtime_commands (void)
{
g_print ("\nRuntime commands:\n"
"\th: Print this help\n"
"\tq: Quit\n\n" "\tp: Pause\n" "\tr: Resume\n\n");
if (appCtx[0]->config.tiled_display_config.enable) {
g_print
("NOTE: To expand a source in the 2D tiled display and view object details,"
" left-click on the source.\n"
" To go back to the tiled display, right-click anywhere on the window.\n\n");
}
}
/**
* Loop function to check keyboard inputs and status of each pipeline.
*/
static gboolean
event_thread_func (gpointer arg)
{
guint i;
gboolean ret = TRUE;
// Check if all instances have quit
for (i = 0; i < num_instances; i++) {
if (!appCtx[i]->quit)
break;
}
if (i == num_instances) {
quit = TRUE;
g_main_loop_quit (main_loop);
return FALSE;
}
// Check for keyboard input
if (!kbhit ()) {
//continue;
return TRUE;
}
int c = fgetc (stdin);
g_print ("\n");
gint source_id;
GstElement *tiler = appCtx[rcfg]->pipeline.tiled_display_bin.tiler;
if (appCtx[rcfg]->config.tiled_display_config.enable)
{
g_object_get (G_OBJECT (tiler), "show-source", &source_id, NULL);
if (selecting) {
if (rrowsel == FALSE) {
if (c >= '0' && c <= '9') {
rrow = c - '0';
if (rrow < appCtx[rcfg]->config.tiled_display_config.rows){
g_print ("--selecting source row %d--\n", rrow);
rrowsel = TRUE;
}else{
g_print ("--selected source row %d out of bound, reenter\n", rrow);
}
}
} else {
if (c >= '0' && c <= '9') {
unsigned int tile_num_columns = appCtx[rcfg]->config.tiled_display_config.columns;
rcol = c - '0';
if (rcol < tile_num_columns){
selecting = FALSE;
rrowsel = FALSE;
source_id = tile_num_columns * rrow + rcol;
g_print ("--selecting source col %d sou=%d--\n", rcol, source_id);
if (source_id >= (gint) appCtx[rcfg]->config.num_source_sub_bins) {
source_id = -1;
} else {
appCtx[rcfg]->show_bbox_text = TRUE;
appCtx[rcfg]->active_source_index = source_id;
g_object_set (G_OBJECT (tiler), "show-source", source_id, NULL);
}
}else{
g_print ("--selected source col %d out of bound, reenter\n", rcol);
}
}
}
}
}
switch (c) {
case 'h':
print_runtime_commands ();
break;
case 'p':
for (i = 0; i < num_instances; i++)
pause_pipeline (appCtx[i]);
break;
case 'r':
for (i = 0; i < num_instances; i++)
resume_pipeline (appCtx[i]);
break;
case 'q':
quit = TRUE;
g_main_loop_quit (main_loop);
ret = FALSE;
break;
case 'c':
if (appCtx[rcfg]->config.tiled_display_config.enable && selecting == FALSE && source_id == -1) {
g_print ("--selecting config file --\n");
c = fgetc (stdin);
if (c >= '0' && c <= '9') {
rcfg = c - '0';
if (rcfg < num_instances) {
g_print ("--selecting config %d--\n", rcfg);
} else {
g_print ("--selected config file %d out of bound, reenter\n", rcfg);
rcfg = 0;
}
}
}
break;
case 'z':
if (appCtx[rcfg]->config.tiled_display_config.enable && source_id == -1 && selecting == FALSE) {
g_print ("--selecting source --\n");
selecting = TRUE;
} else {
if (!show_bbox_text)
appCtx[rcfg]->show_bbox_text = FALSE;
g_object_set (G_OBJECT (tiler), "show-source", -1, NULL);
appCtx[rcfg]->active_source_index = -1;
selecting = FALSE;
rcfg = 0;
g_print ("--tiled mode --\n");
}
break;
default:
break;
}
return ret;
}
static int
get_source_id_from_coordinates (float x_rel, float y_rel, AppCtx *appCtx)
{
int tile_num_rows = appCtx->config.tiled_display_config.rows;
int tile_num_columns = appCtx->config.tiled_display_config.columns;
int source_id = (int) (x_rel * tile_num_columns);
source_id += ((int) (y_rel * tile_num_rows)) * tile_num_columns;
/* Don't allow clicks on empty tiles. */
if (source_id >= (gint) appCtx->config.num_source_sub_bins)
source_id = -1;
return source_id;
}
/**
* Thread to monitor X window events.
*/
static gpointer
nvds_x_event_thread (gpointer data)
{
g_mutex_lock (&disp_lock);
while (display) {
XEvent e;
guint index;
memset(&e, 0, sizeof(XEvent));
while (XPending (display)) {
XNextEvent (display, &e);
switch (e.type) {
case ButtonPress:
{
XWindowAttributes win_attr;
XButtonEvent ev = e.xbutton;
gint source_id;
GstElement *tiler;
memset(&win_attr, 0, sizeof(XWindowAttributes));
XGetWindowAttributes (display, ev.window, &win_attr);
for (index = 0; index < MAX_INSTANCES; index++)
if (ev.window == windows[index])
break;
tiler = appCtx[index]->pipeline.tiled_display_bin.tiler;
g_object_get (G_OBJECT (tiler), "show-source", &source_id, NULL);
if (ev.button == Button1 && source_id == -1 && (index >=0 && index < MAX_INSTANCES )) {
source_id =
get_source_id_from_coordinates (ev.x * 1.0 / win_attr.width,
ev.y * 1.0 / win_attr.height, appCtx[index]);
if (source_id > -1) {
g_object_set (G_OBJECT (tiler), "show-source", source_id, NULL);
appCtx[index]->active_source_index = source_id;
appCtx[index]->show_bbox_text = TRUE;
}
} else if (ev.button == Button3) {
g_object_set (G_OBJECT (tiler), "show-source", -1, NULL);
appCtx[index]->active_source_index = -1;
if (!show_bbox_text)
appCtx[index]->show_bbox_text = FALSE;
}
}
break;
case KeyRelease:
case KeyPress:
{
KeySym p, r, q;
guint i;
p = XKeysymToKeycode (display, XK_P);
r = XKeysymToKeycode (display, XK_R);
q = XKeysymToKeycode (display, XK_Q);
if (e.xkey.keycode == p) {
for (i = 0; i < num_instances; i++)
pause_pipeline (appCtx[i]);
break;
}
if (e.xkey.keycode == r) {
for (i = 0; i < num_instances; i++)
resume_pipeline (appCtx[i]);
break;
}
if (e.xkey.keycode == q) {
quit = TRUE;
g_main_loop_quit (main_loop);
}
}
break;
case ClientMessage:
{
Atom wm_delete;
for (index = 0; index < MAX_INSTANCES; index++)
if (e.xclient.window == windows[index])
break;
wm_delete = XInternAtom (display, "WM_DELETE_WINDOW", 1);
if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
quit = TRUE;
g_main_loop_quit (main_loop);
}
}
break;
}
}
g_mutex_unlock (&disp_lock);
g_usleep (G_USEC_PER_SEC / 20);
g_mutex_lock (&disp_lock);
}
g_mutex_unlock (&disp_lock);
return NULL;
}
/**
* callback function to add application specific metadata.
* Here it demonstrates how to display the URI of source in addition to
* the text generated after inference.
*/
static gboolean
overlay_graphics (AppCtx * appCtx, GstBuffer * buf,
NvDsBatchMeta * batch_meta, guint index)
{
int srcIndex = appCtx->active_source_index;
if (srcIndex == -1)
return TRUE;
NvDsFrameLatencyInfo *latency_info = NULL;
NvDsDisplayMeta *display_meta =
nvds_acquire_display_meta_from_pool (batch_meta);
display_meta->num_labels = 1;
display_meta->text_params[0].display_text = g_strdup_printf ("Source: %s",
appCtx->config.multi_source_config[srcIndex].uri);
display_meta->text_params[0].y_offset = 20;
display_meta->text_params[0].x_offset = 20;
display_meta->text_params[0].font_params.font_color = (NvOSD_ColorParams) {
0, 1, 0, 1};
display_meta->text_params[0].font_params.font_size =
appCtx->config.osd_config.text_size * 1.5;
display_meta->text_params[0].font_params.font_name = "Serif";
display_meta->text_params[0].set_bg_clr = 1;
display_meta->text_params[0].text_bg_clr = (NvOSD_ColorParams) {
0, 0, 0, 1.0};
if(nvds_enable_latency_measurement) {
g_mutex_lock (&appCtx->latency_lock);
latency_info = &appCtx->latency_info[index];
display_meta->num_labels++;
display_meta->text_params[1].display_text = g_strdup_printf ("Latency: %lf",
latency_info->latency);
g_mutex_unlock (&appCtx->latency_lock);
display_meta->text_params[1].y_offset = (display_meta->text_params[0].y_offset * 2 )+
display_meta->text_params[0].font_params.font_size;
display_meta->text_params[1].x_offset = 20;
display_meta->text_params[1].font_params.font_color = (NvOSD_ColorParams) {
0, 1, 0, 1};
display_meta->text_params[1].font_params.font_size =
appCtx->config.osd_config.text_size * 1.5;
display_meta->text_params[1].font_params.font_name = "Arial";
display_meta->text_params[1].set_bg_clr = 1;
display_meta->text_params[1].text_bg_clr = (NvOSD_ColorParams) {
0, 0, 0, 1.0};
}
nvds_add_display_meta_to_frame (nvds_get_nth_frame_meta (batch_meta->
frame_meta_list, 0), display_meta);
return TRUE;
}
static gboolean
recreate_pipeline_thread_func (gpointer arg)
{
guint i;
gboolean ret = TRUE;
AppCtx *appCtx = (AppCtx *) arg;
g_print ("Destroy pipeline\n");
destroy_pipeline (appCtx);
g_print ("Recreate pipeline\n");
if (!create_pipeline (appCtx, NULL,
all_bbox_generated, perf_cb, overlay_graphics)) {
NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
return_value = -1;
return FALSE;
}
if (gst_element_set_state (appCtx->pipeline.pipeline,
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
NVGSTDS_ERR_MSG_V ("Failed to set pipeline to PAUSED");
return_value = -1;
return FALSE;
}
for (i = 0; i < appCtx->config.num_sink_sub_bins; i++) {
if (!GST_IS_VIDEO_OVERLAY (appCtx->pipeline.instance_bins[0].sink_bin.
sub_bins[i].sink)) {
continue;
}
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (appCtx->pipeline.
instance_bins[0].sink_bin.sub_bins[i].sink),
(gulong) windows[appCtx->index]);
gst_video_overlay_expose (GST_VIDEO_OVERLAY (appCtx->pipeline.
instance_bins[0].sink_bin.sub_bins[i].sink));
}
if (gst_element_set_state (appCtx->pipeline.pipeline,
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
g_print ("\ncan't set pipeline to playing state.\n");
return_value = -1;
return FALSE;
}
return ret;
}
int
main (int argc, char *argv[])
{
GOptionContext *ctx = NULL;
GOptionGroup *group = NULL;
GError *error = NULL;
guint i;
ctx = g_option_context_new ("Nvidia DeepStream Demo");
group = g_option_group_new ("abc", NULL, NULL, NULL, NULL);
g_option_group_add_entries (group, entries);
g_option_context_set_main_group (ctx, group);
g_option_context_add_group (ctx, gst_init_get_option_group ());
GST_DEBUG_CATEGORY_INIT (NVDS_APP, "NVDS_APP", 0, NULL);
int current_device = -1;
cudaGetDevice(&current_device);
struct cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, current_device);
if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
NVGSTDS_ERR_MSG_V ("%s", error->message);
return -1;
}
if (print_version) {
g_print ("deepstream-app version %d.%d.%d\n",
NVDS_APP_VERSION_MAJOR, NVDS_APP_VERSION_MINOR, NVDS_APP_VERSION_MICRO);
nvds_version_print ();
return 0;
}
if (print_dependencies_version) {
g_print ("deepstream-app version %d.%d.%d\n",
NVDS_APP_VERSION_MAJOR, NVDS_APP_VERSION_MINOR, NVDS_APP_VERSION_MICRO);
nvds_version_print ();
nvds_dependencies_version_print ();
return 0;
}
if (cfg_files) {
num_instances = g_strv_length (cfg_files);
}
if (input_uris) {
num_input_uris = g_strv_length (input_uris);
}
if (!cfg_files || num_instances == 0) {
NVGSTDS_ERR_MSG_V ("Specify config file with -c option");
return_value = -1;
goto done;
}
for (i = 0; i < num_instances; i++) {
appCtx[i] = g_malloc0 (sizeof (AppCtx));
appCtx[i]->person_class_id = -1;
appCtx[i]->car_class_id = -1;
appCtx[i]->index = i;
appCtx[i]->active_source_index = -1;
if (show_bbox_text) {
appCtx[i]->show_bbox_text = TRUE;
}
if (input_uris && input_uris[i]) {
appCtx[i]->config.multi_source_config[0].uri =
g_strdup_printf ("%s", input_uris[i]);
g_free (input_uris[i]);
}
if(g_str_has_suffix(cfg_files[i], ".yml") ||
g_str_has_suffix(cfg_files[i], ".yaml")) {
if (!parse_config_file_yaml (&appCtx[i]->config, cfg_files[i])) {
NVGSTDS_ERR_MSG_V ("Failed to parse config file '%s'", cfg_files[i]);
appCtx[i]->return_value = -1;
goto done;
}
} else if (g_str_has_suffix(cfg_files[i], ".txt")) {
if (!parse_config_file (&appCtx[i]->config, cfg_files[i])) {
NVGSTDS_ERR_MSG_V ("Failed to parse config file '%s'", cfg_files[i]);
appCtx[i]->return_value = -1;
goto done;
}
}
}
for (i = 0; i < num_instances; i++) {
if (!create_pipeline (appCtx[i], NULL,
all_bbox_generated, perf_cb, overlay_graphics)) {
NVGSTDS_ERR_MSG_V ("Failed to create pipeline");
return_value = -1;
goto done;
}
}
main_loop = g_main_loop_new (NULL, FALSE);
_intr_setup ();
g_timeout_add (400, check_for_interrupt, NULL);
g_mutex_init (&disp_lock);
display = XOpenDisplay (NULL);
for (i = 0; i < num_instances; i++) {
guint j;
#if defined(__aarch64__)
if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
NVGSTDS_ERR_MSG_V ("Failed to set pipeline to PAUSED");
return_value = -1;
goto done;
}
#endif
for (j = 0; j < appCtx[i]->config.num_sink_sub_bins; j++) {
XTextProperty xproperty;
gchar *title;
guint width, height;
XSizeHints hints = {0};
if (!GST_IS_VIDEO_OVERLAY (appCtx[i]->pipeline.instance_bins[0].
sink_bin.sub_bins[j].sink)) {
continue;
}
if (!display) {
NVGSTDS_ERR_MSG_V ("Could not open X Display");
return_value = -1;
goto done;
}
if (appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.width)
width =
appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.width;
else
width = appCtx[i]->config.tiled_display_config.width;
if (appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.height)
height =
appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.height;
else
height = appCtx[i]->config.tiled_display_config.height;
width = (width) ? width : DEFAULT_X_WINDOW_WIDTH;
height = (height) ? height : DEFAULT_X_WINDOW_HEIGHT;
hints.flags = PPosition | PSize;
hints.x = appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.offset_x;
hints.y = appCtx[i]->config.sink_bin_sub_bin_config[j].render_config.offset_y;
hints.width = width;
hints.height = height;
windows[i] =
XCreateSimpleWindow (display, RootWindow (display,
DefaultScreen (display)), hints.x, hints.y, width, height, 2,
0x00000000, 0x00000000);
XSetNormalHints(display, windows[i], &hints);
if (num_instances > 1)
title = g_strdup_printf (APP_TITLE "-%d", i);
else
title = g_strdup (APP_TITLE);
if (XStringListToTextProperty ((char **) &title, 1, &xproperty) != 0) {
XSetWMName (display, windows[i], &xproperty);
XFree (xproperty.value);
}
XSetWindowAttributes attr = { 0 };
if ((appCtx[i]->config.tiled_display_config.enable &&
appCtx[i]->config.tiled_display_config.rows *
appCtx[i]->config.tiled_display_config.columns == 1) ||
(appCtx[i]->config.tiled_display_config.enable == 0)) {
attr.event_mask = KeyPress;
} else if (appCtx[i]->config.tiled_display_config.enable) {
attr.event_mask = ButtonPress | KeyRelease;
}
XChangeWindowAttributes (display, windows[i], CWEventMask, &attr);
Atom wmDeleteMessage = XInternAtom (display, "WM_DELETE_WINDOW", False);
if (wmDeleteMessage != None) {
XSetWMProtocols (display, windows[i], &wmDeleteMessage, 1);
}
XMapRaised (display, windows[i]);
XSync (display, 1); //discard the events for now
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (appCtx
[i]->pipeline.instance_bins[0].sink_bin.sub_bins[j].sink),
(gulong) windows[i]);
gst_video_overlay_expose (GST_VIDEO_OVERLAY (appCtx[i]->
pipeline.instance_bins[0].sink_bin.sub_bins[j].sink));
if (!x_event_thread)
x_event_thread = g_thread_new ("nvds-window-event-thread",
nvds_x_event_thread, NULL);
}
#if !defined(__aarch64__)
if (!prop.integrated) {
if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
NVGSTDS_ERR_MSG_V ("Failed to set pipeline to PAUSED");
return_value = -1;
goto done;
}
}
#endif
}
/* Dont try to set playing state if error is observed */
if (return_value != -1) {
for (i = 0; i < num_instances; i++) {
if (gst_element_set_state (appCtx[i]->pipeline.pipeline,
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
g_print ("\ncan't set pipeline to playing state.\n");
return_value = -1;
goto done;
}
if (appCtx[i]->config.pipeline_recreate_sec)
g_timeout_add_seconds (appCtx[i]->config.pipeline_recreate_sec,
recreate_pipeline_thread_func, appCtx[i]);
}
}
print_runtime_commands ();
changemode (1);
g_timeout_add (40, event_thread_func, NULL);
g_main_loop_run (main_loop);
changemode (0);
done:
g_print ("Quitting\n");
for (i = 0; i < num_instances; i++) {
if (appCtx[i]->return_value == -1)
return_value = -1;
destroy_pipeline (appCtx[i]);
g_mutex_lock (&disp_lock);
if (windows[i])
XDestroyWindow (display, windows[i]);
windows[i] = 0;
g_mutex_unlock (&disp_lock);
g_free (appCtx[i]);
}
g_mutex_lock (&disp_lock);
if (display)
XCloseDisplay (display);
display = NULL;
g_mutex_unlock (&disp_lock);
g_mutex_clear (&disp_lock);
if (main_loop) {
g_main_loop_unref (main_loop);
}
if (ctx) {
g_option_context_free (ctx);
}
if (return_value == 0) {
g_print ("App run successful\n");
} else {
g_print ("App run failed\n");
}
gst_deinit ();
return return_value;
}
......@@ -14,10 +14,11 @@ source:
location: ../../../streams/cr7_1920x1080.h264
streammux:
batch-size: 1
batch-size: 5
batched-push-timeout: 40000
width: 1920
height: 1080
gpu-id: 2
## Inference using nvinferserver:
primary-gie:
......
......@@ -12,7 +12,7 @@
infer_config {
unique_id: 1
gpu_ids: [0]
gpu_ids: [2]
max_batch_size: 30
backend {
inputs: [ {
......
......@@ -409,7 +409,7 @@ main (int argc, char *argv[])
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
g_object_set (G_OBJECT (streammux), "batch-size", 1, NULL);
g_object_set (G_OBJECT (streammux), "gpu-id", 3, NULL);
g_object_set (G_OBJECT (streammux), "width", MUXER_OUTPUT_WIDTH, "height",
MUXER_OUTPUT_HEIGHT,
"batched-push-timeout", MUXER_BATCH_TIMEOUT_USEC, NULL);
......
......@@ -18,16 +18,17 @@ streammux:
batched-push-timeout: 40000
width: 1920
height: 1080
gpu-id: 3
tracker:
tracker-width: 960
tracker-height: 544
gpu-id: 0
gpu-id: 3
ll-lib-file: /opt/deepstream/lib/libnvds_nvmultiobjecttracker.so
# ll-config-file required to set different tracker types
# ll-config-file: ../../../../samples/configs/deepstream-app/config_tracker_IOU.yml
ll-config-file: ../../../../samples/configs/deepstream-app/config_tracker_NvSORT.yml
ll-config-file: ../../../sugon_samples/configs/deepstream-app/config_tracker_NvSORT.yml
# ll-config-file: ../../../../samples/configs/deepstream-app/config_tracker_NvDCF_perf.yml
# ll-config-file: ../../../../samples/configs/deepstream-app/config_tracker_NvDCF_accuracy.yml
# ll-config-file: ../../../../samples/configs/deepstream-app/config_tracker_NvDeepSORT.yml
......
......@@ -12,7 +12,7 @@
infer_config {
unique_id: 1
gpu_ids: [0]
gpu_ids: [3]
max_batch_size: 30
backend {
inputs: [ {
......
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