教材给一条 gcc 命令,它藏着三个前提:编译器、pkg-config、头文件。 本机三个全没有,所以这一节能落地的只有「流程」。
3.6 排了函数表,这一节是把它编成一个能跑的程序。 下一章 4.1 转去讲框架流程,也就是责任链设计模式。
一、教材的命令串,拆成三段
教材原文大概是这一句:
gcc -o demux demux.c $(pkg-config --libs libavformat libavcodec libavutil)
拆开看是三段活儿。
段 干什么 需要什么
gcc 把 .c 编成可执行文件 编译器 pkg-config 查出 -I 与 -L 与 -l pkg-config 与 .pc 文件 libav* 链接时找到符号 开发包,含 .so 与 .h
三段任意一段缺了都编不出。 报错长得完全不一样:缺 gcc 是 command not found, 缺 pkg-config 是空字符串,缺开发包是一堆 undefined reference。 这三种错误要能一眼分清,否则会往错的方向查半天。
二、三个前提逐个查,全部落空
本机是个纯运行环境,不是开发环境。 要的东西 本机 说明
gcc/g++ 没有 没有编译器 cl/msbuild 没有 也没有 MSVC make/cmake 没有 没有构建工具 pkg-config 没有 查不出库参数 ffmpeg/ffprobe 没有 命令行工具也没有 git/bash 有 来自 PortableGit
有 git 不等于能编译。 这两个是沙箱自带的版本控制工具,跟工具链没关系。 这说明一件事:PyAV 的轮子是预编译好的,装它不需要编译器。 所以这个环境跑得动 PyAV,编不动一行 C。
三、头文件也没有,这一条最要紧
av.get_include() 返回一个路径,但那个目录不存在。 PyAV 把 libav 静态编进了 .pyd 文件里,运行时该有的全有,编译时该有的一个没有。 所以「拿 PyAV 的环境去编 C 程序」这条路是堵死的*,不是缺一个包的问题——就算装上 gcc, 也没有 avcodec.h。 需要的 PyAV 提供吗
运行时 .pyd 提供 .h 头文件 不提供 .lib/.dll 导入库 不提供 .pc 文件 不提供
要编 C 就得另装一套开发版的 FFmpeg,与 PyAV 完全独立。 两套并存没问题,注意别让它们的库混到一起去。
四、Windows 上怎么落地这一节
四行里三行做不到,只剩最后一行。 教材的做法 本机的等价事
apt install 开发包 装不了 gcc 编 demux.c 改写成 Python pkg-config 找库 用不上 ./demux 跑起来 Python 脚本直接跑
这不是偷懒。 这一节的产物本来就只有一句话:把 C 的五步流程走出来。 流程走通了,用哪种语言写是次要的。 但有一件事必须按 C 的规矩记住:出错要一层层往回退,第 4 步失败要释放第 3 步的资源, 第 3 步失败要关第 1 步的文件。这叫 goto fail 链。 Python 的 with 语句做的是同一件事。 写法变了,责任没变:谁开的谁关,先开的后关。 不合格的写法在 C 侧泄漏内存,在 Python 侧静默回收——后果不同,规矩一样。
五、动手 Demo:Ubuntu 编译清单,在本机逐条对一遍
把教材那条命令的前提逐个查,看差在哪。
import os
import shutil
import av
for t in ("gcc", "make", "pkg-config", "ffmpeg"):
print(t, shutil.which(t))
inc = av.get_include()
print(inc, os.path.isdir(inc))
实跑输出:
--------------------------------------------------------------------
Ubuntu 编译清单,在本机逐条对一遍
--------------------------------------------------------------------
**① 教材 3.7 的做法:在 Ubuntu 上编译一个解封装程序**
命令大概是这一串(教材原文):
gcc -o demux demux.c $(pkg-config --libs libavformat libavcodec libavutil)
这一句里藏着三个前提:有 gcc、有 pkg-config、有开发包(头文件)。逐个查本机有没有。
**② 逐个查,全部落空**
要的东西 本机 来自哪里
---------------------------------------
gcc 没有 —
g++ 没有 —
cl 没有 —
make 没有 —
cmake 没有 —
pkg-config 没有 —
ffmpeg 没有 —
ffprobe 没有 —
git 有 PortableGit
bash 有 PortableGit
10 个里只有 2 个在:git 与 bash,都来自 PortableGit。
git → D:\WorkBuddy\resources\vendor\PortableGit\mingw64\bin\git.EXE
bash → D:\WorkBuddy\resources\vendor\PortableGit\usr\bin\bash.EXE
**这两个是沙箱自带的版本控制工具,不是编译工具链。** 有 git 不等于能编译。
**③ 头文件也没有——这一条最要紧**
av.get_include() 返回:
C:\Users\admin\.workbuddy\binaries\python\envs\default\Lib\site-packages\av\..\include
这个目录存在吗:**不存在**。
**PyAV 只发轮子,不发头文件。** 它把 libav* 静态编进了 .pyd,运行时该有的全有,
编译时该有的一个没有。
**所以「拿 PyAV 的环境去编 C 程序」这条路是堵死的**,不是缺一个包的问题。
**④ 那这一节在 Windows 上怎么落地**
教材的做法 本机能做的等价事
----------------------------------------------------
apt install 开发包 没有 apt,装不了
gcc 编译 demux.c 没有编译器,改写成 Python
pkg-config 找库 没用,库静态在 pyd 里
./demux 跑起来 Python 脚本直接跑
**四行里三行做不到,只剩最后一行。** 这不是偷懒,是这一节的产物本来就只有一句话:
**把 C 的五步流程走出来**。流程走出来,用哪种语言写是次要的。
**⑤ 但有一件事必须按 C 的规矩记住**
C 侧写这五步,出错要一层层往回退:第 4 步失败要释放第 3 步的资源,第 3 步失败要关第 1 步的文件。
这叫 goto fail 链,教材的例子里有。Python 侧 with 语句替你做了同样的事。
**写法变了,责任没变**:谁开的谁关,先开的后关。
这条规矩在两边都成立,只是 Python 侧不合格时静默回收,C 侧不合格时内存泄漏。
四个 None 加一个 False,就是这一节的全部结论。
第一条 gcc 是 None,走 gcc --version 会抛 WinError 2。
30 秒实验
shutil.which("cl")看 MSVC 在不在。av.get_include()打出来,用资源管理器去看看那个目录在不在。- 在别的机器(有 MSYS2 的)上跑同一段,对比四个返回值。
常见误区
- 以为装 PyAV 就有编译环境。 轮子只有运行时,没有头文件。
- 把「command not found」当代码错。 那是工具链没装。
- 跳过收尾顺序。 C 侧必须倒着释放,顺序错了会泄漏或崩。
自测三问(点开对答案)
1. `gcc -o demux demux.c $(pkg-config --libs …)` 里 pkg-config 干什么? —— 查出 -I 头文件路径与 -L -l 库路径库名,塞进命令行。 2. 为什么 av.get_include() 返回的目录不存在? —— PyAV 只发预编译轮子,把库静态编进 .pyd,不带头文件。 3. 这一节在 Windows 上唯一能落地的是什么? —— 流程本身。五步走出来,用 Python 写等价代码。- ☐ (本章完,下一节 4.1 击鼓传花之责任链设计模式简介)