FFmpeg与Qt开发环境搭建指南:从下载配置到链接运行全流程
2026/9/15 21:09:21 网站建设 项目流程

搞音视频处理的人,迟早会撞上FFmpeg和QT这对组合。一边是业界事实标准的音视频处理库,一边是成熟的跨平台GUI框架,两者加起来几乎就是一套“本地视频工具箱”的万能底座。但真正把FFmpeg+QT开发环境搭起来的时候,很多人第一轮就卡住了:不是头文件找不到,就是链接报undefined reference,要么好不容易编译通过,程序一运行又报“找不到dll”。

这篇文章就只干一件事:把ffmpeg+QT开发环境搭建讲透,从选型、下载、配置到验证,一条龙走完。目标很简单——让你拿到代码能编译、能运行、能自己打印出视频信息。适合刚接触FFmpeg的QT开发者,也适合准备做播放器、转码工具、视频分析工具但还没迈出第一步的朋友。

1. 为什么非要把FFmpeg和QT绑在一起

1.1 这对组合到底解决什么问题

FFmpeg最常用的方式是在命令行敲命令,比如转格式、截图、抽帧、拼接视频,命令行确实强大,但交互体验很原始。你要做一个给同事、给客户用的工具,总不能要求人家背命令。QT的价值就在这里——它负责界面、按钮、文件选择、进度条、预览窗口这些“看得见摸得着”的部分,FFmpeg负责后台真正的音视频处理。

我用这对组合做过一个批量转码小工具,界面就一个列表加两个按钮,选了视频文件后调用FFmpeg转成统一分辨率,再在进度条上显示转换进度。没有QT的话,这个工具只能停留在“我自己在命令行敲命令”的阶段;没有FFmpeg的话,QT再漂亮也做不了核心的编解码功能。两者互补性非常强。

1.2 FFmpeg不是只有一个“ffmpeg.exe”

很多刚接触的人有个误区,以为FFmpeg就是那个命令行exe。实际上,FFmpeg给开发者提供了一整套C接口的库,按功能拆成几个核心模块:

  • libavformat:封装格式处理,负责解析和封装MP4、MKV、FLV等容器格式
  • libavcodec:编解码核心,负责H.264、HEVC、AAC等编码解码
  • libavfilter:滤镜链,负责缩放、裁剪、水印、画中画等处理
  • libswscale:像素格式转换和图像缩放
  • libswresample:音频重采样和格式转换
  • libavdevice:设备输入输出,比如采集摄像头画面
  • libpostproc:后处理,主要用于一些兼容性场景

命令行里的ffmpeg.exe本质上就是把这些库拼装起来的壳。我们自己写QT程序的时候,不需要去调用ffmpeg.exe进程,而是直接链接这些库,在代码里调用API。这样效率更高、可控制性更强,而且可以实现细节定制——比如转码到一半取消、实时预览画面、按帧处理,这些靠命令行很难做到。

1.3 什么情况下这个组合才值得用

如果你是七八年前的我,纯粹想快速做一个小工具,可以先问自己几个问题:要不要GUI?要不要在界面上实时显示画面或进度?处理逻辑复杂不复杂?答案都是“是”的话,FFmpeg+QT基本就是最优解。

如果你的需求只是批处理转码,不想写界面,那直接用FFmpeg命令行脚本就够了,没必要搬出QT这套重家伙。反过来,如果你的目标是在界面上播放视频、做帧级别的滤镜预览,那FFmpeg+QT这套组合基本上绕不开。搞清楚使用边界,环境搭建才不会白费功夫。

2. 动手前的选型:QT套件和FFmpeg构建版本怎么配对

2.1 QT用MinGW还是MSVC,这件事别搞错

QT在Windows上安装时会让你选编译器套件,最常见的是MinGW和MSVC两套。很多新手不管三七二十一,看到哪个装哪个,结果后面链接FFmpeg库的时候各种报错,问题多半就出在这。

注意,FFmpeg的官方Windows构建包通常是用MinGW/GCC工具链编译出来的,也就是说它提供的是.a后缀的库文件。这和你QT里选用的编译器必须匹配:QT如果用的是MinGW套件,直接下载BtbN或类似渠道的MinGW版FFmpeg构建,链接就顺了;QT如果用的是MSVC套件,那就不能用MinGW编译出的.a库去链接,否则会看到一堆莫名其妙的undefined reference。

我的建议非常直接:新手统一用QT的MinGW套件,不要碰MSVC。原因很简单,MinGW版FFmpeg构建包满大街都是,下载解压就能用,几乎零成本。MSVC用户想要顺利链接FFmpeg,通常得借助vcpkg或者自己编译,这已经超出了“搭建环境”的范畴,属于给自己加戏。

2.2 FFmpeg选shared还是static,这个选择直接决定后期工作量

在FFmpeg构建页面里,每个平台通常会提供两种版本:shared(动态库版)和static(静态库版)。我见过太多人在这一步踩坑——看着static版本体积小、文件少,就下载了,结果配置链接时发现还得手动加一堆系统库,比如-lz、-lws2_32、-lbcrypt、-lsecur32什么的,少一个就报错,补了一个又冒出来一个,折腾到怀疑人生。

老实选shared版本。shared包里带的是dll和配合dll使用的导入库(MinGW下是.dll.a),你的程序链接导入库,运行时加载dll。优点是链接参数简单,项目里只要写那几个FFmpeg模块名就行;缺点是发布程序时需要把一堆dll一起拷过去。但说实话,多用几步“拷贝文件”换来的省心,绝对值。

2.3 QT版本选5还是选6,不要被“新版更好”绑架

QT 5.15和QT 6.x现在是两大主流。QT 6性能更好、对现代C++支持更到位,但如果你去网上下载QT 5.15.2的离线安装包,会发现相当多教程和第三方库都还是基于QT 5写的,遇到问题容易搜到答案。

我的建议是:老实用QT 5.15.2,直到你觉得某个新特性非用不可。这个版本有MinGW 8.1配套的离线安装包,稳定,资料多,网上你踩过的坑几乎都有人踩过。QT 6不是不能用,但新手阶段多一个变量,排查问题时就多一分困难。等环境跑通了,哪天想升级再升级,不影响你已经掌握的知识框架。

3. 下载与安装:从零到能用的完整过程

3.1 QT安装的两种方式,离线包省心很多

先去QT官方或者国内镜像站下载QT安装包。在线安装器需要登录账号,而且装的时候还要现场拉包,网络不好能装到你崩溃。离线安装包就老实多了,下载一个几GB的exe,装的时候勾选一下组件就行。

装的时候注意两点。第一,选择安装路径时,路径里不要带中文和空格,这算是Windows下C++开发的基本素养,后面编译遇到奇奇怪怪的问题,第一反应就检查路径。第二,组件勾选一定要包含对应版本的MinGW编译器。光有QT库文件不够,编译器是另一个独立组件,漏了这一步,装完QT打开Creator会发现没有可用的工具链。

以QT 5.15.2为例,离线包安装时找MinGW 8.1.0相关的组件,确认勾上。QT Creator本身也会装好,不用单独处理。

3.2 FFmpeg开发包去哪里下载,怎么挑

FFmpeg官网其实不直接提供Windows的二进制包,它会引导你到第三方维护的构建。目前比较省心的渠道是BtbN的自动构建版本,在它的Release页面里挑带“win64-gpl-shared”字样的7z压缩包即可。

“win64”代表64位Windows,“gpl”代表GPL协议版本(包含x264、x265这些GPL组件),“shared”代表动态库版。如果你只是做个人工具,GPL版本完全没问题;如果你的项目要闭源商用,可能需要换成LGPL版本,但很多滤镜和编码器会缺失——这个先记在心里,大多数教程场景用GPL版就行。

下载后解压,把解压出来的整个目录放到一个固定位置,比如D:/dev/ffmpeg或者你QT工程目录下的third_party/ffmpeg。注意包里的目录结构是这样的:

ffmpeg/ ├── bin/ (所有dll和exe) ├── include/ (头文件) │ ├── libavcodec/ │ ├── libavformat/ │ ├── libavutil/ │ └── ... └── lib/ (链接用的.a库文件)

这个结构很清晰,include是给编译器找头文件的,lib是给链接器找库的,bin是程序运行时找dll的。把这几个目录用对了,环境就搭好了一半。

3.3 把FFmpeg的dll位置告诉程序,有两条路

程序编译成exe后,运行时会在exe所在目录、系统目录、环境变量PATH里找dll。所以你有两个选择:

第一,把FFmpeg的整个bin目录加入系统PATH,这样所有QT程序都能找到dll,但会产生“污染”——别的程序可能会受影响,而且部署到别的电脑还要重新配环境变量。

第二,把bin目录下的所有dll直接拷贝到你的QT构建目录(也就是exe生成的那个目录)。这个方法最省心,调试和发布都是同一个套路,无非拷贝次数多一点。我一般直接新建一个部署脚本,统一处理QT的dll和FFmpeg的dll。

我推荐第二种,简单粗暴,而且和后面的程序发布流程天然兼容。QT自己的dll用官方工具windeployqt处理,FFmpeg的dll直接拷,两者加一起就是一个可执行目录。

4. 把FFmpeg“塞”进QT工程:pro文件逐行拆解

4.1 先在工程目录里建立FFmpeg的固定位置

现在打开QT Creator,新建一个普通的Qt Widgets Application,名字随意,比如ffmpeg_demo。工程创建好之后,在工程目录下建一个third_party/ffmpeg文件夹,把前面下载的FFmpeg包内容解压进去,形成这样的结构:

ffmpeg_demo/ ├── third_party/ │ └── ffmpeg/ │ ├── bin/ │ ├── include/ │ └── lib/ ├── ffmpeg_demo.pro └── main.cpp

用相对路径$$PWD而不是绝对路径,这样工程换了电脑、换了目录,只要整体移动就不用改配置。我见过有人把绝对路径写死在pro文件里,换台电脑环境就崩了,这习惯不好。

4.2 手写pro文件,把FFmpeg库链接进来

ffmpeg_demo.pro里加上以下内容:

QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++17 console TARGET = ffmpeg_demo TEMPLATE = app FFMPEG_DIR = $$PWD/third_party/ffmpeg INCLUDEPATH += $$FFMPEG_DIR/include LIBS += -L$$FFMPEG_DIR/lib \ -lavformat \ -lavcodec \ -lavutil \ -lswscale \ -lswresample \ -lavfilter \ -lavdevice \ -lpostproc

逐条解释一下:

  • CONFIG += console:加上这个才能保证printf输出在控制台可见。不加的话,程序跑起来是“窗口程序”子系统,printf的内容不会显示出来,调试时一脸懵。
  • INCLUDEPATH += $$FFMPEG_DIR/include:告诉编译器去这个目录找libavformat/avformat.h等头文件。
  • LIBS += -L$$FFMPEG_DIR/lib:告诉链接器去这个目录找库文件。
  • -lavformat -lavcodec...:链接这几个模块的库。这里要注意顺序,一般建议依赖关系靠前的放后面,比如avformat依赖avcodecavutil,所以avutil放在最后面。现实中MinGW下顺序偶尔不敏感,但一旦遇到undefined reference,调整库顺序是首先尝试的解决方案之一。

lib目录下这些库的实际文件名是libavformat.dll.a这样的形式,但在LIBS里写-lavformat,编译器会自动加前缀和后缀去找,不需要手写完整文件名。

4.3 运行前别忘拷dll,这一步漏了必报错

配置好pro文件后,先编译一次。编译大概率能通过,因为头文件和链接库都找到了。但直接运行一定会报错,提示avformat-*.dll找不到,这几乎是FFmpeg+QT新手必经的一关。

这时候去FFmpeg的bin目录,把所有的dll(包括avcodec-*.dllavformat-*.dllavutil-*.dllswscale-*.dllswresample-*.dllavfilter-*.dllavdevice-*.dllpostproc-*.dll等)一次性拷贝到QT构建输出目录,就是build-ffmpeg_demo-Desktop_Qt_5_15_2_MinGW_64_bit-Debug里面那个debug文件夹。拷完后重新运行,就会发现不报dll错误了。

提示:如果不想每次手动拷,可以在pro文件里写一个自定义的QMAKE_POST_LINK,编译完成后自动执行拷贝命令。但这个属于锦上添花,环境搭建阶段手动拷一次,把原理搞明白更重要。

5. 写一个验证demo:从版本号到读取视频信息

5.1 第一个demo:打印FFmpeg版本和协议列表

环境搭建成功与否,不能光看“编译通过”就算数,必须要跑起来看到实际输出。新建一个main.cpp,代码如下:

#include <QCoreApplication> #include <QDebug> extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qDebug() << "FFmpeg version:" << av_version_info(); avformat_network_init(); qDebug() << "avformat_network_init done"; void *opaque = nullptr; const AVClass *c = avio_protocol_next(&opaque); qDebug() << "supported protocols:"; while (c) { qDebug() << " " << c->item_name; c = avio_protocol_next(&opaque); } return 0; }

这里有几个非常关键的细节:

第一,extern "C"包裹头文件。FFmpeg是C库,头文件没有做C++兼容处理,在C++工程里直接include,链接时会由于名字修饰规则不同报一堆undefined reference。这个坑我初学时就踩过,加了extern "C"瞬间痊愈。

第二,avformat_network_init()用来初始化网络模块,如果你后面要拉流、读网络视频,这一行就不能少。即使现在只是本地文件,提前写上也无妨。

第三,avio_protocol_next是遍历FFmpeg当前支持的协议,比如file、pipe、tcp、udp等。这一小段代码能顺带验证你的FFmpeg构建里包含哪些功能模块。

编译运行,如果一切正常,Qt的应用程序输出窗口里会出现类似这样的信息:

FFmpeg version: 6.0-full_build-www.gyan.dev avformat_network_init done supported protocols: file fd ... tcp udp

看到“FFmpeg version”这一行,说明环境已经基本打通了。

5.2 第二个demo:读取一个视频文件的信息

接下来做一个更实战的验证:给定一个视频文件路径,用FFmpeg把封装格式、时长、分辨率、编码格式、帧率全部读出来。这算是FFmpeg开发最基础的操作,也是后面做播放器、转码器的第一步。

#include <QCoreApplication> #include <QDebug> #include <QString> extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QString filePath = QStringLiteral("C:/test/video.mp4"); AVFormatContext *fmtCtx = nullptr; if (avformat_open_input(&fmtCtx, filePath.toLocal8Bit().data(), nullptr, nullptr) != 0) { qDebug() << "open input failed"; return 1; } if (avformat_find_stream_info(fmtCtx, nullptr) < 0) { qDebug() << "find stream info failed"; avformat_close_input(&fmtCtx); return 1; } qDebug() << "format:" << fmtCtx->iformat->name; qDebug() << "duration:" << fmtCtx->duration << "us"; qDebug() << "bit_rate:" << fmtCtx->bit_rate; av_dump_format(fmtCtx, 0, filePath.toLocal8Bit().data(), 0); for (unsigned int i = 0; i < fmtCtx->nb_streams; ++i) { AVStream *stream = fmtCtx->streams[i]; AVCodecParameters *codecPar = stream->codecpar; if (codecPar->codec_type == AVMEDIA_TYPE_VIDEO) { double fps = av_q2d(stream->avg_frame_rate); qDebug() << "video stream index:" << i; qDebug() << " codec:" << avcodec_get_name(codecPar->codec_id); qDebug() << " resolution:" << codecPar->width << "x" << codecPar->height; qDebug() << " fps:" << fps; } else if (codecPar->codec_type == AVMEDIA_TYPE_AUDIO) { qDebug() << "audio stream index:" << i; qDebug() << " codec:" << avcodec_get_name(codecPar->codec_id); qDebug() << " sample rate:" << codecPar->sample_rate; qDebug() << " channels:" << codecPar->ch_layout.nb_channels; } } avformat_close_input(&fmtCtx); return 0; }

拆解几个关键操作:

  • avformat_open_input:按文件路径打开媒体文件,创建AVFormatContext。注意第二个参数要用toLocal8Bit().data()而不是toUtf8(),Windows下中文路径用UTF-8直接传经常打不开,这个细节能帮你在中文路径上少踩一次坑。
  • avformat_find_stream_info:读取流信息,比如分辨率、编码格式、帧率等。不调用这个函数,很多字段会是0。
  • av_dump_format:这是FFmpeg自带的话痨函数,会把整个文件的流信息一股脑打印出来。调试阶段极其好用。
  • fmtCtx->duration:单位是微秒,除以1,000,000就是秒。
  • stream->codecpar:新版FFmpeg里流参数都放在codecpar里而不是codec里,如果你看到老教程在访问stream->codec,那是旧API,在新版会编译报错。

滤镜一下视角:读视频信息是所有处理的第一步,这个demo跑通,后面读帧、解码、转码都只是在这个基础上往深处走。所以把这段代码吃透,比你多装十个环境都值。

5.3 测试用视频文件怎么选

测试文件不要找太大的,几十秒的短视频最好。可以在自己电脑上随便录一段手机视频,或者找一个公开的测试视频下载。关键是路径别带中文,比如C:/test/video.mp4,省得又遇到编码问题。

如果demo跑完看到了视频流、分辨率、编码格式这些都正常打印出来,恭喜,你的FFmpeg+QT开发环境已经彻底搭好了。

6. 常见问题与排查实录:这些坑我基本都踩过

6.1 常见报错速查表

先整理一张表,日常使用最频繁遇到的几个问题对应排查思路:

现象常见原因解决办法
找不到头文件libavformat/avformat.hINCLUDEPATH路径配置错误检查pro文件里FFMPEG_DIR路径是否存在;修改后重新执行qmake再构建
编译报undefined reference toavformat_open_input没有用extern "C"包裹头文件,或库没链接,或编译器类型不匹配依次排查:extern "C"是否加了;LIBS是否写了;MinGW套件是否在链接MinGW版FFmpeg库
编译报av_register_all未声明新版FFmpeg已移除该API4.0之后AVFormatContext全部自动注册,直接删掉这行代码
程序启动报“无法定位程序输入点”或0xc000007b32位/64位不匹配,或dll缺失确认exe是64位,确认FFmpeg下载的是win64版本,把FFmpeg bin目录的dll全部拷过来
中文路径文件打不开编码问题路径用toLocal8Bit()转一下再传给avformat_open_input
链接成功但运行提示缺少libwinpthread-1.dllMinGW运行时dll没找到把QT对应MinGW编译器的bin目录(比如Tools/mingw810_64/bin)加入PATH,或者拷贝需要的运行时dll到exe目录

6.2 链接报错:64位和32位搞混是最隐蔽的坑

有一次我在网上帮人排查问题,对方QT装的是MinGW 64位套件,但下载FFmpeg构建时手滑下了个32位版本,结果编译链接全都过了,一运行就弹“应用程序无法正常启动0xc000007b”。

这个错误信息迷惑性极强,因为0xc000007b不是“缺dll”那种明确提示。排查方法很简单:去任务管理器看进程位数,或者用Dependencies工具打开exe看一下,如果显示依赖的dll架构不匹配,基本就是32/64位混了。

所以下载FFmpeg时一定看清“win64”字样。同样,QT套件选择时要确认是“MinGW 64-bit”。两者都是64位,这一组合就是稳的。

6.3 static库的衍生问题,能避开就避开

前面提到过static库的链接会带出一堆系统依赖库。有一个很常见的场景,你按照教程把LIBS写了一大串,什么-lz -lws2_32 -lbcrypt -lsecur32全加上了,编译过了,但程序跑起来后又说缺libgcc_s_seh-1.dlllibstdc++-6.dll,这些又得去MinGW的bin目录里找。

这也是我为什么不推荐新手用static版FFmpeg的原因——它把动态库的“运行环境依赖”又加重了一层。用shared版,你只需要处理FFmpeg自己的dll和QT自己的dll,边界清晰,出了问题也好排查。

6.4 折腾一晚上不如先跑通一个Hello World

环境搭建阶段最容易犯的毛病是“贪多”。今天想集成x264,明天想用硬件解码,后天想接摄像头。每一项都会引入新的参数、新的依赖,一旦出错你根本分不清是环境问题还是代码问题。

我自己的经验是:第一阶段目标就锁定“能打印出FFmpeg版本号”,第二阶段“能读出一个本地文件的视频信息”,第三阶段才考虑“把第一帧显示到QLabel上”。每跨一步都重新保存进度,出问题就在最近这一步里找,比一口气搭完再排查高效得多。

提示:如果调试时打印信息乱码,检查QT Creator的“应用程序输出”窗口编码设置,一般UTF-8即可。中文路径导致的乱码问题,优先用toLocal8Bit()而不是toUtf8()

6.5 老教程和新版本之间的API差异要心里有数

BtbN的构建更新很快,现在下载的基本是6.x或者7.x版本,网上很多教程可能是基于4.x、5.x写的。最典型的差异就是avcodec_register_all()这个函数,老教程里大家都还在调用,4.0之后直接被移除,你按老教程写这行代码,编译直接报错。

另一个常见差异是AVCodecContext字段的访问方式。老代码里直接访问codecCtx->width,新版本这些字段大多是通过codecpar获取。看到老代码别急着抄,先看有没有对应的新API替代。一般FFmpeg的API改动在文档里有标注,或者在源码的注释中会写明“deprecated”。

7. 给第一次搭这套环境的你,最后几句心里话

环境搭建这件事,真的就是“一次痛,终身爽”。我第一次搭FFmpeg+QT的时候,光处理static库那些乱七八糟的系统依赖就磨了一个晚上,后来换成shared版本,半小时全部跑通。从那之后我给自己定了个规矩:工程依赖能用动态库就不用静态库,能在配置里解决的就不去动环境变量,这一步帮我省了无数时间。

另外,调试阶段千万别嫌打印麻烦。av_dump_formatav_version_info这些函数就是你的眼睛,多打印、多观察,你会比看十篇文档都更快理解FFmpeg的工作方式。这套环境跑通之后,下一步我打算继续写怎么用FFmpeg解码视频流,然后在QLabel上把第一帧画面显示出来——那是“环境搭建”和“真正开发”的分界线,也是最有成就感的一步。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询