拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ffmpeg-3.4-mingw32实战:Qt 32位项目解码配置与避坑指南

ffmpeg-3.4-mingw32实战:Qt 32位项目解码配置与避坑指南 简介面向32位Windows系统的FFmpeg 3.4 MinGW构建版本是供Qt或其他C/C项目直接引用的音视频处理开发包适合需要编解码、格式转换、流媒体接入的开发者使用。压缩包共170个文件以头文件h、C源码、导入库lib/dll/def和pkg-config配置pc为主附带少量命令行工具与说明文档整体约2.62MB属于裁剪后的轻量实现。这份资源已有226人学习/下载常被用于补全MinGW环境下的FFmpeg依赖或作为离线独立组件嵌入小型工具。包内除预编译的编码解码库外还包含多个转码与解封装示例工程可直接对照学习API调用和滤镜、协议等模块的组织方式若只做播放或解码用途选择该版本可显著减小体积、降低内存与CPU占用。FFmpeg 3.4版本于2017年发布在保持稳定API的同时引入多项格式支持与性能优化适合需要兼容旧项目的中高级开发者。1. 为什么你会在2024年翻出一个ffmpeg-3.4-mingw3232位Qt项目的现实选择如果你还陷在工业质检、摄像头采集这种老业务里八成会遇到一个给不了退路的约束现场Socket SDK、加密狗或者某个采集卡驱动只有32位整个Qt工程被迫用MinGW 32位工具链。这时候你找一个FFmpeg的Windows版会发现在线教程和预编译包几乎都是64位、MSVC编译的mingw32这一个词就能劝退一半人。这个ffmpeg-3.4-mingw32包就是给Qt的mingw32套件准备的预编译FFmpeg版本锁定在3.4API还保留着avcodec_decode_video2跟Qt 5.9/5.12自带的mingw53_32、mingw73_32是同一个年代的东西。它能解决从include到link再到运行时DLL一条链上的匹配问题适合直接抄起来写解码、拉流、转格式代码的人。2. 把ffmpeg-3.4-mingw32装进Qt项目目录结构、pro配置与头文件路径2.1 解压后先认清这四类文件bin/lib/include/share到底谁有用下载下来的是个zip解压后第一件事不是把整个目录拖进工程而是先搞清楚每个子目录用来做什么。因为这包是给MinGW用户自包含的顶层只有bin、lib、include、share四类少一个都会在某个阶段翻车。bin目录里除了ffmpeg.exe、ffprobe.exe还有一堆带版本号的DLL比如avcodec-57.dll、avformat-57.dll、avutil-55.dll、swscale-4.dll、swresample-2.dll。这个57、55、4、2就是FFmpeg 3.4的库版本号不同小版本之间头文件和DLL不能混用。很多老玩家喜欢把高版本FFmpeg的DLL直接替换过来结果是头文件是3.4的DLL是4.x一编译就崩。lib目录里的libavformat.a、libavcodec.a这些是导入库对应bin下的同名DLL链接时用它们运行时找DLL。有的包还会提供libavcodec.dll.a跟.a看着像双胞胎实际上dll.a是指向DLL的导入符号更推荐优先链接dll.a。区分方法是看文件大小和内容通常dll.a很小只是个转发壳。include目录下是libavcodec、libavformat、libavutil、libswscale等子目录代码里#include libavcodec/avcodec.h正是指向这里。share目录基本可以忽略里面是doc和examplesexamples偶尔值得翻但不是构建依赖。这个包真正生效的只有bin和includelib是给链接器用的桥。所以轻量做法是只把include和lib拷贝到工程目录把bin下的DLL拷贝到exe输出目录或者把bin目录加进系统的PATH。我一般选择前者便于控制每台机器的部署版本不会出现PATH里混着两个FFmpeg的现象。2.2 pro文件里的三种关键配置LIBS、INCLUDEPATH、DEPENDPATH在Qt Creator里新建一个空项目把编译套件切到MinGW 32位然后打开.pro文件做下面三件事。# ffmpeg-3.4-mingw32 的安装根目录注意用正斜杠 FFMPEG_DIR D:/sdk/ffmpeg-3.4-mingw32 # 编译器找头文件的路径 INCLUDEPATH $$FFMPEG_DIR/include # 依赖搜索路径主要用于make时的头文件依赖检查 DEPENDPATH $$FFMPEG_DIR/include # 链接lib目录下的导入库顺序别乱 LIBS -L$$FFMPEG_DIR/lib \ -lavformat \ -lavcodec \ -lavutil \ -lswscale \ -lswresample # 老式API需要这个宏见2.3 DEFINES __STDC_CONSTANT_MACROS这里最容易被忽略的是库的排列顺序。MinGW的ld在静态链接时是一个单向扫描过程左边的库引用了右边的库右边的库会被解引用反过来就会报undefined reference。avformat依赖avcodec和avutil所以它必须写在最左avcodec在avutil左边。swscale和swresample是被你自己的代码调用的放最后就行。如果这样写了还报符号找不到极可能是链接的库没加对或者工具链架构不对。一个常见误操作是把MSVC编译的FFmpeg链接进来了。MSVC的导入库是.lib格式MinGW的ld无法理解反过来MinGW的.a在MSVC的link.exe下也没法用。判断方法很简单如果编译时出现cannot find -lavformat先把libavformat.a是否存在、是不是i686架构检查一遍用file libavformat.a能看到目标架构。QMake有个好处是路径里有空格也能处理因为qmake可以自动加引号。但如果你喜欢直接把FFMPEG_DIR拼接进LIBS路径里的空格会翻车。我一般选择不带空格的路径比如D:/sdk/ffmpeg-3.4-mingw32一劳永逸。2.3 头文件包含的 extern C 和 libavutil 宏pro文件配好了下一道关卡是头文件。FFmpeg用C写的而你的代码大概率是C两者之间的桥就是extern C。不规范的做法是直接在CPP文件里#include libavformat/avformat.h看起来编译都没问题但链接阶段会冒出一堆undefined reference。原因很简单C编译器对函数名做了name mangling符号对不上了。extern C { #include libavformat/avformat.h #include libavcodec/avcodec.h #include libswscale/swscale.h #include libavutil/imgutils.h }光包extern C还不够3.4的头文件在C编译时可能会用到UINT64_C、INT64_C这类宏而它们只在C99里定义。解决方案是在extern C之前定义__STDC_CONSTANT_MACROS或者在pro文件里统一加DEFINES。很多老教程只让你包extern C没提这个宏导致你在编译器报UINT64_C was not declared in this scope时一脸懵。#define __STDC_CONSTANT_MACROS extern C { #include libavutil/avutil.h }这个宏只影响头文件的解析不加也不一定崩但我们要的不是“不一定”是稳。所以我把DEFINES写在pro里而不是散布在每个cpp文件开头。2.4 用ffmpeg命令行先验证包和安装是否正常在写任何代码之前我强烈建议先让命令行跑一遍确认这套库本身可用且DLL依赖没断。命令行工具就在bin目录里命令写起来也很顺手。cd D:/sdk/ffmpeg-3.4-mingw32/bin ffmpeg -version ffmpeg -formats | findstr m3u8 ffprobe -v error -show_format C:/test/input.mp4第一行验证版本与编译配置第二行看看有没有hls/m3u8解复用器第三行用ffprobe读一个视频文件的封装格式。这比打开Qt Creator省时间得多。如果ffmpeg.exe直接报“找不到libwinpthread-1.dll”说明系统的PATH里没有MinGW运行时这一步就会暴露依赖问题不用等到exe做出来再去排错。这里顺带回应一个高频搜索词FFmpeg有Windows版吗答案是有而且这份资源就是给Windows MinGW做的版本。但要注意区分官网默认下载是win64的static或shared版本没有官方prebuilt的32位MinGW版本所以这个ffmpeg-3.4-mingw32多数是自己动手或者第三方编译的产物使用上要认准它self-contained的DLL集合。命令行验证通过后再进入pro配置和编码心态会稳很多。3. 解码实战用3.4的API把本地MP4拉成RGB帧的最短路径3.1 打开文件与网络流初始化av_register_all的不可省略时代FFmpeg 3.4的decode流程跟4.x最大的区别是有一个绕不过去的老朋友av_register_all。在4.0之后它被移除了空调用就可以了但3.4不调用它所有复用器都是空的后面的avformat_open_input会直接返回找不到demuxer。所以看到“Unknown input format”这类错误时先检查是不是忘了这个。#include iostream extern C { #define __STDC_CONSTANT_MACROS #include libavformat/avformat.h #include libavcodec/avcodec.h #include libswscale/swscale.h } int main() { // 旧版本必须手动注册所有demuxer和muxer av_register_all(); // 如果要拉rtsp、http、m3u8网络流网络层初始化 avformat_network_init(); AVFormatContext *fmtCtx nullptr; const char *path C:/test/input.mp4; if (avformat_open_input(fmtCtx, path, nullptr, nullptr) 0) { std::cerr open input failed: path std::endl; return -1; } if (avformat_find_stream_info(fmtCtx, nullptr) 0) { avformat_close_input(fmtCtx); return -1; } av_dump_format(fmtCtx, 0, path, 0); avformat_close_input(fmtCtx); return 0; }这段代码的注意点有四个第一fmtCtx置nullptravformat_open_input内部会做分配如果是个野指针它会以为已经有Context直接给你报invalid argument。第二avformat_network_init只在需要网络流时调用不调也不影响本地文件但保险起见都调上它内部有判断。第三avformat_close_input会释放所有内部资源不要手动释放streams。第四av_dump_format打印的是调试信息在控制台输出到stderr这在排查流信息时极其有用。从热搜词可以看到很多人被“ffmpeg invalid argument”困扰其实一多半就是上面的fmtCtx野指针或路径不可读导致的。碰到这个报错别急着怀疑库坏了先把路径换成绝对路径纯英文再把fmtCtx初始化为nullptr排除两个最常见因素。3.2 找到视频流并打开解码器codecpar还是codec打开容器之后要挑出视频流并初始化解码器。有些老代码还在用stream-codec直接操作codec参数3.4里虽然还能用但已经是废弃方向。标准做法是走codecpar。int videoStreamIdx -1; for (unsigned int i 0; i fmtCtx-nb_streams; i) { AVStream *stream fmtCtx-streams[i]; if (stream-codecpar-codec_type AVMEDIA_TYPE_VIDEO) { videoStreamIdx i; break; } } const AVCodec *decoder avcodec_find_decoder( fmtCtx-streams[videoStreamIdx]-codecpar-codec_id); AVCodecContext *decoderCtx avcodec_alloc_context3(decoder); avcodec_parameters_to_context(decoderCtx, fmtCtx-streams[videoStreamIdx]-codecpar); decoderCtx-thread_count 4; if (avcodec_open2(decoderCtx, decoder, nullptr) 0) { std::cerr open decoder failed std::endl; return -1; }avcodec_alloc_context3先分配一个默认解码器上下文avcodec_parameters_to_context把容器里记录的编码参数填进去然后avcodec_open2真正打开解码器。这里千万别自己直接拿fmtCtx-streams[i]-codec赋值给decoderCtx那会把容器管理的codec上下文和独立分配的上下文搅在一起后面释放时double free。thread_count这个参数值得多说一句。有些朋友看到别人的demo写4就抄4结果在一些低分辨率视频上反而解码变慢。解码器的多线程有sync和async两种模式不是所有解码器都支持设了无效也不会报错。我的习惯是桌面端本地播放设4推流或者实时性要求高的场景设1避免线程同步带来额外延迟和内存开销。如果设0则使用FFmpeg的自动选择但结果不确定所以还是自己定一个。find_stream_info之后streams数组里的codecpar已经填好了但有些封装格式如某些MP4的宽高可能直到解码第一帧才更新。所以如果你在open decoder前发现width是0不要慌等解码后从frame里拿。3.3 循环解码av_read_frame avcodec_decode_video2 unref解码的核心循环在3.4下依然是经典的read_packet/decode_video2组合。AVPacket packet; av_init_packet(packet); AVFrame *frame av_frame_alloc(); int gotPicture 0; int ret 0; while ((ret av_read_frame(fmtCtx, packet)) 0) { if (packet.stream_index videoStreamIdx) { ret avcodec_decode_video2(decoderCtx, frame, gotPicture, packet); if (ret 0) { fprintf(stderr, decode error code%d\n, ret); break; } if (gotPicture) { // 这里得到一帧YUV或其它原始格式 processFrame(decoderCtx, frame); } } av_packet_unref(packet); } av_frame_free(frame);每个AVPacket在使用完后必须av_packet_unref否则内部引用计数泄漏。老版本的av_free_packet在3.4已经deprecated直接unref就对了。avcodec_decode_video2的返回值是实际消耗的字节数不是错误码要跟gotPicture配合看。如果返回负数才表示错误比如-1094995529对应AVERROR_INVALIDDATA。gotPicture代表这一轮是否成功产出一帧因为H.264存在B帧重排一个packet读出多个frame或者读不出frame都是正常的。一个容易踩的边界av_read_frame返回0时是正常读到包返回AVERROR_EOF才是读完了返回负值也可能是IO错误。我们在循环里用0判断遇到EOF就退出但这样结束原因显示不出来。如果要知道正常结束还是中途出错最好把EOF单独判断if (ret AVERROR_EOF) { /* 正常播放完成 */ } else if (ret 0) { /* 流错误 */ }另一点av_init_packet只是把packet的字段清零并没有分配data。av_read_frame内部给packet的data和size赋值。如果你在循环外自己定义packet并忘了init某些编译器下data是随机值av_read_frame会直接崩。所以init这步不能省。3.4 Swscale转RGB32并构造QImage不花屏解码出来的是YUV系数据Qt的QImage不认识需要经过swscale转到RGB。这一节是最容易出花屏的地方代码写对了像素格式和stride才能得到干净的图片。AVFrame *frame /* 上面解码产出的帧 */; // 初始化一次不要在循环里重复调用 SwsContext *swsCtx sws_getContext( decoderCtx-width, decoderCtx-height, decoderCtx-pix_fmt, decoderCtx-width, decoderCtx-height, AV_PIX_FMT_BGR32, SWS_BILINEAR, nullptr, nullptr, nullptr); uint8_t *rgbData[4] { nullptr }; int rgbLinesize[4] { 0 }; av_image_alloc(rgbData, rgbLinesize, decoderCtx-width, decoderCtx-height, AV_PIX_FMT_BGR32, 1); sws_scale(swsCtx, (const uint8_t *const *)frame-data, frame-linesize, 0, decoderCtx-height, rgbData, rgbLinesize); QImage image(rgbData[0], decoderCtx-width, decoderCtx-height, rgbLinesize[0], QImage::Format_RGB32); // 使用image例如 setPixmap(QPixmap::fromImage(image)); av_freep(rgbData[0]); sws_freeContext(swsCtx);这里有个关键点像素格式选了AV_PIX_FMT_BGR32而QImage用Format_RGB32。在Windows小端机上QImage::Format_RGB32的内存顺序是B、G、R、A正好对应FFmpeg的BGR32。如果你直接用AV_PIX_FMT_RGB32显示出来蓝色和红色会互换典型症状是天空变橙色。这是一个很玄学但对上就再也不会忘的细节。还需要注意stride。QImage构造函数的第三个参数是bytesPerLine如果你漏填QImage会默认按width*4计算。FFmpeg的linesize可能做了16字节或32字节对齐尤其是宽度不是2的幂时两者数值不同图像会倾斜或者右半部分是绿噪声。上面用rgbLinesize[0]显式传进去就不会出这种问题。av_image_alloc分配的内存通过av_freep释放注意参数必须传rgbData[0]因为av_freep会置空指针。SwsContext也是一样用完sws_freeContext。3.5 m3u8转换mp4的实战该用命令还是用API很多项目里接触的是m3u8这种HLS切片而不是单个MP4。这时候两条路命令行一条ffmpeg -i index.m3u8 -c copy output.mp4就完成了API则需要AVFormatContext能识别http形式和TS分片。在3.4里HLS解复用器已经编译进去的情况下用avformat_network_init avformat_open_input(“http://.../index.m3u8”)完全可以打开。代码逻辑和上面一致只有两个额外注意。第一m3u8的URL经常带问号参数avformat_open_input原样接收但要在URL前手动拼接http://等完整协议不能只写域名。第二HLS在联网状态下打开慢通常要设置超时option比如AVDictionary *opts nullptr; av_dict_set(opts, rw_timeout, 5000000, 0); // 单位是微秒 av_dict_set(opts, http_persistent, 1, 0); ret avformat_open_input(fmtCtx, url, nullptr, opts); av_dict_free(opts);这样转换时不会因为某个TS分片延迟而卡死。日常我调试HLS时先用ffprobe看分片列表是否正确再用命令做一次copy转码最后才嵌入到程序里。命令行验证这个资源好不好用比写代码快十倍。4. 避坑总动员32位MinGW下FFmpeg预编译包最常见的五个坑这五个坑一个是自己当初入坑时踩的另一个是帮同事排查时撞上的全部来自真实项目已经跟写代码的习惯绑定在了一起。每一个都按“现象 → 原因 → 解决”的顺序讲方便你直接对着症状查。4.1 链接报错undefined reference toav_register_all不是库没加是符号被C改名了现象pro文件里LIBS已经写好库路径也没问题一编译却刷出几十个undefined reference全是av_开头的函数。精确报错是undefined reference to av_register_all()而不是cannot find -lavformat。原因这种错误有九成是C编译时没有包extern C。看到av_register_all和undefined reference并举第一反应要是检查头文件包含。C的name mangling规则会把函数名改写成带参数类型和命名空间的符号链接器按C符号去找自然找不到。还有一种少见情况同一个源文件前面已经用extern C包含过一次avformat.h后面不小心又直接include了一遍同一头文件预处理防止了重复内容但extern C的作用域没有覆盖第二次符号还是被mangle。解决统一在项目里做一个ffmpeg_init.h#ifndef FFMPEG_INIT_H #define FFMPEG_INIT_H #define __STDC_CONSTANT_MACROS extern C { #include libavformat/avformat.h #include libavcodec/avcodec.h #include libswscale/swscale.h #include libavutil/imgutils.h } #endif所有cpp文件只include这个头不要再直接include FFmpeg头文件。如果问题依旧用nm libavformat.a | grep av_register_all确认符号存在再用objdump -t看符号名如果前面有_Z结尾的乱码说明该库本身是C编译的不是预编译包里该有的状态。4.2 运行时报错0xc000007b 或找不到 libgcc_s_dw2-1.dll现象exe编译成功后双击Windows直接弹0xc000007b或者提示“由于找不到libgcc_s_dw2-1.dll无法继续执行代码”。有的机器Windows 10弹Windows 7反而不弹很诡异。原因0xc000007b的本质是系统尝试以某一架构加载DLL但DLL架构不匹配。你的exe是32位运行时却加载了64位的FFmpeg DLL或者反过来。更隐蔽的是MinGW运行时DLL缺失或版本冲突ffmpeg-3.4-mingw32包里的DLL依赖libgcc_s_dw2-1.dll和libstdc-6.dllQt安装目录下的mingw53_32/bin里有对应版本但你如果没把Qt的bin目录放进PATH或者exe独立输出后没把这些运行时带过去就会触发。dw2后缀表示这个包是DWARF异常处理模型和你用的MinGW不一定一致。解决先把ffmpeg/bin下的所有av开头的DLL和libgcc_s_dw2-1.dll复制到exe目录。然后在Qt Creator的构建步骤里加一条windeployqt命令windeployqt --compiler-runtime --release .它会自动扫描Qt依赖把libstdc-6.dll、libwinpthread-1.dll补齐。但注意windeployqt不会处理FFmpeg的DLL所以FFmpeg还是要手动拷。排查时用Process Explorer查看exe进程加载的DLL路径如果看到C:\Windows\System32下的64位dll被加载到32位进程那基本就是0xc000007b的实锤与其冒险删系统目录里的同名DLL不如在环境变量PATH里把32位Qt bin目录往前排。4.3 解码出来的图像绿得发慌stride对齐和像素格式顺序的联合坑现象代码能编译能运行视频也播放了但屏幕上一片绿或每行图像错位越往下偏移越大。尤其在某些分辨率宽度不是16倍数比如1280x720时更容易出现换到1920x1080又好了。原因两个叠加的坑。第一个是直接拿AVFrame的data和linesize构造QImage。FFmpeg内部为了SIMD优化每行像素会做16/32字节对齐linesize可能大于width4。如果QImage构造函数不指定bytesPerLineQt会按width4去解析每行数据从第二行开始就偏了。第二个坑是像素格式选错QImage::Format_RGB32在Windows上的存储顺序是BGRAFFmpeg的AV_PIX_FMT_RGB32也是小端BGRA看起来一样但很多文档把AV_PIX_FMT_RGB32描述成RGB导致有人选AV_PIX_FMT_RGB24或GRAY8结果颜色完全不对。解决统一走sws_scale转成AV_PIX_FMT_BGR32再构造QImage时显式传入rgbLinesize[0]。转之前先在代码里打印frame-linesize[0]和width*4的差值如果两次运行不一致就要检查是否切换了分辨率。另外要特别留意QImage::Format_RGB32默认alpha是0xff如果sws_scale的输出没有填alphaQt会用0填充图像会透明不见。用BGR32并在背景填充时置alpha为255可以规避但更简单的做法是再用QImage::convertToFormat(QImage::Format_RGB888)避开alpha问题。4.4 avformat_open_input返回Invalid argument路径中文和NULL指针的锅现象同一份代码在Linux上正常打开Windows上一执行就返回-22即AVERROR(EINVAL)。有人叫它ffmpeg invalid argument一搜全是同样问题。如果你的输入路径是D:\项目素材\video.mp4破案率更高。原因Windows的IO层在3.4中通过标准的fopen打开本地文件fopen不支持UTF-8编码路径默认可能采用ANSI代码页。含中文的路径会被转成乱码打开失败。第二个原因更隐蔽avformat_open_input的第一个参数是AVFormatContext**调用前必须确保指针指向的AVFormatContext是NULL。很多老代码从网上抄来时在函数外new了一个AVFormatContext然后直接塞进去FFmpeg会认为这已经是一个初始化好的Context于是拒绝再次打开返回EINVAL。解决路径不在代码里硬编码用QFile::encodeName转换它会把QString转成本地编码的字节数组。或者干脆要求用户把视频放到纯英文路径。对于AVFormatContext严格写成AVFormatContext *fmtCtx nullptr;并在每次打开前都置空if (fmtCtx) { avformat_close_input(fmtCtx); fmtCtx nullptr; }如果是网络流返回EINVAL还要确认avformat_network_init已调用并且URL前面带了protocol://没带会被当成本地文件路径去找自然报Invalid argument。4.5 avcodec_decode_video2返回码正常但gotPicture始终为0refcounted_frames的约定现象解码循环里ret0但gotPicture一直为0帧数始终不增加。播放同一视频用ffplay正常换成API就出不来画面。原因在3.4中AVCodecContext有个成员refcounted_frames它决定AVFrame是否采用引用计数模式。如果你是从FFmpeg 4.x时代迁移下来的老代码可能在某个地方手动把它设成过1或者从某个示例里复制过这个设置之后忘了改回来。在这种模式下avcodec_decode_video2要求调用者正确使用av_frame_ref管理生命周期如果用户没有按约定做解码器会认为帧还没有被妥善引用而拒绝输出。换成avcodec_send_packet/avcodec_receive_frame的新API则没有这个问题但3.4头文件里根本没有receive_frame的声明。解决在avcodec_open2之前强制置0decoderCtx-refcounted_frames 0;这样decode_video2直接把数据填到你传进的AVFrame上你可以在回调里任意处理不需要av_frame_ref。同时还要保证传进去的frame是用av_frame_alloc分配的不能用堆栈上的AVFrame对象。这个约定在官方文档里写得不多多半是你抄demo的时候被省略了。如果置0后还是gotPicture0检查pkt的stream_index是不是视频流、pkt.size是不是0size为0的packet通常表示flush也会导致gotPicture置0。这五个坑基本覆盖了从编译到运行到图像显示的主干路线。遇到问题时先把4.4的路径和指针查一遍再查4.1的符号最后看图像颜色排查逻辑清晰很多。5. 进阶验证预编译包的边界和自制含x264的32位FFmpeg的取舍5.1 先跑ffmpeg -version确认configuration和编码器列表拿到包之后别急着写代码先验证边界。预编译包的作者通常只启用了一部分功能你需要的编码器可能根本不在里面。FFmpeg提供了非常直接的命令行自检手段ffmpeg -version ffmpeg -encoders ffmpeg -decoders我把这三行命令的检查结果整理成了一个小表方便对照。检查项命令预期结果架构ffmpeg -versioni686 / x86_32不要出现x86_64configurationffmpeg -version能看到--enable-shared、--disable-static等libx264ffmpeg -encoders编码器列表里如果没有libx264说明基础包缺GPL编码器这一步如果发现没有libx264意味着你不能直接用命令做H.264编码API里用avcodec_find_encoder(AV_CODEC_ID_H264)只会返回nullptr。不要到写代码那一步才崩溃。5.2 当业务需要H.264编码或者AAC时这个预编译包给不了你3.4-mingw32如果是一个普通构建编解码器都是基础集没有GPL的libx264也没有fdk-aac。遇到需要编码推流的场景要么换用别人打包好的完整版要么自己用MSYS2编一版32位。自己编会踩坑但路径是清晰的。# 在MSYS2的MinGW32环境中执行 pacman -S mingw-w64-i686-toolchain mingw-w64-i686-x264 mingw-w64-i686-sdl2 cd /path/to/ffmpeg-src ./configure --prefix/mingw32 \ --archx86 --target-osmingw32 \ --enable-shared --disable-static \ --enable-gpl --enable-version3 \ --enable-libx264 --enable-libfdk-aac \ --enable-nonfree make -j4 make install--archx86是横向32位如果忘了加默认会把当前编译器当成x86_64来配置出来的库还是64位。--enable-shared生成DLL--disable-static避免同时存在.a和.dll.a而导致链接时优先选择静态库、使得exe体积巨大。fdk_aac需要--enable-nonfree商用前确认license不要指望开箱即用。5.3 一百行的自检demo解码前三十帧打印耗时与分辨率最后给出我每次安装FFmpeg包之后都会跑一遍的最小验证demo它把前面章节的核心调用串成一条链不涉及Qt纯粹验证FFmpeg本身能否工作。新环境配好后先编译运行它通过再谈业务。#include chrono #include iostream extern C { #include libavformat/avformat.h #include libavcodec/avcodec.h #include libswscale/swscale.h } int main() { av_register_all(); AVFormatContext *fmt nullptr; const char *url C:/test/input.mp4; if (avformat_open_input(fmt, url, nullptr, nullptr) 0) return 1; if (avformat_find_stream_info(fmt, nullptr) 0) return 1; int vIdx -1; for (unsigned i 0; i fmt-nb_streams; i) { if (fmt-streams[i]-codecpar-codec_type AVMEDIA_TYPE_VIDEO) { vIdx i; break; } } AVCodecContext *ctx avcodec_alloc_context3(nullptr); avcodec_parameters_to_context(ctx, fmt-streams[vIdx]-codecpar); ctx-refcounted_frames 0; avcodec_open2(ctx, avcodec_find_decoder(ctx-codec_id), nullptr); std::cout codec: ctx-codec-name ctx-width x ctx-height std::endl; AVPacket pkt; av_init_packet(pkt); AVFrame *frame av_frame_alloc(); int got 0, count 0; auto start std::chrono::steady_clock::now(); while (av_read_frame(fmt, pkt) 0) { if (pkt.stream_index vIdx) { avcodec_decode_video2(ctx, frame, got, pkt); if (got) { count; if (count 30) break; } } av_packet_unref(pkt); } auto end std::chrono::steady_clock::now(); std::cout decoded count frames in std::chrono::duration_caststd::chrono::milliseconds(end - start).count() ms std::endl; av_frame_free(frame); avformat_close_input(fmt); return 0; }这段代码打印解码器名字、分辨率统计30帧解码耗时。30帧不大如果是4K视频也能看出来解码性能行不行。从那以后我每次配好新机器、或者换Qt套件都强制先跑一遍这个自检demo再决定是否继续往业务里接FFmpeg。很多时候看起来难查的bug在这个demo上跑不出来就说明环境没搭稳。希望帮到你。本文还有配套的精品资源点击获取
返回列表