557 个编解码器住在这儿,同一个名字可能只是解码器,不带编码。 5.0 之后只剩 send/receive 一套接口,老教程里的 decode_video2 已经删了。
2.3 把压缩包搬出来了,这一篇把包变成帧。 两边的接头是 AVPacket 进、AVFrame 出。
一、名字不等于实现
h264 是解码器,libx264 是编码器。差三个字母,方向相反。
名字 方向 来源
h264 解码 FFmpeg 内置 libx264 编码 外挂,GPL aac 解码(也能编码) FFmpeg 内置 libfdk_aac 编码 外挂,本机没有
「我能播 4K H.264」与「我能压 4K H.264」是两件完全不同的能力。 前者只要有 h264 解码器,
后者得有 libx264。
查自己有没有,看 codecs_available 里那个名字在不在就行。 本机 557 个名字里,
libx264 与 libx265 都在。
二、五个函数走完一条流水线
步 函数 做什么
1 avcodec_find_decoder 按 ID 找解码器 2 avcodec_alloc_context3 建上下文 3 avcodec_open2 打开,之后才能用 4 avcodec_send_packet 喂压缩包 5 avcodec_receive_frame 取原始帧
编码是同一套函数换个方向:find_encoder、send_frame、receive_packet。
名字对称,用起来就忘不掉。
老教程里的 avcodec_decode_video2 已经删了。 5.0 那轮大改把它去掉,
照抄会直接编译不过——这正是 1.1 说的「先看函数还在不在」。
三、一进一出的节奏:为什么要冲一遍
送一个包,可能一帧也取不到,也可能一次取出好几帧。 因为 B 帧要等后面的参考帧, 解码器会先攒着。 所以收尾必须送一次空包,把缓冲区里剩的帧冲出来。 阶段 动作 拿到
正常 send 一个包 0 到 n 帧 收尾 send 空包 剩下那几帧 返回 AVERROR_EOF 解完了
忘了冲这一步,成片少最后几帧,不报错。 这类「静默丢数据」的 bug,只有对着总帧数才查得出来。
四、上下文里三样必须配的东西
字段 从哪来 不配对会怎样
pix_fmt 解码器输出/编码器要求 报格式不支持 width height 同上 画面撕裂或崩 time_base 编码器自己定 时间戳全乱
编码时 time_base 由你定,解码时由流决定。 两边搞反,出来的片子会变成慢动作或快进。 像素格式最容易忽略:解码器给你什么就用什么,编码器要什么就得先转——转格式是 2.9 的活。
五、动手 Demo:libavcodec——557 个编解码器住在这儿
这一段查几个名字在不在,把「名字不等于实现」摆成表。 libfdk_aac 那一行是本机的「不在」。
import av
ca = set(av.codec.codecs_available)
for n in ("h264", "libx264", "libx265", "libfdk_aac"):
print(n, n in ca)
实跑输出:
--------------------------------------------------------------------
libavcodec——557 个编解码器住在这儿
--------------------------------------------------------------------
**① 名字不等于实现**
名字 是什么 在不在
------------------------------------------------
h264 解码器(内置) 在
libx264 编码器(外挂) 在
libx265 编码器(外挂) 在
libfdk_aac AAC 编码器(外挂) 不在
**`h264` 是解码器,`libx264` 是编码器。** 名字只差三个字母,方向完全相反。
**「我能播 4K H.264」跟「我能压 4K H.264」是两件事。**
**② 五个核心函数,一条流水线**
步 函数 干什么
------------------------------------------------------
1 avcodec_find_decoder 按 ID 找解码器
2 avcodec_alloc_context3 建上下文
3 avcodec_open2 打开,之后才能用
4 avcodec_send_packet 喂压缩数据
5 avcodec_receive_frame 取解出来的帧
**5.0 之后只有这一套接口。** 老教程里的 `avcodec_decode_video2` 已经被删了,
照抄会直接编译不过——这也是 1.1 里说的「先看函数还在不在」。
**③ 一进一出的节奏**
送一个包,可能一帧也取不到(B 帧要等后续帧),也可能一次取出好几帧。
**所以必须写成「送完了再冲一遍」**:最后一包送完,还要送一次空包,把缓冲区里剩的帧冲出来。
**忘了冲这一步,成片就会少最后几帧**,而且不报任何错。
h264、libx264、libx265 都在,libfdk_aac 不在。 h264 是解码器,libx264 是编码器——名字只差三个字母,方向相反。
30 秒实验
python -c "import av; print(len(av.codec.codecs_available))"数编解码器。- 在列表里数一遍
lib开头的有几个,那些多半是外挂库。 - 找一个 mp4,只解码最前面十帧,看输出帧的 pix_fmt 是什么。
常见误区
- 把 h264 当编码器用。 它只解不编,要编码得找 libx264。
- 收尾不送空包。 最后几帧留在解码器里,成片短一截还不报错。
- 编码时自己乱定 time_base。 时间戳全乱,成片快进或慢放。
自测三问(点开对答案)
1. h264 与 libx264 的区别是什么? —— 前者是解码器,只能解;后者是外部编码库,只能编。名字像,方向相反。 2. 为什么解码收尾要送一个空包? —— 解码器为了 B 帧会缓存若干帧,送空包才会把剩下的冲出来。 3. 557 这个数字能说明什么? —— 是这套构建的编解码器条目数。跟别人不一样,说明装的不是同一版。- ☐ (本节完,下一节 2.5 libpostproc 库简介)