多摄像头长时间分段录制系统设计思路

一、系统概述

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)

每个摄像头独立的处理线程,包含:

  1. RGA初始化:硬件加速格式转换

  2. 环形缓冲区创建:缓存待编码帧

  3. DVP 摄像头初始化:图像采集

  4. GStreamer管道初始化:视频编码和存储

  5. 帧率控制器初始化:时间戳管理

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 工作原理

  1. 初始化:根据目标FPS计算每帧持续时间

  2. 时间戳生成:确保时间戳单调递增且连续

  3. 帧计数:精确统计实际推送的帧数

  4. 线程安全:使用互斥锁保护共享数据

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 工作模式

  1. 生产者模式(DVP回调线程):

  2. 非阻塞推送:push_nonblock()

  3. 缓冲区满时丢弃最旧帧

  4. 记录丢弃帧数用于监控

  5. 消费者模式(GStreamer推送线程):

  6. 阻塞读取:pop_blocking()

  7. 缓冲区空时等待

  8. 支持优雅停止

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 优化策略

  1. 缓冲区容量优化

  2. 环形缓冲区:150帧(5秒缓冲)

  3. GStreamer队列:20帧(0.67秒缓冲)

  4. 帧数据管理

  5. 避免不必要的拷贝

  6. 及时释放不再使用的帧

  7. 内存 监控

  8. 定期检查内存使用情况

  9. 动态调整缓冲区大小(可选)

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 设计亮点

  1. 硬件加速:充分利用RK3588硬件特性

  2. 精确帧率控制:确保时间准确性和连续性

  3. 健壮的 内存 管理:在有限内存下稳定运行

  4. 完善的监控机制:实时掌握系统状态

  5. 优雅的错误处理:增强系统稳定性

9.2 适用场景

  • 长时间监控录制

  • 多角度视频采集

  • 需要精确时间同步的应用

  • 嵌入式视频录制系统

9.3 未来改进方向

  1. 网络流输出:支持RTSP/RTMP推流

  2. 智能分析:集成移动检测、人脸识别

  3. 远程控制:Web界面控制录制参数

  4. 云存储:自动上传到云存储

本测试是一个完整、高效的多摄像头录制解决方案,经过优化和测试,能够在RK3588平台上稳定运行,满足长时间、高质量的视频录制需求。