news 2026/9/6 3:07:49

Qt+MinGW32+FFmpeg+RTSP:Windows平台视频拉流播放编译实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt+MinGW32+FFmpeg+RTSP:Windows平台视频拉流播放编译实战

简介:面向Windows下Qt与FFmpeg开发RTSP视频流显示的工程示例,适合需要快速集成实时视频预览的C++开发者。压缩包共103个文件,以81个头文件与5个动态库、5个静态库为主,另含4个源码文件、界面文件、图标及工程配置等,整体约11.58MB,结构精简便于直接查阅。目前已有1163人学习下载,广受同类项目开发者关注。代码无冗余封装,取消影子构建后在Windows下可完整编译运行,视频响应速度优于VLC、QTAV等播放器。支持三通道同显一个视频流,可单击截图保存至最后通道,也支持通道双击最大化,便于多路监控或比对场景。还附带编译好的可执行文件与FFmpeg运行库,节省环境配置时间。Linux用户只需替换对应FFmpeg库文件即可移植参考,虽未包含存储与回放功能,但作为实时显示基础框架具有实用价值。 “qt+mingw32+windows+ffmpeg+rtsp保证编译可用”,这个标题组合在我的收藏夹里躺了有一阵子了,也是很多做Windows桌面视频应用的开发者绕不开的一条路。说白了就是用Qt写界面,用FFmpeg做视频解码和RTSP拉流,再用MinGW32这套编译器把工程编译成一个能在Windows上直接跑的exe。这种需求在安防监控、工业上位机、远程画面预览这类项目里太常见了,几乎每个月都能在技术群看到有人在问。

但真正动手搭的时候,坑是一个接一个:Qt装错版本、FFmpeg库选不对、编译器链接器对不上、拉流没画面甚至直接闪退,问题五花八门。这篇文章就把我在真实项目里折腾出来的完整方案记下来,从环境搭建到工程配置,从核心代码到编译排错,把那些坑提前帮你趟平。打算做视频播放器、或者要给设备写RTSP监控客户端的开发者,都可以直接照着操作,整套流程是经过实际验证的。

1. 方案选型:Qt、MinGW32、FFmpeg这套组合怎么定位

1.1 这套组合要解决什么场景

一个视频类桌面工具,核心诉求通常是三件事:一个看得过去的操作界面、一个稳定的视频流拉取通道、一套可跨平台的消息和线程机制。Qt负责第一件和第三件,FFmpeg负责第二件,MinGW32则负责把这些东西在Windows上编译成一个可执行文件。

RTSP拉流本身不复杂,FFmpeg的libavformat库把RTSP协议的握手、包解析、音视频流分离全部封装好了,我们只要打开协议、读包、送解码器、取帧,再做一次像素格式转换,就能得到一帧可以往Qt界面上贴的图像。这里的关键不在协议本身,而在于怎么让这几个库在Windows环境下正确链接、正确部署,让程序在别人机器上也能双击就跑。

实际项目里,这类工具往往要对接海康、大华或者各种第三方RTSP流媒体服务器,地址格式差别不小,有的要求TCP传输,有的默认UDP,还有的带鉴权参数。这也是为什么代码里必须预留灵活的配置入口,不能写死传输方式。

1.2 为什么选中MinGW32而不是MSVC或MinGW64

很多人在选型阶段就纠结到不行。我的建议是:如果你不是已经在用MSVC配合Visual Studio做界面、且确定FFmpeg相关依赖都能找到兼容版本,那么在Qt + FFmpeg这条技术路线上,MinGW32是省心程度最高的选择。

原因也实在。第一,FFmpeg官方社区里,面向Windows的预编译包大量以MinGW为编译环境,MSVC版本的库需要额外去第三方站点找,而且经常要和C运行时库的版本做匹配,出问题很难查。第二,MinGW32生成的exe不依赖Visual Studio那套庞大的运行库,部署起来干净很多。第三,在32位环境下,FFmpeg的内存分配逻辑和各个模块的初始化和老一代视频采集设备SDK的兼容性都更好,很多工控机上的摄像头SDK至今只提供32位库,这点在做集成时相当关键。

MinGW64在纯64位场景下性能确实更好,但换来的是兼容性风险:一旦你的工程里某个不起眼的静态库还是32位的,整个链接过程就直接卡死。反过来,32位程序在64位Windows上运行一点问题没有。所以“能用32位就不折腾64位”,这是我踩过几次坑后的真实体会。

2. 环境搭建:下载、安装、匹配,一步不到位后面全完

2.1 Qt与MinGW32的安装配合

Qt这块我建议直接用Qt 5.15.2 LTS版本,配套的编译器选MinGW 32-bit。注意选择安装组件时,除了勾选Qt库本身,还要展开Tools节点,把MinGW编译器一并选上,否则打开Qt Creator连构建套件(Kit)都配不全。

安装时有一点容易被忽略:组件的勾选会直接影响编译路径。比如你只装了MSVC 2019 64-bit,然后又单独下载了MinGW编译器,手动去Kit设置里拼出来也是能编译的,但路径问题、环境变量问题一旦出错了,排查成本会高很多。更稳妥的做法是让Qt官方安装器把Qt库和对应的MinGW编译器作为一套整体装好,至少在版本匹配上不会出错。

装完以后,到“选项 - Kits - 编译器”里确认一下MinGW的编译器路径已经自动识别。如果没识别出来,手动把编译器路径指到MinGW的bin目录下的gcc.exe和g++.exe。这一步很多人忽略,最后编译时Qt Creator报“compiler cannot run”,绝大多数情况就是编译器没配对或者路径里的环境变量没生效。

2.2 FFmpeg开发版与共享版的区别和选择

FFmpeg在Windows下的下载包分很多种,最容易让人搞混的是dev和shared这两类。dev包里面是头文件(include目录)和静态链接库(lib目录),编译链接阶段要用的就是这些;shared包里面是动态链接库DLL和一整套exe工具,运行阶段必须有这些DLL。

实际下载时,还需要注意选32位版本,和项目的MinGW32保持一致。可以从gyan.dev或者BtbN的Windows builds页面下载,选择一个稳定release版本,比如4.4或者5.1系列,而不要追最新版。FFmpeg太新的版本往往编译工具链要求更高,和Qt附带的MinGW版本可能不匹配。

下载完成后,解压到项目里的第三方目录,比如3rdparty/ffmpeg,目录结构大概是:

3rdparty/ffmpeg/ include/ libavcodec/ libavformat/ libavutil/ libswscale/ lib/ libavcodec.dll.a libavformat.dll.a libavutil.dll.a libswscale.dll.a bin/ avcodec-59.dll avformat-59.dll avutil-57.dll ...

注意MinGW的lib目录里,库文件是.dll.a后缀的导入库,不是MSVC那种.lib文件。这一点决定了GCC链接时会用哪些文件。如果你的FFmpeg包是从MSVC版站点下载的,那里面提供的是.lib文件,在MinGW下链接会直接报错“file not recognized”,很多人都卡在这里。

2.3 运行目录的DLL部署策略

编译成功的exe放到别的机器上如果报“找不到avformat-59.dll”,多半是没有把FFmpeg建目录下的所有DLL复制到exe同目录。简单粗暴的姿势就是整个bin目录的DLL全拷过去,尤其别只拷自己以为用到的那几个,因为FFmpeg各模块之间也有依赖,漏掉一个都会启动失败。

Qt自己的运行库则用自带工具部署。在Qt安装目录的bin下有一个windeployqt.exe,命令行进入exe目录执行:

windeployqt RtspPlayer.exe

它会自动把Qt的core、gui、widgets等运行库和平台插件拷到exe同目录。有用户问为什么FFmpeg没有类似的部署工具,那是两码事,FFmpeg的DLL还是得手动拷。

3. 工程配置:.pro文件决定编译成败

3.1 合理的目录结构

工程文件不建议把所有头文件和库堆在一层目录里,时间长了自己都分不清。建议按下面这种结构组织:

RtspPlayer/ RtspPlayer.pro src/ main.cpp MainWindow.h MainWindow.cpp VideoThread.h VideoThread.cpp 3rdparty/ ffmpeg/ include/ lib/ bin/ bin/

整个项目只把3rdparty作为只读依赖,不往里面混入自己写的代码。这样以后换FFmpeg版本,只需要整体替换3rdparty/ffmpeg,不必动工程文件。

3.2 .pro文件详解

核心的.pro文件长这样:

QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = RtspPlayer TEMPLATE = app CONFIG += c++11 # FFmpeg 头文件目录 INCLUDEPATH += $$PWD/3rdparty/ffmpeg/include # FFmpeg 链接库 LIBS += -L$$PWD/3rdparty/ffmpeg/lib \ -lavformat \ -lavcodec \ -lavutil \ -lswscale \ -lswresample # 编译后的可执行文件输出到 bin DESTDIR = $$PWD/bin # 自动拷贝 FFmpeg DLL 到运行目录 FFMPEG_DLL_PATH = $$PWD/3rdparty/ffmpeg/bin QMAKE_POST_LINK += $$quote(cmd /c copy /y $$FFMPEG_DLL_PATH\\*.dll $$DESTDIR\\)

最后两行的QMAKE_POST_LINK是个很实用的配置,编译完自动把DLL复制到输出目录,省得每次手动拷。路径里的反斜杠注意用双反斜杠转义。如果是在其他shell环境,可能需要调整命令,但Windows下用cmd /c是稳的。

3.3 库链接顺序的门道

MinGW的GCC链接器对库的顺序非常敏感。链接时,处于左边的库如果引用了右边库的符号,能正确找到;反过来就找不到。所以写LIBS时,要把依赖别人的一方写在前面,被依赖的基础库写在后面。

在我上面的配置里,avformat依赖avcodec和avutil,avcodec又依赖avutil,所以顺序必须是avformat、avcodec、avutil。如果把avutil写在avformat前面,编译时大概率会冒出一堆undefined reference to avformat_xxx,而这个错误很容易让人误以为是库文件没放好。

可以在.cpp文件里这样引入FFmpeg头文件,为了兼容C和C++的符号规则:

extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libswscale/swscale.h> }

不包这个extern "C",链接时也会遇到找不到符号的问题,因为C++会把函数名做名字修饰(name mangling),而FFmpeg的库是C编译的。这个细节在新手项目里出现频率极高。

4. RTSP拉流与播放的实现

4.1 初始化和打开RTSP流

新版FFmpeg(4.x以后)已经不需要调用av_register_all()了,但网络初始化还是必须的。打开RTSP流之前,先调用一次网上初始化:

avformat_network_init();

然后设置连接参数。RTSP传输方式建议显式指定为TCP,否则默认走UDP,在弱网环境下很容易出现花屏、马赛克甚至拉不到流的问题。稳定性和实时性的权衡上,内网监控场景TCP更可靠:

AVFormatContext *fmtCtx = nullptr; AVDictionary *options = nullptr; av_dict_set(&options, "rtsp_transport", "tcp", 0); av_dict_set(&options, "stimeout", "5000000", 0); QByteArray urlBytes = url.toUtf8(); int ret = avformat_open_input(&fmtCtx, urlBytes.constData(), nullptr, &options); if (ret < 0) { char errbuf[128] = {0}; av_strerror(ret, errbuf, sizeof(errbuf)); qDebug() << "打开RTSP流失败:" << errbuf; return; }

注意stimeout单位是微秒,这里设成5秒,意思是超时5秒断开,避免网络抖动时程序一直挂起。

打开成功后紧接着avformat_find_stream_info获取流信息,再用av_find_best_stream找到视频流索引。RTSP流通常不止一路,可能是视频加音频,不找对索引,后面读包读到的可能就是音频数据。

4.2 解码线程与画面显示

这里强烈建议不要把av_read_frame放在Qt的UI线程里跑,否则解码一卡,整个界面就假死了。最常用的方案是写一个继承QThread的工作线程,循环读包、解码、发送信号,主线程更新QLabel。大致骨架如下:

void VideoThread::run() { // 前面步骤:open input、find stream info、open decoder…… AVPacket *pkt = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); while (!m_stopFlag && av_read_frame(fmtCtx, pkt) >= 0) { if (pkt->stream_index == m_videoIdx) { if (avcodec_send_packet(m_codecCtx, pkt) == 0) { while (avcodec_receive_frame(m_codecCtx, frame) == 0) { // 把YUV转换成RGB sws_scale(m_swsCtx, frame->data, frame->linesize, 0, m_codecCtx->height, m_rgbFrame->data, m_rgbFrame->linesize); QImage image(m_rgbFrame->data[0], m_codecCtx->width, m_codecCtx->height, QImage::Format_RGB888); emit frameReady(image.copy()); // 跨线程发送信号 } } } av_packet_unref(pkt); } // 清理资源 }

这里有两件事让我吃了不少亏。第一,FFmpeg解码出来默认是YUV420P,QLabel不认这个格式,必须用sws_getContext建一个转换上下文,再用sws_scale转成RGB24。第二,QImage对象在emit出去的时候要做一次深拷贝image.copy(),否则工作线程下一轮循环复用buffer后,UI线程拿到的图像已经被覆盖了,画面会闪。

信号连接放到MainWindow的构造函数里:

connect(&m_thread, &VideoThread::frameReady, this, &MainWindow::onFrameReady); void MainWindow::onFrameReady(const QImage &img) { ui->videoLabel->setPixmap( QPixmap::fromImage(img).scaled(ui->videoLabel->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); }

4.3 关键参数:rtsp_transport与超时设置

上一节提到的参数,我单独拎出来再说一下。RTSP默认走UDP,好处是实时性更好,但坏处是丢包时画面会碎掉,而且有些网络环境屏蔽UDP大包,根本拉不出来。用TCP能避免大部分网络问题,代价是延迟稍高,摄像头内网直连的场景完全感知不到。

参数设置时不光有rtsp_transport,还有一个常见的max_delay,这个参数对播放流畅度有影响。如果发现画面总是卡顿,可以试着把它调大一点:

av_dict_set(&options, "max_delay", "500000", 0);

数值单位是微秒,500000就是0.5秒。值得注意的是,这些参数不传也是可以打开的,默认行为和具体构建版本的FFmpeg配置有关,显式设置以后行为才有保证。

我还碰到过一种情况:RTSP流地址是H.265编码,而目标机器解码能力弱,画面经常花屏。这种情况和FFmpeg本身关系不大,更多是解码吞吐量和显示刷新率的匹配问题,需要做抽帧降帧率,或者换个支持硬解码的构建版本。这里不展开,但遇到类似问题时心里要有个概念。

5. 常见编译错误与排查

5.1 编译期问题速查表

把我在实际操作中遇到的高频问题和解决办法整理成一张表,方便快速对照:

报错信息原因解决办法
cannot find -lavformat-L路径不对或者库里没有对应的导入库文件检查3rdparty/ffmpeg/lib路径下是否存在libavformat.dll.a且位数一致
undefined reference to avformat_open_input链接顺序不对,或者头文件没包extern "C"调整LIBS顺序,把avformat放最前;检查extern "C"花括号范围
file not recognized: File format not recognized拿到了MSVC版的.lib文件,MinGW无法用换MinGW版的FFmpeg开发包,确保lib目录是.dll.a后缀
cannot run compilerQt Creator的Kit里编译器路径未正确识别到工具-选项-Kits-编译器里重新配置MinGW的gcc/g++
no such file or directory: libavformat/avformat.hINCLUDEPATH没写对确认.pro里INCLUDEPATH指向/include目录
程序启动直接崩溃 / 弹窗提示缺失DLLFFmpeg的DLL没拷贝到exe同目录检查bin目录下DLL数量和版本,把3rdparty/ffmpeg/bin里所有DLL拷过去

这里面有一个特别隐蔽的:32位和64位混用。Qt装了64位的MinGW,FFmpeg却下载了32位的库,链接时不一定立刻报错,但程序一运行就可能崩溃。我建议选型时就把位数钉死在一个方向上,检查3rdparty/ffmpeg/lib里的导入库,可以用file命令或者objdump确认架构。

5.2 运行期问题与调试技巧

编译通过只是第一步,运行期的问题更考验耐心。最常见的现象是程序能启动,但拉流失败,控制台没有任何输出。这种情况先在代码里把av_strerror的错误信息打印出来,再确认整个链路的每个包是否正常。

如果是卡在avformat_open_input那一步,先排除网络因素,用VLC播放器连接同一个RTSP地址,看能不能正常播放。VLC能播而自己代码播不了,多半是参数设置差了什么。优先检查rtsp_transport是不是需要设成tcp,以及是不是需要额外的鉴权参数。

解码开始后如果只有黑屏没有崩溃,检查一下sws_scale的调用参数。我遇到过把width和height传反的情况,输出画面是一种变形的条纹状,看起来像花屏但其实是尺寸不对。QImage构造时宽度高度顺序写反也是一个常见原因。

调试时建议先做一件事:把qDebug()挂到关键节点,比如打开流成功、找到解码器、拿到第一帧。在avcodec_receive_frame返回0时打印一次帧宽高,比对摄像头实际分辨率,能快速发现数据是否异常。我第一次跑通一个1080p的RTSP源时,就是这样一步步定位到画面被压缩变形的。

6. 写在最后的几点经验

这套环境在Windows下跑通之后,再用MSYS2去编译更新版本的FFmpeg也会顺手很多,因为两者的工具链是同源的。

分享一个很多人不知道的细节:Qt Creator里如果发现断点打不上、调试器不生效,检查一下Kit里的Debugger路径是不是指向了MinGW的gdb.exe。默认安装的Qt一般把Debugger配置好了,但如果你曾经动过环境变量,或者从旧版本升级过来,这个配置经常失效。别问我是怎么知道的。

还有一点,项目里尽量用QThread的第一种写法,就是继承QThread并实现run(),虽然网上很多人说应该用moveToThread加信号槽的方式,但在简单的RTSP拉流场景里,直接继承QThread代码结构更直观,也更容易控制解码循环的终止逻辑。线程退出时记得在析构函数里调用requestInterruption,再wait一下,保证资源干净释放。

最后再给一个可以立刻去测试的RTSP地址:rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,这是一个公开测试流,很适合验证你的环境是否已经跑通。拿它做完链路测试后,再换成自己项目里的真实摄像头地址,整个调试过程就不会有太多疑问了。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 23:02:06

网络竞价推广托管公司核心能力解析与实践参考:基于丽鸿科技服务案例的专业分析

一、网络竞价推广托管的核心定义与行业价值网络竞价推广托管是指企业将SEM竞价推广账户的全流程运营工作&#xff0c;委托给专业营销服务机构完成的服务模式。依托服务商的专业能力、媒体资源与运营经验&#xff0c;帮助企业提升投放效果&#xff0c;降低投入成本与内部管理成本…

作者头像 李华
网站建设 2026/9/4 4:35:41

SpringBoot快速入门:2小时搭建Java后端CRUD项目实战

这次我们来看一个面向 Java 开发新手的 SpringBoot 快速入门指南。对于很多刚接触后端开发的同学来说&#xff0c;SpringBoot 是一个绕不开的框架&#xff0c;但官方文档庞大&#xff0c;网络教程质量参差不齐&#xff0c;很容易让人在环境配置、依赖管理和项目结构上浪费大量时…

作者头像 李华
网站建设 2026/9/4 8:37:22

大模型输出转Word格式全攻略:从Markdown到docx的自动化方案

我相信你一定遇到过这个场景&#xff1a;让大模型写一份周报、技术方案或者需求文档&#xff0c;生成内容在网页端看整整齐齐&#xff0c;标题是标题&#xff0c;代码块是代码块&#xff0c;表格也有模有样。结果复制到 Word 里&#xff0c;标题层级全没了&#xff0c;列表缩进…

作者头像 李华
网站建设 2026/9/4 1:48:59

基于TensorFlow和CNN的花朵识别系统:从训练到部署全流程实战

简介&#xff1a;花朵识别系统是一套基于Python与TensorFlow实现的深度学习图像分类项目&#xff0c;面向希望入门卷积神经网络&#xff08;CNN&#xff09;的开发者与机器学习爱好者。项目通过TensorFlow搭建CNN模型&#xff0c;可对多种花朵图片进行自动化识别与分类&#xf…

作者头像 李华