简介这份资源是面向音视频开发者的 FFmpeg n5.1.2 开发库适合需要在 Windows 平台进行多媒体应用开发、转码推流或实时视频分析的中高级工程师。包内共 377 个文件以 274 个 h 头文件、23 个 c 源文件为主辅以 14 个 dll、8 个 lib、8 个 a 静态库及 8 个 pc 配置文件和 3 个 exe 可执行程序压缩包约 9.12MB。头文件定义了 libavcodec、libavformat、libavfilter、libavutil 等库的调用接口库文件与可执行程序可直接用于链接和运行MinGW64 编译说明则记录了构建过程与配置信息。借助这套开发库读者可以快速搭建 FFmpeg 开发环境理解 AVPacket、AVFormatContext、AVCodecContext、AVFrame 等核心数据结构并实践编码解码、封装转码、过滤器处理等任务。目前已有 2459 人学习下载适合作为音视频开发的参考工具包。1. 拿到 FFmpeg n5.1.2 开发库先搞清楚它能省掉哪几步如果你正在 Windows 上做音视频开发大概率经历过这样的循环想调avcodec_send_frame发现手里只有ffmpeg.exe没有头文件去官网找 dev 包下载下来是一堆.dll和.lib但不知道哪个对应avformat、哪个对应swscale好不容易把库链上了运行时报0xc000007b排查半天发现是 32 位和 64 位混用。这套 FFmpeg n5.1.2 开发库就是冲着这个场景来的——它不是命令行工具包而是给 C/C 工程直接链接用的 SDK包含include头文件目录、lib导入库目录和bin运行时动态库目录版本锁定在 n5.1.2 这个长期稳定分支。适合两类人一是用 Qt、MFC 或纯 Win32 写播放器、转码器、推流端的桌面开发者二是需要在 Windows 上先验证算法逻辑、再往 Linux 或 Android 移植的跨平台开发者。如果你只是想把mkv转mp4那用ffmpeg.exe就够了这份开发库对你来说反而重。2. 开发库目录结构与链接选型为什么不是所有 .lib 都要塞进工程2.1 三个目录各管什么先分清再动手解压之后你会看到典型的 FFmpeg Windows 构建产物布局常见做法是分成三个顶层目录目录内容工程里的角色include/libavcodec/、libavformat/、libavutil/、libswscale/、libswresample/、libavfilter/、libavdevice/等子目录每个下面有.h头文件编译期告诉编译器函数声明和结构体定义lib/avcodec.lib、avformat.lib、avutil.lib、swscale.lib、swresample.lib、avfilter.lib、avdevice.lib等导入库链接期告诉链接器去哪个 DLL 找符号bin/avcodec-59.dll、avformat-59.dll、avutil-57.dll、swscale-6.dll、swresample-4.dll、avfilter-8.dll、avdevice-59.dll等运行期真正干活的动态库n5.1.2 对应的主版本号是avcodec 59、avformat 59、avutil 57、swscale 6、swresample 4、avfilter 8、avdevice 59。这个版本号很重要因为如果你之前工程里链的是 n4.x 的库头文件里的结构体成员偏移和函数签名可能已经变了混用会直接编译报错或运行崩溃。2.2 链接顺序有讲究顺序错了就是一堆 undefined reference在 Visual Studio 里配置附加依赖项时不是把七个.lib随便列上去就行。FFmpeg 的库之间有依赖关系链接器是从左到右解析符号的被依赖的库要放在依赖它的库后面。我一般会按这个顺序写avdevice.lib avfilter.lib avformat.lib avcodec.lib swresample.lib swscale.lib avutil.lib逻辑说明avdevice依赖avfilter和avformatavfilter依赖avformat、avcodec、swscale、swresampleavformat依赖avcodecavcodec依赖avutilswresample和swscale都依赖avutil。avutil是最底层的放最后。如果你只做解码播放不涉及滤镜和设备采集可以只链avformat、avcodec、swscale、swresample、avutil这五个avdevice和avfilter可以先不加减少 DLL 数量。参数说明在 VS 的「链接器 → 输入 → 附加依赖项」里逐行填入上述.lib文件名在「链接器 → 常规 → 附加库目录」里填入lib文件夹的绝对路径在「C/C → 常规 → 附加包含目录」里填入include文件夹的绝对路径。运行的时候把bin目录下的所有 DLL 复制到.exe同级目录或者把bin目录加入系统PATH。我一般选前者因为部署的时候不会漏。2.3 一个最小验证工程打开一个视频文件读出时长和编码格式光配好环境不算数得跑一段代码确认库真的能用。下面这段代码用avformat打开文件找到视频流打印时长和编码器名称#include stdio.h #include libavformat/avformat.h #include libavcodec/avcodec.h int main(int argc, char* argv[]) { if (argc 2) { printf(usage: %s input file\n, argv[0]); return -1; } AVFormatContext* fmt_ctx NULL; // 打开输入文件只读模式 int ret avformat_open_input(fmt_ctx, argv[1], NULL, NULL); if (ret 0) { char errbuf[AV_ERROR_MAX_STRING_SIZE]; av_strerror(ret, errbuf, sizeof(errbuf)); printf(open input failed: %s\n, errbuf); return -1; } // 探测流信息对于没有头信息的裸流必须调用 ret avformat_find_stream_info(fmt_ctx, NULL); if (ret 0) { printf(find stream info failed\n); avformat_close_input(fmt_ctx); return -1; } // 打印容器格式和时长AV_TIME_BASE 是微秒 printf(format: %s, duration: %.2f s\n, fmt_ctx-iformat-name, fmt_ctx-duration / (double)AV_TIME_BASE); // 遍历所有流找到视频流 for (unsigned i 0; i fmt_ctx-nb_streams; i) { AVStream* st fmt_ctx-streams[i]; if (st-codecpar-codec_type AVMEDIA_TYPE_VIDEO) { const AVCodec* codec avcodec_find_decoder(st-codecpar-codec_id); printf(video stream index: %u, codec: %s, %dx%d\n, i, codec ? codec-name : unknown, st-codecpar-width, st-codecpar-height); } } avformat_close_input(fmt_ctx); return 0; }逻辑说明avformat_open_input负责打开文件并读取容器头avformat_find_stream_info负责在没有完整头信息时探测流参数。fmt_ctx-duration的单位是微秒除以AV_TIME_BASE值为 1000000得到秒。st-codecpar是 n5.x 里推荐的流参数访问方式替代了旧版直接读st-codec的做法。参数说明编译时记得链接上面列出的.lib。如果运行时报找不到 avformat-59.dll检查bin目录是否在.exe同级或PATH里。如果avformat_open_input返回-2AVERROR_NOENT说明文件路径不对返回-1094995529AVERROR_INVALIDDATA说明文件格式不被识别或文件损坏。3. 解码与帧处理从 avcodec_send_packet 到 sws_scale 的完整链路3.1 解码 API 在 n5.x 里的正确用法FFmpeg 从 n3.1 开始引入了avcodec_send_packet/avcodec_receive_frame这组新 APIn5.1.2 里旧版的avcodec_decode_video2虽然还能用但已经被标记为废弃。新 API 的好处是把「送一个包」和「收一帧」解耦了解码器内部有缓冲一个包可能产出零帧或多帧。常见做法是循环送包、循环收帧AVCodecContext* codec_ctx avcodec_alloc_context3(codec); avcodec_parameters_to_context(codec_ctx, st-codecpar); avcodec_open2(codec_ctx, codec, NULL); AVPacket* pkt av_packet_alloc(); AVFrame* frame av_frame_alloc(); while (av_read_frame(fmt_ctx, pkt) 0) { if (pkt-stream_index video_stream_index) { int ret avcodec_send_packet(codec_ctx, pkt); if (ret 0 ret ! AVERROR(EAGAIN)) { printf(send packet failed: %d\n, ret); av_packet_unref(pkt); continue; } while (1) { ret avcodec_receive_frame(codec_ctx, frame); if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) break; if (ret 0) { printf(receive frame failed\n); break; } // 到这里 frame 里就是解码后的 YUV 数据 // 可以送 sws_scale 转 RGB或者直接写文件 av_frame_unref(frame); } } av_packet_unref(pkt); } // 冲刷解码器把缓存的帧全部取出来 avcodec_send_packet(codec_ctx, NULL); while (avcodec_receive_frame(codec_ctx, frame) 0) { av_frame_unref(frame); }逻辑说明avcodec_send_packet返回EAGAIN表示解码器内部缓冲区满了需要先调avcodec_receive_frame取走一些帧再继续送。循环收帧时遇到EAGAIN说明当前包已经处理完遇到EOF说明解码器已经冲刷完毕。最后送NULL包是必须的否则 B 帧参考的缓存帧会丢。参数说明codec_ctx-thread_count可以设为 0 让 FFmpeg 自动决定线程数也可以手动设为 CPU 核心数。codec_ctx-flags里可以加AV_CODEC_FLAG_LOW_DELAY降低延迟但会牺牲一些压缩效率。如果解码出来花屏先检查avcodec_parameters_to_context是否在avcodec_open2之前调用。3.2 sws_scale 做像素格式转换参数别填反解码出来的AVFrame通常是 YUV420P要显示到 Windows 窗口上一般得转成 RGB24 或 BGRA。sws_scale是干这个的但它的参数顺序容易搞混struct SwsContext* sws_ctx sws_getContext( codec_ctx-width, codec_ctx-height, codec_ctx-pix_fmt, codec_ctx-width, codec_ctx-height, AV_PIX_FMT_BGRA, SWS_BILINEAR, NULL, NULL, NULL); // 目标缓冲区BGRA 每像素 4 字节 int dst_linesize[1] { codec_ctx-width * 4 }; uint8_t* dst_data[1] { (uint8_t*)malloc(codec_ctx-height * dst_linesize[0]) }; sws_scale(sws_ctx, frame-data, frame-linesize, 0, codec_ctx-height, dst_data, dst_linesize);逻辑说明sws_getContext的前三个参数是源宽高和源像素格式中间三个是目标宽高和目标像素格式SWS_BILINEAR是缩放算法。sws_scale的frame-data和frame-linesize是源数据指针数组0是起始行codec_ctx-height是要转换的行数。目标dst_data和dst_linesize同理。参数说明如果源和目标尺寸一样SWS_BILINEAR可以换成SWS_POINT更快但画质略差。dst_linesize不一定要等于width * 4有些显示库要求行对齐到 4 字节或 8 字节按实际需求设。转换完的dst_data记得freesws_ctx记得sws_freeContext。3.3 音频重采样用 swresample别自己写循环音频解码出来可能是AV_SAMPLE_FMT_FLTP浮点平面格式而声卡要的是AV_SAMPLE_FMT_S1616 位交错。libswresample就是干这个的SwrContext* swr_ctx swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100, // 输出 codec_ctx-channel_layout, codec_ctx-sample_fmt, codec_ctx-sample_rate, // 输入 0, NULL); swr_init(swr_ctx); uint8_t* out_buf[1]; int out_samples swr_convert(swr_ctx, out_buf, max_out_samples, (const uint8_t**)frame-data, frame-nb_samples);逻辑说明swr_alloc_set_opts的参数顺序是「输出通道布局、输出采样格式、输出采样率、输入通道布局、输入采样格式、输入采样率」。swr_convert返回实际输出的采样数可能小于请求的max_out_samples因为重采样器内部有缓冲。参数说明max_out_samples一般按av_rescale_rnd算出来简单场景可以直接给frame-nb_samples * 2留足余量。如果输出声音有杂音或断续先检查swr_init是否返回 0再检查输入frame-channel_layout是否为空有些流不带头信息需要手动设默认值。4. 避坑与排查链接、运行、版本三个层面的血泪经验4.1 现象编译通过但链接报 LNK2019 无法解析的外部符号原因附加依赖项里漏了某个.lib或者链接顺序不对导致被依赖的库没被解析到。比如只写了avformat.lib没写avcodec.libavformat_open_input能链上但avcodec_find_decoder就找不到。解决按第 2.2 节的顺序把七个.lib都加上确认「附加库目录」指向的是lib文件夹而不是bin文件夹。如果还是报错用dumpbin /symbols avformat.lib | findstr avcodec确认这个.lib里确实引用了avcodec的符号。4.2 现象程序启动时报 0xc000007b 或找不到 DLL原因32 位程序链了 64 位库或者反过来。FFmpeg 的 Windows 构建分 32 位和 64 位头文件看起来一样但.lib和.dll不能混用。另一个常见原因是bin目录下的 DLL 没复制到.exe同级或者复制了但版本不对比如把 n4.x 的 DLL 和 n5.x 的.lib混在一起。解决用dumpbin /headers avcodec.lib | findstr machine确认库的位数x86 显示14Cx64 显示8664。在 VS 里确认工程平台是x64还是Win32和库保持一致。把bin目录下所有 DLL 复制到输出目录不要只复制一两个。4.3 现象avformat_open_input 返回失败但错误信息是乱码或空原因av_strerror的缓冲区太小或者没有调用avformat_network_init虽然本地文件不需要但有些构建默认开了网络支持。更常见的是文件路径里有中文而avformat_open_input在 Windows 上默认用 UTF-8 解释路径导致找不到文件。解决把errbuf开到AV_ERROR_MAX_STRING_SIZE1024 字节。路径问题用avformat_open_input之前先把宽字符路径转成 UTF-8或者直接用avpriv_open的替代方案——在 Windows 上我一般用_wfopen打开文件再交给avformat_open_input的自定义 IO但更简单的做法是把文件路径改成全英文。4.4 现象解码几帧后 avcodec_receive_frame 一直返回 EAGAIN原因送包和收帧的循环写错了。常见错误是只调了一次avcodec_receive_frame就继续送下一个包导致解码器内部缓冲越积越多最后avcodec_send_packet返回EAGAIN程序卡死。解决严格按第 3.1 节的双层循环写——外层送包内层循环收帧直到EAGAIN或EOF。如果送包返回EAGAIN不要丢弃这个包先收几帧再重新送同一个包。AVPacket在avcodec_send_packet之后不要立即av_packet_unref等确认包被消费了再释放。4.5 现象n5.1.2 的头文件里某些结构体成员编译报错「未定义」原因FFmpeg 在 n5.x 里把很多结构体改成了不透明指针比如AVCodecContext的某些字段需要通过av_opt_get或专门的 getter 访问不能直接读。另外AVStream-codec在 n5.x 里已经废弃要用AVStream-codecpar。解决查 FFmpeg 的doc/APIchanges文件确认你用的字段在 n5.1.2 里是否还存在。如果是从 n4.x 迁移过来的代码重点检查AVCodecContext的channels和channel_layoutn5.x 推荐用ch_layout、AVFrame的pkt_duration改成了duration等字段。5. 进阶技巧用 avfilter 做零拷贝滤镜链和硬件解码探测5.1 avfilter 滤镜链的搭建与零拷贝思路如果你要做缩放、裁剪、旋转、加水印用libavfilter比手动调sws_scale更灵活。n5.1.2 里滤镜链的搭建流程是avfilter_graph_alloc→avfilter_graph_create_filter创建源和宿 →avfilter_link连接 →avfilter_graph_config配置。下面是一个缩放滤镜的最小示例AVFilterGraph* graph avfilter_graph_alloc(); AVFilterContext *src_ctx, *sink_ctx, *scale_ctx; // 创建 buffer 源参数从解码器上下文取 avfilter_graph_create_filter(src_ctx, avfilter_get_by_name(buffer), in, video_size1920x1080:pix_fmt0:time_base1/25:pixel_aspect1/1, NULL, graph); // 创建 scale 滤镜缩放到 1280x720 avfilter_graph_create_filter(scale_ctx, avfilter_get_by_name(scale), scale, 1280:720, NULL, graph); // 创建 buffersink 宿 avfilter_graph_create_filter(sink_ctx, avfilter_get_by_name(buffersink), out, NULL, NULL, graph); // 连接src - scale - sink avfilter_link(src_ctx, 0, scale_ctx, 0); avfilter_link(scale_ctx, 0, sink_ctx, 0); avfilter_graph_config(graph, NULL);逻辑说明buffer源滤镜的参数video_size、pix_fmt、time_base必须和解码器输出的帧一致否则av_buffersrc_add_frame会返回错误。pix_fmt0对应AV_PIX_FMT_YUV420P具体值查AVPixelFormat枚举。scale滤镜的参数就是目标宽高冒号分隔。参数说明如果要加多个滤镜比如缩放后再裁剪就在scale后面再创建一个crop滤镜参数写w:h:x:y。滤镜链配置好后解码出的AVFrame用av_buffersrc_add_frame送入源用av_buffersink_get_frame从宿取出处理后的帧。零拷贝的关键是AVFrame在滤镜链内部传递时只传引用不复制数据所以不要在送入滤镜后立即av_frame_unref。5.2 硬件解码探测先问再开别硬编n5.1.2 支持 D3D11VA、DXVA2、QSV、CUDA 等硬件解码方式。但硬件解码不是所有机器都支持正确做法是先探测再初始化enum AVHWDeviceType type AV_HWDEVICE_TYPE_NONE; while ((type av_hwdevice_iterate_types(type)) ! AV_HWDEVICE_TYPE_NONE) { printf(supported hw device: %s\n, av_hwdevice_get_type_name(type)); } // 尝试用 D3D11VA 创建硬件设备上下文 AVBufferRef* hw_device_ctx NULL; if (av_hwdevice_ctx_create(hw_device_ctx, AV_HWDEVICE_TYPE_D3D11VA, NULL, NULL, 0) 0) { codec_ctx-hw_device_ctx av_buffer_ref(hw_device_ctx); // 还需要设置 get_format 回调告诉解码器用哪个像素格式 }逻辑说明av_hwdevice_iterate_types遍历当前构建支持的硬件类型但不代表当前机器能用。av_hwdevice_ctx_create才是真正尝试创建返回 0 才算成功。设置hw_device_ctx后必须实现get_format回调在回调里返回AV_PIX_FMT_D3D11或对应的硬件格式否则解码器还是会走软解。参数说明硬件解码出来的帧在 GPU 显存里要转成普通AVFrame需要av_hwframe_transfer_data。这一步有拷贝开销但比全软解还是快。如果av_hwframe_transfer_data返回错误检查目标帧的format是否设成了AV_PIX_FMT_NV12或AV_PIX_FMT_YUV420P。我一般会在工程里保留软解回退路径——硬件解码初始化失败就自动切软解别让程序直接崩。从那以后我每次拿到新的 FFmpeg 开发库都强制先跑一遍第 2.3 节的最小验证工程确认头文件、.lib、.dll三者版本一致再往正式工程里集成。这个习惯帮我省掉了至少三次因为版本混用导致的通宵排查。希望帮到你。本文还有配套的精品资源点击获取
