📹 第二章 VLC及FFplay流媒体播放器
📄 一句话概括

「支持多少种格式」这句话没有意义,除非说清是哪一层。 容器层认得、编码层解得开,是两个独立的清单——少任何一个,表现都是打不开。

📝 本节和前面几节的关系

上一节讲了 VLC 是什么,这一节把它号称的能力拆开数一遍。 数出来的数比宣传页有用:宣传页说「支持几乎所有格式」, 而你手上这台机器实际能解的只有那么多。 这一节的方法可以套到任何播放器上——包括你自己写的那个。

一、能力分三层,别混着说

一个播放器的「支持」分三层,每层的判断依据完全不同。

层 管什么 怎么判断 缺了的表现
协议层 地址能不能打开 看能不能建连接 直接报错打不开
容器层 能不能拆出包 看格式注册表 能连上但没画面
编码层 能不能解成帧 看解码器注册表 有包但黑屏

这三层的报错长得完全不一样,这是排查的关键: 协议层缺 → 立刻失败,连不上; 容器层缺 → 连上了,读到一堆字节,但说「不认识的格式」; 编码层缺 → 包拆出来了,解码器报「找不到 decoder」。 知道卡在哪一层,就知道该装什么、该换什么。

二、数一遍本机有多少

D20 数出来的结果:414 个格式名目、557 个编解码器。 其中能当输入用的 368 个,能当输出用的 182 个。

这三个数要这么读:

数 含义 为什么不是最大
414 注册表里的全部名目 含只用一次的别名
368 能读的 有些只用来写
182 能写的 能读的比能写的多得多

「能读 368 而能写只有 182」这个差距不是 bug:现实里格式的变体极多,解析一个别人的文件容易, 生成一个别人认的文件难。 写要比读严格得多——读的时候可以宽容,写的时候一点不合规就没人认。

三、监控这门里,哪几个名字必须认得

D20 挑的十个名字,正好覆盖监控链路的每一环:

名字 属于哪层 什么时候用
rtsp 协议 连摄像机,最常见
rtp 协议 裸 RTP 流,抓包分析时
sdp 协议 RTSP 里带的那段描述
mpegts 容器 GB/T 28181 用的就是它
flv 容器 网页直播
h264 编码 主流摄像机
hevc 编码 4K 与新设备
mjpeg 编码 老式网络头
rawvideo 编码 不压缩,调试用
rtmp 协议 推流到直播平台

本机这十个里 rtmp 是「无」,这点值得单独说:RTMP 需要 librtmp 或新版 FFmpeg 内置的 rtmp 支持, 不是所有构建都带。 这就是「同一份代码换台机器就跑不了」的典型原因——不是代码错,是那份二进制没编进去。

四、功能清单里最容易被忽略的三项

宣传页只讲格式,不讲这三件事,但它们决定能不能用在项目里:

能力 为什么重要 怎么验
多路并发 几十路上限差很多 同时开十路看 CPU
硬解 能省一大半 CPU 看有没有 d3d11va/cuvid
码流切换 主码流卡了切子码流 看能不能双流并存

第二项在监控里是硬需求:几十路 1080p 用软解,一台机器撑不了几路;硬解能把 CPU 从满载压到三成。 但硬解不是白给的——它对像素格式、分辨率上限、并发数都有额外限制,调不通时表现是「能解但花屏」。

五、动手 Demo:数一遍本机的格式与解码器

不查资料、不背清单,直接问 FFmpeg 库:你认识多少个名字? 这些数字就是你手上这台机器真实的天花板,和宣传页无关。

import av
from av.format import ContainerFormat

n_fmt = len(list(av.formats_available))
n_codec = len(list(av.codecs_available))

n_in = n_out = 0
for nm in av.formats_available:
    cf = ContainerFormat(nm)
    n_in += bool(cf.is_input)
    n_out += bool(cf.is_output)

T([u"格式", u"%d" % n_fmt, u"能读 %d" % n_in,
   u"能写 %d" % n_out], [0, 0, 0, 0])
T([u"编解码器", u"%d" % n_codec], [0, 0])

实跑输出:

--------------------------------------------------------------------
功能清单不靠背,数一遍本机支持多少
--------------------------------------------------------------------
**VLC 的卖点是「什么都能放」,这个「什么」到底是多少?** 不看宣传页,
直接数本机 FFmpeg 库的注册表。

  FFmpeg 库版本:62.12.102

  能力               本机数量     怎么数出来的
  -------------------------------------------------------------
  容器/协议格式     414          av.formats_available 总量
  其中能读           368          is_input 为真
  其中能写           182          is_output 为真
  编解码器           557          av.codecs_available 总量
  两项都不占         0            构造失败,跳过

**挑几个和监控直接相关的,看本机认不认:**

  名字         在不在     用途
  ---------------------------------------------------
  rtsp         有         RTSP 协议(监控最常用)
  rtp          有         RTP 裸包
  sdp          有         会话描述
  rtmp         无         直播推流
  h264         有         H.264 解码
  hevc         有         H.265 解码
  mjpeg        有         JPEG 序列(很多 IP 头)
  mpegts       有         TS 流(GB28181 常用)
  flv          有         FLV 封装
  rawvideo     有         裸像素

**这张表能解释一件常被搞混的事:**「支持 XX 格式」分两层——**容器层认得**和**编码层解得开**。
  rtsp 属于容器/协议层,h264 属于编码层。两个都认,才放得出来;少任何一个,表现都是「打不开」。

  VLC 之所以「什么都能放」,是因为它**自己也带了一套解码器**,不全靠系统装的。
  所以在同一台机器上,VLC 能放而浏览器放不了,一点都不奇怪——它们手里的解码器清单不一样长。

414 和 557 是这台机器上的实数,换台机器装不同版本的 FFmpeg 就变。 所以「支持多少种」这种问法本身没意义——得说「这台机器上」。

那张十个名字的表里,rtmp 报「无」。这不是代码写错,是这个构建没带 RTMP 支持。 这类差异是最难查的一类故障:同一份程序在 A 机器能用、B 机器不能,日志里没有任何报错, 只有「打不开」。

所以部署前先跑一遍这种「注册表点名」,把项目要用的几个名字逐个确认——比出问题再翻日志快得多。

30 秒实验

  1. 把 names 列表换成你项目实际用到的格式,跑一遍看有没有「无」。
  2. 把你平时用的播放器(不是 VLC)也装上,用同样的地址试, 看它缺哪一层——这能解释很多「为什么这个软件放不了」。
  3. 查一下 av.formats_available 里有没有 d3d11va、cuda 这类硬件加速名字, 有就说明这台机器能硬解。

常见误区

  1. 以为「支持 H.264」就意味着能放所有 H.264。 同一个 H.264,高档位(High 10、 4:2:2)很多解码器不认;要看到 profile 级别,不是看到编码名。
  2. 以为格式越多越好。 实际项目只用五六个,多出来的注册项只会让「探测格式」这一步变慢。
  3. 以为能读写对称。 能读 368 而能写只有 182,生成一个合规文件比解析一个别人的文件难得多。
自测三问(点开对答案) 1. 「打不开」和「黑屏」分别卡在哪一层? —— 打不开多半卡在协议层或容器层(连不上、认不出格式); 黑屏是包拆出来了但解码器解不出帧,卡在编码层。 2. 为什么能读的格式比能写的多? —— 读可以宽容,遇到不规范的地方能猜;写必须严格,一个字段不合规,别人就打不开。 所以解析器总是比生成器多。 3. 同一份程序在两台机器上一台能跑一台不能,怎么快速定位? —— 先数两台机器的格式与解码器注册表,逐个点名项目用到的格式。 缺哪个立刻就知道了——这比翻日志快得多。

下一节让 VLC 真连一次:在这一节的能力清单基础上,看它握手时到底发了什么、收回了什么。

相关笔记