基于 FFmpeg 与 V4L2 的多路摄像头视频采集,图像处理处理与 RTMP 推流项目(开源)

在直播、远程监控、视频会议等场景中,实时视频推流是核心需求之一。本文将分享一个基于 Linux 平台的解决方案,通过 V4L2 采集摄像头数据,结合 OpenCV 进行图像处理,最终使用 FFmpeg 编码并通过 RTMP 协议推流至服务器。项目代码模块化程度高,可扩展性强。

(本项目已完成pc平台及rk3566平台测试。)

项目代码已开源,链接:https://github.com/OHMYGODJONY/MultiCam-Realtime-Capture-and-Streaming.

对单个视频流灰度转换后效果:

扩展目标检测算法后检测效果:

一、项目背景与技术选型

1.1 需求场景

  • 实时采集摄像头视频数据(支持多设备)
  • 对视频帧进行自定义处理(如美颜、目标检测)
  • 将处理后的视频编码为 H.264 并通过 RTMP 推流

1.2 技术栈选择

  • V4L2:Linux 下标准的视频设备接口,直接操作硬件设备,支持 DMA 零拷贝,效率高于普通 USB 摄像头库。

v4l2教程:V4L2 使用教程_v4l2-compliance-CSDN博客

  • FFmpeg:强大的音视频处理库,提供编码、格式转换、协议推流等一站式功能。
  • 多线程 + 线程安全队列:实现采集、处理、推流的异步解耦,提升系统吞吐量。

二、整体架构设计

项目采用模块化设计,分为 4 个核心模块,数据流向清晰:

复制代码
摄像头设备 → CameraCapture(采集) → ThreadSafeQueue(数据传递) → EncoderStreamer(编码推流)
                                                                          ↑
                                                                   ImageProcessor(图像处理)
  • 采集模块(CameraCapture):基于 V4L2 实现摄像头初始化、参数配置、帧数据捕获。
  • 处理模块接口(ImageProcessor):提供可扩展的图像处理接口(提供BGR24DE cv::Mat格式数据),支持自定义算法。
  • 编码推流模块(EncoderStreamer):通过 FFmpeg 将视频编码为 H.264 并推流至 RTMP 服务器。
  • 线程安全队列(ThreadSafeQueue):实现采集线程与编码线程的解耦,避免阻塞。

三、开发环境与依赖安装

3.1 环境要求

  • 操作系统:Ubuntu 20.04+
  • 依赖库:
    • FFmpeg(libavformat、libavcodec、libswscale 等)

    • V4L2 开发库(libv4l-dev)

    • OpenCV(libopencv-dev)

    • pthread(线程库)

      安装FFmpeg

      sudo apt-get install libavutil-dev libavcodec-dev libavformat-dev libswscale-dev

      安装V4L2

      sudo apt-get install libv4l-dev v4l-utils

      安装OpenCV

      sudo apt-get install libopencv-dev

      安装线程库(通常系统已预装)

      sudo apt-get install libpthread-stubs0-dev

四、核心模块实现详解

4.1 摄像头采集模块(CameraCapture)

基于 V4L2 实现,核心是通过 IOCTL 命令与设备交互,流程如下:

  • 打开设备与检查能力

    通过open()打开摄像头设备(如/dev/video0),使用VIDIOC_QUERYCAP查询设备是否支持视频捕获和流式 IO。

cpp 复制代码
// 关键代码示例:打开设备并检查能力
fd_ = open(device_path_.c_str(), O_RDWR | O_NONBLOCK, 0);
v4l2_capability cap;
IOCTL_RETRY(fd_, VIDIOC_QUERYCAP, &cap);
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
    // 设备不支持视频捕获
}
  • 设置采集参数

    通过VIDIOC_S_FMT设置分辨率、像素格式(默认 YUYV422),通过VIDIOC_S_PARM设置帧率。

  • 缓冲区管理

    申请 4 个 DMA 缓冲区(VIDIOC_REQBUFS),通过mmap()映射到用户空间,实现零拷贝数据访问。缓冲区通过VIDIOC_QBUF(入队)和VIDIOC_DQBUF(出队)循环使用。

  • 帧采集线程

    启动独立线程循环调用get_frame(),通过select()等待缓冲区就绪,获取帧数据后通过回调函数推送至处理队列。

4.2 图像处理模块(ImageProcessor)

设计为可扩展接口,默认提供空实现,用户可通过继承重写processFrame()实现自定义逻辑:

cpp 复制代码
class ImageProcessor {
public:
    virtual void processFrame(cv::Mat& mat) {} // 基类空实现
};

// 示例:自定义添加水印
class WatermarkProcessor : public ImageProcessor {
    void processFrame(cv::Mat& mat) override {
        cv::putText(mat, "Live Stream", cv::Point(10, 30), 
                   cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);
    }
};

数据流转:摄像头原始帧(YUYV422)→ 转换为 BGR24(OpenCV 格式)→ 封装成cv::Mat → 调用processFrame()处理 → 转换回 YUV420P(编码所需格式)。

4.3 编码与 RTMP 推流模块(EncoderStreamer)

基于 FFmpeg 实现,核心流程:

  1. 初始化 FFmpeg

    • 调用avformat_network_init()初始化网络模块。
    • 创建输出格式上下文(AVFormatContext),指定 RTMP 地址和格式(flv)。
    • 查找 H.264 编码器(avcodec_find_encoder(AV_CODEC_ID_H264)),配置编码器参数(比特率、帧率、GOP 大小等)。
  2. 格式转换

    使用sws_scale()将图像处理后的 BGR24 格式转换为编码器所需的 YUV420P 格式。

  3. 编码与推流

    • 将转换后的帧通过avcodec_send_frame()送入编码器。
    • 通过avcodec_receive_packet()获取编码后的数据包(AVPacket)。
    • 调整时间戳(PTS)后,通过av_interleaved_write_frame()推流至 RTMP 服务器。
  4. 关键参数优化

    • 编码器参数:preset=ultrafast(快速编码,牺牲部分压缩率)、crf=23(质量控制)。
    • 推流参数:max_delay=0(减少延迟)、stimeout=2000000(2 秒超时)。

4.4 线程安全队列(ThreadSafeQueue)

基于std::mutexstd::condition_variable实现,支持超时机制,用于采集线程与编码线程的异步数据传递:

cpp 复制代码
// 入队(生产者)
bool push(const T& item, int timeout_ms = -1);

// 出队(消费者)
bool pop(T& item, int timeout_ms = 50);

通过队列解耦,避免采集线程因编码阻塞而丢帧,同时防止编码线程因无数据而空转。

五、常见问题与解决方案

  1. 摄像头无法打开

    • 检查设备权限:sudo chmod 666 /dev/video0
    • 确认设备存在:v4l2-ctl --list-devices
  2. 格式不支持

    • v4l2-ctl --list-formats-ext查询设备支持的分辨率和格式,调整初始化参数。
  3. 推流延迟高

    • 减少max_delay,降低编码器preset等级(如ultrafast)。
    • 缩小队列大小,避免帧堆积。
  4. 编码失败

    • 检查 FFmpeg 是否编译了 H.264 编码器:ffmpeg -encoders | grep h264
    • 确保输入帧格式为 YUV420P(编码器要求)。

总结

本文实现了从摄像头采集到 RTMP 推流的完整链路,核心在于通过模块化设计和多线程异步处理提升系统效率。V4L2 保证了采集性能,FFmpeg 简化了编码推流流程,而可扩展的图像处理接口为业务定制提供了便利。