news 2026/10/27 4:37:29

32位Qt工程集成FFmpeg 3.4:环境配置与链接排错指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
32位Qt工程集成FFmpeg 3.4:环境配置与链接排错指南

简介:ffmpeg-3.4-mingw32 是一份面向 32 位 Windows 平台的多媒体开发预编译包,基于 MinGW32 工具链构建,适合使用 Qt/C++ 开发音视频采集、转码、播放或流媒体处理程序的开发者。它对应 FFmpeg 3.4 版本,可作为不依赖 Visual Studio 时的集成选择,便于把编解码、封装与滤镜能力接入桌面项目。压缩包共 170 个文件,约 2.62MB,内容以 C 源码与头文件、动态库和静态库、导入库及 pkg-config 配置为主,并附带导出定义、命令行可执行程序和说明文档,兼顾二次开发与直接调用。已有 229 人学习或下载,说明其在 MinGW32 音视频工程中具有一定参考价值。读者可获得可链接的库文件、开发头文件以及转码、封装、解封装等示例源码,用于搭建 Qt 工程、验证接口调用和排查编译链接问题,也能借助命令行工具完成格式转换与媒体信息查看。

1. 32 位 Qt 工程接 FFmpeg 3.4:先卡住的往往不是代码

上周又被问到一个老问题:Qt 项目里加 FFmpeg,代码是从网上抄的,编译却死活过不去。远程一看,Qt 装的是 64 位 MinGW 套件,手里的 ffmpeg-3.4-mingw32 是 32 位包,链接器一句skipping incompatible就把人挡在门外。这类工程在国内存量很大——老项目、老工控机、老 XP/Win7 现场机,还跑着 32 位 Qt 5.x + MinGW + FFmpeg 3.4 这套组合。装不好不是报错这么简单,而是编过了运行崩、dll 找不到、解出来的帧是花屏。这篇记录的是我从零把这套环境搭通、并且能在同事机器上复现的全过程,适合还在维护 32 位 Qt 工程、又必须用 3.4 这套老 API 的人照着走。

2. 环境对表:Qt 套件、MinGW 位数和 FFmpeg 3.4 包型怎么配

动手之前先把三件事对齐:Qt 套件是多少位、MinGW 是多少位、FFmpeg 包是哪种形态。这三者任意一个错位,后面所有步骤都是白费功夫。我见过最冤的一种情况是:Qt Creator 里明明有Desktop Qt 5.12.x MinGW 32-bit这个套件,但工程一直用的是默认 64 位套件,开发者以为自己在编 32 位程序,直到链接 FFmpeg 才暴露。

2.1 先确认 Qt 套件是不是真的 32 位

不要看安装包名字猜,直接在套件对应的 Qt 命令行环境里问编译器。Qt Creator 菜单里打开对应 MinGW 套件的命令行(或者在 Qt Creator 的「工具 → 选项 → Kits」里找到该套件的编译器路径),执行:

# 用套件自带的 gcc 问目标三元组,这是最硬的证据 gcc -dumpmachine # 补充确认:看指针宽度 gcc -dumpversion

i686-w64-mingw32才是 32 位,x86_64-w64-mingw32是 64 位。这两个字符串记住,后面排查链接错误时要反复用到。

还有一种更省事的办法,在.pro里临时把关键变量打出来,编译一次看「概要信息」面板:

# 临时调试用,确认无误后删掉这几行 message("HOST_ARCH = $$QMAKE_HOST.arch") message("QMAKE_CXX = $$QMAKE_CXX") message("QT_ARCH = $$QT_ARCH")

QMAKE_HOST.arch是构建机架构,容易误导人——64 位机器上跑 32 位套件时它照样显示 x86_64。真正要看的是QT_ARCH,Qt 5 里 32 位套件输出i386,64 位输出x86_64。这一步做过一次,后面就不会再怀疑「我到底编的是几位」。

提示:Qt 5.15 之后 MinGW 套件的目录名有变化,QT_ARCH的取值也随 Qt 版本略有差异,以实际编译输出为准,别背结论。

2.2 FFmpeg 3.4 预编译包的三种形态与取舍

Windows 下拿到的 ffmpeg-3.4-mingw32 一般有三种形态,选错一种,就是几小时的返工。

形态目录特征MinGW 下能否直接用适用场景
shared + 导入库bin 下有avcodec-57.dll等,lib 下有.dll.a能,直接-l链接一般 Qt 工程,推荐
shared 但只有.libbin 有 dll,lib 里是 MSVC 导入库不能,需要自己转需要额外做一步 dlltool
static 静态库lib 下是.a静态库,无 dll能,但要处理依赖顺序和编译选项单文件分发、现场不能放 dll

对 Qt 桌面工程,我一般选第一种:dll 随 exe 走,升级 FFmpeg 只换 dll,不用重编整个工程。静态库看着省事,实际上会把libwinpthread、libz之类的依赖一起拖进来,链接顺序一错就是一屏 undefined reference,新手不建议碰。

还有两个包内属性要留意:线程模型和许可证构建。带--enable-w32threads的包在多线程解码时行为更贴近 Windows 原生;带 GPL 组件的构建(比如含 x264)在商用分发前要先确认合规。这些信息在包里的ffmpeg -version输出或构建说明里能看到,解压后花两分钟看一眼,比事后返工划算。

2.3 目录归置:include / lib / bin 三处落点

不要把工程直接指向解压出来的原始目录。原始目录名常带版本号和后缀,路径里还可能有空格,一旦换包,.pro里所有路径全要改。我的习惯是在工程根目录下建固定的第三方目录:

MyPlayer/ ├── MyPlayer.pro ├── src/ └── third_party/ └── ffmpeg/ ├── include/ # libavcodec/ libavformat/ libavutil/ libswscale/ libswresample/ ├── lib/ # libavcodec.dll.a 等导入库 └── bin/ # avcodec-57.dll 等运行库

把解压包里的include、lib、bin三个目录整体搬过来,之后.pro里只写相对路径$$PWD/third_party/ffmpeg。这样做的好处是工程可提交、可复现,同事拉下来不用问「你的 FFmpeg 装哪了」。搬家之后先确认include下是libavcodec/avcodec.h这种两级结构,而不是把avcodec.h直接摊在include根下——3.4 的包都是两级结构,但有些重新打包的版本会摊平,摊平的包包含路径要另写。

3. 让 qmake 找得到:INCLUDEPATH 与 LIBS 的写法与链接顺序

目录摆好,接下来是.pro。这一步出问题最多,因为 qmake 不报「路径写错了」,它只在链接阶段甩一句找不到库。

3.1 头文件包含:extern "C" 与 3.4 的头文件布局

FFmpeg 是 C 库,Qt 工程是 C++,直接在.cpp里#include <libavcodec/avcodec.h>会编译失败或链接期找不到符号——C++ 会对函数名做名字修饰(mangling),而 dll 里导出的是未经修饰的 C 符号。必须用extern "C"包起来:

// ffmpeghelper.h #ifndef FFMPEGHELPER_H #define FFMPEGHELPER_H extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libavutil/avutil.h> #include <libavutil/imgutils.h> #include <libswscale/swscale.h> #include <libswresample/swresample.h> } #endif // FFMPEGHELPER_H

写成一个单独的头文件集中包含,好处是全工程只有这一处extern "C",以后升级或替换 FFmpeg 只改一个文件。注意 3.4 的头文件路径是libavcodec/avcodec.h这种带库名前缀的写法,不是avcodec.h。

老版本在 C++ 里还会要求#define __STDC_CONSTANT_MACROS和__STDC_FORMAT_MACROS,3.4 基本不需要,但如果你的 Qt 工程开了严格 C++ 标准(比如CONFIG += c++11 strict_c++),偶尔还是会撞上INT64_C未定义的报错,那时候在extern "C"之前补这两个宏即可。

3.2 链接顺序:-lavformat 为什么必须写在 -lavcodec 前面

.pro里最关键的几行:

# 第三方库根目录,换包只改这一行 FFMPEG_DIR = $$PWD/third_party/ffmpeg INCLUDEPATH += $$FFMPEG_DIR/include # 导入库搜索路径 LIBS += -L$$FFMPEG_DIR/lib # 依赖在右,被依赖在左:avformat 依赖 avcodec,avcodec 依赖 avutil LIBS += -lavformat -lavcodec -lswscale -lswresample -lavutil

GNU ld 是单遍扫描:从左往右读库,遇到未解析符号就去后面的库里找,找到就停下。所以-lavformat必须写在-lavcodec前面,-lavcodec又必须在-lavutil前面。写反了的典型症状是undefined reference to avcodec_alloc_context3这类——明明库在,就是解不出来。这个坑非常玄学,因为报错信息完全不提顺序问题。

如果工程已经很多依赖、顺序实在捋不清,可以让链接器自己循环查找:

# 兜底写法:顺序敏感时用 group 包住,代价是链接稍慢 LIBS += -Wl,--start-group -lavformat -lavcodec -lswscale -lswresample -lavutil -Wl,--end-group

3.3 只有 .lib 没有 .dll.a 时,用 gendef + dlltool 补一个

拿到手的包如果lib目录里全是.lib,那是 MSVC 的导入库,MinGW 的 ld 读不了。不用重新找包,用 MinGW 自带的两个工具从 dll 反推一个.dll.a出来:

# 在 MinGW 套件的命令行里,进入 dll 所在目录 cd third_party/ffmpeg/bin # 生成 def 文件 gendef avcodec-57.dll # 由 def 生成 MinGW 可用的导入库,-D 指定 dll 名,运行时按这个名字找 dlltool -d avcodec-57.def -l ../lib/libavcodec.dll.a -D avcodec-57.dll

六个库(avformat、avcodec、avutil、swscale、swresample、avfilter)都要来一遍,写个循环更快:

# 批量转换,省得一个个敲 for f in *.dll; do base=$(basename "$f" .dll) gendef "$f" >/dev/null dlltool -d "${base}.def" -l "../lib/lib${base}.dll.a" -D "$f" done

-D参数的意义是告诉链接器「运行期去加载这个文件名的 dll」,不写的话生成的导入库里记录的是构建机上的路径,换机器就崩。生成的.def用完可以删,.dll.a留着。

4. 编译通过之后的运行期:dll 落位与 3.4 的 API 分岔

编过只是第一关。我见过太多「Qt Creator 里 F5 跑得好好的,双击 exe 就闪退」的案例,根子都在运行期。

4.1 dll 的三种摆放位置

dll 的搜索路径优先级大致是:exe 所在目录 → 系统目录 → PATH。三种摆法各有适用场景:

# 摆法一:跟 exe 放一起,最稳,发布就这么干 windeployqt.exe --release --no-translations MyPlayer.exe copy third_party\ffmpeg\bin\*.dll build-MyPlayer-Desktop_Qt_5_12_9_MinGW_32_bit-Release\release\

摆法二是把bin加进系统 PATH,本地开发省事,但换 FFmpeg 版本时会污染其他工程,不推荐。摆法三是在 Qt Creator 的「项目 → 运行 → 运行环境」里改 PATH,只对这个工程生效——这是我最常用的开发期做法,容器化得干净。

注意:Qt Creator 里运行时的当前工作目录是 build 目录,不是源码目录。代码里写相对路径(比如"test.mp4")时,文件要放在 build 目录下,否则会得到「文件打不开」的假象,白白怀疑 FFmpeg。

4.2 av_register_all 到底该在哪调、调几次

3.4 这个版本,av_register_all()和avcodec_register_all()仍然需要调用,直到 4.0 才被标记废弃。它们内部有一次性保护,多调无害但没必要。常见做法是放在程序启动的早期,一次就够:

// main.cpp #include "ffmpeghelper.h" #include <QApplication> int main(int argc, char *argv[]) { // 3.4 必须注册,否则 avformat_open_input 找不到任何解复用器 av_register_all(); avcodec_register_all(); QApplication a(argc, argv); // ... return a.exec(); }

放在QApplication之前还是之后都可以,但一定要在主线程、任何解码逻辑之前。漏调的典型症状是avformat_open_input返回负值、av_find_input_format返回 nullptr,日志里看不出所以然,纯黑匣子。

4.3 3.4 与 4.x 的 API 分岔:codec 与 codecpar

这是抄代码最容易翻车的地方。网上大量的 FFmpeg 教程是 4.x 之后的,视频流参数从AVStream::codecpar取;而 3.4 里codecpar还没成为主流入口,要用AVStream::codec。照着新教程写,编译期就报「AVStream 没有 codecpar 成员」。

// 兼容写法:用版本宏隔开,将来升级 4.x 时少改一点 #if LIBAVFORMAT_VERSION_MAJOR < 58 // FFmpeg 3.4:libavformat 主版本是 57 AVCodecContext *codecCtx = fmtCtx->streams[videoIndex]->codec; AVCodec *decoder = avcodec_find_decoder(codecCtx->codec_id); if (!decoder || avcodec_open2(codecCtx, decoder, nullptr) < 0) { return false; } #else // 4.x 及以后:从 codecpar 取参数,自己分配上下文 AVCodecParameters *par = fmtCtx->streams[videoIndex]->codecpar; const AVCodec *decoder = avcodec_find_decoder(par->codec_id); AVCodecContext *codecCtx = avcodec_alloc_context3(decoder); avcodec_parameters_to_context(codecCtx, par); avcodec_open2(codecCtx, decoder, nullptr); #endif

判断依据是LIBAVFORMAT_VERSION_MAJOR:3.4 对应 57,4.x 对应 58 及以上。同理,avcodec_decode_video2在 3.4 里还能用,新接口avcodec_send_packet/avcodec_receive_frame也已经存在,两者都能跑;写新代码建议直接用后者,升级时不用再改。

另外要留意AVStream::codec在 3.4 里是「借用」关系,avformat_close_input会自动释放它,别再手动avcodec_free_context,否则就是二次释放。这类内存在 3.x 和 4.x 之间的所有权规则有变化,换版本时值得重新读一遍对应版本的示例代码。

5. 排查清单:链接期和运行期最容易翻车的五个点

5.1 链接报 skipping incompatible

现象:链接阶段出现skipping incompatible .../libavcodec.dll.a when searching for -lavcodec,最后汇总成cannot find -lavcodec。

原因:库的位数和编译器目标不一致。最常见的是 32 位 Qt 套件配了 64 位的 FFmpeg 包,或者反过来。

解决:回到 2.1 确认gcc -dumpmachine输出是i686-w64-mingw32;再看包名里的mingw32字样,用file libavcodec.dll.a也能看出目标架构。两者对不上就换包,别试图用链接器参数绕过。

5.2 -L 路径对了,还是 cannot find -lavcodec

现象:-L指到了lib目录,目录里也看得见文件,链接器就是不认。

原因:目录里放的是 MSVC 的.lib,或者导入库文件名不符合 ld 的命名约定(lib前缀 + 库名 +.dll.a或.a)。

解决:按 3.3 用gendef+dlltool现做一份.dll.a,确保文件名形如libavcodec.dll.a。文件名少个lib前缀,-lavcodec就找不到。

5.3 双击 exe 提示丢失 avcodec-57.dll

现象:Qt Creator 里 F5 正常,双击 exe 弹窗提示缺少 dll,或者直接静默退出。

原因:Qt Creator 运行环境里 PATH 带了 FFmpeg 的 bin,系统环境里没有;exe 同目录也没放 dll。

解决:用windeployqt收一遍 Qt 的依赖之后,把third_party/ffmpeg/bin/*.dll一并拷到 exe 同级目录。32 位程序的 dll 必须是 32 位,64 位 dll 混进去会变成 0xc000007b 而不是「找不到」。

5.4 编译报 undefined reference to av_register_all

现象:头文件包含没问题,-lavcodec也写了,就是链不上这几个符号。

原因:要么extern "C"漏了(C++ 名字修饰导致符号名不匹配),要么LIBS顺序反了。

解决:先检查是否走了统一的ffmpeghelper.h;再检查.pro里-lavformat是否在-lavcodec左边。两个都排除后,用nm -g libavcodec.dll.a | grep av_register_all确认导入库里确实有这个符号。

5.5 avformat_open_input 返回失败,路径明明是对的

现象:文件路径确信无误,avformat_open_input就是返回负数,拿不到任何流信息。

原因:一是漏调av_register_all();二是路径里有中文或空格,QString::toUtf8()在某些构建下处理不当。

解决:先补注册调用;再把路径统一走QFile::encodeName(path)转成本地编码传进去,别直接path.toStdString().c_str()。这两条我踩过不止一次,尤其是 Windows 上含中文的目录。

6. 装完怎么验证:三段自检从「能编过」推到「能解出一帧」

装完必须自证,不然只是「看起来能编」。我习惯分三段做,每段一个明确的通过标准。

6.1 第一段:把版本号打出来

// 放在 main 里,av_register_all 之后 qDebug() << "libavcodec :" << AV_VERSION_MAJOR(avcodec_version()) << AV_VERSION_MINOR(avcodec_version()) << AV_VERSION_MICRO(avcodec_version()); qDebug() << "configuration:" << avcodec_configuration();

通过标准:主版本打出 57,configuration里能看到--enable-shared和线程模型。这一行输出同时证明了三件事:头文件对、导入库对、dll 能加载。如果程序在这一步就崩,问题一定在 dll 环节,不用往下查。

6.2 第二段:打开一个 mp4 并读出流信息

// 读流信息,不做解码,验证 demuxer 是否正常 AVFormatContext *fmtCtx = nullptr; QByteArray ba = QFile::encodeName(filePath); // 中文路径必须转本地编码 if (avformat_open_input(&fmtCtx, ba.constData(), nullptr, nullptr) < 0) { qWarning() << "open failed"; return; } avformat_find_stream_info(fmtCtx, nullptr); int vIdx = av_find_best_stream(fmtCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); qDebug() << "video stream index:" << vIdx << "duration(us):" << fmtCtx->duration; avformat_close_input(&fmtCtx);

通过标准:duration大于 0,vIdx不小于 0。到这里编解码器还没上场,测的是容器解析链路。

6.3 第三段:解出第一帧并存成图片

// 在 6.2 的基础上继续,解第一帧转 RGB24 存盘 AVCodecContext *ctx = fmtCtx->streams[vIdx]->codec; // 3.4 的取法 AVCodec *dec = avcodec_find_decoder(ctx->codec_id); avcodec_open2(ctx, dec, nullptr); AVPacket pkt; av_init_packet(&pkt); AVFrame *frame = av_frame_alloc(); int got = 0; while (av_read_frame(fmtCtx, &pkt) >= 0) { if (pkt.stream_index == vIdx) { avcodec_decode_video2(ctx, frame, &got, &pkt); av_packet_unref(&pkt); if (got) break; } else { av_packet_unref(&pkt); } } SwsContext *sws = sws_getContext(ctx->width, ctx->height, ctx->pix_fmt, ctx->width, ctx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr); QImage img(ctx->width, ctx->height, QImage::Format_RGB888); uint8_t *dst[4] = { img.bits(), nullptr, nullptr, nullptr }; int dstStride[4] = { img.bytesPerLine(), 0, 0, 0 }; sws_scale(sws, frame->data, frame->linesize, 0, ctx->height, dst, dstStride); img.save("first_frame.png"); // 打开看,能出图就说明整条链路通了
验证阶段通过标准失败通常指向
版本自检主版本 57,configuration 正常输出dll 缺失、位数不匹配
容器解析duration 大于 0,流索引有效漏调 av_register_all、路径编码
首帧解码生成的 png 能正常打开且不花屏链接顺序、pix_fmt 处理

从那以后我每次搭这套环境,都会把这三段做成一个单独的 main 先跑一遍,确认全绿再接业务代码。看起来多花二十分钟,实际省下的是「到底是我代码写错还是环境没装好」的半天纠结。希望帮到你。

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

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

深度掌握impeccable:词根记忆与高频搭配指南

1. 先看懂“impeccable”&#xff1a;含义、发音与词根拆解1.1 核心含义&#xff1a;不是所有“完美”都配叫 impeccable这个词在词典里的解释是“无可挑剔的、毫无瑕疵的”&#xff0c;但我更愿意把它理解成一种“经得起放大镜检视”的质量等级。举个例子&#xff0c;电影里说…

作者头像 李华
网站建设 2026/10/27 4:36:05

端到端测试实战指南:从框架选型到稳定性治理的完整方法论

做端到端&#xff08;e2e&#xff09;测试这些年&#xff0c;我最大的感受是&#xff1a;圈子里对e2e的态度一直很分裂。一边是“测试金字塔”理论把e2e放在塔尖&#xff0c;说它是最后一道防线&#xff0c;另一边是无数团队被e2e用例的脆弱性折磨到直接放弃。我自己经历过从Se…

作者头像 李华
网站建设 2026/10/27 4:36:04

大模型面试必问的8个核心问题:从Transformer到KV Cache与RAG

1. 为什么这8个问题成了LLM面试的硬门槛面过十几场大模型岗位之后&#xff0c;我发现一个很明显的规律&#xff1a;面试官在前二十分钟基本不会问太偏的东西&#xff0c;翻来覆去就是那么几个核心问题。但恰恰是这几个看起来“基础”的问题&#xff0c;淘汰率最高。我见过不少简…

作者头像 李华
网站建设 2026/10/27 4:35:59

计算机网络概述:协议栈分层、封装与时延排查实战

计算机网络这门课&#xff0c;很多人第一次翻开目录看到“概述”两个字&#xff0c;第一反应是跳过——觉得无非是些名词解释、发展历史&#xff0c;真正的干货在后面。我在一线做后端和运维支撑这些年&#xff0c;踩过的坑越多越发现&#xff0c;恰恰是这一章讲的东西&#xf…

作者头像 李华
网站建设 2026/10/27 4:35:50

一套代码覆盖CRC-8到CRC-32:参数化CRC校验实现与踩坑排查

简介&#xff1a;这份循环冗余校验码&#xff08;CRC&#xff09;计算源代码合集面向嵌入式开发、通信协议调试与数据存储校验学习者&#xff0c;聚焦CRC-8、CRC-16、CRC-32等常见算法的代码实现与工程验证。压缩包共1220个文件&#xff0c;约6.65MB&#xff0c;包含C与汇编源文…

作者头像 李华
网站建设 2026/10/27 4:35:50

Rust 重写 TypeScript CLI:冷启动从 1.2 秒降到 120 毫秒的工程实践

1. 从一次冷启动说起&#xff1a;为什么要用 Rust 重写一个 CLI 工具如果你日常在终端里跑各种命令行工具&#xff0c;大概率有过这种体验&#xff1a;敲下回车之后&#xff0c;光标先愣住一两秒&#xff0c;然后才开始吐输出。工具本身功能没问题&#xff0c;但那个"愣住…

作者头像 李华