备份最新边缘设备程序

This commit is contained in:
BBIT-Kai
2026-04-02 09:24:45 +08:00
parent de256fb5e0
commit 63bd6da895
68 changed files with 69340 additions and 2 deletions
+1
View File
@@ -16,3 +16,4 @@ ktor/.kotlin/
*.pt
sentinel/build/
iot/Sentinel/build/
*.engine
+1 -1
View File
@@ -41,7 +41,7 @@ def analysis(state: State):
JSON 示例格式:
{
"have_animal": true,
"livestock_type": "“测试数据:” + 5位随机数", // 例如 测试数据34223
"livestock_type": "", // 例如 牛、羊、猪、鸡、鸭、鹅、钢管、土渣等任何你观察到的
"remark": "<备注>" // 车身描述
}
请确保输出的 JSON 可以被严格解析。
+1 -1
View File
@@ -82,7 +82,7 @@
| | | 8002 | 8002 | ce_kong | Kong | Kong**管理界面**,无需登录 |
| | | 8444 | 8444 | ce_kong | Kong | Kong Admin API 的 HTTPS 端口 |
| | | 8088 | | | ws-scrcpy | Android远程框架 |
| | | 5672 | 5672 | ce_rabbitmq | RabbitMQ | RabbitMQ 客户端连接端口 |
| http | **5672** | 5672 | 5672 | ce_rabbitmq | RabbitMQ | RabbitMQ 客户端连接端口 |
| | | 15672 | 15672 | ce_rabbitmq | RabbitMQ | RabbitMQ**管理界面**admin:123456 |
| | | 9323 | 9323 | | Docker | Docker debug端口 |
| | | 3001 | 3001 | ce_grafana | Grafana | Grafana**管理界面**admin:123456 |
+4
View File
@@ -0,0 +1,4 @@
dist/**/*.onnx
**/*.onnx
**/*.mp4
**/*.jpg
+164
View File
@@ -0,0 +1,164 @@
# 最低CMake版本要求:3.21 以上才支持 RUNTIME_DEPENDENCIES install 功能(自动部署 vcpkg so
cmake_minimum_required(VERSION 3.21)
# =====================================================
# Toolchain (vcpkg)
# =====================================================
# 告诉CMake使用vcpkg作为依赖管理
# 所有 find_package(... CONFIG) 会优先搜索 vcpkg 提供的库
set(CMAKE_TOOLCHAIN_FILE
$ENV{HOME}/vcpkg/scripts/buildsystems/vcpkg.cmake
CACHE STRING "Vcpkg toolchain file")
# =====================================================
# Project定义
# =====================================================
project(sentinel-app LANGUAGES CXX)
# =====================================================
# 全局构建配置
# =====================================================
# 使用C++17标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 对生成的库启用位置无关代码(PIE / PIC)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# =====================================================
# 平台检测 执行命令获取目标机器架构
# =====================================================
execute_process(
COMMAND gcc -dumpmachine
OUTPUT_VARIABLE TARGET_DEVICE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# 如果是 aarch64 (Jetson / Tegra),定义宏 PLATFORM_TEGRA
if(TARGET_DEVICE MATCHES "aarch64")
add_compile_definitions(PLATFORM_TEGRA)
endif()
# =====================================================
# 依赖库
# =====================================================
# pkg-config 必须,用来查询系统库的编译/链接参数
find_package(PkgConfig REQUIRED)
# vcpkg 管理的库(现代CMake target
find_package(miniocpp CONFIG REQUIRED) # minio C++ SDK
find_package(yaml-cpp CONFIG REQUIRED) # yaml parser
# ========================
# 系统库 / NVIDIA stack
# ========================
pkg_check_modules(GST REQUIRED gstreamer-1.0) # gstreamer 头文件与库
pkg_check_modules(UUID REQUIRED uuid) # uuid 库
# =====================================================
# 主可执行文件
# =====================================================
add_executable(sentinel-app
src/app.cpp
src/service/vehicle_event_service.cpp
src/service/rtsp_grabber.cpp
src/utils/ds_yml_parse.cpp
src/utils/my_utils.cpp
)
# 编译选项
target_compile_options(sentinel-app
PRIVATE
-Wall # 打开所有警告
-Werror # 警告当作错误
-Wno-unused-function # 忽略未使用函数警告
)
# 包含目录
target_include_directories(sentinel-app
PRIVATE
src # 自己的源码目录
src/service
src/utils
${GST_INCLUDE_DIRS} # gstreamer include
/opt/nvidia/deepstream/deepstream/sources/includes # DeepStream SDK
/usr/local/cuda/include # CUDA
)
# 链接目录(仅针对非 target 方式库)
target_link_directories(sentinel-app
PRIVATE
/opt/nvidia/deepstream/deepstream/lib # DeepStream 库
/usr/local/cuda/lib64 # CUDA 库
)
# 链接库
target_link_libraries(sentinel-app
PRIVATE
# vcpkg target,现代CMake可追踪
miniocpp::miniocpp
yaml-cpp
# RabbitMQ
SimpleAmqpClient
rabbitmq
# 系统/DeepStream/GStreamer库
${GST_LIBRARIES}
gstrtspserver-1.0
nvds_batch_jpegenc
nvdsgst_meta
nvdsgst_helper
nvds_meta
nvds_yml_parser
# 系统/第三方库
uuid
cudart
avformat
avcodec
avutil
swscale
avdevice
avfilter
curl
m
z
pthread
)
# =====================================================
# RPATH 设置(运行时库搜索路径)
# =====================================================
# BUILD_RPATH:构建时运行可执行文件的搜索路径
# INSTALL_RPATH:安装后运行可执行文件的搜索路径
# $ORIGIN 表示可执行文件所在目录
set_target_properties(sentinel-app PROPERTIES
BUILD_RPATH "\$ORIGIN/lib" # 优先搜索 dist/lib
INSTALL_RPATH "\$ORIGIN/lib:/opt/nvidia/deepstream/deepstream/lib" # fallback DeepStream
)
# =====================================================
# 安装规则(部署阶段)
# =====================================================
# 安装可执行文件,并自动收集 vcpkg 的运行时 so
install(TARGETS sentinel-app
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES
"ld-linux" # 排除系统 loader
"libc.so" # 系统 libc
"libm.so" # 系统 math
"libpthread.so" # 系统线程库
"libdl.so" # 系统动态库
"libcuda" # CUDA 系统库
"libnvidia" # NVIDIA 驱动
"libnv" # NVIDIA 系统库
"libgst" # GStreamer 系统库
"libav" # ffmpeg 系统库
"libcrypto" # 系统加密库
"libssl"
POST_EXCLUDE_REGEXES
".*/opt/nvidia/.*" # 不拷贝 DeepStream 系统库
".*/usr/lib/.*" # 不拷贝系统库
RUNTIME DESTINATION . # 可执行文件放 dist/
LIBRARY DESTINATION lib # 对应 so 库安装到 dist/lib/
)
+9
View File
@@ -0,0 +1,9 @@
FROM nvcr.io/nvidia/l4t-base:r36.2.0
WORKDIR /app
COPY ./dist/ ./
RUN chmod +x ./sentinel-app
CMD ["./sentinel-app", "config/main.yml"]
+25
View File
@@ -0,0 +1,25 @@
[property]
enable=1
#Width height used for configuration to which below configs are configured
config-width=1920
config-height=1080
osd-mode=2
display-font-size=12
[line-crossing-stream-0]
enable=1
# 箭头尾部、箭头头部、横线开始、横线结束、550;550;700;800;200;650;1700;650 # 测试视频
line-crossing-LineA=950;400;1050;600;800;460;1060;460
line-crossing-LineB=1040;630;1200;900;870;750;1280;720
class-id=0
extended=0
# mode=loose、strict、balanced
mode=loose
[line-crossing-stream-1]
enable=1
line-crossing-LineA=930;210;850;430;780;330;1000;330
line-crossing-LineB=750;550;620;900;340;680;990;700
class-id=0
extended=0
mode=loose
+61
View File
@@ -0,0 +1,61 @@
BaseConfig:
minDetectorConfidence: 0.0430 # 如果检测器检测框的置信度低于此值,则不会被用于跟踪
TargetManagement:
enableBboxUnClipping: 1 # 如果检测框可能被图像边界裁剪,则解除裁剪
preserveStreamUpdateOrder: 0 # 分配新目标ID时,是否保持输入流顺序,以在多次运行中保持目标ID确定性
maxTargetsPerStream: 150 # 每个流可跟踪的最大目标数。建议设置大于10。注意:此值应包括以阴影模式跟踪的目标。最大值取决于GPU内存容量
# [目标创建与终止策略]
minIouDiff4NewTarget: 0.7418 # 如果新检测到的对象与已有目标的IOU高于此阈值,则丢弃该新对象
minTrackerConfidence: 0.4009 # 如果对象跟踪器的置信度低于此值,则以阴影模式跟踪。有效范围:[0.0, 1.0]
probationAge: 2 # 目标年龄超过此值,则视为有效目标
maxShadowTrackingAge: 51 # 阴影跟踪的最大长度。如果阴影跟踪年龄超过此值,跟踪器将被终止
earlyTerminationAge: 1 # 如果阴影跟踪年龄在试验期达到此阈值,则目标会提前终止
TrajectoryManagement:
useUniqueID: 0 # 分配跟踪器ID时是否使用64位长唯一ID
DataAssociator:
dataAssociatorType: 0 # 数据关联器类型 { DEFAULT=0 }
associationMatcherType: 1 # 匹配算法类型 { GREEDY=0, CASCADED=1 }
checkClassMatch: 1 # 是否只将同类对象关联,默认值:true
# [关联度指标:有效候选阈值]
minMatchingScore4Overall: 0.4290 # 总匹配分数最小值
minMatchingScore4SizeSimilarity: 0.3627 # 检测框尺寸相似度最小分数
minMatchingScore4Iou: 0.2575 # IOU最小分数
minMatchingScore4VisualSimilarity: 0.5356 # 视觉相似度最小分数
# [关联度指标:权重]
matchingScoreWeight4VisualSimilarity: 0.3370 # 视觉相似度权重(相关响应比率)
matchingScoreWeight4SizeSimilarity: 0.4354 # 尺寸相似度权重
matchingScoreWeight4Iou: 0.3656 # IOU权重
# [关联度指标:试探性检测] 仅使用IOU相似度进行试探性检测匹配
tentativeDetectorConfidence: 0.2008 # 如果检测置信度低于此值但高于minDetectorConfidence,则视为试探性检测
minMatchingScore4TentativeIou: 0.5296 # 匹配目标与试探性检测的最小IOU阈值
StateEstimator:
stateEstimatorType: 1 # 状态估计器类型 { DUMMY=0, SIMPLE=1, REGULAR=2 }
# [动态建模]
processNoiseVar4Loc: 1.5110 # 检测框中心的过程噪声方差
processNoiseVar4Size: 1.3159 # 检测框尺寸的过程噪声方差
processNoiseVar4Vel: 0.0300 # 速度的过程噪声方差
measurementNoiseVar4Detector: 3.0283 # 检测器检测的测量噪声方差
measurementNoiseVar4Tracker: 8.1505 # 跟踪器定位的测量噪声方差
VisualTracker:
visualTrackerType: 1 # 视觉跟踪器类型 { DUMMY=0, NvDCF=1 }
# [NvDCF:特征提取]
useColorNames: 1 # 是否使用ColorNames特征
useHog: 0 # 是否使用HOG(方向梯度直方图)特征
featureImgSizeLevel: 2 # 特征图尺寸等级。有效范围:{1, 2, 3, 4, 5},从最小到最大
featureFocusOffsetFactor_y: -0.2000 # 汉宁窗口中心相对于特征高度的偏移量。中心在垂直方向移动 (featureFocusOffsetFactor_y * featureMatSize.height)
# [NvDCF:相关滤波器]
filterLr: 0.0750 # DCF滤波器指数移动平均学习率,有效范围:[0.0, 1.0]
filterChannelWeightsLr: 0.1000 # 特征通道权重的学习率,有效范围:[0.0, 1.0]
gaussianSigma: 0.7500 # 创建DCF滤波器时期望响应的高斯标准差(像素)
+55
View File
@@ -0,0 +1,55 @@
source-list:
- main: rtsp://admin:cda1b2c3@192.168.1.6:554/Streaming/Channels/101 # 正1
side: rtsp://admin:cda1b2c3@192.168.1.4:554/Streaming/Channels/101 # 辅2
- main: rtsp://admin:cda1b2c3@192.168.1.7:554/Streaming/Channels/101 # 正2
side: rtsp://admin:cda1b2c3@192.168.1.8:554/Streaming/Channels/101 # 辅1
# 测试环境
# - main: file:///opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/sentinel/dist/source/traffic_test.mp4
# side: rtsp://admin:cda1b2c3@10.0.4.41:554/Streaming/Channels/101
vehicle_type:
# 0:不限制 1:轿跑车 2:大型车辆 3:轿车 4:SUV 5:卡车 6:面包车/厢型车
# 7:大型车辆 + 卡车(生产)
type: 0
output:
## 1:file ouput 2:fake output 3:eglsink output 4:rtsp output
type: 4
## 0: H264 encoder 1:H265 encoder
enc: 0
## encoder type 0=Hardware 1=Software
enc-type: 0
bitrate: 4000000
##The file name without suffix
filename: test
primary-gie:
#0:nvinfer, 1:nvinfeserver
plugin-type: 0
##For car detection
config-file-path: pgie_0_traffic_cam_net.yml
unique-id: 1
#If there is ROI
analytics:
enable: 1
config-file: config_nvdsanalytics.txt
secondary-gie1:
unique-id: 2
#0:nvinfer, 1:nvinfeserver
plugin-type: 0
config-file-path: sgie_1_vehicle_type.yml
process-mode: 2
tracker:
enable: 1
tracker-width: 640
tracker-height: 384
gpu-id: 0
ll-lib-file: /opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
ll-config-file: config_tracker.yml
enable-batch-process: 1
+55
View File
@@ -0,0 +1,55 @@
source-list:
- main: rtsp://admin:cda1b2c3@192.168.1.6:554/Streaming/Channels/101 # 正1
side: rtsp://admin:cda1b2c3@192.168.1.4:554/Streaming/Channels/101 # 辅2
- main: rtsp://admin:cda1b2c3@192.168.1.7:554/Streaming/Channels/101 # 正2
side: rtsp://admin:cda1b2c3@192.168.1.8:554/Streaming/Channels/101 # 辅1
# 测试环境
# - main: file:///opt/nvidia/deepstream/deepstream/sources/apps/sample_apps/sentinel/dist/source/traffic_test.mp4
# side: rtsp://admin:cda1b2c3@10.0.4.41:554/Streaming/Channels/101
vehicle_type:
# 0:不限制 1:轿跑车 2:大型车辆 3:轿车 4:SUV 5:卡车 6:面包车/厢型车
# 7:大型车辆 + 卡车(生产)
type: 7
output:
## 1:file ouput 2:fake output 3:eglsink output 4:rtsp output
type: 2
## 0: H264 encoder 1:H265 encoder
enc: 0
## encoder type 0=Hardware 1=Software
enc-type: 0
bitrate: 6000000
##The file name without suffix
filename: test
primary-gie:
#0:nvinfer, 1:nvinfeserver
plugin-type: 0
##For car detection
config-file-path: pgie_0_traffic_cam_net.yml
unique-id: 1
#If there is ROI
analytics:
enable: 1
config-file: config_nvdsanalytics.txt
secondary-gie1:
unique-id: 2
#0:nvinfer, 1:nvinfeserver
plugin-type: 0
config-file-path: sgie_1_vehicle_type.yml
process-mode: 2
tracker:
enable: 1
tracker-width: 640
tracker-height: 384
gpu-id: 0
ll-lib-file: /opt/nvidia/deepstream/deepstream/lib/libnvds_nvmultiobjecttracker.so
ll-config-file: config_tracker.yml
enable-batch-process: 1
+49
View File
@@ -0,0 +1,49 @@
property:
# 官方文档中的配置=============================
gpu-id: 0
infer-dims: 3;544;960
net-scale-factor: 0.00392156862745098
tlt-model-key: tlt_encode
# 模型网络类型 0: Detector 1: Classifier 2: Segmentation 3: Instance Segmentation
network-type: 0
num-detected-classes: 4
# 模型颜色格式 0:RGB 1:BGR 2:灰度
model-color-format: 0
# 保持宽高比 0:不保持 1:保持
maintain-aspect-ratio: 0
# 是否输出张量元数据 0:否 1:是
output-tensor-meta: 0
# 文件路径配置=============================
onnx-file: ../models/traffic_cam_net/resnet18_trafficcamnet_pruned.onnx
labelfile-path: ../models/traffic_cam_net/labels.txt
model-engine-file: ../models/traffic_cam_net/resnet18_trafficcamnet_pruned.onnx_b4_gpu0_fp16.engine
gie-unique-id: 1
batch-size: 4
# 推理模式配置 1:整帧 2:上游ROI (1:主推理 2:次推理(基于主推理))
process-mode: 1
## 0=FP32, 1=INT8, 2=FP16 mode
network-mode: 2
# 推理间隔 0:每帧 1:每1帧 2:每2帧
interval: 0
output-blob-names: output_cov/Sigmoid:0;output_bbox/BiasAdd:0
# 集群模式 0: OpenCV groupRectangles() 1: DBSCAN 2: Non Maximum Suppression 3: DBSCAN + NMS Hybrid 4: No clustering
cluster-mode: 2
# 计算硬件 0: Platform default GPU (dGPU), VIC (Jetson) 1: GPU 2: VIC (Jetson only)
scaling-compute-hw: 0
# 设置具体类别NMS等参数
class-attrs-all:
# 保留的最多对象数
topk: 20
# 两个方案之间的最大 IOU 分数,超过该分数后,置信度较低的方案将被拒绝。
nms-iou-threshold: 0.5
# 预聚类阈值,范围为0到1。较高的值会导致更多的边界框被聚类在一起。
pre-cluster-threshold: 0.2
## Per class configurations
class-attrs-0:
topk: 20
nms-iou-threshold: 0.6
pre-cluster-threshold: 0.4
+28
View File
@@ -0,0 +1,28 @@
property:
gpu-id: 0
gie-unique-id: 2
net-scale-factor: 1
scaling-compute-hw: 0
network-type: 1
infer-dims: 3;224;224
onnx-file: ../models/vehicle_type_net/resnet18_pruned.onnx
model-engine-file: ../models/vehicle_type_net/resnet18_pruned.onnx_b4_gpu0_fp16.engine
# model-engine-file: ../models/vehicle_type_net/resnet18_pruned.onnx_b4_gpu0_int8.engine
labelfile-path: ../models/vehicle_type_net/labels.txt
int8-calib-file: ../models/vehicle_type_net/resnet18_pruned_int8.txt
batch-size: 4
network-mode: 2
input-object-min-width: 10
input-object-min-height: 10
model-color-format: 1
# 基于模型的编号 0此处为车辆检测模型
operate-on-gie-id: 1
# 基于模型类别的编号 0此处为车辆类别
operate-on-class-ids: 0
# 分类器异步模式启动与否
classifier-async-mode: 1
# 分类器阈值,高于该值则认为分类成功
classifier-threshold: 0.51
# 基于上游ROI进行分类
process-mode: 2
#scaling-filter: 0
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
car
bicycle
person
road_sign
@@ -0,0 +1,212 @@
TRT-100300-EntropyCalibration2
input_1:0: 3c010a14
conv1/convolution:0: 3d0b7086
Reshape__113:0_output: 31a60d5b
ONNXTRT_Broadcast_output: 31a60d5b
conv1/BiasAdd:0: 3d0b7086
bn_conv1/batchnorm/mul__10_output: 3d62b50e
bn_conv1/batchnorm/mul_1:0: 3dd92c80
bn_conv1/batchnorm/sub__11_output: 3c296b24
bn_conv1/batchnorm/add_1:0: 3de3af36
activation_1/Relu:0: 3de355a8
block_1a_conv_shortcut/BiasAdd:0: 3d14edf6
block_1a_bn_shortcut/batchnorm/mul__22_output: 3e788ca2
block_1a_bn_shortcut/batchnorm/mul_1:0: 3d88cab4
block_1a_bn_shortcut/batchnorm/sub__23_output: 3cac2012
block_1a_bn_shortcut/batchnorm/add_1:0: 3cb23b6f
block_1a_conv_1/convolution:0: 3e429cd3
Reshape__115:0_output: 31e11e29
ONNXTRT_Broadcast_1_output: 31e11e29
block_1a_conv_1/BiasAdd:0: 3e429cd3
block_1a_bn_1/batchnorm/mul__14_output: 3d97cfd7
block_1a_bn_1/batchnorm/mul_1:0: 3e18ceef
block_1a_bn_1/batchnorm/sub__15_output: 3ccaa65b
block_1a_bn_1/batchnorm/add_1:0: 3dec091e
block_1a_relu_1/Relu:0: 3d3b8c04
block_1a_conv_2/convolution:0: 3db7871d
Reshape__117:0_output: 31ae4bc8
ONNXTRT_Broadcast_3_output: 31ae4bc8
block_1a_conv_2/BiasAdd:0: 3db7871d
block_1a_bn_2/batchnorm/mul__20_output: 3e456d78
block_1a_bn_2/batchnorm/mul_1:0: 3e00e329
block_1a_bn_2/batchnorm/sub__21_output: 3d9d559e
block_1a_bn_2/batchnorm/add_1:0: 3d4cab2b
add_1/add:0: 3d71098b
block_1a_relu/Relu:0: 3d76aa90
block_1b_conv_shortcut/BiasAdd:0: 3cbcc767
block_1b_bn_shortcut/batchnorm/mul__34_output: 3dbd9ab8
block_1b_bn_shortcut/batchnorm/mul_1:0: 3e03be12
block_1b_bn_shortcut/batchnorm/sub__35_output: 3ccc079d
block_1b_bn_shortcut/batchnorm/add_1:0: 3db29028
block_1b_conv_1/convolution:0: 3d684dba
Reshape__119:0_output: 31eb391f
ONNXTRT_Broadcast_5_output: 31eb391f
block_1b_conv_1/BiasAdd:0: 3d684dba
block_1b_bn_1/batchnorm/mul__26_output: 3dceab93
block_1b_bn_1/batchnorm/mul_1:0: 3dd187df
block_1b_bn_1/batchnorm/sub__27_output: 3cefd2d4
block_1b_bn_1/batchnorm/add_1:0: 3d92794f
block_1b_relu_1/Relu:0: 3d718b6b
block_1b_conv_2/convolution:0: 3cc5b5a4
Reshape__121:0_output: 31e7fdd9
ONNXTRT_Broadcast_7_output: 31e7fdd9
block_1b_conv_2/BiasAdd:0: 3cc5b5a3
block_1b_bn_2/batchnorm/mul__32_output: 3e00c649
block_1b_bn_2/batchnorm/mul_1:0: 3e047161
block_1b_bn_2/batchnorm/sub__33_output: 3cf28d32
block_1b_bn_2/batchnorm/add_1:0: 3d9083ce
add_2/add:0: 3e0065eb
block_1b_relu/Relu:0: 3df21e9b
block_2a_conv_shortcut/BiasAdd:0: 3c25e159
block_2a_bn_shortcut/batchnorm/mul__46_output: 3e3987b0
block_2a_bn_shortcut/batchnorm/mul_1:0: 3d8e1310
block_2a_bn_shortcut/batchnorm/sub__47_output: 3cd450fd
block_2a_bn_shortcut/batchnorm/add_1:0: 3c9083e5
block_2a_conv_1/convolution:0: 3d6fc855
Reshape__123:0_output: 3203fc6a
ONNXTRT_Broadcast_9_output: 3203fc6a
block_2a_conv_1/BiasAdd:0: 3d6fc855
block_2a_bn_1/batchnorm/mul__38_output: 3e2ca3aa
block_2a_bn_1/batchnorm/mul_1:0: 3df4778b
block_2a_bn_1/batchnorm/sub__39_output: 3d43c758
block_2a_bn_1/batchnorm/add_1:0: 3da16d21
block_2a_relu_1/Relu:0: 3e0394a2
block_2a_conv_2/convolution:0: 3d3b88b2
Reshape__125:0_output: 31fc2216
ONNXTRT_Broadcast_11_output: 31fc2216
block_2a_conv_2/BiasAdd:0: 3d3b88b2
block_2a_bn_2/batchnorm/mul__44_output: 3de76a8b
block_2a_bn_2/batchnorm/mul_1:0: 3e10c50f
block_2a_bn_2/batchnorm/sub__45_output: 3d31c53b
block_2a_bn_2/batchnorm/add_1:0: 3d94276b
add_3/add:0: 3de729cf
block_2a_relu/Relu:0: 3dee3af2
block_2b_conv_shortcut/BiasAdd:0: 3cbb5111
block_2b_bn_shortcut/batchnorm/mul__58_output: 3e664605
block_2b_bn_shortcut/batchnorm/mul_1:0: 3e0ae0c7
block_2b_bn_shortcut/batchnorm/sub__59_output: 3cd2847a
block_2b_bn_shortcut/batchnorm/add_1:0: 3d86ba75
block_2b_conv_1/convolution:0: 3d15f181
Reshape__127:0_output: 31c5cf05
ONNXTRT_Broadcast_13_output: 31c5cf05
block_2b_conv_1/BiasAdd:0: 3d15f180
block_2b_bn_1/batchnorm/mul__50_output: 3e021c4e
block_2b_bn_1/batchnorm/mul_1:0: 3db0bed5
block_2b_bn_1/batchnorm/sub__51_output: 3d2f0fbd
block_2b_bn_1/batchnorm/add_1:0: 3dcec26a
block_2b_relu_1/Relu:0: 3d891bb9
block_2b_conv_2/convolution:0: 3c7bc75a
Reshape__129:0_output: 31c51f52
ONNXTRT_Broadcast_15_output: 31c51f52
block_2b_conv_2/BiasAdd:0: 3c7bc75a
block_2b_bn_2/batchnorm/mul__56_output: 3e641775
block_2b_bn_2/batchnorm/mul_1:0: 3e125ac1
block_2b_bn_2/batchnorm/sub__57_output: 3cdbec1b
block_2b_bn_2/batchnorm/add_1:0: 3d82849e
add_4/add:0: 3de52960
block_2b_relu/Relu:0: 3e1b45d9
block_3a_conv_shortcut/BiasAdd:0: 3c0c286b
block_3a_bn_shortcut/batchnorm/mul__70_output: 3d0a8ba3
block_3a_bn_shortcut/batchnorm/mul_1:0: 3d68f5cc
block_3a_bn_shortcut/batchnorm/sub__71_output: 3d0457dd
block_3a_bn_shortcut/batchnorm/add_1:0: 3d549449
block_3a_conv_1/convolution:0: 3d87f177
Reshape__131:0_output: 31b5e077
ONNXTRT_Broadcast_17_output: 31b5e077
block_3a_conv_1/BiasAdd:0: 3d87f177
block_3a_bn_1/batchnorm/mul__62_output: 3de6f05e
block_3a_bn_1/batchnorm/mul_1:0: 3e05ef56
block_3a_bn_1/batchnorm/sub__63_output: 3d477467
block_3a_bn_1/batchnorm/add_1:0: 3e0c3ff6
block_3a_relu_1/Relu:0: 3e2ba120
block_3a_conv_2/convolution:0: 3da51990
Reshape__133:0_output: 31ee6a50
ONNXTRT_Broadcast_19_output: 31ee6a50
block_3a_conv_2/BiasAdd:0: 3da51990
block_3a_bn_2/batchnorm/mul__68_output: 3e428ec9
block_3a_bn_2/batchnorm/mul_1:0: 3e29c12b
block_3a_bn_2/batchnorm/sub__69_output: 3d6d2e63
block_3a_bn_2/batchnorm/add_1:0: 3dcaec47
add_5/add:0: 3e1b90c5
block_3a_relu/Relu:0: 3ddb3887
block_3b_conv_shortcut/BiasAdd:0: 3cbce473
block_3b_bn_shortcut/batchnorm/mul__82_output: 3ef99b62
block_3b_bn_shortcut/batchnorm/mul_1:0: 3e13f41d
block_3b_bn_shortcut/batchnorm/sub__83_output: 3d6ea092
block_3b_bn_shortcut/batchnorm/add_1:0: 3d4c522b
block_3b_conv_1/convolution:0: 3d0f318d
Reshape__135:0_output: 320cc604
ONNXTRT_Broadcast_21_output: 320cc604
block_3b_conv_1/BiasAdd:0: 3d0f318e
block_3b_bn_1/batchnorm/mul__74_output: 3e3f059f
block_3b_bn_1/batchnorm/mul_1:0: 3dbc1fb4
block_3b_bn_1/batchnorm/sub__75_output: 3d849e78
block_3b_bn_1/batchnorm/add_1:0: 3da55f89
block_3b_relu_1/Relu:0: 3dcdd28c
block_3b_conv_2/convolution:0: 3c780d92
Reshape__137:0_output: 31c5e0aa
ONNXTRT_Broadcast_23_output: 31c5e0aa
block_3b_conv_2/BiasAdd:0: 3c780d92
block_3b_bn_2/batchnorm/mul__80_output: 3eaa7444
block_3b_bn_2/batchnorm/mul_1:0: 3ddcdf2b
block_3b_bn_2/batchnorm/sub__81_output: 3d58c1ca
block_3b_bn_2/batchnorm/add_1:0: 3db0b712
add_6/add:0: 3e1f33d7
block_3b_relu/Relu:0: 3de16894
block_4a_conv_shortcut/BiasAdd:0: 3c265273
block_4a_bn_shortcut/batchnorm/mul__94_output: 3dd4009d
block_4a_bn_shortcut/batchnorm/mul_1:0: 3e5739f6
block_4a_bn_shortcut/batchnorm/sub__95_output: 3cc128cd
block_4a_bn_shortcut/batchnorm/add_1:0: 3e50e6c2
block_4a_conv_1/convolution:0: 3d2e25b4
Reshape__139:0_output: 320447b1
ONNXTRT_Broadcast_25_output: 320447b1
block_4a_conv_1/BiasAdd:0: 3d2e25b4
block_4a_bn_1/batchnorm/mul__86_output: 3e289108
block_4a_bn_1/batchnorm/mul_1:0: 3e29b2e7
block_4a_bn_1/batchnorm/sub__87_output: 3d4b30fc
block_4a_bn_1/batchnorm/add_1:0: 3dbff4d3
block_4a_relu_1/Relu:0: 3daaee81
block_4a_conv_2/convolution:0: 3cc387b8
Reshape__141:0_output: 31f321af
ONNXTRT_Broadcast_27_output: 31f321af
block_4a_conv_2/BiasAdd:0: 3cc387b9
block_4a_bn_2/batchnorm/mul__92_output: 3e94d870
block_4a_bn_2/batchnorm/mul_1:0: 3e404cec
block_4a_bn_2/batchnorm/sub__93_output: 3d0eb802
block_4a_bn_2/batchnorm/add_1:0: 3e3eb292
add_7/add:0: 3ec7cca9
block_4a_relu/Relu:0: 3ec7cca9
block_4b_conv_shortcut/BiasAdd:0: 3df73d3e
block_4b_bn_shortcut/batchnorm/mul__106_output: 3d1a89be
block_4b_bn_shortcut/batchnorm/mul_1:0: 3d96e059
block_4b_bn_shortcut/batchnorm/sub__107_output: 3c3f22bc
block_4b_bn_shortcut/batchnorm/add_1:0: 3d92edc6
block_4b_conv_1/convolution:0: 3e687860
Reshape__143:0_output: 31ee2bc9
ONNXTRT_Broadcast_29_output: 31ee2bc9
block_4b_conv_1/BiasAdd:0: 3e687860
block_4b_bn_1/batchnorm/mul__98_output: 3ddc6ca7
block_4b_bn_1/batchnorm/mul_1:0: 3e1db447
block_4b_bn_1/batchnorm/sub__99_output: 3ce9eb39
block_4b_bn_1/batchnorm/add_1:0: 3e199719
block_4b_relu_1/Relu:0: 3e199719
block_4b_conv_2/convolution:0: 3e543df6
Reshape__145:0_output: 31fba56d
ONNXTRT_Broadcast_31_output: 31fba56d
block_4b_conv_2/BiasAdd:0: 3e543df6
block_4b_bn_2/batchnorm/mul__104_output: 3dca82e9
block_4b_bn_2/batchnorm/mul_1:0: 3df336dc
block_4b_bn_2/batchnorm/sub__105_output: 3d45d1fd
block_4b_bn_2/batchnorm/add_1:0: 3ddba960
add_8/add:0: 3ddd4ffc
block_4b_relu/Relu:0: 3d80c4f7
output_cov/convolution:0: 3e3dc495
Reshape__147:0_output: 3c60e55a
ONNXTRT_Broadcast_33_output: 3c60e55a
output_cov/BiasAdd:0: 3e44a1e7
output_cov/Sigmoid:0: 3bed8e13
output_bbox/convolution:0: 3d9e875d
Reshape__149:0_output: 3b4a4f27
ONNXTRT_Broadcast_36_output: 3b4a4f27
output_bbox/BiasAdd:0: 3da418a9
+1
View File
@@ -0,0 +1 @@
coupe;largevehicle;sedan;suv;truck;van
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,162 @@
TRT-100200-EntropyCalibration2
input_1:0: 3f9849a2
conv1/convolution:0: 4104ffde
bn_conv1/batchnorm/mul__7_output: 3a11e538
bn_conv1/batchnorm/mul_1:0: 3e233b1e
bn_conv1/batchnorm/sub__8_output: 3cbc1e95
bn_conv1/batchnorm/add_1:0: 3e43d16f
activation_1/Relu:0: 3e43d16f
block_1a_conv_shortcut/convolution:0: 3e44da22
block_1a_bn_shortcut/batchnorm/mul__16_output: 3c5993fc
block_1a_bn_shortcut/batchnorm/mul_1:0: 3e58b585
block_1a_bn_shortcut/batchnorm/sub__17_output: 3d0e8d9c
block_1a_bn_shortcut/batchnorm/add_1:0: 3e24a879
block_1a_conv_1/convolution:0: 3ef9489d
block_1a_bn_1/batchnorm/mul__10_output: 3bbe0daf
block_1a_bn_1/batchnorm/mul_1:0: 3e2c131d
block_1a_bn_1/batchnorm/sub__11_output: 3ce842b6
block_1a_bn_1/batchnorm/add_1:0: 3e030549
block_1a_relu_1/Relu:0: 3db97517
block_1a_conv_2/convolution:0: 3e6e7208
block_1a_bn_2/batchnorm/mul__14_output: 3c21be0c
block_1a_bn_2/batchnorm/mul_1:0: 3e3eae5f
block_1a_bn_2/batchnorm/sub__15_output: 3d0c35f8
block_1a_bn_2/batchnorm/add_1:0: 3e248e17
add_1/add:0: 3e8cde13
block_1a_relu/Relu:0: 3e693c88
block_1b_conv_shortcut/convolution:0: 3e7563dc
block_1b_bn_shortcut/batchnorm/mul__25_output: 3be1c1c3
block_1b_bn_shortcut/batchnorm/mul_1:0: 3de37775
block_1b_bn_shortcut/batchnorm/sub__26_output: 3ca94fcb
block_1b_bn_shortcut/batchnorm/add_1:0: 3dea31ac
block_1b_conv_1/convolution:0: 3f108d0b
block_1b_bn_1/batchnorm/mul__19_output: 3b59415c
block_1b_bn_1/batchnorm/mul_1:0: 3df60981
block_1b_bn_1/batchnorm/sub__20_output: 3c9c865d
block_1b_bn_1/batchnorm/add_1:0: 3dda8cd6
block_1b_relu_1/Relu:0: 3d9902f4
block_1b_conv_2/convolution:0: 3e447d08
block_1b_bn_2/batchnorm/mul__23_output: 3c184256
block_1b_bn_2/batchnorm/mul_1:0: 3df872e1
block_1b_bn_2/batchnorm/sub__24_output: 3c9b9f21
block_1b_bn_2/batchnorm/add_1:0: 3dfbf91b
add_2/add:0: 3e3265c2
block_1b_relu/Relu:0: 3e2c10a9
block_2a_conv_shortcut/convolution:0: 3e131b0b
block_2a_bn_shortcut/batchnorm/mul__34_output: 3bfd825d
block_2a_bn_shortcut/batchnorm/mul_1:0: 3d9bad16
block_2a_bn_shortcut/batchnorm/sub__35_output: 3c835aeb
block_2a_bn_shortcut/batchnorm/add_1:0: 3d91a315
block_2a_conv_1/convolution:0: 3ed683f3
block_2a_bn_1/batchnorm/mul__28_output: 3b659829
block_2a_bn_1/batchnorm/mul_1:0: 3dba1fdf
block_2a_bn_1/batchnorm/sub__29_output: 3c9d687f
block_2a_bn_1/batchnorm/add_1:0: 3dac1cae
block_2a_relu_1/Relu:0: 3dab510e
block_2a_conv_2/convolution:0: 3eb288f4
block_2a_bn_2/batchnorm/mul__32_output: 3bddabb5
block_2a_bn_2/batchnorm/mul_1:0: 3dfdc11c
block_2a_bn_2/batchnorm/sub__33_output: 3d0f3d4c
block_2a_bn_2/batchnorm/add_1:0: 3dcec406
add_3/add:0: 3dfd5e12
block_2a_relu/Relu:0: 3e07822f
block_2b_conv_shortcut/convolution:0: 3e09d3a9
block_2b_bn_shortcut/batchnorm/mul__43_output: 3bf5a610
block_2b_bn_shortcut/batchnorm/mul_1:0: 3daed1f3
block_2b_bn_shortcut/batchnorm/sub__44_output: 3cc68c8d
block_2b_bn_shortcut/batchnorm/add_1:0: 3daa59ed
block_2b_conv_1/convolution:0: 3ed09a96
block_2b_bn_1/batchnorm/mul__37_output: 3b158863
block_2b_bn_1/batchnorm/mul_1:0: 3da46482
block_2b_bn_1/batchnorm/sub__38_output: 3cc7c929
block_2b_bn_1/batchnorm/add_1:0: 3d94cb00
block_2b_relu_1/Relu:0: 3d8718f5
block_2b_conv_2/convolution:0: 3e011b46
block_2b_bn_2/batchnorm/mul__41_output: 3c5fd4f7
block_2b_bn_2/batchnorm/mul_1:0: 3df3033a
block_2b_bn_2/batchnorm/sub__42_output: 3cd030dc
block_2b_bn_2/batchnorm/add_1:0: 3dd13074
add_4/add:0: 3e09e650
block_2b_relu/Relu:0: 3e0ef4cb
block_3a_conv_shortcut/convolution:0: 3dc8c7bd
block_3a_bn_shortcut/batchnorm/mul__52_output: 3bc9e149
block_3a_bn_shortcut/batchnorm/mul_1:0: 3d3cf50d
block_3a_bn_shortcut/batchnorm/sub__53_output: 3c5c49e0
block_3a_bn_shortcut/batchnorm/add_1:0: 3d4dd216
block_3a_conv_1/convolution:0: 3e93d29a
block_3a_bn_1/batchnorm/mul__46_output: 3b372c4d
block_3a_bn_1/batchnorm/mul_1:0: 3d8d421c
block_3a_bn_1/batchnorm/sub__47_output: 3c978479
block_3a_bn_1/batchnorm/add_1:0: 3d842efc
block_3a_relu_1/Relu:0: 3d8ceacf
block_3a_conv_2/convolution:0: 3e217f38
block_3a_bn_2/batchnorm/mul__50_output: 3be940f8
block_3a_bn_2/batchnorm/mul_1:0: 3dbdbb69
block_3a_bn_2/batchnorm/sub__51_output: 3cb023d4
block_3a_bn_2/batchnorm/add_1:0: 3db581fe
add_5/add:0: 3dd75d84
block_3a_relu/Relu:0: 3ddea79e
block_3b_conv_shortcut/convolution:0: 3e19e097
block_3b_bn_shortcut/batchnorm/mul__61_output: 3bcf5e9f
block_3b_bn_shortcut/batchnorm/mul_1:0: 3da454d0
block_3b_bn_shortcut/batchnorm/sub__62_output: 3c98f85c
block_3b_bn_shortcut/batchnorm/add_1:0: 3dad9067
block_3b_conv_1/convolution:0: 3e8da30e
block_3b_bn_1/batchnorm/mul__55_output: 3b232571
block_3b_bn_1/batchnorm/mul_1:0: 3d914223
block_3b_bn_1/batchnorm/sub__56_output: 3cd36c80
block_3b_bn_1/batchnorm/add_1:0: 3d808ddb
block_3b_relu_1/Relu:0: 3d5675a8
block_3b_conv_2/convolution:0: 3de53def
block_3b_bn_2/batchnorm/mul__59_output: 3c39af2b
block_3b_bn_2/batchnorm/mul_1:0: 3dbbc34d
block_3b_bn_2/batchnorm/sub__60_output: 3cbc6410
block_3b_bn_2/batchnorm/add_1:0: 3da23173
add_6/add:0: 3df37510
block_3b_relu/Relu:0: 3de2cddd
block_4a_conv_shortcut/convolution:0: 3d89462c
block_4a_bn_shortcut/batchnorm/mul__70_output: 3c0d611e
block_4a_bn_shortcut/batchnorm/mul_1:0: 3d668aa7
block_4a_bn_shortcut/batchnorm/sub__71_output: 3c4ed7d8
block_4a_bn_shortcut/batchnorm/add_1:0: 3d5c6f62
block_4a_conv_1/convolution:0: 3e40745d
block_4a_bn_1/batchnorm/mul__64_output: 3b37f2b3
block_4a_bn_1/batchnorm/mul_1:0: 3d5c139a
block_4a_bn_1/batchnorm/sub__65_output: 3c6e55b3
block_4a_bn_1/batchnorm/add_1:0: 3d5bbd27
block_4a_relu_1/Relu:0: 3d4477a8
block_4a_conv_2/convolution:0: 3d7a43ae
block_4a_bn_2/batchnorm/mul__68_output: 3c4fa08a
block_4a_bn_2/batchnorm/mul_1:0: 3d983e2e
block_4a_bn_2/batchnorm/sub__69_output: 3c689005
block_4a_bn_2/batchnorm/add_1:0: 3d888944
add_7/add:0: 3db32129
block_4a_relu/Relu:0: 3d9ac9cf
block_4b_conv_shortcut/convolution:0: 3d3061f8
block_4b_bn_shortcut/batchnorm/mul__79_output: 3d0c4910
block_4b_bn_shortcut/batchnorm/mul_1:0: 3e0db980
block_4b_bn_shortcut/batchnorm/sub__80_output: 3c41ab03
block_4b_bn_shortcut/batchnorm/add_1:0: 3dfac1bc
block_4b_conv_1/convolution:0: 3dfc1387
block_4b_bn_1/batchnorm/mul__73_output: 3be0b5cb
block_4b_bn_1/batchnorm/mul_1:0: 3d919cd2
block_4b_bn_1/batchnorm/sub__74_output: 3cce84b6
block_4b_bn_1/batchnorm/add_1:0: 3d85b56d
block_4b_relu_1/Relu:0: 3d73296f
block_4b_conv_2/convolution:0: 3dba0111
block_4b_bn_2/batchnorm/mul__77_output: 3d147ef3
block_4b_bn_2/batchnorm/mul_1:0: 3e668945
block_4b_bn_2/batchnorm/sub__78_output: 3c5dce3e
block_4b_bn_2/batchnorm/add_1:0: 3e73ea14
add_8/add:0: 3e8bcec3
block_4b_relu/Relu:0: 3e90d884
avg_pool/AvgPool:0: 3e90d884
flatten/Reshape:0: 3cf618fd
predictions/kernel/read__81_output: 3c23d576
predictions/MatMul:0: 3ea93495
predictions/bias/read__82_output: 3c021685
ONNXTRT_Broadcast_output: 3c021685
predictions/BiasAdd:0: 3e9dfa2f
ONNXTRT_flattenTensor_output: 3e9dfa2f
predictions/Softmax:0: 3c010a14
predictions/Softmax_output: 3c010a14
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
services:
sentinel:
image: ai.ronsunny.cn:13011/bbit_iot/ce_sentinel:latest
container_name: sentinel_app
runtime: nvidia
privileged: true
environment:
- DEVICE_ID=5dbfb12414a3456d9014d88183e338b1
- LD_LIBRARY_PATH=/opt/nvidia/deepstream/deepstream/lib:/opt/nvidia/deepstream/deepstream/lib/triton:/opt/nvidia/deepstream/deepstream/lib/rivermax:/opt/nvidia/vpi3/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu/nvidia:$LD_LIBRARY_PATH
- GST_PLUGIN_PATH=/opt/nvidia/deepstream/deepstream/lib/gst-plugins
volumes:
- /usr/lib/aarch64-linux-gnu:/usr/lib/aarch64-linux-gnu:ro
- /opt/nvidia/deepstream/deepstream/lib:/opt/nvidia/deepstream/deepstream/lib:ro
- /opt/nvidia/vpi3/lib/aarch64-linux-gnu/:/opt/nvidia/vpi3/lib/aarch64-linux-gnu/:ro
- /usr/local/cuda-12.6/lib64/:/usr/local/cuda-12.6/lib64/:ro
- /tmp/argus_socket:/tmp/argus_socket
# - /usr/local/cuda/lib64:/usr/local/cuda/lib64:ro
# - /usr/lib/aarch64-linux-gnu/nvidia:/usr/lib/aarch64-linux-gnu/nvidia:ro
network_mode: host
# command: ["/bin/bash"]
# stdin_open: true # 保持 STDIN 打开
# tty: true # 分配伪终端
+802
View File
@@ -0,0 +1,802 @@
#include <cuda_runtime_api.h>
#include <glib.h>
#include <gmodule.h>
#include <gst/gst.h>
#include <locale.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ds_yml_parse.h"
#include "nvds_yml_parser.h"
#include "nvdsmeta.h"
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "gstnvdsmeta.h"
#include "nvds_analytics_meta.h"
#include "gst-nvmessage.h"
#include <map>
#include <condition_variable>
#include <list>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_map>
#include "vehicle_event_service.h"
#include <yaml-cpp/yaml.h>
#include <chrono>
#include <filesystem>
#include "nvbufsurface.h"
#include "nvds_obj_encode.h"
// 文本显示最大长度
#define MAX_DISPLAY_LEN 256
// 主检测器中车辆的类别ID
#define PGIE_CLASS_ID_VEHICLE 0
// nvstreammux 输出分辨率
#define MUXER_OUTPUT_WIDTH 1920
#define MUXER_OUTPUT_HEIGHT 1080
// 多路视频 batch 形成的超时时间(微秒)
#define MUXER_BATCH_TIMEOUT_USEC 4000000
// 错误处理宏
#define RETURN_ON_PARSER_ERROR(parse_expr) \
if (NVDS_YAML_PARSER_SUCCESS != parse_expr) { \
g_printerr("Error in parsing configuration file.\n"); \
return -1; \
}
/**
* 唯一组件ID定义
*/
#define SECONDARY_TRAFFIC_UID 1
#define SECONDARY_VEHICLE_TYPE_UID 2
/* NVIDIA Decoder source pad memory feature. This feature signifies that source
* pads having this capability will push GstBuffers containing cuda buffers. */
#define GST_CAPS_FEATURES_NVMM "memory:NVMM"
gchar *yaml_file_path = nullptr;
namespace fs = std::filesystem;
void cleanTempFolder() {
while (true) {
std::this_thread::sleep_for(std::chrono::hours(3)); // 等待3h后清理一次
try {
for (auto &entry : fs::directory_iterator("./temp")) {
fs::remove_all(entry.path());
}
std::cout << "Temp folder cleaned.\n";
} catch (const fs::filesystem_error &e) {
std::cerr << "Error cleaning temp folder: " << e.what() << "\n";
}
}
}
void setYamlFilePath(const gchar *name) {
if (yaml_file_path)
free(yaml_file_path); // 释放旧路径
yaml_file_path = (gchar *)malloc(strlen(name) + 1);
strcpy(yaml_file_path, name);
}
struct SourceInfo {
std::string main_uri;
std::string side_rtsp_uri;
};
bool parse_source_list(const std::string &cfg_file,
std::vector<SourceInfo> &out_list) {
try {
YAML::Node root = YAML::LoadFile(cfg_file);
if (!root["source-list"]) {
std::cerr << "[ERR] source-list not found in YAML\n";
return false;
}
YAML::Node sources = root["source-list"];
if (!sources.IsSequence()) {
std::cerr << "[ERR] source-list should be a sequence\n";
return false;
}
out_list.clear();
for (auto node : sources) {
if (!node["main"] || !node["side"]) {
std::cerr << "[WARN] source missing main or side, skipping\n";
continue;
}
SourceInfo info;
info.main_uri = node["main"].as<std::string>();
info.side_rtsp_uri = node["side"].as<std::string>();
out_list.push_back(info);
}
} catch (const YAML::Exception &e) {
std::cerr << "[ERR] YAML parse error: " << e.what() << "\n";
return false;
}
return true;
}
/**
* 核心算法逻辑
*/
static GstPadProbeReturn
osd_sink_pad_buffer_probe(GstPad *pad, GstPadProbeInfo *info, gpointer ctx) {
GstBuffer *buf = (GstBuffer *)info->data;
NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(buf);
NvDsObjEncCtxHandle enc_ctx = reinterpret_cast<NvDsObjEncCtxHandle>(ctx);
//=======================取帧流程========================
GstMapInfo inmap = GST_MAP_INFO_INIT;
if (!gst_buffer_map(buf, &inmap, GST_MAP_READ)) {
GST_ERROR("input buffer mapinfo failed");
return GST_PAD_PROBE_DROP;
}
NvBufSurface *ip_surf = (NvBufSurface *)inmap.data;
gst_buffer_unmap(buf, &inmap);
//=======================取帧流程========================
for (NvDsMetaList *l_frame = batch_meta->frame_meta_list; l_frame;
l_frame = l_frame->next) {
NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)(l_frame->data);
guint source_id = frame_meta->source_id;
if (!frame_meta)
continue;
// 遍历对象,只处理车辆
for (NvDsMetaList *l_obj = frame_meta->obj_meta_list; l_obj;
l_obj = l_obj->next) {
NvDsObjectMeta *obj_meta = (NvDsObjectMeta *)(l_obj->data);
// 只处理车辆
if (!obj_meta ||
(obj_meta->unique_component_id == SECONDARY_TRAFFIC_UID &&
obj_meta->class_id != PGIE_CLASS_ID_VEHICLE))
continue;
guint64 object_id = obj_meta->object_id;
Record vehicle;
vehicle.object_id = object_id;
vehicle.source_id = source_id;
// 二级分类模型
for (NvDsMetaList *l_class = obj_meta->classifier_meta_list; l_class;
l_class = l_class->next) {
NvDsClassifierMeta *class_meta = (NvDsClassifierMeta *)(l_class->data);
if (!class_meta)
continue;
// 车型分类
if (class_meta->unique_component_id == SECONDARY_VEHICLE_TYPE_UID) {
for (NvDsMetaList *l_label = class_meta->label_info_list; l_label;
l_label = l_label->next) {
NvDsLabelInfo *label_info = (NvDsLabelInfo *)(l_label->data);
if (!label_info || label_info->label_id != 0)
continue;
if (label_info->result_prob > 0.0f)
vehicle.vehicle_type = label_info->result_label;
}
}
update_object_base_info(source_id, vehicle.object_id,
vehicle.vehicle_type);
}
}
// 跨线检测,只触发车辆
for (NvDsMetaList *l_user = frame_meta->frame_user_meta_list; l_user;
l_user = l_user->next) {
NvDsUserMeta *user_meta = (NvDsUserMeta *)(l_user->data);
if (!user_meta ||
user_meta->base_meta.meta_type != NVDS_USER_FRAME_META_NVDSANALYTICS)
continue;
NvDsAnalyticsFrameMeta *meta =
(NvDsAnalyticsFrameMeta *)user_meta->user_meta_data;
// 跨线事件
for (const auto &status : meta->objLCCurrCnt) {
std::string line_id = status.first;
int cross_count = status.second;
if (cross_count <= 0)
continue;
// 回到遍历帧对象,寻找对应的 object_id
for (NvDsMetaList *l_obj = frame_meta->obj_meta_list; l_obj;
l_obj = l_obj->next) {
NvDsObjectMeta *obj_meta = (NvDsObjectMeta *)(l_obj->data);
if (!obj_meta || obj_meta->class_id != PGIE_CLASS_ID_VEHICLE)
continue;
// 遍历对象中
for (NvDsMetaList *l_user_meta = obj_meta->obj_user_meta_list;
l_user_meta != NULL; l_user_meta = l_user_meta->next) {
NvDsUserMeta *user_meta = (NvDsUserMeta *)(l_user_meta->data);
if (user_meta->base_meta.meta_type ==
NVDS_USER_OBJ_META_NVDSANALYTICS) {
if (line_id == "LineA") {
trigger_line_a_capture(source_id, obj_meta->object_id);
} else if (line_id == "LineB") {
// =================截图流程========================start
NvDsObjEncUsrArgs objData = {0};
objData.saveImg = true;
objData.attachUsrMeta = true;
objData.scaleImg = FALSE;
objData.scaledWidth = 0;
objData.scaledHeight = 0;
objData.quality = 100;
char fileNameImgNoPath[FILE_NAME_SIZE];
snprintf(objData.fileNameImg, FILE_NAME_SIZE,
"./temp/%d_%d_%ld_%dx%d.jpg", frame_meta->frame_num,
source_id, obj_meta->object_id,
(int)obj_meta->rect_params.width,
(int)obj_meta->rect_params.height);
nvds_obj_enc_process(enc_ctx, &objData, ip_surf, obj_meta,
frame_meta);
nvds_obj_enc_finish(enc_ctx);
// 去掉 "./temp/" 前缀
snprintf(fileNameImgNoPath, FILE_NAME_SIZE, "%s",
objData.fileNameImg + 7); // "./temp/" 长度为7
// 传去掉前缀的文件名
trigger_line_b_upload(source_id, obj_meta->object_id,
fileNameImgNoPath);
}
}
}
}
}
}
}
return GST_PAD_PROBE_OK;
}
static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
GMainLoop *loop = (GMainLoop *)data;
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_EOS:
g_print("End of stream\n");
g_main_loop_quit(loop);
break;
case GST_MESSAGE_WARNING: {
gchar *debug = NULL;
GError *error = NULL;
gst_message_parse_warning(msg, &error, &debug);
g_printerr("WARNING from element %s: %s\n", GST_OBJECT_NAME(msg->src),
error->message);
g_free(debug);
g_printerr("Warning: %s\n", error->message);
g_error_free(error);
break;
}
case GST_MESSAGE_ERROR: {
gchar *debug = NULL;
GError *error = NULL;
gst_message_parse_error(msg, &error, &debug);
g_printerr("ERROR from element %s: %s\n", GST_OBJECT_NAME(msg->src),
error->message);
if (debug)
g_printerr("Error details: %s\n", debug);
g_free(debug);
g_error_free(error);
g_main_loop_quit(loop);
break;
}
case GST_MESSAGE_ELEMENT: {
if (gst_nvmessage_is_stream_eos(msg)) {
guint stream_id = 0;
if (gst_nvmessage_parse_stream_eos(msg, &stream_id)) {
g_print("Got EOS from stream %d\n", stream_id);
}
}
break;
}
default:
break;
}
return TRUE;
}
static void cb_newpad(GstElement *decodebin, GstPad *decoder_src_pad,
gpointer data) {
GstCaps *caps = gst_pad_get_current_caps(decoder_src_pad);
if (!caps)
caps = gst_pad_query_caps(decoder_src_pad, NULL);
const GstStructure *str = gst_caps_get_structure(caps, 0);
const gchar *name = gst_structure_get_name(str);
GstCapsFeatures *features = gst_caps_get_features(caps, 0);
GstElement *source_bin = (GstElement *)data;
/* 只接 video */
if (!strncmp(name, "video", 5)) {
/* 必须是 NVMMGPU 内存) */
if (gst_caps_features_contains(features, GST_CAPS_FEATURES_NVMM)) {
GstPad *ghost_pad = gst_element_get_static_pad(source_bin, "src");
if (!gst_ghost_pad_set_target(GST_GHOST_PAD(ghost_pad),
decoder_src_pad)) {
g_printerr("Failed to link decoder src pad to source bin ghost pad\n");
}
gst_object_unref(ghost_pad);
}
}
gst_caps_unref(caps);
}
static inline const char *infer_plugin(NvDsGieType type) {
switch (type) {
case NVDS_GIE_PLUGIN_INFER:
return "nvinfer";
case NVDS_GIE_PLUGIN_INFER_SERVER:
return "nvinferserver";
default:
return "unknown";
}
}
static void decodebin_child_added(GstChildProxy *child_proxy, GObject *object,
gchar *name, gpointer user_data) {
g_print("Decodebin child added: %s\n", name);
if (g_strrstr(name, "decodebin") == name) {
g_signal_connect(G_OBJECT(object), "child-added",
G_CALLBACK(decodebin_child_added), user_data);
}
if (g_strrstr(name, "source") == name) {
g_object_set(G_OBJECT(object), "drop-on-latency", true, NULL);
}
}
static GstElement *create_source_bin(guint index, gchar *uri) {
GstElement *bin = NULL, *uri_decode_bin = NULL;
gchar name[64];
g_snprintf(name, sizeof(name), "source-bin-%02d", index);
bin = gst_bin_new(name);
/* 7×24 强烈建议用 nvurisrcbin */
uri_decode_bin = gst_element_factory_make("nvurisrcbin", NULL);
if (!bin || !uri_decode_bin)
return NULL;
g_object_set(G_OBJECT(uri_decode_bin), "uri", uri, NULL); // 地址
// "reconnect-interval", 5, // RTSP
// 断流重连 "drop-on-latency", TRUE, //
// 保实时 NULL);
g_signal_connect(uri_decode_bin, "pad-added", G_CALLBACK(cb_newpad), bin);
g_signal_connect(G_OBJECT(uri_decode_bin), "child-added",
G_CALLBACK(decodebin_child_added), bin);
gst_bin_add(GST_BIN(bin), uri_decode_bin);
/* 对外只暴露一个 src */
if (!gst_element_add_pad(bin,
gst_ghost_pad_new_no_target("src", GST_PAD_SRC))) {
g_printerr("Failed to add ghost pad in source bin\n");
return NULL;
}
return bin;
}
int main(int argc, char *argv[]) {
setYamlFilePath(argv[1]);
GMainLoop *loop = NULL;
GstElement *pipeline = NULL, *streammux = NULL, *sink = NULL,
*nvvidconv = NULL, *nvosd = NULL, *nvvidconv1 = NULL,
*outenc = NULL, *capfilt = NULL, *mux = NULL, *nvtile = NULL,
*encparse = NULL, *rtppay = NULL;
GstElement *primary_detector = NULL, *secondary_vt = NULL, *tracker = NULL,
*nvdsanalytics = NULL;
GstElement *queue1 = NULL, *queue2 = NULL, *queue3 = NULL, *queue4 = NULL,
*queue5 = NULL, *queue6 = NULL, *queue7 = NULL, *queue8 = NULL,
*queue9 = NULL, *queue10 = NULL, *queue11 = NULL;
GstBus *bus = NULL;
// 用来挂 probe 的 pad,临时 pad 指针,用来 link
GstPad *osd_sink_pad = NULL;
// 消息总线回调ID、多路视频拼图行数、列数
guint bus_watch_id;
static guint udp_sink_port_num = 5400;
int enc_type = ENCODER_TYPE_HW;
std::vector<SourceInfo> sources;
// 推理插件类型
NvDsGieType pgie_type = NVDS_GIE_PLUGIN_INFER;
NvDsGieType sgie1_type = NVDS_GIE_PLUGIN_INFER;
if (argc != 2) {
g_printerr("Usage: %s <yml file>\n", argv[0]);
return -1;
}
// CUDA设备查询
int current_device = -1;
cudaGetDevice(&current_device);
// 获取CUDA设备属性 例如是否为集成GPU
struct cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, current_device);
// 初始化 GStreamer
gst_init(&argc, &argv);
loop = g_main_loop_new(NULL, FALSE);
// 创建gstreamer管道
pipeline = gst_pipeline_new("pipeline");
if (!pipeline) {
g_printerr("Pipeline could not be created. Exiting.\n");
return -1;
}
streammux = gst_element_factory_make("nvstreammux", "stream-muxer");
if (!streammux) {
g_printerr("Streammux could not be created. Exiting.\n");
return -1;
}
// 管道加入nv-stream-mux
gst_bin_add(GST_BIN(pipeline), streammux);
// 解析YAML
if (!parse_source_list(yaml_file_path, sources)) {
return -1;
}
RETURN_ON_PARSER_ERROR(
nvds_parse_gie_type(&pgie_type, yaml_file_path, "primary-gie"));
RETURN_ON_PARSER_ERROR(
nvds_parse_gie_type(&sgie1_type, yaml_file_path, "secondary-gie1"));
for (size_t src_cnt = 0; src_cnt < sources.size(); ++src_cnt) {
const auto &src_info = sources[src_cnt]; // 当前主摄像头和侧摄像头地址
g_source_side_map[src_cnt] = src_info.side_rtsp_uri;
GstElement *source_bin =
create_source_bin(src_cnt, (gchar *)src_info.main_uri.c_str());
if (!source_bin) {
g_printerr("Failed to create source bin. Exiting.\n");
return -1;
}
gst_bin_add(GST_BIN(pipeline), source_bin);
gchar pad_name[16];
g_snprintf(pad_name, sizeof(pad_name), "sink_%zu", src_cnt);
GstPad *sinkpad = gst_element_request_pad_simple(streammux, pad_name);
if (!sinkpad)
return -1;
GstPad *srcpad = gst_element_get_static_pad(source_bin, "src");
if (!srcpad)
return -1;
if (gst_pad_link(srcpad, sinkpad) != GST_PAD_LINK_OK)
return -1;
gst_object_unref(srcpad);
gst_object_unref(sinkpad);
}
// 创建推理器元素
primary_detector = gst_element_factory_make(infer_plugin(pgie_type),
"primary-infer-engine1");
if (!primary_detector) {
g_printerr("Primary detector could not be created. Exiting.\n");
return -1;
}
secondary_vt = gst_element_factory_make(infer_plugin(sgie1_type),
"secondary-infer-engine1");
if (!secondary_vt) {
g_printerr("Secondary VT could not be created. Exiting.\n");
return -1;
}
// 创建队列元素
queue1 = gst_element_factory_make("queue", "queue1");
queue2 = gst_element_factory_make("queue", "queue2");
queue3 = gst_element_factory_make("queue", "queue3");
queue4 = gst_element_factory_make("queue", "queue4");
queue5 = gst_element_factory_make("queue", "queue5");
queue6 = gst_element_factory_make("queue", "queue6");
queue7 = gst_element_factory_make("queue", "queue7");
queue8 = gst_element_factory_make("queue", "queue8");
queue9 = gst_element_factory_make("queue", "queue9");
queue10 = gst_element_factory_make("queue", "queue10");
queue11 = gst_element_factory_make("queue", "queue11");
// 添加队列和其他元素到管道
gst_bin_add_many(GST_BIN(pipeline), queue1, queue2, queue3, queue4, queue5,
queue6, queue7, queue8, queue9, queue10, queue11, NULL);
// 配置
// ===============================================================================start
// streammux 配置
g_object_set(G_OBJECT(streammux), "width", MUXER_OUTPUT_WIDTH, "height",
MUXER_OUTPUT_HEIGHT, "batch-size", sources.size(),
"batched-push-timeout", MUXER_BATCH_TIMEOUT_USEC, NULL);
// 主推理器:检测车辆/行人/交通标志/自行车
nvds_parse_gie(primary_detector, yaml_file_path, "primary-gie");
// 第二推理器:车辆类型分类:轿车/SUV/卡车等
nvds_parse_gie(secondary_vt, yaml_file_path, "secondary-gie1");
// 多目标跟踪
tracker = gst_element_factory_make("nvtracker", "nvtracker");
// 多目标跟踪
nvds_parse_tracker(tracker, yaml_file_path, "tracker");
// 跨线检测等高级分析
nvdsanalytics = gst_element_factory_make("nvdsanalytics", "nvdsanalytics");
ds_parse_nvdsanalytics(nvdsanalytics, yaml_file_path, "analytics");
// 配置===============================================================================end
// 添加各个通用元素
gst_bin_add_many(GST_BIN(pipeline), primary_detector, secondary_vt, tracker,
nvdsanalytics, NULL);
// 链接各个通用元素
if (!gst_element_link_many(streammux, queue1, primary_detector, queue2,
tracker, queue3, nvdsanalytics, queue4,
secondary_vt, queue5, NULL)) {
g_printerr("Inferring and tracking elements link failure.\n");
return -1;
}
// 输出方式 默认2:fakesink
guint output_type = 2;
output_type = ds_parse_group_type(yaml_file_path, "output");
if (output_type == 2) {
// 假输出,生产环境用
if (!gst_element_link_many(queue5, queue11, NULL)) {
g_printerr("Inferring and tracking elements link failure.\n");
return -1;
}
sink = gst_element_factory_make("fakesink", "fake-renderer");
g_object_set(G_OBJECT(sink), "sync", FALSE, "async", FALSE, NULL);
} else {
// 输出显示器、文件、RTSP
// 创建视频转换器,将 NV12 转 RGBA,以供 OSD 使用
nvvidconv = gst_element_factory_make("nvvideoconvert", "nvvid-converter");
if (!nvvidconv) {
g_printerr("NVVideoConvert could not be created. Exiting.\n");
return -1;
}
// 创建 OSD,用于在 RGBA 上绘制文字和框
nvosd = gst_element_factory_make("nvdsosd", "nv-onscreendisplay");
if (!nvosd) {
g_printerr("NvOSD could not be created. Exiting.\n");
return -1;
}
// 拼图
nvtile = gst_element_factory_make("nvmultistreamtiler", "nvtiler");
if (!nvtile) {
g_printerr("nvmultistreamtiler could not be created. Exiting.\n");
return -1;
}
// 计算行列和每路尺寸
guint columns = (guint)ceil(sqrt(sources.size()));
guint rows = (guint)ceil((float)sources.size() / columns);
guint src_width = 1280, src_height = 720;
guint single_width = src_width / columns;
guint single_height = single_width * 9 / 16;
if (single_height * rows > src_height) {
// 如果高度超出画布,按高度缩放
single_height = src_height / rows;
single_width = single_height * 16 / 9;
}
// 总画布尺寸
guint total_width = single_width * columns;
guint total_height = single_height * rows;
// 设置 tiler
g_object_set(G_OBJECT(nvtile), "rows", rows, "columns", columns, "width",
total_width, "height", total_height, NULL);
// 添加元素
gst_bin_add_many(GST_BIN(pipeline), nvvidconv, nvosd, nvtile, NULL);
if (!gst_element_link_many(queue5, nvtile, queue6, nvvidconv, queue7, nvosd,
NULL)) {
g_printerr("Inferring and tracking elements link failure.\n");
return -1;
}
if (output_type == 3) {
// 输出显示器
if (!gst_element_link_many(nvosd, queue11, NULL)) {
g_printerr("Inferring and tracking elements link failure.\n");
return -1;
}
if (prop.integrated)
sink = gst_element_factory_make("nv3dsink", "nv3d-sink");
else
#ifdef __aarch64__
sink = gst_element_factory_make("nv3dsink", "nv3d-sink");
#else
sink = gst_element_factory_make("nveglglessink", "nvvideo-renderer");
#endif
} else {
// 还有1 4
// 第二个视频转换器,OSD 后 → 编码前
nvvidconv1 =
gst_element_factory_make("nvvideoconvert", "nvvid-converter1");
// 限制视频格式
capfilt = gst_element_factory_make("capsfilter", "nvvideo-caps");
// 编码类型
bool isH264 = !(ds_parse_enc_codec(yaml_file_path, "output"));
// 编码方式
enc_type = ds_parse_enc_type(yaml_file_path, "output");
if (output_type == 1) {
// 输出文件
// 创建编码器链
create_video_encoder(isH264, enc_type, &capfilt, &outenc, &encparse,
NULL);
if (!capfilt || !outenc || !encparse) {
g_printerr("enc element could not be created. Exiting.\n");
return -1;
}
// 文件输出,需要编码
gchar *filepath = NULL;
// 封装MP4
mux = gst_element_factory_make("qtmux", "mp4-mux");
// 添加元素到管道
gst_bin_add(GST_BIN(pipeline), mux);
// 设置输出文件路径
GString *output_file = ds_parse_file_name(yaml_file_path, "output");
filepath = g_strconcat(output_file->str, ".mp4", NULL);
g_string_free(output_file, TRUE); // ✅ 释放 GString
ds_parse_enc_config(outenc, yaml_file_path, "output");
// 链接/添加元素到管道
gst_bin_add_many(GST_BIN(pipeline), nvvidconv1, capfilt, outenc,
encparse, NULL);
if (!gst_element_link_many(nvosd, queue8, nvvidconv1, queue9, capfilt,
queue10, outenc, encparse, mux, queue11,
NULL)) {
g_printerr("OSD and sink elements link failure.\n");
return -1;
}
sink = gst_element_factory_make("filesink", "nvvideo-renderer");
// 设置 sink 属性
g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
g_object_set(G_OBJECT(sink), "sync", TRUE, NULL);
g_object_set(G_OBJECT(sink), "location", filepath, NULL);
} else if (output_type == 4) {
// UDP输出
// 创建编码器链
create_video_encoder(isH264, enc_type, &capfilt, &outenc, &encparse,
&rtppay);
g_object_set(G_OBJECT(outenc), "preset-level", 3,
NULL); // 0:平衡 1:速度 2:质量 3:无损
g_object_set(G_OBJECT(outenc), "insert-sps-pps", 1,
NULL); // 每个 I 帧插 SPS/PPS
g_object_set(G_OBJECT(outenc), "bitrate", 4000000,
NULL); // 4 Mbps
g_object_set(G_OBJECT(outenc), "iframeinterval", 30,
NULL); // I 帧间隔
if (!capfilt || !outenc || !encparse || !rtppay) {
g_printerr("enc element could not be created. Exiting.\n");
return -1;
}
// 添加元素到管道
gst_bin_add_many(GST_BIN(pipeline), nvvidconv1, capfilt, outenc,
encparse, rtppay, NULL);
// 链接元素
if (!gst_element_link_many(nvosd, queue8, nvvidconv1, capfilt, queue9,
outenc, encparse, queue10, rtppay, queue11,
NULL)) {
g_printerr("OSD and sink elements link failure.\n");
return -1;
}
// 输出UDPRTSP Server读取)
sink = gst_element_factory_make("udpsink", "udp-sink");
// 设置 sink 属性
g_object_set(G_OBJECT(sink), "host", "224.224.255.255", "port",
udp_sink_port_num, "async", FALSE, "buffer-size", 2097152,
"sync", 1, "qos", 0, NULL);
}
}
} // end
// 添加sink元素到管道
if (!sink) {
g_printerr("sink element could not be created. Exiting.\n");
return -1;
}
gst_bin_add_many(GST_BIN(pipeline), sink, NULL);
if (!gst_element_link_many(queue11, sink, NULL)) {
g_printerr("OSD and sink elements link failure.\n");
return -1;
}
// 获取 pipeline 的消息总线
bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
// 注册回调
bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
// 释放引用
gst_object_unref(bus);
// 拿 OSD 的 sink pad
osd_sink_pad = gst_element_get_static_pad(queue5, "sink");
NvDsObjEncCtxHandle obj_ctx_handle =
nvds_obj_enc_create_context(/*GPU id*/ 0);
if (!obj_ctx_handle) {
g_print("Unable to create context\n");
return -1;
}
if (!osd_sink_pad) {
g_print("Unable to get sink pad\n");
} else {
//挂 probe 所有 metadata 在这里被读取
gst_pad_add_probe(osd_sink_pad, GST_PAD_PROBE_TYPE_BUFFER,
osd_sink_pad_buffer_probe, (gpointer)obj_ctx_handle,
NULL);
}
// 释放之前获得的对象引用 不释放会内存泄漏
gst_object_unref(osd_sink_pad);
// 开启RTSP服务
if (output_type == 4) {
// ================= RTSP Server =================
guint rtsp_port_num = 8554;
guint64 udp_buffer_size = 2 * 1024 * 1024; // 2 MB
GstRTSPMountPoints *mounts;
GstRTSPMediaFactory *factory;
char udpsrc_pipeline[512];
char port_num_Str[64] = {0};
const char *codec =
ds_parse_enc_codec(yaml_file_path, "output") ? "H265" : "H264";
sprintf(udpsrc_pipeline,
"( udpsrc name=pay0 port=%d buffer-size=%lu "
"caps=\"application/x-rtp, media=video, "
"clock-rate=90000, encoding-name=%s, payload=96 \" )",
udp_sink_port_num, udp_buffer_size, codec);
sprintf(port_num_Str, "%d", rtsp_port_num);
GstRTSPServer *server = gst_rtsp_server_new();
g_object_set(server, "service", port_num_Str, NULL);
mounts = gst_rtsp_server_get_mount_points(server);
factory = gst_rtsp_media_factory_new();
gst_rtsp_media_factory_set_shared(factory, TRUE);
gst_rtsp_media_factory_set_launch(factory, udpsrc_pipeline);
gst_rtsp_mount_points_add_factory(mounts, "/ds-test", factory);
g_object_unref(mounts);
gst_rtsp_server_attach(server, NULL);
g_print("\n *** DeepStream: Launched RTSP Streaming at "
"rtsp://localhost:%d/ds-test ***\n\n",
rtsp_port_num);
// ================= RTSP Server End =================
}
// 初始化 RTSP 管理器
rtsp_mgr_side.configure(g_source_side_map);
// 启动上传线程
WorkerManager manager;
manager.start();
// 启动清理线程
std::thread cleaner(cleanTempFolder);
cleaner.detach(); // 分离线程,程序退出时不阻塞
// 启动管道
g_print("Now playing: %s\n", yaml_file_path);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
// 主循环
g_print("Running...\n");
g_main_loop_run(loop);
/* Destroy context for Object Encoding */
nvds_obj_enc_destroy_context(obj_ctx_handle);
// 退出,清理
g_print("Returned, stopping playback\n");
gst_element_set_state(pipeline, GST_STATE_NULL);
g_print("Deleting pipeline\n");
// 释放管道和消息总线回调
gst_object_unref(GST_OBJECT(pipeline));
g_source_remove(bus_watch_id);
g_main_loop_unref(loop);
// 停止摄像头取流服务
rtsp_mgr_side.shutdown();
return 0;
}
+428
View File
@@ -0,0 +1,428 @@
#include "rtsp_grabber.h"
#include <chrono>
#include <filesystem>
#include <iostream>
#include <sstream>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
}
#define LOG_ERR(msg) std::cerr << "[ERR] " << msg << std::endl
#define LOG_INF(msg) std::cerr << "[INF] " << msg << std::endl
struct RtspFrameGrabberImpl {
AVFormatContext *fmt_ctx = nullptr;
AVCodecContext *dec_ctx = nullptr;
int video_stream_index = -1;
AVFrame *decoded_frame = nullptr;
AVFrame *sws_frame = nullptr;
SwsContext *sws_ctx = nullptr;
AVCodecContext *jpeg_enc_ctx = nullptr;
AVPacket *enc_pkt = nullptr;
AVFrame *latest_frame_copy = nullptr;
int width = 0;
int height = 0;
AVPixelFormat src_pix_fmt = AV_PIX_FMT_NONE;
std::atomic<bool> stop_flag{false};
std::atomic<bool> started{false};
std::atomic<bool> ready{false};
std::thread th;
int reconnect_delay_ms = 1000;
};
static void free_impl(RtspFrameGrabberImpl *impl) {
if (!impl)
return;
if (impl->th.joinable()) {
impl->stop_flag = true;
impl->th.join();
}
if (impl->enc_pkt)
av_packet_free(&impl->enc_pkt);
if (impl->jpeg_enc_ctx)
avcodec_free_context(&impl->jpeg_enc_ctx);
if (impl->latest_frame_copy)
av_frame_free(&impl->latest_frame_copy);
if (impl->sws_frame)
av_frame_free(&impl->sws_frame);
if (impl->decoded_frame)
av_frame_free(&impl->decoded_frame);
if (impl->sws_ctx)
sws_freeContext(impl->sws_ctx);
if (impl->dec_ctx)
avcodec_free_context(&impl->dec_ctx);
if (impl->fmt_ctx)
avformat_close_input(&impl->fmt_ctx);
delete impl;
}
static AVFrame *avframe_deep_copy(const AVFrame *src) {
if (!src)
return nullptr;
AVFrame *dst = av_frame_alloc();
dst->format = src->format;
dst->width = src->width;
dst->height = src->height;
av_frame_get_buffer(dst, 0);
av_frame_copy(dst, src);
av_frame_copy_props(dst, src);
return dst;
}
static bool init_jpeg_encoder(RtspFrameGrabberImpl *impl, int width, int height,
AVPixelFormat pix_fmt) {
if (!impl || impl->jpeg_enc_ctx)
return true;
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
if (!codec)
return false;
AVCodecContext *c = avcodec_alloc_context3(codec);
c->pix_fmt = pix_fmt;
c->width = width;
c->height = height;
c->time_base = AVRational{1, 25};
// ⭐ 最高质量
c->qmin = 1;
c->qmax = 2;
c->compression_level = 0;
if (avcodec_open2(c, codec, nullptr) < 0) {
avcodec_free_context(&c);
return false;
}
impl->jpeg_enc_ctx = c;
impl->enc_pkt = av_packet_alloc();
return true;
}
static bool encode_frame_to_jpeg(RtspFrameGrabberImpl *impl, AVFrame *frame,
const std::string &filename) {
if (!impl || !impl->jpeg_enc_ctx || !frame)
return false;
AVCodecContext *enc = impl->jpeg_enc_ctx;
AVPacket *pkt = impl->enc_pkt;
av_packet_unref(pkt);
if (avcodec_send_frame(enc, frame) < 0)
return false;
if (avcodec_receive_packet(enc, pkt) < 0)
return false;
FILE *f = fopen(filename.c_str(), "wb");
if (!f)
return false;
fwrite(pkt->data, 1, pkt->size, f);
fclose(f);
av_packet_unref(pkt);
return true;
}
// -------- RtspFrameGrabber Methods --------
RtspFrameGrabber::RtspFrameGrabber(const std::string &rtsp_url)
: rtsp_url_(rtsp_url) {
avformat_network_init();
opaque_impl_ = new RtspFrameGrabberImpl();
}
RtspFrameGrabber::~RtspFrameGrabber() {
stop();
free_impl(reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_));
opaque_impl_ = nullptr;
}
bool RtspFrameGrabber::start() {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl || impl->started)
return false;
impl->stop_flag = false;
impl->th = std::thread([this, impl]() { this->decode_loop(); });
impl->started = true;
return true;
}
void RtspFrameGrabber::stop() {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl)
return;
impl->stop_flag = true;
if (impl->th.joinable())
impl->th.join();
impl->started = false;
impl->ready = false;
}
bool RtspFrameGrabber::is_ready() const {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
return impl && impl->ready;
}
bool RtspFrameGrabber::capture_jpeg(const std::string &filename) {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl)
return false;
std::lock_guard<std::mutex> lk(frame_mutex_);
if (!impl->latest_frame_copy)
return false;
int w = impl->latest_frame_copy->width;
int h = impl->latest_frame_copy->height;
AVPixelFormat pf = (AVPixelFormat)impl->latest_frame_copy->format;
if (!init_jpeg_encoder(impl, w, h, pf))
return false;
return encode_frame_to_jpeg(impl, impl->latest_frame_copy, filename);
}
void RtspFrameGrabber::decode_loop() {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl)
return;
AVDictionary *opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
av_dict_set(&opts, "stimeout", "2000000", 0);
av_dict_set(&opts, "fflags", "nobuffer", 0);
av_dict_set(&opts, "flags", "low_delay", 0);
av_dict_set(&opts, "max_delay", "0", 0);
av_dict_set(&opts, "buffer_size", "102400", 0);
AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUVJ420P;
#if LIBAVUTIL_VERSION_MAJOR >= 57
dst_pix_fmt = AV_PIX_FMT_YUV420P;
#endif
while (!impl->stop_flag) {
AVFormatContext *fmt_ctx = nullptr;
int ret = avformat_open_input(&fmt_ctx, rtsp_url_.c_str(), nullptr, &opts);
if (ret != 0) {
std::this_thread::sleep_for(
std::chrono::milliseconds(impl->reconnect_delay_ms));
continue;
}
impl->fmt_ctx = fmt_ctx;
impl->reconnect_delay_ms = 1000;
if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
avformat_close_input(&impl->fmt_ctx);
continue;
}
int video_index = -1;
for (unsigned i = 0; i < fmt_ctx->nb_streams; ++i) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_index = i;
break;
}
}
if (video_index < 0) {
avformat_close_input(&impl->fmt_ctx);
continue;
}
impl->video_stream_index = video_index;
AVCodecParameters *codecpar = fmt_ctx->streams[video_index]->codecpar;
const AVCodec *dec = avcodec_find_decoder(codecpar->codec_id);
if (!dec) {
avformat_close_input(&impl->fmt_ctx);
continue;
}
AVCodecContext *dec_ctx = avcodec_alloc_context3(dec);
avcodec_parameters_to_context(dec_ctx, codecpar);
if (avcodec_open2(dec_ctx, dec, nullptr) < 0) {
avcodec_free_context(&dec_ctx);
avformat_close_input(&impl->fmt_ctx);
continue;
}
impl->dec_ctx = dec_ctx;
impl->decoded_frame = av_frame_alloc();
impl->width = dec_ctx->width;
impl->height = dec_ctx->height;
impl->src_pix_fmt = dec_ctx->pix_fmt;
impl->sws_frame = av_frame_alloc();
impl->sws_frame->format = dst_pix_fmt;
impl->sws_frame->width = impl->width;
impl->sws_frame->height = impl->height;
av_frame_get_buffer(impl->sws_frame, 32);
impl->sws_ctx = sws_getContext(impl->width, impl->height, impl->src_pix_fmt,
impl->width, impl->height,
(AVPixelFormat)impl->sws_frame->format,
SWS_BICUBIC, nullptr, nullptr, nullptr);
init_jpeg_encoder(impl, impl->width, impl->height,
(AVPixelFormat)impl->sws_frame->format);
impl->ready = true;
// LOG_INF("connected and decoding: " << rtsp_url_);
AVPacket *pkt = av_packet_alloc();
while (!impl->stop_flag) {
int r = av_read_frame(fmt_ctx, pkt);
if (r < 0) {
av_packet_unref(pkt);
impl->ready = false;
break;
}
if (pkt->stream_index != impl->video_stream_index) {
av_packet_unref(pkt);
continue;
}
if (avcodec_send_packet(dec_ctx, pkt) < 0) {
av_packet_unref(pkt);
continue;
}
while (!impl->stop_flag) {
r = avcodec_receive_frame(dec_ctx, impl->decoded_frame);
if (r == AVERROR(EAGAIN) || r == AVERROR_EOF)
break;
sws_scale(impl->sws_ctx, impl->decoded_frame->data,
impl->decoded_frame->linesize, 0, impl->height,
impl->sws_frame->data, impl->sws_frame->linesize);
std::lock_guard<std::mutex> lk(frame_mutex_);
if (impl->latest_frame_copy)
av_frame_free(&impl->latest_frame_copy);
impl->latest_frame_copy = avframe_deep_copy(impl->sws_frame);
}
av_packet_unref(pkt);
}
av_packet_free(&pkt);
impl->ready = false;
if (impl->sws_ctx) {
sws_freeContext(impl->sws_ctx);
impl->sws_ctx = nullptr;
}
if (impl->sws_frame) {
av_frame_free(&impl->sws_frame);
impl->sws_frame = nullptr;
}
if (impl->decoded_frame) {
av_frame_free(&impl->decoded_frame);
impl->decoded_frame = nullptr;
}
if (impl->dec_ctx) {
avcodec_free_context(&impl->dec_ctx);
impl->dec_ctx = nullptr;
}
if (impl->fmt_ctx) {
avformat_close_input(&impl->fmt_ctx);
impl->fmt_ctx = nullptr;
}
if (!impl->stop_flag)
std::this_thread::sleep_for(
std::chrono::milliseconds(impl->reconnect_delay_ms));
}
impl->ready = false;
LOG_INF("decode thread exit for: " << rtsp_url_);
}
RtspManager::~RtspManager() { shutdown(); }
void RtspManager::configure(
const std::unordered_map<int, std::string> &side_sources) {
std::lock_guard<std::mutex> lk(mgr_mutex_);
// 删除不再需要的grabber
for (auto it = side_grabbers_.begin(); it != side_grabbers_.end();) {
auto f = side_sources.find(it->first);
if (f == side_sources.end() || f->second != it->second->get_rtsp_url()) {
try {
it->second->stop();
} catch (...) {
// 忽略异常,避免影响主程序
}
it = side_grabbers_.erase(it);
} else {
++it;
}
}
// 新增grabber
for (const auto &kv : side_sources) {
int sid = kv.first;
const std::string &url = kv.second;
if (side_grabbers_.find(sid) != side_grabbers_.end())
continue;
try {
auto g = std::make_unique<RtspFrameGrabber>(url);
if (!g) {
continue;
}
// start失败直接跳过
if (!g->start()) {
continue;
}
side_grabbers_.emplace(sid, std::move(g));
} catch (...) {
// 捕获所有异常,避免configure影响系统稳定
continue;
}
}
}
void RtspManager::shutdown() {
std::lock_guard<std::mutex> lk(mgr_mutex_);
for (auto &kv : side_grabbers_) {
kv.second->stop();
}
side_grabbers_.clear();
}
std::string RtspManager::capture_side_frame(int source_id) {
if (!is_ready(source_id))
return "";
std::lock_guard<std::mutex> lk(mgr_mutex_);
auto it = side_grabbers_.find(source_id);
if (it == side_grabbers_.end())
return "";
std::filesystem::create_directories("./temp");
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::ostringstream oss;
oss << "side_" << source_id << "_" << t << ".jpg";
std::string name = oss.str();
std::string path = "./temp/" + name;
if (!it->second->capture_jpeg(path))
return "";
return name;
}
bool RtspManager::is_ready(int source_id) const {
std::lock_guard<std::mutex> lk(mgr_mutex_);
auto it = side_grabbers_.find(source_id);
return it != side_grabbers_.end() && it->second->is_ready();
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
class RtspFrameGrabber {
public:
explicit RtspFrameGrabber(const std::string &rtsp_url);
~RtspFrameGrabber();
bool start();
void stop();
bool capture_jpeg(const std::string &filename);
bool is_ready() const;
const std::string &get_rtsp_url() const { return rtsp_url_; }
private:
void decode_loop();
private:
std::string rtsp_url_;
std::thread worker_;
std::atomic<bool> running_{false};
std::atomic<bool> ready_{false};
mutable std::mutex frame_mutex_;
void *opaque_impl_ = nullptr;
};
class RtspManager {
public:
RtspManager() = default;
~RtspManager();
void configure(const std::unordered_map<int, std::string> &side_sources);
void shutdown();
std::string capture_side_frame(int source_id);
bool is_ready(int source_id) const;
private:
std::unordered_map<int, std::unique_ptr<RtspFrameGrabber>> side_grabbers_;
mutable std::mutex mgr_mutex_;
};
+375
View File
@@ -0,0 +1,375 @@
#include "rtsp_grabber.h"
#include <chrono>
#include <filesystem>
#include <iostream>
#include <sstream>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
}
#define LOG_ERR(msg) std::cerr << "[ERR] " << msg << std::endl
#define LOG_INF(msg) std::cerr << "[INF] " << msg << std::endl
struct RtspFrameGrabberImpl {
AVFormatContext *fmt_ctx = nullptr;
AVCodecContext *dec_ctx = nullptr;
int video_stream_index = -1;
AVFrame *decoded_frame = nullptr;
AVFrame *sws_frame = nullptr;
SwsContext *sws_ctx = nullptr;
AVCodecContext *jpeg_enc_ctx = nullptr;
AVPacket *enc_pkt = nullptr;
AVFrame *latest_frame_copy = nullptr;
int width = 0;
int height = 0;
AVPixelFormat src_pix_fmt = AV_PIX_FMT_NONE;
std::atomic<bool> stop_flag{false};
std::atomic<bool> started{false};
std::atomic<bool> ready{false};
std::thread th;
int reconnect_delay_ms = 1000;
};
static void free_impl(RtspFrameGrabberImpl *impl) {
if (!impl)
return;
if (impl->th.joinable()) {
impl->stop_flag = true;
impl->th.join();
}
if (impl->enc_pkt)
av_packet_free(&impl->enc_pkt);
if (impl->jpeg_enc_ctx)
avcodec_free_context(&impl->jpeg_enc_ctx);
if (impl->latest_frame_copy)
av_frame_free(&impl->latest_frame_copy);
if (impl->sws_frame)
av_frame_free(&impl->sws_frame);
if (impl->decoded_frame)
av_frame_free(&impl->decoded_frame);
if (impl->sws_ctx)
sws_freeContext(impl->sws_ctx);
if (impl->dec_ctx)
avcodec_free_context(&impl->dec_ctx);
if (impl->fmt_ctx)
avformat_close_input(&impl->fmt_ctx);
delete impl;
}
static AVFrame *avframe_deep_copy(const AVFrame *src) {
if (!src)
return nullptr;
AVFrame *dst = av_frame_alloc();
dst->format = src->format;
dst->width = src->width;
dst->height = src->height;
av_frame_get_buffer(dst, 0);
av_frame_copy(dst, src);
av_frame_copy_props(dst, src);
return dst;
}
static bool init_jpeg_encoder(RtspFrameGrabberImpl *impl, int width, int height,
AVPixelFormat pix_fmt) {
if (!impl || impl->jpeg_enc_ctx)
return true;
const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
if (!codec)
return false;
AVCodecContext *c = avcodec_alloc_context3(codec);
c->pix_fmt = pix_fmt;
c->width = width;
c->height = height;
c->time_base = AVRational{1, 25};
av_opt_set_int(c->priv_data, "quality", 1, 0);
if (avcodec_open2(c, codec, nullptr) < 0) {
avcodec_free_context(&c);
return false;
}
impl->jpeg_enc_ctx = c;
impl->enc_pkt = av_packet_alloc();
return true;
}
static bool encode_frame_to_jpeg(RtspFrameGrabberImpl *impl, AVFrame *frame,
const std::string &filename) {
if (!impl || !impl->jpeg_enc_ctx || !frame)
return false;
AVCodecContext *enc = impl->jpeg_enc_ctx;
AVPacket *pkt = impl->enc_pkt;
av_packet_unref(pkt);
if (avcodec_send_frame(enc, frame) < 0)
return false;
if (avcodec_receive_packet(enc, pkt) < 0)
return false;
FILE *f = fopen(filename.c_str(), "wb");
if (!f)
return false;
fwrite(pkt->data, 1, pkt->size, f);
fclose(f);
av_packet_unref(pkt);
return true;
}
// -------- RtspFrameGrabber Methods --------
RtspFrameGrabber::RtspFrameGrabber(const std::string &rtsp_url)
: rtsp_url_(rtsp_url) {
avformat_network_init();
opaque_impl_ = new RtspFrameGrabberImpl();
}
RtspFrameGrabber::~RtspFrameGrabber() {
stop();
free_impl(reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_));
opaque_impl_ = nullptr;
}
bool RtspFrameGrabber::start() {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl || impl->started)
return false;
impl->stop_flag = false;
impl->th = std::thread([this, impl]() { this->decode_loop(); });
impl->started = true;
return true;
}
void RtspFrameGrabber::stop() {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl)
return;
impl->stop_flag = true;
if (impl->th.joinable())
impl->th.join();
impl->started = false;
impl->ready = false;
}
bool RtspFrameGrabber::is_ready() const {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
return impl && impl->ready;
}
bool RtspFrameGrabber::capture_jpeg(const std::string &filename) {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl)
return false;
std::lock_guard<std::mutex> lk(frame_mutex_);
if (!impl->latest_frame_copy)
return false;
int w = impl->latest_frame_copy->width;
int h = impl->latest_frame_copy->height;
AVPixelFormat pf = (AVPixelFormat)impl->latest_frame_copy->format;
if (!init_jpeg_encoder(impl, w, h, pf))
return false;
return encode_frame_to_jpeg(impl, impl->latest_frame_copy, filename);
}
void RtspFrameGrabber::decode_loop() {
RtspFrameGrabberImpl *impl =
reinterpret_cast<RtspFrameGrabberImpl *>(opaque_impl_);
if (!impl)
return;
AVDictionary *opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
av_dict_set(&opts, "stimeout", "2000000", 0);
AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUVJ420P;
#if LIBAVUTIL_VERSION_MAJOR >= 57
dst_pix_fmt = AV_PIX_FMT_YUV420P;
#endif
while (!impl->stop_flag) {
AVFormatContext *fmt_ctx = nullptr;
int ret = avformat_open_input(&fmt_ctx, rtsp_url_.c_str(), nullptr, &opts);
if (ret != 0) {
std::this_thread::sleep_for(
std::chrono::milliseconds(impl->reconnect_delay_ms));
continue;
}
impl->fmt_ctx = fmt_ctx;
impl->reconnect_delay_ms = 1000;
if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
avformat_close_input(&impl->fmt_ctx);
continue;
}
int video_index = -1;
for (unsigned i = 0; i < fmt_ctx->nb_streams; ++i) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_index = i;
break;
}
}
if (video_index < 0) {
avformat_close_input(&impl->fmt_ctx);
continue;
}
impl->video_stream_index = video_index;
AVCodecParameters *codecpar = fmt_ctx->streams[video_index]->codecpar;
const AVCodec *dec = avcodec_find_decoder(codecpar->codec_id);
if (!dec) {
avformat_close_input(&impl->fmt_ctx);
continue;
}
AVCodecContext *dec_ctx = avcodec_alloc_context3(dec);
avcodec_parameters_to_context(dec_ctx, codecpar);
if (avcodec_open2(dec_ctx, dec, nullptr) < 0) {
avcodec_free_context(&dec_ctx);
avformat_close_input(&impl->fmt_ctx);
continue;
}
impl->dec_ctx = dec_ctx;
impl->decoded_frame = av_frame_alloc();
impl->width = dec_ctx->width;
impl->height = dec_ctx->height;
impl->src_pix_fmt = dec_ctx->pix_fmt;
impl->sws_frame = av_frame_alloc();
impl->sws_frame->format = dst_pix_fmt;
impl->sws_frame->width = impl->width;
impl->sws_frame->height = impl->height;
av_frame_get_buffer(impl->sws_frame, 32);
impl->sws_ctx = sws_getContext(impl->width, impl->height, impl->src_pix_fmt,
impl->width, impl->height,
(AVPixelFormat)impl->sws_frame->format,
SWS_BICUBIC, nullptr, nullptr, nullptr);
init_jpeg_encoder(impl, impl->width, impl->height,
(AVPixelFormat)impl->sws_frame->format);
impl->ready = true;
// LOG_INF("connected and decoding: " << rtsp_url_);
AVPacket *pkt = av_packet_alloc();
while (!impl->stop_flag) {
int r = av_read_frame(fmt_ctx, pkt);
if (r < 0) {
av_packet_unref(pkt);
impl->ready = false;
break;
}
if (pkt->stream_index != impl->video_stream_index) {
av_packet_unref(pkt);
continue;
}
if (avcodec_send_packet(dec_ctx, pkt) < 0) {
av_packet_unref(pkt);
continue;
}
while (!impl->stop_flag) {
r = avcodec_receive_frame(dec_ctx, impl->decoded_frame);
if (r == AVERROR(EAGAIN) || r == AVERROR_EOF)
break;
sws_scale(impl->sws_ctx, impl->decoded_frame->data,
impl->decoded_frame->linesize, 0, impl->height,
impl->sws_frame->data, impl->sws_frame->linesize);
std::lock_guard<std::mutex> lk(frame_mutex_);
if (impl->latest_frame_copy)
av_frame_free(&impl->latest_frame_copy);
impl->latest_frame_copy = avframe_deep_copy(impl->sws_frame);
}
av_packet_unref(pkt);
}
av_packet_free(&pkt);
impl->ready = false;
if (impl->sws_ctx) {
sws_freeContext(impl->sws_ctx);
impl->sws_ctx = nullptr;
}
if (impl->sws_frame) {
av_frame_free(&impl->sws_frame);
impl->sws_frame = nullptr;
}
if (impl->decoded_frame) {
av_frame_free(&impl->decoded_frame);
impl->decoded_frame = nullptr;
}
if (impl->dec_ctx) {
avcodec_free_context(&impl->dec_ctx);
impl->dec_ctx = nullptr;
}
if (impl->fmt_ctx) {
avformat_close_input(&impl->fmt_ctx);
impl->fmt_ctx = nullptr;
}
if (!impl->stop_flag)
std::this_thread::sleep_for(
std::chrono::milliseconds(impl->reconnect_delay_ms));
}
impl->ready = false;
LOG_INF("decode thread exit for: " << rtsp_url_);
}
// -------- RtspManager Methods --------
RtspManager::~RtspManager() { shutdown(); }
void RtspManager::configure(
const std::unordered_map<int, std::string> &source_map) {
std::lock_guard<std::mutex> lk(mgr_mutex_);
for (auto it = grabbers_.begin(); it != grabbers_.end();) {
int sid = it->first;
auto found = source_map.find(sid);
if (found == source_map.end() ||
found->second != it->second->get_rtsp_url()) {
it->second->stop();
it = grabbers_.erase(it);
} else
++it;
}
for (const auto &kv : source_map) {
int sid = kv.first;
const std::string &url = kv.second;
if (grabbers_.find(sid) == grabbers_.end()) {
auto g = std::make_unique<RtspFrameGrabber>(url);
g->start();
grabbers_.emplace(sid, std::move(g));
}
}
}
void RtspManager::shutdown() {
std::lock_guard<std::mutex> lk(mgr_mutex_);
for (auto &kv : grabbers_)
kv.second->stop();
grabbers_.clear();
}
std::string RtspManager::capture_frame_by_source(int source_id) {
std::lock_guard<std::mutex> lk(mgr_mutex_);
auto it = grabbers_.find(source_id);
if (it == grabbers_.end())
return "";
std::filesystem::create_directories("./temp");
auto now = std::chrono::system_clock::now();
std::time_t t = std::chrono::system_clock::to_time_t(now);
std::ostringstream oss;
oss << "source_" << source_id << "_" << t << ".jpg";
std::string filename_only = oss.str();
std::string full_path = "./temp/" + filename_only;
if (!it->second->capture_jpeg(full_path))
return "";
return filename_only; // 只返回文件名
}
bool RtspManager::is_ready(int source_id) const {
std::lock_guard<std::mutex> lk(mgr_mutex_);
auto it = grabbers_.find(source_id);
return it != grabbers_.end() && it->second->is_ready();
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
class RtspFrameGrabber {
public:
explicit RtspFrameGrabber(const std::string &rtsp_url);
~RtspFrameGrabber();
bool start();
void stop();
bool capture_jpeg(const std::string &filename);
bool is_ready() const;
// 新增 getter,让外部访问 rtsp_url
const std::string &get_rtsp_url() const { return rtsp_url_; }
private:
void decode_loop();
private:
std::string rtsp_url_;
std::thread worker_;
std::atomic<bool> running_{false};
std::atomic<bool> ready_{false};
mutable std::mutex frame_mutex_;
void *opaque_impl_ = nullptr;
};
class RtspManager {
public:
RtspManager() = default;
~RtspManager();
void configure(const std::unordered_map<int, std::string> &source_map);
void shutdown();
std::string capture_frame_by_source(int source_id);
bool is_ready(int source_id) const;
private:
std::unordered_map<int, std::unique_ptr<RtspFrameGrabber>> grabbers_;
mutable std::mutex mgr_mutex_;
};
@@ -0,0 +1,363 @@
#include <curl/curl.h>
#include <gst/app/gstappsink.h>
#include <gst/gst.h>
#include <fstream>
#include <iostream>
#include <mutex>
#include <string>
#include <condition_variable>
#include <list>
#include <mutex>
#include <queue>
#include <thread>
#include <unordered_map>
#include "my_utils.h"
#include "vehicle_event_service.h"
#include <filesystem>
#include <optional>
#include <atomic>
#include <chrono>
#include <filesystem>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
#include <miniocpp/client.h>
#include "ds_yml_parse.h"
#include <SimpleAmqpClient/SimpleAmqpClient.h>
// ======================== 配置区 ========================
// 全局记录存储
RecordStore g_store;
// 全局请求队列
RecordQueue g_queue;
// 正面摄像头 RTSP 地址映射表
std::unordered_map<int, std::string> g_source_front_map;
// 侧面摄像头 RTSP 地址映射表
std::unordered_map<int, std::string> g_source_side_map;
RtspManager rtsp_mgr_side;
static std::mutex g_vehicle_mutex;
std::atomic<bool> stop_flag(false);
#include <algorithm>
#include <string>
bool delete_file(const std::string &file_path) {
try {
if (std::filesystem::exists(file_path)) {
return std::filesystem::remove(file_path); // 成功返回 true
} else {
std::cout << "file does not exist: " << file_path << std::endl;
return false;
}
} catch (const std::filesystem::filesystem_error &e) {
std::cout << "failed to delete file: " << e.what() << std::endl;
return false;
}
}
/**
* 更新车辆基础信息(跨线前,可多次更新)
*/
void update_object_base_info(guint source_id, guint64 object_id,
const std::string &vehicle_type) {
std::lock_guard<std::mutex> lk(g_vehicle_mutex);
TrackKey key{source_id, object_id};
Record record;
if (g_store.get(key, record)) {
// 已经过 Line B 上传的不再更新车牌/车型
if (record.crossed_line_b) {
return;
}
// vehicle_type 一般比车牌稳定,可直接覆盖或也做类似策略
if (!vehicle_type.empty()) {
record.vehicle_type = vehicle_type;
}
} else {
// 新对象
record.source_id = source_id;
record.object_id = object_id;
record.vehicle_type = vehicle_type;
record.insert_time = std::chrono::steady_clock::now();
}
g_store.upsert(key, record);
}
/**
* 触发车辆跨线事件(拍照线 Line A)
*/
void trigger_line_a_capture(guint source_id, guint64 object_id) {
std::lock_guard<std::mutex> lk(g_vehicle_mutex);
TrackKey key{source_id, object_id};
Record record;
if (!g_store.get(key, record)) {
// 没有基础信息,不允许触发
return;
}
if (record.crossed_line_a) {
// 已经在 Line A 拍照过,不再重复
return;
}
// 状态迁移:未拍照 -> 已拍照
record.crossed_line_a = true;
// 拍照,但不上传
record.vehicle_image_side = rtsp_mgr_side.capture_frame_by_source(source_id);
g_store.upsert(key, record);
}
/**
* 触发车辆跨线事件(上传线 Line B)
*/
void trigger_line_b_upload(guint source_id, guint64 object_id,
const std::string &file_name) {
std::lock_guard<std::mutex> lk(g_vehicle_mutex);
TrackKey key{source_id, object_id};
Record record;
if (!g_store.get(key, record)) {
// 没有基础信息,不允许触发
return;
}
if (!record.crossed_line_a) {
// 没有经过 Line A,不允许上传
g_store.remove(key);
return;
}
if (record.crossed_line_b) {
// 已经在 Line B 上传过,保证只触发一次
g_store.remove(key);
return;
}
record.vehicle_image_front = file_name;
// 状态迁移:未上传 -> 已上传
record.crossed_line_b = true;
g_store.upsert(key, record);
int vehicle_type = ds_parse_group_type(yaml_file_path, "vehicle_type");
bool match = false;
switch (vehicle_type) {
case 0:
match = true;
break;
case 1:
match = (record.vehicle_type == "coupe");
break;
case 2:
match = (record.vehicle_type == "largevehicle");
break;
case 3:
match = (record.vehicle_type == "sedan");
break;
case 4:
match = (record.vehicle_type == "suv");
break;
case 5:
match = (record.vehicle_type == "truck");
break;
case 6:
match = (record.vehicle_type == "van");
break;
case 7:
match = (record.vehicle_type == "largevehicle" ||
record.vehicle_type == "truck");
break;
default:
match = false;
break;
}
if (match) {
g_queue.push(record);
} else {
// 删除不符合条件的记录与图片,避免占用空间
g_store.remove(key);
}
}
// =======================================================
/**
* MinIO 上传
*/
bool upload_minio(const std::string &object_name,
const std::string &file_path) {
// Create S3 base URL.
minio::s3::BaseUrl base_url;
base_url.host = "ai.ronsunny.cn";
base_url.port = 9000;
base_url.https = true;
base_url.region = "China.Chengdu";
// Create credential provider.
minio::creds::StaticProvider provider("minioadmin", "minioadmin");
// Create S3 client.
minio::s3::Client client(base_url, &provider);
std::string bucket_name = "sentinel";
// Check 'asiatrip' bucket exist or not.
bool exist;
{
minio::s3::BucketExistsArgs args;
args.bucket = bucket_name;
minio::s3::BucketExistsResponse resp = client.BucketExists(args);
if (!resp) {
std::cout << "unable to do bucket existence check:" << resp.Error()
<< std::endl;
return EXIT_FAILURE;
}
exist = resp.exist;
}
if (!exist) {
minio::s3::MakeBucketArgs args;
args.bucket = bucket_name;
minio::s3::MakeBucketResponse resp = client.MakeBucket(args);
if (!resp) {
std::cout << "unable to create bucket; " << resp.Error() << std::endl;
return EXIT_FAILURE;
}
}
minio::s3::UploadObjectArgs args;
args.bucket = bucket_name;
args.object = object_name;
args.filename = file_path;
minio::s3::UploadObjectResponse resp = client.UploadObject(args);
if (!resp) {
std::cout << "unable to upload object; " << resp.Error() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
// void front_worker_thread() {
// std::string device_id = GetDeviceID();
// AmqpClient::Channel::OpenOpts opts;
// opts.host = "ai.ronsunny.cn";
// opts.port = 5672;
// opts.auth =
// AmqpClient::Channel::OpenOpts::BasicAuth("jetson_sentinel", "123456");
// opts.vhost = "sentinel";
// auto channel = AmqpClient::Channel::Open(opts);
// std::string queue = "sentinel.front_pic." + device_id;
// channel->DeclareQueue(queue, false, true, false, false);
// std::string consumer_tag =
// channel->BasicConsume(queue, "", true, true, false);
// while (!stop_flag) {
// AmqpClient::Envelope::ptr_t envelope;
// bool received = channel->BasicConsumeMessage(consumer_tag, envelope);
// if (received) {
// printf("Received message: %s\n", envelope->Message()->Body().c_str());
// std::string recordId = envelope->Message()->Body();
// // 1. 上传正面照到OSS
// Record record;
// if (!g_store.get_by_id(recordId, record)) {
// std::cerr << "No record found for id=" << recordId << std::endl;
// continue;
// }
// if (record.vehicle_image_front.empty()) {
// std::cerr << "No front image for record id=" << recordId <<
// std::endl; continue;
// }
// auto file_path =
// "./temp/" +
// std::filesystem::path(record.vehicle_image_front).filename().string();
// auto upload_result = upload_minio(
// "vehicle_image_front/" + record.vehicle_image_side, file_path);
// if (EXIT_FAILURE == upload_result) {
// std::cerr << "Failed to upload image for record id=" <<
// record.object_id
// << std::endl;
// continue;
// }
// // 2. HTTP 请求服务器
// AnalyticsClient client("https://ai.ronsunny.cn:8090/api/public/"
// "sentinel-record-analytics");
// std::string err;
// nlohmann::json j;
// j["Id"] = recordId;
// j["DeviceId"] = device_id;
// j["vehicleImageFront"] = record.vehicle_image_front;
// j["vehicleImageSide"] = record.vehicle_image_side;
// if (!client.call(j, err)) {
// std::cerr << "Analytics request failed: " << err << "\n";
// }
// TrackKey key{record.source_id, record.object_id};
// g_store.remove(key);
// }
// }
// }
void side_worker_thread() {
while (!stop_flag) {
Record record = g_queue.pop();
if (record.vehicle_image_side.empty() ||
record.vehicle_image_front.empty()) {
std::cerr << "No side image for record id=" << record.object_id
<< std::endl;
continue;
}
auto file_path =
"./temp/" +
std::filesystem::path(record.vehicle_image_side).filename().string();
if (!std::filesystem::exists(file_path) ||
std::filesystem::is_directory(file_path)) {
std::cerr << "invalid file: " << file_path << std::endl;
continue;
}
auto upload_result = upload_minio(
"vehicle_image_side/" + record.vehicle_image_side, file_path);
if (EXIT_FAILURE == upload_result) {
std::cerr << "Failed to upload image for record id=" << record.object_id
<< std::endl;
continue;
}
file_path =
"./temp/" +
std::filesystem::path(record.vehicle_image_front).filename().string();
if (!std::filesystem::exists(file_path) ||
std::filesystem::is_directory(file_path)) {
std::cerr << "invalid file: " << file_path << std::endl;
continue;
}
upload_result = upload_minio(
"vehicle_image_front/" + record.vehicle_image_front, file_path);
if (EXIT_FAILURE == upload_result) {
std::cerr << "Failed to upload image for record id=" << record.object_id
<< std::endl;
continue;
}
// 2. HTTP 请求服务器
AnalyticsClient client("https://ai.ronsunny.cn:8090/api/public/"
"sentinel-record-analytics");
std::string err;
std::string id = generateUUID();
record.id = id;
nlohmann::json j;
j["Id"] = id;
j["DeviceId"] = GetDeviceID();
j["VehicleType"] = record.vehicle_type;
j["vehicleImageFront"] = record.vehicle_image_front;
j["vehicleImageSide"] = record.vehicle_image_side;
if (!client.call(j, err)) {
std::cerr << "Analytics request failed: " << err << "\n";
}
// 删除本地文件和记录
TrackKey key{record.source_id, record.object_id};
g_store.remove(key);
}
}
@@ -0,0 +1,268 @@
#include "rtsp_grabber.h"
#include <atomic>
#include <chrono>
#include <optional>
#include <string>
/**
* 车辆信息记录-结构体
*/
struct Record {
std::string id; // 记录ID(唯一,用于寻找对应记录)
guint source_id = 0; // 资源id
guint64 object_id = 0; // 当前source下的车辆id
std::string vehicle_type = ""; // 车类型
std::string vehicle_image_front = ""; // 车身正面图片地址
std::string vehicle_image_side = ""; // 车身侧面图片地址
bool crossed_line_a = false; // 是否跨线A
bool crossed_line_b = false; // 是否跨线B
bool crossed = false; // 是否已经跨线
std::chrono::steady_clock::time_point
insert_time; // 记录插入时间,用于过期清理
};
/**
* 车辆信息记录-键
*/
struct TrackKey {
guint source_id;
guint64 object_id;
bool operator==(const TrackKey &other) const {
return source_id == other.source_id && object_id == other.object_id;
}
};
/**
* 车辆信息记录-存储
*/
class RecordStore {
public:
explicit RecordStore(size_t capacity = 50, int expire_seconds = 60 * 10)
: capacity_(capacity),
expire_duration_(std::chrono::seconds(expire_seconds)) {}
void upsert(const TrackKey &key, const Record &record) {
std::lock_guard<std::mutex> lock(mtx_);
clean_expired();
auto it = map_.find(key);
if (it != map_.end()) {
// 已存在,更新并移动到最新
it->second.first = record;
lru_.splice(lru_.begin(), lru_, it->second.second);
it->second.second = lru_.begin();
} else {
// 新增
if (map_.size() >= capacity_) {
evict_one();
}
lru_.push_front(key);
map_[key] = {record, lru_.begin()};
}
}
bool get(const TrackKey &key, Record &out) {
std::lock_guard<std::mutex> lock(mtx_);
auto it = map_.find(key);
if (it == map_.end())
return false;
out = it->second.first;
return true;
}
bool get_by_id(const std::string &id, Record &out_record) {
std::lock_guard<std::mutex> lock(mtx_);
for (const auto &pair : map_) {
const Record &rec = pair.second.first;
if (rec.id == id) {
out_record = rec;
return true;
}
}
return false; // 没找到
}
bool remove(const TrackKey &key) {
std::lock_guard<std::mutex> lock(mtx_);
auto it = map_.find(key);
if (it == map_.end())
return false;
// 删除图片文件
const Record &record = it->second.first;
if (!record.vehicle_image_front.empty()) {
std::remove(record.vehicle_image_front.c_str());
}
if (!record.vehicle_image_side.empty()) {
std::remove(record.vehicle_image_side.c_str());
}
// 移除 map 和 lru
lru_.erase(it->second.second);
map_.erase(it);
return true;
}
private:
void evict_one() {
auto last = lru_.back();
map_.erase(last);
lru_.pop_back();
}
void clean_expired() {
auto now = std::chrono::steady_clock::now();
for (auto it = lru_.begin(); it != lru_.end();) {
auto &record = map_[*it].first;
auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(
now - record.insert_time);
if (elapsed >= expire_duration_) {
// 删除对应文件
if (!record.vehicle_image_front.empty()) {
std::remove(record.vehicle_image_front.c_str());
}
if (!record.vehicle_image_side.empty()) {
std::remove(record.vehicle_image_side.c_str());
}
// 移除 map 和 lru
map_.erase(*it);
it = lru_.erase(it);
} else {
++it;
}
}
}
struct TrackKeyHash {
std::size_t operator()(const TrackKey &k) const {
// 64位安全
return std::hash<guint>()(k.source_id) ^
std::hash<guint64>()(k.object_id);
}
};
private:
size_t capacity_;
std::list<TrackKey> lru_;
std::chrono::seconds expire_duration_;
std::unordered_map<TrackKey, std::pair<Record, std::list<TrackKey>::iterator>,
TrackKeyHash>
map_;
std::mutex mtx_;
};
/**
* 车辆信息记录-队列
*/
class RecordQueue {
public:
void push(const Record &record) {
{
std::lock_guard<std::mutex> lock(mtx_);
queue_.push(record);
}
cv_.notify_one();
}
Record pop() {
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock, [&] { return !queue_.empty(); });
Record r = queue_.front();
queue_.pop();
return r;
}
private:
std::queue<Record> queue_;
std::mutex mtx_;
std::condition_variable cv_;
};
#include <curl/curl.h>
#include <iostream>
#include <nlohmann/json.hpp>
#include <string>
class AnalyticsClient {
public:
AnalyticsClient(const std::string &url) : url_(url) {
curl_global_init(CURL_GLOBAL_DEFAULT);
curl_ = curl_easy_init();
if (!curl_)
throw std::runtime_error("Failed to init curl");
// 设置全局 headers
headers_ = curl_slist_append(headers_, "Content-Type: application/json");
headers_ =
curl_slist_append(headers_, "apikey: NzusyzcLIUoZ22tflHN2sOjHrry3W7zJ");
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT_MS, 3000L);
}
~AnalyticsClient() {
if (curl_)
curl_easy_cleanup(curl_);
if (headers_)
curl_slist_free_all(headers_);
curl_global_cleanup();
}
// 返回 true/false,并可通过 err_msg 获取错误
bool call(nlohmann::json j, std::string &err_msg, bool is_side = true) {
std::string json_str = j.dump();
curl_easy_setopt(curl_, CURLOPT_URL, url_.c_str());
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, json_str.c_str());
CURLcode res = curl_easy_perform(curl_);
if (res != CURLE_OK) {
err_msg = curl_easy_strerror(res);
return false;
}
return true;
}
private:
std::string url_;
CURL *curl_{nullptr};
struct curl_slist *headers_{nullptr};
};
// 函数声明
std::string capture_frame_from_rtsp(const std::string &rtsp_url,
unsigned int source_id,
unsigned int object_id);
void update_object_base_info(guint source_id, guint64 object_id,
const std::string &vehicle_type);
void trigger_line_a_capture(guint source_id, guint64 object_id);
void trigger_line_b_upload(guint source_id, guint64 object_id,
const std::string &file_name);
void worker_start();
// 全局变量声明(不要定义)
extern RecordStore g_store;
extern RecordQueue g_queue;
extern std::unordered_map<int, std::string> g_source_side_map;
extern std::unordered_map<int, std::string> g_source_front_map;
extern RtspManager rtsp_mgr_front;
extern RtspManager rtsp_mgr_side;
void side_worker_thread();
// void front_worker_thread();
extern std::atomic<bool> stop_flag;
class WorkerManager {
public:
void start() {
// threads.emplace_back(front_worker_thread);
threads.emplace_back(side_worker_thread);
}
void stop() {
stop_flag = true;
for (auto &t : threads) {
if (t.joinable())
t.join();
}
threads.clear();
}
private:
std::vector<std::thread> threads;
};
+67
View File
@@ -0,0 +1,67 @@
A
B
C
D
E
F
G
H
J
K
L
M
N
P
Q
R
S
T
U
V
W
X
Y
Z
0
1
2
3
4
5
6
7
8
9
+806
View File
@@ -0,0 +1,806 @@
/*
* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <yaml-cpp/yaml.h>
#include <string>
#include <cstring>
#include <iostream>
#include <unordered_map>
#include <cuda_runtime_api.h>
#include "ds_yml_parse.h"
#include "nvds_yml_parser.h"
static gchar *get_absolute_file_path(gchar *cfg_file_path, const gchar *file_path) {
gchar abs_cfg_path[PATH_MAX + 1];
gchar *abs_file_path;
gchar *delim;
if (file_path && file_path[0] == '/') {
return (gchar *)file_path;
}
if (!realpath(cfg_file_path, abs_cfg_path)) {
return NULL;
}
/* Return absolute path of config file if file_path is NULL. */
if (!file_path) {
abs_file_path = g_strdup(abs_cfg_path);
return abs_file_path;
}
delim = g_strrstr(abs_cfg_path, "/");
*(delim + 1) = '\0';
abs_file_path = g_strconcat(abs_cfg_path, file_path, NULL);
return abs_file_path;
}
NvDsYamlParserStatus
ds_parse_rtsp_output(GstElement * sink,
GstRTSPServer *server, GstRTSPMediaFactory *factory,
gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
YAML::const_iterator itr = docs[i].begin();
std::string group_name = itr->first.as<std::string>();
docs_indx_umap[group_name] = i;
docs_indx_vec.push_back(i);
}
}
if (!sink || !server || !factory)
{
std::cerr << "[ERROR] Pass element does not exist!" << std::endl;
return NVDS_YAML_PARSER_ERROR;
} else {
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(sink)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "udpsink")) {
std::cerr << "[ERROR] Passed element is not udpsink" << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
}
int docs_indx_vec_size = docs_indx_vec.size();
int docs_indx_umap_size = docs_indx_umap.size();
if (docs_indx_umap_size != docs_indx_vec_size) {
std::cerr << "[ERROR] Duplicate group names in the config file : " << group << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
for (int i = 0; i< docs_indx_vec_size; i++)
{
int indx = docs_indx_vec [i];
for(YAML::const_iterator itr = docs[indx][group].begin(); itr != docs[indx][group].end(); ++itr)
{
paramKey = itr->first.as<std::string>();
if(paramKey == "udpport") {
char udpsrc_pipeline[512];
g_object_set (G_OBJECT (sink), "host", "224.224.255.255", "port",
itr->second.as<guint>(), "async", FALSE, "sync", 0, NULL);
sprintf (udpsrc_pipeline,
"( udpsrc name=pay0 port=%s buffer-size=65536 caps=\"application/x-rtp, media=video, "
"clock-rate=90000, encoding-name=H264, payload=96 \" )",
itr->second.as<std::string>().c_str());
gst_rtsp_media_factory_set_launch (factory, udpsrc_pipeline);
}
else if(paramKey == "rtspport") {
g_object_set (G_OBJECT(server), "service", itr->second.as<std::string>().c_str(), NULL);
g_print("Please reach RTSP with rtsp://ip:%s/ds-out-avc\n", itr->second.as<std::string>().c_str());
}
else {
std::cerr << "!! [WARNING] Unknown param found : " << paramKey << std::endl;
}
}
}
return NVDS_YAML_PARSER_SUCCESS;
}
NvDsYamlParserStatus
ds_parse_enc_config(GstElement *encoder,
gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
YAML::const_iterator itr = docs[i].begin();
std::string group_name = itr->first.as<std::string>();
docs_indx_umap[group_name] = i;
docs_indx_vec.push_back(i);
}
}
if (!encoder)
return NVDS_YAML_PARSER_ERROR;
else {
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(encoder)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "nvv4l2h264enc")
&& g_strcmp0(GST_OBJECT_NAME(factory), "nvv4l2h265enc")) {
std::cerr << "[ERROR] Passed element is not encoder" << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
}
int docs_indx_vec_size = docs_indx_vec.size();
int docs_indx_umap_size = docs_indx_umap.size();
if (docs_indx_umap_size != docs_indx_vec_size) {
std::cerr << "[ERROR] Duplicate group names in the config file : " << group << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
for (int i = 0; i< docs_indx_vec_size; i++)
{
int indx = docs_indx_vec [i];
for(YAML::const_iterator itr = docs[indx][group].begin(); itr != docs[indx][group].end(); ++itr)
{
paramKey = itr->first.as<std::string>();
if(paramKey == "bitrate") {
g_object_set(G_OBJECT(encoder), "bitrate",
itr->second.as<guint>(), NULL);
}
else if(paramKey == "iframeinterval") {
g_object_set(G_OBJECT(encoder), "iframeinterval",
itr->second.as<guint>(), NULL);
}
else {
std::cerr << "!! [WARNING] Unknown param found : " << paramKey << std::endl;
}
}
}
return NVDS_YAML_PARSER_SUCCESS;
}
NvDsYamlParserStatus
ds_parse_videotemplate_config(GstElement *vtemplate,
gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
YAML::const_iterator itr = docs[i].begin();
std::string group_name = itr->first.as<std::string>();
docs_indx_umap[group_name] = i;
docs_indx_vec.push_back(i);
}
}
if (!vtemplate)
return NVDS_YAML_PARSER_ERROR;
else {
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(vtemplate)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "nvdsvideotemplate")) {
std::cerr << "[ERROR] Passed element is not nvdsvideotemplate" << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
}
int docs_indx_vec_size = docs_indx_vec.size();
int docs_indx_umap_size = docs_indx_umap.size();
if (docs_indx_umap_size != docs_indx_vec_size) {
std::cerr << "[ERROR] Duplicate group names in the config file : " << group << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
for (int i = 0; i< docs_indx_vec_size; i++)
{
int indx = docs_indx_vec [i];
for(YAML::const_iterator itr = docs[indx][group].begin(); itr != docs[indx][group].end(); ++itr)
{
paramKey = itr->first.as<std::string>();
if(paramKey == "customlib-name") {
g_object_set(G_OBJECT(vtemplate), "customlib-name",
itr->second.as<std::string>().c_str(), NULL);
}
else if(paramKey == "customlib-props") {
g_object_set(G_OBJECT(vtemplate), "customlib-props",
itr->second.as<std::string>().c_str(), NULL);
}
else {
std::cerr << "!! [WARNING] Unknown param found : " << paramKey << std::endl;
}
}
}
return NVDS_YAML_PARSER_SUCCESS;
}
NvDsYamlParserStatus
ds_parse_ocdr_videotemplate_config(GstElement *vtemplate,
gchar *cfg_file_path, const char* group)
{
if (!vtemplate)
return NVDS_YAML_PARSER_ERROR;
else {
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(vtemplate)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "nvdsvideotemplate")) {
std::cerr << "[ERROR] Passed element is not nvdsvideotemplate" << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
}
YAML::Node node = YAML::LoadFile(cfg_file_path);
YAML::Node docs = node[group];
if(docs["customlib-name"]) {
std::string libPath = docs["customlib-name"].as<std::string>();
g_object_set(G_OBJECT(vtemplate), "customlib-name",
libPath.c_str(), NULL);
}
if(docs["customlib-props"]) {
auto listNode = docs["customlib-props"];
for(uint32_t i = 0; i < listNode.size(); i++) {
std::string tmpProb = listNode[i].as<std::string>();
g_object_set(G_OBJECT(vtemplate), "customlib-props",
tmpProb.c_str(), NULL);
}
}
return NVDS_YAML_PARSER_SUCCESS;
}
guint
ds_parse_group_type(gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
guint val = 0;
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
if (docs[i][group]["type"]) {
val= docs[i][group]["type"].as<guint>();
return val;
}
}
}
return 0;
}
NvDsYamlParserStatus ds_parse_nvdsanalytics(GstElement *element, gchar *cfg_file_path, const char* group)
{
NvDsYamlParserStatus ret = NVDS_YAML_PARSER_SUCCESS;
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(element)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "nvdsanalytics")) {
std::cerr << "[ERROR] Passed element is not nvdsanalytics" << std::endl;
ret = NVDS_YAML_PARSER_ERROR;
return ret;
}
std::string paramKey = "";
auto docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
for (int i =0; i < total_docs;i++)
{
if (docs[i][group].Type() != YAML::NodeType::Null) {
if (docs[i][group]["enable"]) {
gboolean val= docs[i][group]["enable"].as<gboolean>();
if(val == FALSE) {
g_print("!! [WARNING] \"analytics\" group not enabled.\n");
g_object_set(G_OBJECT(element), "enable", val, NULL);
}
}
YAML::const_iterator itr = docs[i].begin();
std::string group_name = itr->first.as<std::string>();
docs_indx_umap[group_name] = i;
docs_indx_vec.push_back(i);
}
}
int docs_indx_vec_size = docs_indx_vec.size();
int docs_indx_umap_size = docs_indx_umap.size();
if (docs_indx_umap_size != docs_indx_vec_size) {
std::cerr << "[ERROR] Duplicate group names in the config file : " << group << std::endl;
ret = NVDS_YAML_PARSER_ERROR;
return ret;
}
for (int i = 0; i< docs_indx_vec_size; i++)
{
int indx = docs_indx_vec [i];
for(YAML::const_iterator itr = docs[indx][group].begin(); itr != docs[indx][group].end(); ++itr)
{
paramKey = itr->first.as<std::string>();
if (paramKey == "enable" && docs[indx][group]["enable"].as<gboolean>() == TRUE) {
continue;
} else if(paramKey == "config-file") {
std::string temp = itr->second.as<std::string>();
if (temp.empty()) {
g_printerr ("Error: Could not parse config-file-path in %s.\n", group);
return NVDS_YAML_PARSER_ERROR;
}
char *config_file_path = get_absolute_file_path(cfg_file_path, temp.c_str());
if (!config_file_path) {
g_printerr ("Error: Could not get absolute path for config-file-path in %s.\n", group);
return NVDS_YAML_PARSER_ERROR;
}
g_print("Setting config-file for nvdsanalytics: %s\n", config_file_path);
g_object_set(G_OBJECT(element), "config-file", config_file_path, NULL);
g_free(config_file_path);
} else {
std::cerr << "!! [WARNING] Unknown param found for nvdsanalytics: " << paramKey << std::endl;
}
}
}
return ret;
}
static const char* dgpus_unsupport_hw_enc[] = {
"NVIDIA A100",
"NVIDIA A30",
"NVIDIA H100", // NVIDIA H100 SXM, NVIDIA H100 PCIe, NVIDIA H100 NVL
"NVIDIA T500",
"GeForce MX570 A",
"DGX A100"
};
static bool
is_enc_hw_support() {
int current_device = -1;
cudaGetDevice(&current_device);
struct cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, current_device);
bool enc_hw_support = TRUE;
if (prop.integrated) {
char device_name[50];
FILE* ptr = fopen("/proc/device-tree/model", "r");
if (ptr) {
while (fgets(device_name, 50, ptr) != NULL) {
if (strstr(device_name,"Orin") && (strstr(device_name,"Nano")))
enc_hw_support = FALSE;
}
}
fclose(ptr);
} else {
for (uint32_t i = 0; i < sizeof(dgpus_unsupport_hw_enc)/sizeof(dgpus_unsupport_hw_enc[0]); i++) {
if (!strncasecmp(prop.name, dgpus_unsupport_hw_enc[i], strlen(dgpus_unsupport_hw_enc[i]))) {
enc_hw_support = FALSE;
break;
}
}
}
return enc_hw_support;
}
guint
ds_parse_enc_type(gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
guint val = 0;
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
if (docs[i][group]["enc-type"]) {
val = docs[i][group]["enc-type"].as<guint>();
// If hardware encoding is configured but the hardware does not support it,
// fallback to software encoding
if (val == 0 && !is_enc_hw_support()) {
g_print("** WARN: hardware encoding is not supported, fallback to software encoding \n");
val = 1;
}
return val;
}
}
}
return 0;
}
guint
ds_parse_enc_codec(gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
guint val = 0;
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
if (docs[i][group]["codec"]) {
val= docs[i][group]["codec"].as<guint>();
return val;
}
}
}
return 0;
}
GString *
ds_parse_file_name(gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
GString *str = NULL;
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
if (docs[i][group]["filename"]) {
std::string temp = docs[i][group]["filename"].as<std::string>();
str = g_string_new(temp.c_str());
return str;
}
}
}
return NULL;
}
GString *
ds_parse_config_yml_filepath(gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
int total_docs = docs.size();
GString *str = NULL;
g_print("total %d item\n",total_docs);
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
g_print("group %s found %d\n", group, !(docs[i][group]["config-file-path"]));
if (docs[i][group]["config-file-path"]) {
std::string temp = docs[i][group]["config-file-path"].as<std::string>();
str = g_string_new(temp.c_str());
return str;
}
}
}
return NULL;
}
/* this is only for video encoding. */
void
create_video_encoder(bool isH264, int enc_type, GstElement** conv_capfilter,
GstElement** outenc, GstElement** encparse, GstElement** rtppay)
{
GstCaps *caps = NULL;
GstCapsFeatures *feature = NULL;
g_print("in create_video_encoder, isH264:%d, enc_type:%d\n", isH264, enc_type);
caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
"I420", NULL);
if(enc_type == ENCODER_TYPE_HW) {
feature = gst_caps_features_new ("memory:NVMM", NULL);
gst_caps_set_features (caps, 0, feature);
}
g_object_set (G_OBJECT (*conv_capfilter), "caps", caps, NULL);
if(isH264) {
if(enc_type == ENCODER_TYPE_HW) {
*outenc = gst_element_factory_make ("nvv4l2h264enc" ,"nvvideo-h264enc");
} else {
*outenc = gst_element_factory_make ("x264enc" ,"x264enc");
}
*encparse = gst_element_factory_make ("h264parse", "encparse");
if(rtppay)
*rtppay = gst_element_factory_make ("rtph264pay", "rtppay");
} else {
if(enc_type == ENCODER_TYPE_HW) {
*outenc = gst_element_factory_make ("nvv4l2h265enc" ,"nvvideo-h265enc");
} else {
*outenc = gst_element_factory_make ("x265enc" ,"x265enc");
}
*encparse = gst_element_factory_make ("h265parse", "encparse");
if(rtppay)
*rtppay = gst_element_factory_make ("rtph265pay", "rtppay");
}
}
guint
ds_parse_group_enable(gchar *cfg_file_path, const char* group)
{
std::string paramKey = "";
std::vector<YAML::Node> docs = YAML::LoadAllFromFile(cfg_file_path);
std::vector<int> docs_indx_vec;
std::unordered_map<std::string, int> docs_indx_umap;
int total_docs = docs.size();
guint val = 0;
for (int i =0; i < total_docs;i++)
{
if (!docs[i][group].IsNull()) {
if (docs[i][group]["enable"]) {
val= docs[i][group]["enable"].as<guint>();
return val;
}
}
}
return 0;
}
/** Function to get the absolute path of a file.*/
gboolean
get_absolute_file_path_yaml (
const gchar * cfg_file_path, const gchar * file_path,
char *abs_path_str)
{
gchar abs_cfg_path[PATH_MAX + 1];
gchar abs_real_file_path[PATH_MAX + 1];
gchar *abs_file_path;
gchar *delim;
/* Absolute path. No need to resolve further. */
if (file_path[0] == '/') {
/* Check if the file exists, return error if not. */
if (!realpath (file_path, abs_real_file_path)) {
/* Ignore error if file does not exist and use the unresolved path. */
if (errno != ENOENT)
return FALSE;
}
g_strlcpy (abs_path_str, abs_real_file_path, _PATH_MAX);
return TRUE;
}
/* Get the absolute path of the config file. */
if (!realpath (cfg_file_path, abs_cfg_path)) {
return FALSE;
}
/* Remove the file name from the absolute path to get the directory of the
* config file. */
delim = g_strrstr (abs_cfg_path, "/");
*(delim + 1) = '\0';
/* Get the absolute file path from the config file's directory path and
* relative file path. */
abs_file_path = g_strconcat (abs_cfg_path, file_path, nullptr);
/* Resolve the path.*/
if (realpath (abs_file_path, abs_real_file_path) == nullptr) {
/* Ignore error if file does not exist and use the unresolved path. */
if (errno == ENOENT)
g_strlcpy (abs_real_file_path, abs_file_path, _PATH_MAX);
else
return FALSE;
}
g_free (abs_file_path);
g_strlcpy (abs_path_str, abs_real_file_path, _PATH_MAX);
return TRUE;
}
NvDsYamlParserStatus
nvds_parse_postprocess (GstElement *element, gchar* app_cfg_file_path, const char* group)
{
NvDsYamlParserStatus ret = NVDS_YAML_PARSER_SUCCESS;
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(element)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "nvdspostprocess")) {
std::cerr << "[ERROR] Passed element is not nvdspostprocess" << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
if (!app_cfg_file_path) {
printf("Config file not provided.\n");
return NVDS_YAML_PARSER_ERROR;
}
YAML::Node configyml = YAML::LoadFile(app_cfg_file_path);
for(YAML::const_iterator itr = configyml[group].begin();
itr != configyml[group].end(); ++itr)
{
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "config-file-path") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1024);
char *path = (char*) malloc(sizeof(char) * 1024);
if (!get_absolute_file_path_yaml (app_cfg_file_path, str,
path)) {
ret = NVDS_YAML_PARSER_ERROR;
}
g_object_set(G_OBJECT(element), "postprocesslib-config-file",
path, NULL);
printf("postprocesslib-config-file:%s\n", path);
g_free (str);
g_free(path);
} else if (paramKey == "lib-name") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1024);
char *path = (char*) malloc(sizeof(char) * 1024);
if (!get_absolute_file_path_yaml (app_cfg_file_path, str,
path)) {
ret = NVDS_YAML_PARSER_ERROR;
}
g_object_set(G_OBJECT(element), "postprocesslib-name",
path, NULL);
printf("postprocesslib-name:%s\n", path);
g_free (str);
g_free(path);
} else {
printf("[WARNING] Unknown param found in postprocess: %s\n", paramKey.c_str());
}
}
return ret;
}
NvDsYamlParserStatus
nvds_parse_preprocess (GstElement *element, gchar* app_cfg_file_path, const char* group)
{
NvDsYamlParserStatus ret = NVDS_YAML_PARSER_SUCCESS;
GstElementFactory *factory = GST_ELEMENT_GET_CLASS(element)->elementfactory;
if (g_strcmp0(GST_OBJECT_NAME(factory), "nvdspreprocess")) {
std::cerr << "[ERROR] Passed element is not nvdspreprocess" << std::endl;
return NVDS_YAML_PARSER_ERROR;
}
if (!app_cfg_file_path) {
printf("Config file not provided.\n");
return NVDS_YAML_PARSER_ERROR;
}
YAML::Node configyml = YAML::LoadFile(app_cfg_file_path);
for(YAML::const_iterator itr = configyml[group].begin();
itr != configyml[group].end(); ++itr)
{
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "config-file-path") {
std::string temp = itr->second.as<std::string>();
char* str = (char*) malloc(sizeof(char) * 1024);
std::strncpy (str, temp.c_str(), 1024);
char *config_file_path = (char*) malloc(sizeof(char) * 1024);
if (!get_absolute_file_path_yaml (app_cfg_file_path, str,
config_file_path)) {
ret = NVDS_YAML_PARSER_ERROR;
}
g_object_set(G_OBJECT(element), "config-file",
config_file_path, NULL);
printf("config_file_path:%s\n", config_file_path);
g_free (str);
g_free(config_file_path);
} else {
printf("[WARNING] Unknown param found in preprocess: %s\n", paramKey.c_str());
}
}
return ret;
}
void
parse_streammux_width_height_yaml (gint *width, gint *height, gchar *cfg_file_path)
{
YAML::Node configyml = YAML::LoadFile(cfg_file_path);
for(YAML::const_iterator itr = configyml["streammux"].begin();
itr != configyml["streammux"].end(); ++itr) {
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "width") {
*width = itr->second.as<gint>();
} else if(paramKey == "height"){
*height = itr->second.as<gint>();
}
}
}
void
parse_sink_type_yaml (gint *type, gchar *cfg_file_path)
{
YAML::Node configyml = YAML::LoadFile(cfg_file_path);
for(YAML::const_iterator itr = configyml["sink"].begin();
itr != configyml["sink"].end(); ++itr) {
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "sink-type") {
*type = itr->second.as<gint>();
}
}
}
void
parse_sink_enc_type_yaml (gint *enc_type, gchar *cfg_file_path)
{
YAML::Node configyml = YAML::LoadFile(cfg_file_path);
for(YAML::const_iterator itr = configyml["sink"].begin();
itr != configyml["sink"].end(); ++itr) {
std::string paramKey = itr->first.as<std::string>();
if (paramKey == "enc-type") {
int value = itr->second.as<gint>();
if(value == 0 || value == 1){
*enc_type = value;
}
}
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef _DS_YAML_PARSER_H_
#define _DS_YAML_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "nvds_yml_parser.h"
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#define _PATH_MAX 1024
typedef enum { ENCODER_TYPE_HW, ENCODER_TYPE_SW } EncHwSwType;
NvDsYamlParserStatus ds_parse_rtsp_output(GstElement *sink,
GstRTSPServer *server,
GstRTSPMediaFactory *factory,
gchar *cfg_file_path,
const char *group);
NvDsYamlParserStatus ds_parse_enc_config(GstElement *encoder,
gchar *cfg_file_path,
const char *group);
guint ds_parse_group_type(gchar *cfg_file_path, const char *group);
guint ds_parse_enc_type(gchar *cfg_file_path, const char *group);
guint ds_parse_enc_codec(gchar *cfg_file_path, const char *group);
GString *ds_parse_file_name(gchar *cfg_file_path, const char *group);
GString *ds_parse_config_yml_filepath(gchar *cfg_file_path, const char *group);
NvDsYamlParserStatus ds_parse_videotemplate_config(GstElement *vtemplate,
gchar *cfg_file_path,
const char *group);
NvDsYamlParserStatus ds_parse_ocdr_videotemplate_config(GstElement *vtemplate,
gchar *cfg_file_path,
const char *group);
NvDsYamlParserStatus ds_parse_nvdsanalytics(GstElement *element,
gchar *cfg_file_path,
const char *group);
void create_video_encoder(bool isH264, int enc_type,
GstElement **conv_capfilter, GstElement **outenc,
GstElement **encparse, GstElement **rtppay);
/** Function to get the absolute path of a file.*/
gboolean get_absolute_file_path_yaml(const gchar *cfg_file_path,
const gchar *file_path,
char *abs_path_str);
/** Parse preprocess configurations. */
NvDsYamlParserStatus nvds_parse_preprocess(GstElement *element,
gchar *app_cfg_file_path,
const char *group);
/** Parse postprocess configurations. */
NvDsYamlParserStatus nvds_parse_postprocess(GstElement *element,
gchar *app_cfg_file_path,
const char *group);
/** Parse width and height of nvstreammux. */
void parse_streammux_width_height_yaml(gint *width, gint *height,
gchar *cfg_file_path);
/** Parse type of sink. */
void parse_sink_type_yaml(gint *type, gchar *cfg_file_path);
/** Parse enc type of sink. */
void parse_sink_enc_type_yaml(gint *enc_type, gchar *cfg_file_path);
#ifdef __cplusplus
}
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
#include <cstring>
#include <fstream>
#include <ifaddrs.h>
#include <iostream>
#include <net/if.h>
#include <sstream>
#include <string>
#include <sys/ioctl.h>
#include <unistd.h>
#include <uuid/uuid.h>
// 获取第一个非零 MAC
std::string getFirstMac() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return "";
struct ifreq ifr;
struct ifconf ifc;
char buf[1024];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(fd, SIOCGIFCONF, &ifc) == -1) {
close(fd);
return "";
}
struct ifreq *it = ifc.ifc_req;
const struct ifreq *const end = it + (ifc.ifc_len / sizeof(struct ifreq));
for (; it != end; ++it) {
strcpy(ifr.ifr_name, it->ifr_name);
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
unsigned char *mac =
reinterpret_cast<unsigned char *>(ifr.ifr_hwaddr.sa_data);
if (!(mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
mac[4] == 0 && mac[5] == 0)) {
char mac_str[18];
snprintf(mac_str, sizeof(mac_str), "%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
close(fd);
return std::string(mac_str);
}
}
}
close(fd);
return "";
}
std::string GetDeviceID() {
// 优先读取环境变量 DEVICE_ID
const char *env = std::getenv("DEVICE_ID");
if (env && *env)
return std::string(env);
// 尝试读取 /etc/machine-id
std::ifstream f("/etc/machine-id");
if (f) {
std::string id;
std::getline(f, id);
id.erase(0, id.find_first_not_of(" \t\n\r"));
id.erase(id.find_last_not_of(" \t\n\r") + 1);
if (!id.empty())
return id;
}
// fallback: hostname + first non零 MAC
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) != 0) {
strcpy(hostname, "unknown");
}
std::string mac = getFirstMac();
return std::string(hostname) + "|" + mac;
}
std::string generateUUID() {
uuid_t id;
uuid_generate(id);
char str[37]; // UUID 36 个字符 + 终止符
uuid_unparse_lower(id, str);
return std::string(str);
}
+8
View File
@@ -0,0 +1,8 @@
// my_utils.h
#pragma once
#include <string>
extern gchar *yaml_file_path;
std::string getFirstMac();
std::string GetDeviceID();
std::string generateUUID();
+53
View File
@@ -0,0 +1,53 @@
# 打包
tar czvf sentinel.tar.gz --exclude='*.onnx' dist/
# 编译,在build下:
cmake ..
make
cmake --install . --prefix ../dist
# docker构建
sudo docker build \
-t ai.ronsunny.cn:13011/bbit_iot/ce_sentinel:1.1.2 \
-t ai.ronsunny.cn:13011/bbit_iot/ce_sentinel:latest \
.
# docker推送
sudo docker push ai.ronsunny.cn:13011/bbit_iot/ce_sentinel:1.1.2
sudo docker push ai.ronsunny.cn:13011/bbit_iot/ce_sentinel:latest
# docker试运行
docker compose up
# RTSP VLC 查看效果
rtsp://10.0.4.117:8554/ds-test
# 新机器需要运行
sudo apt-get install -y libgstrtspserver-1.0-0
sudo apt-get install language-pack-zh-hans
docker login ai.ronsunny.cn:13011
账号:iot_device
密码:Bbit000000
sudo systemctl daemon-reload
sudo systemctl enable edge_agent.service
sudo systemctl restart edge_agent.service
sudo systemctl stop edge_agent.service
sudo systemctl status edge_agent.service
# 查看日志
journalctl -u edge_agent.service -f
# ssh隧道
ssh -N -L 8080:192.168.1.3:80 bbit@10.1.5.103
# debug 日志级别
export GST_DEBUG=
级别 含义
0 NONE(最少日志)
1 ERROR
2 WARNING
3 INFO
4 DEBUG
5 LOG(最详细,包括每个 pad/element 处理信息)