本文介绍如何在 Windows C 项目中集成 FFmpeg实现 RTSP 流的低延迟解码并与 OpenCV 无缝衔接同时保持项目架构清晰、不依赖系统环境变量、便于分发。所有关键配置均用 Visual Studio 项目属性完成不修改系统 PATH。1. 准备工作获取 FFmpeg 预编译包从 FFmpeg 官方或第三方构建如 BtbN/FFmpeg-Builds下载 Windows 平台的dev与shared压缩包。解压将存在如下目录结构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.lib、avcodec-60.dll后续配置中需填写实际文件名。2. 创建 FFmpegCore 静态库“壳”项目在解决方案中新建一个静态库(.lib)项目命名为FFmpegCore。该项目仅用于集中管理 FFmpeg 头文件和导入库依赖不添加业务逻辑代码。2.1 项目属性配置所有配置、x64 平台属性页设置项值C/C → 常规附加包含目录$(ProjectDir)include链接器 → 常规附加库目录$(ProjectDir)lib链接器 → 输入附加依赖项Releaseavcodec.libavformat.libavutil.libswscale.lib...按实际文件名链接器 → 输入附加依赖项Debug同上若 Debug 库带-d后缀则需对应说明此项目仅需包含一个预编译头pch或空源文件确保能编译出FFmpegCore.lib。其作用是为其他项目提供统一的 FFmpeg 依赖描述。2.2 头文件可视化管理可选在 Visual Studio 解决方案资源管理器中为FFmpegCore项目添加筛选器如libavcodec、libavformat等然后将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(); // 返回解码后的 AVFrameYUV 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::MatBGR24 格式方便显示与处理。转换前后需合理管理SwsContext缓存。5. 将流接收器嵌入 GetVideoFrame主类在 GetVideoFrame类中增加 RTSP 模式的成员bool use_rtsp_ false; std::string rtsp_url_; std::unique_ptrVideoStreamReceiver 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. 编译与测试首先生成FFmpegCore项目确认FFmpegCore.lib输出无误。生成 GetVideoFrame项目检查 FFmpeg DLL 是否正确拷贝至Bin\ffmpeg\。确认可执行程序已调用SetDllDirectory(L.\\ffmpeg)。运行程序使用 RTSP 地址进行采集观察延迟与帧率。7. 常见问题链接错误 LNK1181无法打开输入文件“avcodec.lib”检查附加库目录是否包含$(SolutionDir)FFmpegCore\lib。检查附加依赖项中的文件名是否与lib目录下的实际文件名一致可能带版本号。运行时找不到 DLL确认生成后事件将.dll拷贝到了正确的子目录。确认SetDllDirectory在加载 GetVideoFrame.dll 之前执行。延迟较高确保在open中设置了rtsp_transporttcp或 udp 根据网络选择及flagslow_delay。解码器可开启多线程codecCtx-thread_count 0。通过以上步骤我们可以在不修改系统环境、不暴露核心业务代码的前提下将 FFmpeg 稳定地集成到现有 C 项目中实现高性能 RTSP 视频流处理。当然如果后续需要保证采集性能可以将VideoStreamReceiver开启将拉流、解码、颜色转换合并到一个独立的生产者线程然后把cv::Mat放入线程安全队列让业务线程如GetVideoFrame的采集线程直接取出使用这样拉取帧线程和采集视频帧线程隔离生产者-消费者模型建立网络/解码/转换的耗时完全隔离在生产者线程消费者线程每次只需取队列里的成品基本无延迟队列有最大容量防止内存无限制增长。