🎬 第一章 FFmpeg入门简介
📄 一句话概括

八个小库叠成一套 SDK,每个管一段:格式、编解码、滤镜、转换、设备。 命令行就是调这套 SDK 的一个现成程序,所以命令行能做的,SDK 都能做。

📝 本节和前面几节的关系

1.3.1 看了三个外壳,这一节钻到外壳里头看零件。 1.3.3 再往下,看这些零件是怎么编出来的。

一、八个库,从上到下

按「离用户远近」排一遍,层次关系就出来了:

库 管什么 少了它会怎样


libavdevice 采集设备,摄像头与声卡 不能直接读设备 libavformat 封装与解封装 打不开文件也存不了文件 libavcodec 编解码 只能转封装,不能转码 libavfilter 滤镜 不能改画面,只能原样过 libswscale 像素格式与缩放 分辨率改不了 libswresample 音频重采样 采样率与声道改不了 libavutil 公共底座 什么都跑不起来

看「少了它会怎样」这一列,会发现一个规律: 越靠下的库,缺了越致命;越靠上的库,缺了只是少个功能。 libavutil 缺了是什么都干不了,libavdevice 缺了只是不能直接读摄像头。

这就是为什么要分层:让上层能单独替换。 你要做一个只转封装不做转码的工具,可以完全绕开 libavcodec。

二、每一层的入口函数

每一层都有一组固定的入口,认得出前缀就知道该翻哪个头文件:

库 函数前缀 典型入口


libavformat avformat_ avformat_open_input libavcodec avcodec_ avcodec_send_packet libavfilter avfilter_ avfilter_graph_create_filter libswscale sws_ sws_scale libswresample swr_ swr_convert libavutil av_ av_frame_alloc

注意前三个是 av 加库名,后三个不是。 swscale 和 swresample 是后加的,用了自己的前缀;libavutil 直接用 av_ 开头。

这个不一致不是设计失误,是历史:先有的库用一套命名,后加的沿用不进去。 认得出这个别扭,就不会在一堆头文件里找不到函数了。

三、命令行与 SDK 是一份东西的两面

这是理解 FFmpeg 架构最省力的一句话:

命令行做的事 SDK 里对应的动作


-i 打开输入 avformat_open_input -c:v 选编码器 avcodec_find_encoder_by_name -vf 加滤镜 avfilter_graph_create_filter 输出文件名 avformat_alloc_output_context2 进度条 自己算,SDK 不管

前四行都是「命令行参数 → 一次或几次 SDK 调用」。 所以命令行能做的事,SDK 都能做;命令行做不了的(比如边转边改内容),SDK 也能做。

反过来不成立:命令行帮你处理了大量默认值和边界情况。 用 SDK 自己写时,那些「默认帮你选好了」的东西全要自己填,这就是为什么很多人宁可调 exe。

四、那些「可能不存在」的库

教材列 SDK 时常常一口气列八个,但实际装到的可能是七个:

库 为什么可能没有


libpostproc 后处理,GPL 组件,很多构建不带 libavdevice 需要系统特定的采集接口,精简构建会去掉 libswresample 新旧替代关系,极简构建只剩 swscale

判断方法很简单:ffmpeg -version 后面那几行列的就是实际编进去的库。 少一个就是没有,不用猜。

这件事对写代码的人影响很实际: 你在文档里看到一个 av_ 开头的函数,不能假定它一定可用—— 得先确认那个库在你的构建里。 这也是为什么跨平台的项目要做能力检测,而不是假定版本够新。

五、动手 Demo:SDK 是八个库——每个库管一段

这一段把七个库的版本与职责摊开, 再看每个库在 Python 这一侧对应哪堆模块。

import av
import av.filter

for k, v in sorted(av.library_versions.items()):
    print(k, v)

print(u"codecs", len(av.codecs_available))
print(u"formats", len(av.formats_available))
print(u"filters", len(av.filter.filters_available))

实跑输出:

--------------------------------------------------------------------
SDK 是八个库——每个库管一段,缺一段就缺一块能力
--------------------------------------------------------------------

**① 七个库,本机能数出东西的只有三个**

  库                本机能数出多少东西             管什么
  ---------------------------------------------------------------------------------
  libavcodec        557 个编解码器                 编与解,所有 codec 都在这儿
  libavdevice       数不出来,PyAV 没开这个口      采集,摄像头与声卡
  libavfilter       468 个滤镜                     滤镜,改画面不改编码
  libavformat       414 个格式                     封装与解封装,认文件也认流
  libavutil         数不出来,它是底座不是清单     公共底座,内存、数学、错误码
  libswresample     数不出来,只是一个转换器       音频重采样与声道转换
  libswscale        数不出来,只是一个转换器       像素格式与分辨率转换

**三个能数的,正好是「有条目」的三层:codec 是一个个编码器,**
**format 是一个个格式,filter 是一个个滤镜。**
**另外四个不是清单型的东西,你没法说它「有几百个」。**

**② 教材说 SDK,其实还说漏了一个:libpostproc**

**libpostproc 是解码后的后处理,它是 GPL 组件,很多构建干脆不带。**
**本机这七个里就没有它——不是名字写错,是这个构建没编。**

**③ 每个库在 Python 这一侧对应哪堆模块,也能看出来**

  库                对应的 Python 扩展模块
  ---------------------------------------------------------
  libavformat       container/ 下五个
  libavcodec        codec/ 下四个,加 packet
  libavfilter       filter/ 下六个
  libavutil         dictionary、rational、error、buffer
  libswscale        video/reformatter
  libswresample     audio/resampler
  libavdevice       device

**这张表说明一件事:SDK 不是一坨,是一层一层叠出来的。**
**你调 format 打开文件,它回头去调 codec;你调 filter 改画面,它回头去调 swscale。**
**所以命令行能做的事,SDK 都能做——命令行就是调 SDK 的一个现成程序。**

第一张表把七个库摊开,注意第四列「本机能数出多少东西」。 三个能数,四个数不出来——不是缺信息,是那四个本来就没有「条目」这个概念。

能数的三层正好都是清单型:codec 一个个编码器,format 一个个格式,filter 一个个滤镜。 libswscale 只有一个转换器,你没法说它「有几百个」。

第二张表说 libpostproc 不在本机。 它是 GPL 组件,很多构建干脆不带——不是名字写错,是没编。

第三张表把每个库对应到 Python 的模块目录。 这张表说明 SDK 不是一坨,是一层叠一层: 你调 format 打开文件,它回头去调 codec。

30 秒实验

  1. 数自己的库:ffmpeg -version 后面那几行,数一数出现了几个 lib。七个是完整的,五个就是被裁过。
  2. 找 postproc:ffmpeg -version | grep postproc,没有输出说明你这个构建不带后处理。它不影响常规转码。
  3. 验分层:ffmpeg -codecs | wc -l 和 ffmpeg -formats | wc -l 两个数差很多,说明编解码层比格式层厚得多——这也是为什么转码比转封装慢。

常见误区

  1. 以为 SDK 是一个库。它是八个库的组合,每个能单独编、单独装。所谓「装 FFmpeg SDK」其实是一堆头文件加一堆 .so。
  2. 以为函数前缀统一是 av_。libavformat、libavcodec、libavfilter、libavutil 用的是 av_ 系,但 swscale 是 sws_、swresample 是 swr_。认错前缀就找不到函数。
  3. 以为文档里有的函数一定有。文档按源码清单写,你的构建可能没编那个库。用 -version 确认,别假定。
自测三问(点开对答案) 1. 只做转封装(`-c copy`)需要哪些库? —— 只需要 libavformat 和 libavutil,完全用不到 libavcodec。这就是转封装特别快的原因——一步都不碰像素。 2. 为什么 libswscale 的函数前缀不是 av_? —— 它是后来从主代码里拆出来的库,拆的时候没跟着改命名。类似的还有 libswresample 用的 swr_。 3. libavutil 缺了会怎样? —— 什么都跑不起来。它是内存管理、错误码、数据结构的地基,其他七个库全依赖它。

相关笔记