开发包就是「头文件 + 库文件」,让你不通过命令行直接调库。 拿到它,才谈得上自定义流程。
7.1.2 讲了命令行,这一节讲它背后那层。 7.2 转向第一个专用库:Live555,只做 RTSP。
一、开发包里有四样东西
① 头文件:libavcodec/avcodec.h 之类,给编译器看。
② 库文件:.a 是静态、.so 或 .dll 是动态。
③ pkg-config 描述:libavcodec.pc 之类,给构建脚本看。
④ 命令行工具:编出来顺手带的,给用人看。
第三样最容易被忽略,但它是「编译能不能过」的关键。
没有 .pc 文件,你就得手工敲一堆 -I 与 -L 参数,
换台机器路径一变就全挂。
所以检查开发包装没装好,最快的办法是:
pkg-config --libs libavcodec,能打出一串库名就算装好了。
二、直接调库要过四道门
① 打开:avformat_open_input 拿到上下文。
② 找流:avformat_find_stream_info 拿到流的参数。
③ 读包:av_read_frame 一个个取出压缩包。
④ 解码:avcodec_send_packet 加 avcodec_receive_frame。
实测用 PyAV 走一遍,这几步对应得很清楚: 打开得到容器名与流数、找流得到编码名、读包得到 25 个包。
注意第 ④ 步是「送一个、收一个」的模型,不是「解一个」: 因为一个包可能解不出帧(比如 B 帧要等后续帧), 也可能一个包解出多帧。所以 API 拆成了 send 与 receive 两个动作。
三、动态库版本号那点事
libavcodec.so 后面常带一个主版本号,比如 62。
大版本变了就不能直接换——API 与 ABI 都可能不兼容。
这就是为什么升级 FFmpeg 之后,老程序会报「找不到 libavcodec.so.60」。 它要找的是 60,而系统里现在只有 62。
三种应对方式,代价各不相同: ① 重编程序——最干净,但要能拿到源码。 ② 装兼容包——系统里同时留两个版本,靠 soname 区分。 ③ 容器化——把程序与它依赖的库一起打包,一劳永逸。
生产环境里第 ③ 种最省心, 这也是为什么媒体处理服务特别爱用容器部署—— 它依赖的库太多,版本冲突的坑太深。
四、动手 Demo:开发包——没有它就只能用命令行
列出开发包的四样东西,再用 PyAV 走一遍「打开、找流、读包」四道门。 命令行做不到的事,只有在这一层才做得到。
import av, numpy as np, tempfile, os
p = os.path.join(tempfile.mkdtemp(), "a.mp4")
c = av.open(p, "w", format="mp4")
st = c.add_stream("libx264", rate=25)
st.width, st.height, st.pix_fmt = 320, 240, "yuv420p"
st.options = {"crf": "30", "preset": "veryfast", "g": "25"}
for i in range(25):
f = av.VideoFrame(320, 240, "yuv420p")
y = np.zeros((240, 320), np.uint8)
y[:, :] = np.linspace(16, 200, 320, dtype=np.uint8)[None, :]
f.planes[0].update(y.tobytes())
for pi in (1, 2):
f.planes[pi].update(bytes([128]) * f.planes[pi].buffer_size)
f.pts = i
for pk in st.encode(f):
c.mux(pk)
for pk in st.encode(None):
c.mux(pk)
c.close()
with av.open(p) as c2:
print("① 打开:容器 %s,流数 %d" % (c2.format.name, len(c2.streams)))
s = c2.streams.video[0]
print("② 找流:%s,编码 %s" % (s.type, s.codec_context.name))
n = 0
for pk in c2.demux(s):
if pk.size:
n += 1
print("③ 读包:%d 个" % n)
print("④ 解码:需要 send/receive 两步,一个包不一定出一帧")
实跑输出:
--------------------------------------------------------------------
开发包——没有它就只能用命令行
--------------------------------------------------------------------
**① 开发包里到底有什么**
部分 内容 给谁用
--------------------------------------------------------
头文件 libavcodec/avcodec.h 等 编译器
库文件 .a 静态或 .so/.dll 动态 链接器
pkg-config libavcodec.pc 等 构建脚本
命令行工具 ffmpeg/ffprobe/ffplay 人
**② 真用一次,看它调了哪些能力**
**容器 mov,mp4,m4a,3gp,3g2,mj2**,**流数 1**
**流 #0:video,编码 h264**
**解封装出 25 个包**
**上面这几行分别用到了:**
**libavformat(打开容器与解封装)、libavcodec(识别编码)、**
**libavutil(处理帧与包对象)。**
**所以「用开发包」不是一句空话,它就是直接调这三个库。**
**③ 动态库版本号那点事**
**libavcodec.so 后面常带一个主版本号,比如 62。**
**大版本变了就不能直接换——API 与 ABI 都可能不兼容。**
**这就是为什么装完 FFmpeg 升级后,老程序会报「找不到 libavcodec.so.60」。**
容器名是一串别名(mov/mp4/m4a…),因为它们共用同一套解析器。 25 个包对 25 帧是一一对应,但没有 B 帧,所以包数与帧数相等。 有 B 帧时包数会多于帧数,这正是 send/receive 要分开的原因。
30 秒实验
- 打印
s.codec_context.name,换成 ts 文件再跑——变成 h264,说明编码识别是跨容器的。 - 把 GOP 改成 5,看包数变不变——不变,GOP 只影响关键帧位置,不影响包数。
- 加一行
print(s.bit_rate),看码率——可能是 None,因为封装里不一定写了码率。
常见误区
- 「装了 ffmpeg 命令行就等于装了开发包」——两回事,头文件不在里面。
- 「一个包一定解出一帧」——不一定,B 帧会推迟。
- 「升级库不用重编程序」——大版本变了就要重编。
自测三问(点开对答案)
1. 开发包里有哪四样东西? —— **头文件、库文件、pkg-config 描述、命令行工具。** 2. 为什么解码要 send/receive 两步? —— **一个包不一定出一帧**,也可能一个包出多帧。 3. 升级后报找不到 .so.60 怎么办? —— **重编、装兼容包、或容器化**,三者代价不同。- ☐ (本节完,下一节 7.2 Live555)