DirectShow 的三个角色,与滤镜图的三段一一对应。 Windows 上想拿摄像头,只能走这条路——FFmpeg 只是把它包了一层。
9.1 讲的图是 FFmpeg 自己的滤镜图,这一节讲 Windows 自带的另一套图。 两套图长得一样,因为它们解决的是同一类问题。
一、三个角色
DirectShow 把一条采集链拆成三种 Filter。 角色 干什么 对应滤镜图里的
Source Filter 出数据 buffer Transform Filter 处理数据 中间那些滤镜 Renderer Filter 收数据 buffersink
形状一模一样:源头、中间干活、终点收下。 两边都叫「图」, 因为本质都是有向连线串起来的节点集合。 这个相似不是巧合:Unix 的管道、编译器的多趟处理、音视频的处理链,全是这个形状。 它是「数据流经一串处理器」这个老思路的不同实现。
二、为什么 Windows 上要借这条路
因为摄像头不在 FFmpeg 手里,在操作系统手里。 平台 采集要走谁
Windows DirectShow(dshow) Windows 桌面 gdigrab 抓屏 Linux Video4Linux2(v4l2) macOS AVFoundation
FFmpeg 的 AVDevice 只是「统一的门面」:同一条 API,背后按平台换成不同的实现。
这就是「跨平台」的真实代价——每平台各写一套。
所以设备名不能用同一套写法:Windows 上写 video=摄像头名字,Linux 上写 /dev/video0。
混着写就报「找不到设备」。
三、本机有没有这条路
查格式表:名字在不在,能不能读、能不能写。 名字 能不能读 能不能写
dshow 有 False gdigrab 有 False v4l2 不在表里 — avfoundation 不在表里 —
这台 Windows 机器上 dshow 与 gdigrab 在,Linux 与苹果的都不在。 这正说明「格式表是本机构建的指纹」:同一个 FFmpeg,换平台编出来的表就不一样。 查表得到的是「这台机器能不能」,不是「FFmpeg 能不能」。 注意 dshow 只能读不能写:摄像头是输入设备,「写」到摄像头这件事在 FFmpeg 里没做。 这是正常现象,不是阉割。
四、有路不等于有设备
格式在、路也在,开一下还是失败。 验证 结果 说明
名字在格式表 在 这个平台的支持编进来了 真开一次 OSError 路通了,但没设备
注意报的不是「没有这个格式」,是 I/O 错误。 这两句话的区别很大: 前者说你写错了名字或没编进模块,后者说名字对、模块在,只是机器上没有那个东西。 这跟第六章那套「真开一次看报错」的判法完全一致:报错类型就是证据,别猜。 所以「这台机器能不能采」这个问题,只能靠真开一次回答。查表只能回答「支不支持这个平台」。
五、这一节为什么只能讲到框架
没有硬件,就只能讲到「路通不通」这一层。 能讲清 讲不了
三个角色怎么分工 真实采集的帧长什么样 设备名怎么写 不同摄像头的参数差异 报错怎么分辨 掉帧、花屏这类实况问题
这不是偷懒,是边界。 沙箱里没有摄像头, 也不该假装有——把「没测到」标清楚,比编一段看起来很真的输出有用得多。 第十二章讲 AVDevice 时会再碰一次这条路,结论会一样:没有硬件,能验的只有「调用形状对不对、 报错能不能分辨」。
六、动手 Demo:DirectShow 那套图,跟滤镜图像在哪
先查格式表里有哪些「设备」类格式,再真开一次摄像头看报什么。
import av
# 先查表:哪些「采集类」格式编进了这台机器
tab = av.formats_available # 414 个名字的集合
for n in ["dshow", "gdigrab", "v4l2", "avfoundation"]:
print(n, "在表里" if n in tab else "不在表里")
# 能力两列:是输入还是输出
f = av.ContainerFormat("dshow")
print(f.name, f.is_input, f.is_output)
# 再真开一次:路通不通,看报什么错
try:
c = av.open("video=0", format="dshow")
print("开成功", c)
except Exception as e:
print("失败", type(e).__name__)
实跑输出:
--------------------------------------------------------------------
DirectShow 那套图,跟滤镜图像在哪
--------------------------------------------------------------------
**① DirectShow 的三个角色**
角色 干什么 对应 FFmpeg 里的
------------------------------------------------------
Source Filter 出数据 buffer
Transform Filter 处理数据 中间那些滤镜
Renderer Filter 收数据 buffersink
**形状一模一样:源头、中间干活、终点收下。** 两边都叫「图」,
因为本质都是**有向连线串起来的节点集合**。
**这个相似不是巧合**:都是「数据流经一串处理器」的老思路,Unix 的管道、编译器的多趟处理,
全是这个形状。
**② 本机有没有 DirectShow 这条路**
名字 能不能读 能不能写
--------------------------------------------
dshow 有 False
gdigrab 有 False
v4l2 不在格式表 —
avfoundation 不在格式表 —
**`dshow` 与 `gdigrab` 在,`v4l2`(Linux)与 avfoundation(苹果)不在。**
**这正说明「格式表是本机构建的指纹」**——同一个 FFmpeg,换平台编出来的表就不一样。
**③ 真有摄像头就能用吗**
拿 `video=0` 去开,报 **OSError**。
**注意报的不是「没有这个格式」**,而是 I/O 错误——格式在、路也在,
只是这台机器上没有那个设备(或者被占用了)。**这跟第六章那套「真开一次看报错」的判法一致。**
**所以本节能讲清框架,跑不了采集。** 第十二章讲 AVDevice 时会再碰一次,结论会一样:
**没有硬件,就只能讲到「路通不通」这一层。**
dshow 与 gdigrab 在表里,v4l2 与 avfoundation 不在——表的差别就是平台差别。 报的是 OSError,不是「格式不存在」,说明路通、只是没有设备。
30 秒实验
- 把
video=0改成一段不存在的中文名字,看报错变不变。 - 打印
av.ContainerFormat("gdigrab").is_output,看它能不能写。 - 用
av.open("desktop", format="gdigrab")试抓一次桌面,看能不能开。
常见误区
- 以为格式表里没有就是不能用。 rtmp 就不在表里但能用,得真开一次。
- 把 OSError 当成「名字写错了」。 名字写错通常报「找不到设备」一类。
- 以为 dshow 不能写是被阉割。 摄像头本来就是输入设备。
自测三问(点开对答案)
1. DirectShow 的三个 Filter 各对应滤镜图的什么? —— Source 对应 buffer,Transform 对应中间滤镜,Renderer 对应 buffersink。 2. 为什么 Windows 上采摄像头非得走 DirectShow? —— 设备归操作系统管,FFmpeg 只是在它外面包了一层统一接口。 3. 打开摄像头报 OSError 说明什么? —— 格式与模块都在,只是这台机器上没有那个设备或设备被占用。- ☐ (本节完,下一节 9.3.1 FFmpeg中使用过滤器的步骤)