32位Qt项目集成FFmpeg 3.4 MinGW动态库实战指南

发布时间:2026/10/24 19:22:52
32位Qt项目集成FFmpeg 3.4 MinGW动态库实战指南

简介:ffmpeg-3.4-mingw32 是面向 32 位 Windows 平台、基于 MinGW32 编译环境构建的 FFmpeg 3.4 版本资源包,适合需要在 Qt 项目中集成音视频编解码能力的 C/C++ 开发者,以及希望免编译直接调用命令行工具处理多媒体的终端用户。压缩包为 7z 格式,共 170 个文件,整体约 2.62MB,其中 107 个 h 头文件与 21 个 c 源文件构成二次开发所需的接口与示例基础,6 个 dll、6 个 lib 及 6 个 def 提供动态与静态链接支持,6 个 a 与 6 个 pc 文件便于 MinGW 环境下链接与 pkg-config 配置,另有 2 个 exe 命令行程序、ffpreset 预设及 makefile、readme 等辅助内容。资源内含 transcode_aac、transcoding、muxing、demuxing_decoding 等示例源码,可帮助读者理解封装、解封装、转码与 AAC 编码流程,并借助 libavutil、libavcodec、libavformat、libavfilter、libswscale、libswresample 等模块完成自定义功能集成。目前已有 227 人学习下载,适合作为 Qt+MinGW32 音视频开发的入门与排错参考。

1. 32 位 Qt 项目卡在音视频解码上:为什么我最后翻出了 ffmpeg-3.4-mingw32

前阵子帮一个做工业上位机的朋友排查问题,他的 Qt 程序在 64 位机器上跑得好好的,一放到现场那台老工控机上就崩,日志里全是avcodec_open2返回负值。折腾半天才定位到根因:现场系统是 32 位的,而他链接的 FFmpeg 库是 64 位 MinGW 编译出来的,ABI 对不上。这种场景其实不少见——老旧设备、嵌入式面板、某些只提供 32 位驱动的采集卡,都会把你逼回 32 位工具链。ffmpeg-3.4-mingw32 这个资源包,就是针对这类需求准备的:一套用 MinGW 32 位工具链编译好的 FFmpeg 3.4 动态库、头文件和配套的 pkg-config 文件,能直接塞进 Qt 的.pro里用。它适合谁?适合那些被 32 位环境锁死、又不想自己从零交叉编译 FFmpeg 的 Qt 开发者。下面我按实际拆包和集成的顺序,把这份资源怎么用、参数怎么配、哪里容易翻车讲清楚。

2. 拆开这个包:MinGW 32 位 FFmpeg 3.4 的目录结构与链接模型

2.1 目录里到底放了什么

拿到一个预编译的 FFmpeg 包,第一件事不是急着往 Qt 里塞,而是先看清楚它的布局。常见的 MinGW 32 位发行包一般会按下面这种结构组织:

ffmpeg-3.4-mingw32/ ├── bin/ │ ├── avcodec-57.dll │ ├── avformat-57.dll │ ├── avutil-55.dll │ ├── swresample-2.dll │ ├── swscale-4.dll │ └── ffmpeg.exe / ffprobe.exe ├── include/ │ ├── libavcodec/ │ ├── libavformat/ │ ├── libavutil/ │ ├── libswresample/ │ └── libswscale/ ├── lib/ │ ├── libavcodec.dll.a │ ├── libavformat.dll.a │ ├── libavutil.dll.a │ ├── libswresample.dll.a │ └── libswscale.dll.a └── pkgconfig/ (可选) ├── libavcodec.pc └── ...

这里有几个关键点值得展开。bin下的.dll是运行时真正干活的动态库,版本号后缀(57、55、2、4)是 FFmpeg 3.4 这一代固定的 soname 主版本,不能随便换。lib下的.dll.a是导入库,MinGW 链接器靠它找到 DLL 里的符号,它本身不包含实现代码,所以体积很小。include是编译期需要的头文件,Qt 工程里INCLUDEPATH要指到这里。

提示:如果你拿到的包里lib目录只有.a静态库而没有.dll.a,那说明它是静态编译版本,链接方式完全不同,别混用。

2.2 动态链接和静态链接,32 位下怎么选

在 32 位 MinGW 环境里,动态链接和静态链接的取舍比 64 位更敏感。动态链接的优点是主程序体积小、多个模块可以共享同一份 DLL;缺点是部署时必须把一堆 DLL 跟 exe 放在一起,漏一个就启动失败。静态链接把代码全打进 exe,部署干净,但 32 位地址空间有限,FFmpeg 全静态链接后 exe 动辄几十兆,加载时内存压力明显。

我一般建议:如果是桌面工具类 Qt 程序,用动态链接,把 DLL 放bin目录同级;如果是给现场工控机做的小型采集程序,且只用到解码和缩放,可以考虑静态链接裁掉不用的组件。ffmpeg-3.4-mingw32 这个包提供的是动态链接所需的.dll.a和.dll,所以下面的集成步骤都按动态链接走。

2.3 版本号背后的兼容性约束

FFmpeg 3.4 对应的库版本是 libavcodec 57、libavformat 57、libavutil 55、libswresample 2、libswscale 4。这个版本组合是锁死的,你不能拿 3.4 的头文件去配 4.x 的 DLL,反之亦然。常见做法是:在.pro里显式写死库名,不要用通配符去匹配libav*,否则升级环境时容易链到错误的版本。另外,MinGW 32 位工具链本身也有版本差异,老版本的 MinGW(比如 5.x 系列)和新版(8.x 以上)在 C++ ABI 上有细微差别,如果 DLL 是用老工具链编的,你的 Qt 也得用同代工具链,否则会出现符号找不到的玄学问题。

3. 把 FFmpeg 3.4 接进 Qt:.pro 配置、头文件包含与首个解码 Demo

3.1 .pro 文件里怎么写才不出错

Qt 的.pro文件是 qmake 的入口,FFmpeg 的路径和库都要在这里声明。下面是一个经过验证的配置模板,假设你把 ffmpeg-3.4-mingw32 解压到了D:/libs/ffmpeg-3.4-mingw32:

# 工程配置:32 位 MinGW 下链接 FFmpeg 3.4 动态库 QT += core gui TARGET = VideoProbe TEMPLATE = app CONFIG += c++11 # 根据构建类型选择 debug 或 release 目录(如果包里有区分) FFMPEG_ROOT = D:/libs/ffmpeg-3.4-mingw32 # 头文件路径 INCLUDEPATH += $$FFMPEG_ROOT/include # 导入库路径 LIBS += -L$$FFMPEG_ROOT/lib # 显式链接五个核心库,顺序不能乱 LIBS += -lavformat -lavcodec -lavutil -lswresample -lswscale # 如果 MinGW 链接时报 undefined reference,补上系统库 LIBS += -lws2_32 -lsecur32 -lbcrypt -luser32 -ladvapi32

逻辑说明:INCLUDEPATH让编译器找到libavcodec/avcodec.h这类头文件。LIBS里的-L指定库搜索路径,后面跟的-lavformat等会去找libavformat.dll.a。链接顺序很重要,avformat依赖avcodec,avcodec依赖avutil,所以avformat要写在前面,否则 MinGW 链接器可能报符号未定义。最后那串系统库是 FFmpeg 在 Windows 下用到的网络和加密相关依赖,缺了会在链接阶段报错。

参数说明:FFMPEG_ROOT建议用绝对路径,相对路径在 shadow build 下容易失效。如果你的包放在工程目录内,可以用$$PWD/third_party/ffmpeg这种写法。

3.2 第一个能跑的解码流程:打开文件、找流、解码一帧

配置好.pro之后,写一段最小可运行代码验证链接是否成功。下面这段代码打开一个视频文件,找到视频流,解码出第一帧的宽高信息:

// main.cpp:验证 FFmpeg 3.4 链接与基本解码流程 extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> } #include <QDebug> int main(int argc, char *argv[]) { // 1. 注册所有组件(FFmpeg 3.x 仍需手动调用) av_register_all(); AVFormatContext *fmtCtx = nullptr; // 2. 打开输入文件 if (avformat_open_input(&fmtCtx, "test.mp4", nullptr, nullptr) != 0) { qDebug() << "打开文件失败"; return -1; } // 3. 探测流信息 if (avformat_find_stream_info(fmtCtx, nullptr) < 0) { qDebug() << "找不到流信息"; avformat_close_input(&fmtCtx); return -1; } // 4. 找视频流索引 int videoIdx = -1; for (unsigned i = 0; i < fmtCtx->nb_streams; i++) { if (fmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoIdx = i; break; } } if (videoIdx == -1) { qDebug() << "没有视频流"; avformat_close_input(&fmtCtx); return -1; } // 5. 用 codecpar 找解码器(3.x 推荐方式) AVCodecParameters *par = fmtCtx->streams[videoIdx]->codecpar; AVCodec *codec = avcodec_find_decoder(par->codec_id); if (!codec) { qDebug() << "找不到解码器"; avformat_close_input(&fmtCtx); return -1; } // 6. 分配解码上下文并打开 AVCodecContext *codecCtx = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codecCtx, par); if (avcodec_open2(codecCtx, codec, nullptr) < 0) { qDebug() << "打开解码器失败"; avcodec_free_context(&codecCtx); avformat_close_input(&fmtCtx); return -1; } qDebug() << "视频尺寸:" << codecCtx->width << "x" << codecCtx->height; // 7. 清理 avcodec_free_context(&codecCtx); avformat_close_input(&fmtCtx); return 0; }

逻辑说明:这段代码走的是 FFmpeg 3.x 的标准解码准备流程。av_register_all在 3.x 里必须调用,4.0 之后才废弃。avformat_open_input打开容器,avformat_find_stream_info读取流参数。找流时用codecpar->codec_type判断类型,这是 3.x 引入的分离式参数结构,比老版本直接读codecCtx更安全。avcodec_parameters_to_context把流参数拷贝到解码上下文,最后avcodec_open2真正初始化解码器。

参数说明:avformat_open_input第三个参数可以指定输入格式,传nullptr让 FFmpeg 自动探测。avcodec_alloc_context3传入找到的codec指针,它会预填一些默认值。如果avcodec_open2返回负值,常见原因是 DLL 版本不匹配或缺少依赖库,用 Dependency Walker 类工具查一下avcodec-57.dll的依赖是否齐全。

3.3 部署时 DLL 怎么放

编译通过只是第一步,运行时报「找不到 avcodec-57.dll」是新手最常翻的车。Qt 的 shadow build 会把 exe 生成在单独的目录里,你需要把 ffmpeg-3.4-mingw32 的bin目录下所有 DLL 复制到 exe 同级目录。如果用了 Qt 的动态库,还要把 Qt 的windeployqt跑一遍。我一般会在.pro里加一条自定义命令,构建后自动拷贝:

# 构建后自动拷贝 FFmpeg DLL 到输出目录 win32 { FFMPEG_BIN = $$FFMPEG_ROOT/bin DESTDIR_WIN = $$replace(OUT_PWD, /, \\) QMAKE_POST_LINK += $$quote(cmd /c copy /y $$replace(FFMPEG_BIN, /, \\)\\*.dll $$DESTDIR_WIN) }

这段 qmake 脚本在链接完成后执行,把bin下所有 DLL 复制到构建输出目录。注意路径分隔符要转成 Windows 反斜杠,否则copy命令会失败。

4. 避坑排查:32 位 FFmpeg 3.4 集成时最容易翻车的五个点

4.1 链接报 undefined reference toavcodec_register_all

现象:编译阶段就报一堆未定义符号,全是avcodec_、avformat_开头的。原因通常是.pro里库的顺序写反了,或者-L路径没指对,链接器根本没找到.dll.a文件。解决:先确认lib目录下确实有libavcodec.dll.a,然后把-lavformat放在-lavcodec前面,-lavcodec放在-lavutil前面。如果还不行,在LIBS末尾加上-Wl,--enable-stdcall-fixup试试,某些 MinGW 版本对符号修饰处理不一致。

4.2 运行时报「无法定位程序输入点」于 avutil-55.dll

现象:exe 能启动,但一调用 FFmpeg 函数就弹窗说找不到某个输入点。原因是你链接时用的头文件版本和运行时 DLL 版本不一致,比如头文件是 3.4 的,但 PATH 里混进了另一个版本的avutil-55.dll。解决:把 ffmpeg-3.4-mingw32 的bin目录放在系统 PATH 最前面,或者干脆把 DLL 拷到 exe 同级,确保加载的是配套的那一份。用where avutil-55.dll命令可以查当前生效的是哪个。

4.3 Qt 的 minGW 版本和 FFmpeg 编译工具链不匹配

现象:链接通过,但运行时崩溃在avcodec_open2内部,堆栈显示异常。原因:FFmpeg 的 DLL 是用某个特定版本的 MinGW 编译的,而你的 Qt 用的是另一个版本的 MinGW,两者 C 运行时库(msvcrt 或 ucrt)不一致。解决:查清楚 Qt 安装目录下Tools/mingwXX_32的版本号,尽量找同代工具链编译的 FFmpeg 包。如果找不到,用objdump -p avcodec-57.dll | findstr "DLL Name"看它依赖哪个运行时,再决定是否换 Qt 工具链。

4.4 32 位地址空间不足导致大文件解码失败

现象:解码小文件正常,一上 1080p 长视频就报内存分配失败。原因:32 位进程用户态地址空间只有 2GB 左右,FFmpeg 解码时内部缓冲、帧队列加上 Qt 自身的开销,很容易触顶。解决:在avcodec_open2之前设置codecCtx->thread_count = 1减少线程栈开销,或者用av_dict_set传threads=1。另外及时调用av_frame_free释放不再用的帧,别攒着。如果还是不够,只能考虑换 64 位方案,32 位是硬上限。

4.5 pkg-config 文件路径写死导致换机器就失效

现象:在自己机器上编译好好的,换一台机器就报找不到 libavcodec。原因:.pc文件里的prefix是绝对路径,指向你本地的解压目录。解决:要么不用 pkg-config,直接在.pro里写路径;要么在换机器后重新生成.pc文件,把prefix改成新路径。我一般倾向于前者,Qt 工程里显式写路径虽然土,但可移植性反而更好。

5. 进阶技巧:用 ffprobe 快速验证包完整性,以及一个我常备的检测脚本

拿到任何一个预编译 FFmpeg 包,在往工程里集成之前,我习惯先用它自带的ffprobe.exe跑一遍,确认这个包本身是完整可用的。这一步能省掉后面很多扯皮时间。具体做法是:进入bin目录,执行ffprobe -version,看输出的configuration行里有没有--enable-shared,有的话说明是动态库版本,跟你手里的.dll.a对得上。然后随便找个视频文件跑ffprobe -show_streams test.mp4,能正常输出 JSON 就说明解码组件没问题。

下面这个 bash 脚本是我常备的,放在包根目录跑一遍,能快速检查关键文件是否齐全:

#!/bin/bash # check_ffmpeg_pkg.sh:检查 ffmpeg-3.4-mingw32 包完整性 PKG_ROOT="$(cd "$(dirname "$0")" && pwd)" REQUIRED_DLLS=("avcodec-57.dll" "avformat-57.dll" "avutil-55.dll" "swresample-2.dll" "swscale-4.dll") REQUIRED_LIBS=("libavcodec.dll.a" "libavformat.dll.a" "libavutil.dll.a" "libswresample.dll.a" "libswscale.dll.a") echo "检查 bin 目录..." for dll in "${REQUIRED_DLLS[@]}"; do if [ -f "$PKG_ROOT/bin/$dll" ]; then echo " [OK] $dll" else echo " [缺失] $dll" fi done echo "检查 lib 目录..." for lib in "${REQUIRED_LIBS[@]}"; do if [ -f "$PKG_ROOT/lib/$lib" ]; then echo " [OK] $lib" else echo " [缺失] $lib" fi done echo "检查头文件..." for header in "libavcodec/avcodec.h" "libavformat/avformat.h" "libavutil/avutil.h"; do if [ -f "$PKG_ROOT/include/$header" ]; then echo " [OK] $header" else echo " [缺失] $header" fi done

逻辑说明:脚本先定位自身所在目录作为包根目录,然后分三组检查 DLL、导入库和头文件。每组用数组存文件名,循环判断文件是否存在。输出里[OK]和[缺失]一目了然。参数说明:如果你把包解压后改了目录名,脚本不受影响,因为它用的是dirname $0动态获取路径。在 Windows 的 Git Bash 里也能跑。

跑完这个脚本,如果全绿,基本可以放心往 Qt 里集成;如果有缺失,先补文件再往下走,别硬上。从那以后我每次拿到新的预编译包,都强制走一遍这个检查,省得在链接阶段浪费时间猜来猜去。希望帮到你。

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