一、系统概述
1.1 测试用例简介
本测试demo是一个基于RK3588平台的多摄像头长时间录制demo,支持同时连接多个DVP摄像头,以固定帧率(30fps)和分辨率(1920×1080)进行视频采集,并通过RGA硬件加速进行格式转换,使用GStreamer管道进行硬件编码和分段存储,确保连续两端之段视频帧率一致,空隙没有帧丢失。
1.2 设计目标
-
长时间稳定录制:支持不间断长时间录制(可设置分段时长)
-
多摄像头同步:确保多个摄像头录制起始时间一致
-
帧率精确控制:保证每段时间内帧数准确无误
-
内存 优化:在有限内存(7.68GB)下实现高效录制
-
分段存储:按时间自动分割视频文件,便于管理和检索
1.3 系统特性
-
支持最多3个摄像头同时录制
-
每段录制时长可配置(当前为30分钟/段)
-
使用硬件加速(RGA、MPP H.264编码器)
-
环形缓冲区机制,防止帧丢失
-
实时监控和状态报告
二、系统架构设计
2.1 整体架构
2.2 模块划分
2.2.1 主控制模块(main函数)
-
系统初始化和配置
-
摄像头检测和选择
-
信号处理(Ctrl+C,SIGTERM)
-
用户交互(开始/停止)
-
整体状态监控和报告
2.2.2 摄像头线程模块(camera_thread_func)
每个摄像头独立的处理线程,包含:
-
RGA初始化:硬件加速格式转换
-
环形缓冲区创建:缓存待编码帧
-
DVP 摄像头初始化:图像采集
-
GStreamer管道初始化:视频编码和存储
-
帧率控制器初始化:时间戳管理
2.2.3 DVP回调模块(dvp_frame_callback)
-
图像采集回调函数
-
RGA格式转换(BGR → NV12)
-
帧数据包装和时间戳生成
-
环形缓冲区推送
2.2.4 GStreamer推送模块(gst_push_thread_func)
-
从环形缓冲区获取帧数据
-
推送帧到GStreamer管道
-
分段控制和验证
-
状态监控和报告
三、关键技术实现
3.1 帧率控制机制
3.1.1 帧率控制器设计
typedef struct {
int64_t frame_duration_ns; // 每帧的纳秒数(33,333,333 ns @ 30fps)
int64_t next_frame_time; // 下一帧应该出现的时间
int64_t frame_counter; // 帧计数器
pthread_mutex_t mutex; // 互斥锁,确保线程安全
} FrameRateController;
3.1.2 工作原理
-
初始化:根据目标FPS计算每帧持续时间
-
时间戳生成:确保时间戳单调递增且连续
-
帧计数:精确统计实际推送的帧数
-
线程安全:使用互斥锁保护共享数据
3.1.3 关键函数
-
frame_rate_controller_init():初始化控制器 -
frame_rate_controller_reset():重置时间基准 -
frame_rate_controller_get_next_pts():获取下一个时间戳
3.2 环形缓冲区设计
3.2.1 数据结构
template<typename T>
class RingBuffer {
private:
std::vector<T> buffer_; // 缓冲区数组
size_t capacity_; // 缓冲区容量
size_t head_ = 0; // 读指针
size_t tail_ = 0; // 写指针
size_t count_ = 0; // 当前帧数
std::mutex mutex_; // 互斥锁
std::condition_variable not_empty_; // 非空条件变量
std::condition_variable not_full_; // 非满条件变量
std::atomic<bool> stop_{false}; // 停止标志
std::atomic<size_t> discarded_count_{0}; // 丢弃帧计数器
};
3.2.2 工作模式
-
生产者模式(DVP回调线程):
-
非阻塞推送:
push_nonblock() -
缓冲区满时丢弃最旧帧
-
记录丢弃帧数用于监控
-
消费者模式(GStreamer推送线程):
-
阻塞读取:
pop_blocking() -
缓冲区空时等待
-
支持优雅停止
3.2.3 内存优化
-
固定容量,避免动态内存分配
-
帧数据使用智能指针管理
-
丢弃统计,便于性能分析
3.3 同步控制机制
3.3.1 启动同步
// 所有摄像头线程等待屏障
pthread_barrier_wait(&g_sync_control.start_barrier);
3.3.2 状态同步
-
ready_count:摄像头就绪计数 -
should_stop:全局停止标志 -
recording_started:录制开始标志 -
使用原子操作确保线程安全
3.4 GStreamer管道设计
3.4.1 管道配置
appsrc → queue → mpph264enc → h264parse → splitmuxsink
3.4.2 关键参数
// 管道配置字符串
"appsrc name=source is-live=true format=time do-timestamp=true "
"caps=video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! "
"queue max-size-buffers=20 max-size-time=666666666 ! " // 约20帧缓冲
"mpph264enc gop=9000 ! " // 5分钟关键帧间隔
"h264parse ! "
"splitmuxsink max-size-time=54000000000 " // 30分钟分段
"location=/path/to/output_%04d.mp4"
3.4.3 分段控制
-
max-size-time:控制每段时长 -
max-files:最大文件数(0表示无限制) -
异步文件写入:
async-finalize=true
四、内存管理策略
4.1 内存使用分析
4.1.1 主要内存消耗组件
| 组件 | 每摄像头 | 3个摄像头 | 备注 |
|---|---|---|---|
| RGA缓冲区 | ~9MB | ~27MB | BGR(6MB) + NV12(3MB) |
| 环形缓冲区 | ~450MB | ~1.35GB | 150帧 × 3MB/帧 |
| GStreamer队列 | ~60MB | ~180MB | 20帧 × 3MB/帧 |
| 编码器内部缓冲 | ~100MB | ~300MB | 估算值 |
| 总计 | ~619 MB | ~1.857 GB |
4.1.2 优化策略
-
缓冲区容量优化:
-
环形缓冲区:150帧(5秒缓冲)
-
GStreamer队列:20帧(0.67秒缓冲)
-
帧数据管理:
-
避免不必要的拷贝
-
及时释放不再使用的帧
-
内存 监控:
-
定期检查内存使用情况
-
动态调整缓冲区大小(可选)
4.2 内存泄漏预防
4.2.1 资源清理顺序
1. 停止DVP摄像头采集
2. 停止GStreamer推送线程
3. 发送EOS信号到GStreamer管道
4. 等待管道停止
5. 释放GStreamer资源
6. 释放环形缓冲区
7. 释放RGA缓冲区
8. 重置状态标志
4.2.2 异常处理
-
信号处理:优雅退出
-
错误检查:及时发现和处理错误
-
资源释放:确保所有资源都被正确释放
五、性能优化
5.1 实时性保障
5.1.1 线程优先级
-
DVP回调线程:高优先级,确保及时处理图像
-
GStreamer推送线程:中优先级,平衡处理速度
-
主控制线程:低优先级,响应性要求不高
5.1.2 缓冲区设计
-
环形缓冲区:吸收处理延迟
-
GStreamer队列:平衡编码速度
-
适当的缓冲区大小,避免过度缓冲
5.2 编码优化
5.2.1 GOP配置
-
GOP大小:9000帧(5分钟)
-
平衡编码效率和随机访问性
-
避免过大GOP导致文件损坏风险
5.2.2 硬件加速
-
RGA:BGR到NV12格式转换
-
MPP H.264编码器:硬件编码,降低CPU负载
-
DMA传输:减少内存拷贝
六、错误处理和恢复
6.1 错误类型和处理策略
6.1.1 可恢复错误
-
单个帧处理失败:丢弃当前帧,继续录制
-
短暂的系统资源不足:等待并重试
-
文件系统空间不足:停止录制并报告
6.1.2 不可恢复错误
-
摄像头设备断开:停止该摄像头录制
-
硬件编码器故障:停止所有录制
-
内存耗尽:立即停止并清理资源
6.2 监控和报警
6.2.1 实时监控
-
帧率监控:确保实际帧率接近目标帧率
-
内存监控:防止内存泄漏
-
磁盘空间监控:避免写入失败
6.2.2 状态报告
-
周期性状态报告(每5分钟)
-
分段完成报告
-
错误事件立即报告
七、配置和扩展
7.1 可配置参数
7.1.1 录制参数
// 在程序头部的宏定义中修改
#define VIDEO_WIDTH 1920 // 视频宽度
#define VIDEO_HEIGHT 1080 // 视频高度
#define FPS 30 // 帧率
#define SEGMENT_DURATION 1800 // 分段时长(秒)
#define RING_BUFFER_CAPACITY 150 // 环形缓冲区容量
7.1.2 路径参数
#define VIDEO_FILE_BASE "/mnt/usbhd/zqz_project/monkey/video/test_30min/"
7.2 扩展可能性
7.2.1 更多摄像头
-
修改
MAX_CAMERAS宏定义 -
确保硬件支持更多摄像头连接
-
调整内存分配策略
7.2.2 不同分辨率/帧率
-
修改视频参数宏定义
-
调整RGA缓冲区大小
-
更新GStreamer管道配置
7.2.3 其他编码格式
-
修改GStreamer管道,使用不同编码器
-
调整编码参数
-
确保硬件支持目标编码格式
八、部署和运行
8.1 环境要求
8.1.1 硬件要求
-
RK3588开发板
-
DVP摄像头(支持1920×1080@30fps)
-
充足的内存(建议≥4GB)
-
大容量存储(SD卡或USB硬盘)
8.1.2 软件要求
-
Linux操作系统
-
GStreamer 1.0及以上
-
RGA驱动
-
DVP摄像头驱动
-
C++11编译器
8.2 编译和运行
8.2.1 编译命令
g++ -std=c++11 -o video_recorder 1224.cpp \
$(pkg-config --cflags --libs gstreamer-1.0 gstreamer-app-1.0) \
-lpthread -lrga
8.2.2 运行命令
# 直接运行
./video_recorder
# 后台运行并记录日志
nohup ./video_recorder > recording.log 2>&1 &
8.3 监控和调试
8.3.1 日志文件
-
程序输出日志:
recording.log -
状态记录日志:
recording_status.log -
GStreamer调试日志:设置
GST_DEBUG环境变量
8.3.2 性能监控
# 查看进程状态
top -p $(pgrep video_recorder)
# 查看内存使用
cat /proc/$(pgrep video_recorder)/status
# 查看磁盘空间
df -h /mnt/usbhd
九、总结
9.1 设计亮点
-
硬件加速:充分利用RK3588硬件特性
-
精确帧率控制:确保时间准确性和连续性
-
健壮的 内存 管理:在有限内存下稳定运行
-
完善的监控机制:实时掌握系统状态
-
优雅的错误处理:增强系统稳定性
9.2 适用场景
-
长时间监控录制
-
多角度视频采集
-
需要精确时间同步的应用
-
嵌入式视频录制系统
9.3 未来改进方向
-
网络流输出:支持RTSP/RTMP推流
-
智能分析:集成移动检测、人脸识别
-
远程控制:Web界面控制录制参数
-
云存储:自动上传到云存储
本测试是一个完整、高效的多摄像头录制解决方案,经过优化和测试,能够在RK3588平台上稳定运行,满足长时间、高质量的视频录制需求。
