FFmpeg 集成指南(RTSP 实时视频流 + OpenCV 处理)

本文介绍如何在 Windows C++ 项目中集成 FFmpeg,实现 RTSP 流的低延迟解码,并与 OpenCV 无缝衔接,同时保持项目架构清晰、不依赖系统环境变量、便于分发。所有关键配置均用 Visual Studio 项目属性完成,不修改系统 PATH。


1. 准备工作:获取 FFmpeg 预编译包

从 FFmpeg 官方或第三方构建(如 BtbN/FFmpeg-Builds)下载 Windows 平台的 devshared 压缩包。解压将存在如下目录结构:

复制代码
FFmpeg\
  include\          (开发头文件)
    libavcodec\
    libavformat\
    libavutil\
    libswscale\
    ...
  lib\              (导入库 .lib,编译链接时使用)
    avcodec.lib
    avformat.lib
    avutil.lib
    swscale.lib
    ...
  bin\              (动态库 .dll,运行时使用)
    avcodec-60.dll
    avformat-60.dll
    avutil-60.dll
    swscale-7.dll
    ...

注意 :不同版本的 FFmpeg 库文件名可能带有主版本号(如 avcodec-60.libavcodec-60.dll),后续配置中需填写实际文件名。


2. 创建 FFmpegCore 静态库"壳"项目

在解决方案中新建一个 静态库(.lib) 项目,命名为 FFmpegCore。该项目仅用于集中管理 FFmpeg 头文件和导入库依赖,不添加业务逻辑代码。

2.1 项目属性配置(所有配置、x64 平台)

属性页 设置项
C/C++ → 常规 附加包含目录 $(ProjectDir)include
链接器 → 常规 附加库目录 $(ProjectDir)lib
链接器 → 输入 附加依赖项(Release) avcodec.lib avformat.lib avutil.lib swscale.lib ...(按实际文件名)
链接器 → 输入 附加依赖项(Debug) 同上(若 Debug 库带 -d 后缀则需对应)

说明 :此项目仅需包含一个预编译头(pch)或空源文件,确保能编译出 FFmpegCore.lib。其作用是为其他项目提供统一的 FFmpeg 依赖描述。

2.2 头文件可视化管理(可选)

在 Visual Studio 解决方案资源管理器中,为 FFmpegCore 项目添加筛选器(如 libavcodeclibavformat 等),然后将 include 下对应子目录的所有 .h 文件作为"现有项"添加进去。这样无需离开 IDE 即可浏览 FFmpeg API 声明。


3. 配置 GetVideoFrame项目以使用 FFmpeg

假设视频采集核心模块为动态库项目 GetVideoFrame,现需让它能够调用 FFmpeg 函数。

3.1 添加项目引用

在 GetVideoFrame项目上右键 → 添加 → 引用,勾选 FFmpegCore

这将自动设置构建顺序,并在链接时包含 FFmpegCore.lib

3.2 配置包含与链接

在 GetVideoFrame项目属性(Debug/Release | x64)中:

属性页 设置项 追加内容
C/C++ → 常规 附加包含目录 $(SolutionDir)FFmpegCore\include
链接器 → 常规 附加库目录 $(SolutionDir)FFmpegCore\lib
链接器 → 输入 附加依赖项 与 FFmpegCore 中完全相同的 FFmpeg .lib 列表

原因 :GetVideoFrame将直接使用 FFmpeg 的 C API,因此必须在自己编译时解析这些符号;静态库 FFmpegCore.lib 不会传递导入库依赖。

3.3 运行时 DLL 部署

为了保证 FFmpeg 的动态库 .dll 必须能被最终可执行文件找到 。采用 子目录隔离 避免 Bin 目录杂乱。

GetVideoFrame 的生成后事件中添加(Release 与 Debug 均需):

复制代码
xcopy /y "$(SolutionDir)..\FFmpegCore\bin\*.dll" "$(SolutionDir)..\Bin\ffmpeg\"

此时最终输出目录结构为:

复制代码
Bin\
  MyApp.exe
  GetVideoFrame.dll
  ffmpeg\
    avcodec-60.dll
    avformat-60.dll
    ...

在可执行程序的入口点添加 DLL 搜索路径 (例如在 main()WinMain() 最开始):

复制代码
SetDllDirectory(L".\\ffmpeg");

这样系统就能自动找到子目录下的 FFmpeg DLL,无需修改环境变量。


4. 编写流接收器工具类(位于 GetVideoFrame项目内)

在 GetVideoFrame项目中新增 VideoStreamReceiver 类,负责 RTSP 拉流、解码并输出 YUV→RGB 转换后的帧。核心接口如下:

复制代码
class VideoStreamReceiver {
public:
    bool open(const std::string& rtsp_url, const AVDictionary* opts = nullptr);
    AVFrame* getYUVFrame();          // 返回解码后的 AVFrame(YUV)
    bool convertToRGB(const AVFrame* yuv, cv::Mat& rgb);
    void close();
    bool isOpen() const;
};

4.1 低延迟参数设置

open 方法中,必须通过字典传递 RTSP 和缓冲区参数以保障实时性:

复制代码
AVDictionary* opts = nullptr;
av_dict_set(&opts, "rtsp_transport", "tcp", 0);   // TCP 传输更稳定
av_dict_set(&opts, "flags", "low_delay", 0);       // 低延迟标志
av_dict_set(&opts, "probesize", "500000", 0);      // 减小探测数据量
av_dict_set(&opts, "analyzeduration", "500000", 0);// 缩短分析时长
// 也可通过 fmtCtx->flags |= AVFMT_FLAG_NOBUFFER; 禁用缓冲

avformat_open_input(&fmtCtx, url.c_str(), nullptr, &opts);
av_dict_free(&opts);

4.2 帧转换

使用 sws_scale 将 AVFrame 转换为 OpenCV 的 cv::Mat(BGR24 格式,方便显示与处理)。转换前后需合理管理 SwsContext 缓存。


5. 将流接收器嵌入 GetVideoFrame主类

在 GetVideoFrame类中增加 RTSP 模式的成员:

复制代码
bool use_rtsp_ = false;
std::string rtsp_url_;
std::unique_ptr<VideoStreamReceiver> stream_receiver_;

5.1 构造函数重载

增加直接接受 RTSP URL 的构造函数,设置 use_rtsp_=true

5.2 初始化与启动

  • 初始化 :调用如果当前使用RTSP拉流,则创建实例,然后开启接收 stream_receiver_->open() ,并stream_receiver_->getYUVFrame() + convertToRGB() 得到 RGB 帧获取一帧以确定帧尺寸、通道数等信息,方便你的设备获取到帧信息,进行你设备的初始化。

  • 开启线程采集:直接启动采集线程。

  • 采集线程 :循环调用 stream_receiver_->getYUVFrame() + convertToRGB() 得到 RGB 帧,加入处理队列,方便后续处理流程使用RGB帧进行处理。

  • 停止线程采集 :增加对 stream_receiver_->close() 的调用。

5.3 接口保持统一

原有所有功能(图像设置曝光、亮度、饱和度等)均无需修改,因为它们操作的是处理后的 cv::Mat,与帧来源无关。


6. 编译与测试

  1. 首先生成 FFmpegCore 项目,确认 FFmpegCore.lib 输出无误。

  2. 生成 GetVideoFrame项目,检查 FFmpeg DLL 是否正确拷贝至 Bin\ffmpeg\

  3. 确认可执行程序已调用 SetDllDirectory(L".\\ffmpeg")

  4. 运行程序,使用 RTSP 地址进行采集,观察延迟与帧率。


7. 常见问题

链接错误 LNK1181:无法打开输入文件"avcodec.lib"

  • 检查 附加库目录 是否包含 $(SolutionDir)FFmpegCore\lib

  • 检查 附加依赖项 中的文件名是否与 lib 目录下的实际文件名一致(可能带版本号)。

运行时找不到 DLL

  • 确认生成后事件将 .dll 拷贝到了正确的子目录。

  • 确认 SetDllDirectory 在加载 GetVideoFrame.dll 之前执行。

延迟较高

  • 确保在 open 中设置了 rtsp_transport=tcp(或 udp 根据网络选择)及 flags=low_delay

  • 解码器可开启多线程:codecCtx->thread_count = 0


通过以上步骤,我们可以在不修改系统环境、不暴露核心业务代码的前提下,将 FFmpeg 稳定地集成到现有 C++ 项目中,实现高性能 RTSP 视频流处理。

当然,如果后续需要保证采集性能,可以将VideoStreamReceiver开启将拉流、解码、颜色转换合并到一个独立的生产者线程,然后把 cv::Mat 放入线程安全队列,让业务线程(如 GetVideoFrame的采集线程)直接取出使用,这样,拉取帧线程和采集视频帧线程隔离,生产者-消费者模型建立:

  • 网络/解码/转换的耗时完全隔离在生产者线程;

  • 消费者线程每次只需取队列里的成品,基本无延迟;

  • 队列有最大容量,防止内存无限制增长。