🎬 第二章 把控FFmpeg骨架:【八大金刚】核心开发库
📄 一句话概括

设备在 FFmpeg 里就是一种「格式」,所以摄像头与文件走同一个打开函数。 本机是 Windows 构建:dshow 与 gdigrab 在,v4l2、alsa、x11grab 全不在。

📝 本节和前面几节的关系

2.5 讲的是退场的库,这一篇讲一个你迟早要用的库。 它给的帧往往不是 yuv420p,转格式要等 2.9。

一、设备就是格式

判断有没有某个设备后端,查格式名表就行。 后端 平台 管什么


dshow Windows 摄像头、话筒 gdigrab Windows 屏幕 v4l2 Linux 摄像头 alsa Linux 声卡 x11grab Linux 屏幕 avfoundation macOS 摄像头与屏幕

本机是 Windows 构建,所以只有前两个在。 这不是缺功能, 是编译时按平台挑的——Linux 上那几个在 Windows 上编不出来。

二、四个函数,两个跟文件共用

函数 干什么


avdevice_register_all 注册设备(新版多为自动) av_find_input_format 按名字找后端 avformat_open_input 打开设备 av_read_frame 收一帧

第三、四行跟读文件是同一个函数。 换的只是给的名字:文件给路径, 设备给 video=Integrated Camera。 这是 FFmpeg 设计得最漂亮的一处:把设备抽象成格式, 于是「采集 + 编码 + 推流」与「读文件 + 编码 + 推流」共用一套代码。

三、摄像头给你的东西不听话

情况 你会遇到什么


像素格式不对 mjpeg 或 nv12,编码器不收 帧率不稳 实际帧率比设的低 分辨率被改 设备不支持你设的值,自动降级 没有时间戳 要自己按帧率补

四条里最坑的是第三条:你设 1920×1080,设备只给 1280×720,它不报错,直接降级。 编出来的片子分辨率跟你以为的不一样。 所以采集之后第一件事是打印实际参数,不是闷头往下编。

四、一次采集的最小流程

步 做什么


1 av_find_input_format 找后端 2 avformat_open_input 打开设备 3 avformat_find_stream_info 取实际参数 4 av_read_frame 循环收帧 5 转格式、编码、写文件或推流

第 3 步是分水岭:走到这儿你才知道设备真正给了什么。 后面的编码器参数,全要按它来配,不能按你希望的来配。 本机没有可用设备后端(Demo 里 dshow 与 gdigrab 在,但没有真的摄像头可用), 所以这一节的流程在第十二章才真跑;这里先把接口认全。

五、动手 Demo:libavdevice——摄像头与声卡,靠格式名露头

设备后端在 FFmpeg 里就是格式,所以查格式名表就能看出装了哪些。 本机八行里只有两行「在」,全是 Windows 的。

import av

fa = set(av.formats_available)
for n in ("dshow", "gdigrab", "v4l2", "alsa", "x11grab", "avfoundation"):
    print(n, n in fa)

实跑输出:

--------------------------------------------------------------------
libavdevice——摄像头与声卡,靠格式名露头
--------------------------------------------------------------------

**① 设备在 FFmpeg 里其实就是一种「格式」**

所以判断有没有某个设备后端,直接查格式名表就行。

  设备后端         平台             本机在不在
  ------------------------------------------------
  dshow            Windows          在
  gdigrab          Windows 屏幕     在
  v4l2             Linux 摄像头     不在
  alsa             Linux 声卡       不在
  x11grab          Linux 屏幕       不在
  fbdev            Linux 帧缓冲     不在
  avfoundation     macOS            不在
  pulse            Linux 音频       不在

**本机是 Windows 构建,所以 dshow 与 gdigrab 在,Linux 那五个全不在。** 这不是缺陷,
是编译时按平台挑的。

**② 它的四个函数**

  函数                      干什么
  ------------------------------------------------------------
  avdevice_register_all     注册所有设备(老版本要手动调)
  av_find_input_format      按名字找设备后端
  avformat_open_input       打开设备,跟打开文件同一个函数
  av_read_frame             收一帧,也跟读文件一样

**第三、四行是重点:设备与文件走的是同一套接口。** 换的只是「打开时给的名字」——文件给路径,
设备给 `video=摄像头名`。**代码几乎不用改,这是 FFmpeg 设计得最漂亮的一处。**

**③ 摄像头给你的通常不是 yuv420p**

多数摄像头出的是 mjpeg 或 nv12。
**不转格式直接送编码器,会报像素格式不支持。** 转格式这一步在第十二章细讲,用的是 libswscale。

dshow 与 gdigrab 在,其余四个不在。 编译时按平台挑,不是装漏了。

30 秒实验

  1. python -c "import av; print('dshow' in av.formats_available)" 查后端。
  2. 命令行 ffmpeg -list_devices true -f dshow -i dummy 列摄像头(要在 Windows 上跑)。
  3. 把本节四个函数按顺序写一遍,标出哪两个跟读文件共用。

常见误区

  1. 以为摄像头就是文件。 名字不同但走同一套接口,参数含义差很多。
  2. 设了分辨率就以为设备会给。 不支持时它会静默降级。
  3. 不打印实际参数。 采到的分辨率与帧率可能跟你设的不一样。
自测三问(点开对答案) 1. 为什么设备能跟文件用同一个打开函数? —— FFmpeg 把设备抽象成一种格式。换的只是给的名字,接口不变。 2. 本机为什么没有 v4l2? —— 那是 Linux 的摄像头后端,Windows 构建里编不出来,也不需要。 3. 采集之后第一件事为什么是打印参数? —— 设备会静默降级分辨率与帧率,不打印就不知道实际拿到了什么。

相关笔记