初始化项目
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
dist/**/*.onnx
|
||||
**/*.onnx
|
||||
**/*.mp4
|
||||
**/*.jpg
|
||||
+164
@@ -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/
|
||||
)
|
||||
@@ -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"]
|
||||
Vendored
+25
@@ -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
|
||||
Vendored
+61
@@ -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滤波器时期望响应的高斯标准差(像素)
|
||||
Vendored
+55
@@ -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
|
||||
|
||||
|
||||
Vendored
+55
@@ -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
@@ -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
|
||||
Vendored
+28
@@ -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
|
||||
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+4
@@ -0,0 +1,4 @@
|
||||
car
|
||||
bicycle
|
||||
person
|
||||
road_sign
|
||||
Binary file not shown.
BIN
Binary file not shown.
@@ -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
@@ -0,0 +1 @@
|
||||
coupe;largevehicle;sedan;suv;truck;van
|
||||
+65025
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -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
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
@@ -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
@@ -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)) {
|
||||
/* 必须是 NVMM(GPU 内存) */
|
||||
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(¤t_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;
|
||||
}
|
||||
// 输出UDP(RTSP 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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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(¤t_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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 处理信息)
|
||||
Reference in New Issue
Block a user