摘要

  • RFC 9628 为 VP9 的跨画面依赖定义了两套表达:灵活模式随预测画面携带最多三个 P_DIFF,非灵活模式则在 SS 中预告一个循环的 Picture Group。
  • Picture ID、层级字段、引用偏移、B/E 和 RTP Marker 说明发送方宣称的结构;它们不证明全部分片到达、参考画面仍在、隐藏的编解码状态安全,或最终画面被显示。
  • 可审计的结果必须串起 SDP 协商、模式与结构世代、RTP 覆盖、真实引用闭合、码流校验、解码输出和应用呈现。

故障发生时,中间转发器给出了一张很漂亮的依赖图。每条边都能回到一个 Picture ID,空间层和时间层也没有越界。值班人员于是把问题排除在视频链路之外。

用户看到的仍是一张静止画面。

这两件事可以同时为真。RFC 9628 规定的是 VP9 如何装入 RTP,以及可伸缩视频的结构如何被端点或选择性转发设备识别。它让引用关系可见,却没有把“结构可见”升级成“结果完成”。

真正需要回答的不是引用图是否漂亮,而是这张图在哪个世代、由谁声明、经过什么删减,以及它所指向的内容是否真的存在于接收端。

灵活模式把过去写在当前画面上

当 F=1 时,Picture ID 必须出现。预测画面必须带至少一个、最多三个 P_DIFF。当前 PID 减去这个相对偏移,并按七位或十五位编号空间取模,就得到发送方声明的参考画面。偏移为零无效。

这种做法允许编码器动态改变时间层结构。观察者不必先记住一个固定周期,就能读出当前画面的跨画面引用。代价是每个预测画面都要支付这些描述字节。

但字段只写“我引用谁”,不写“你是否还有它”。参考画面可能在网络中丢失,可能被转发器按照结构合法丢弃,也可能已经从解码缓存中退出。当前画面本身也可能少一个中间分片。P_DIFF 完全正确时,引用仍可能无法满足。

所以验证器必须分别保存申报图和接收端可用图。把两者合并,会把发送端知识冒充成接收端状态。

非灵活模式把解释权交给一个历史世代

非灵活模式不逐帧重复引用列表。SS 数据可以预先定义 Picture Group:组内每个位置的 TID、是否允许上切,以及一个或多个 P_DIFF。关键画面的 PID 对应第一位置,后续 PID 按 N_G 循环映射。

TL0PICIDX 另外追踪时间基层。TID=0 时它递增;更高时间层用它说明依赖哪一个基层画面。这个八位计数器到 255 后归零。

省下的字节变成了共同记忆。中途开始抓包的系统,可能看见 PID 和层号,却没见过赋予它们含义的 SS。转发器重启后可能恢复流量但丢掉关键画面锚点。两台观察器即使持有同一个结构,也可能在周期位置上相差一格。

因此,非灵活模式不能只记录“当前结构”。它需要一个明确世代:SS 哈希、引入它的包、关键画面 PID、组长度、起始相位和变更边界。RFC 9628 只允许在关键画面的第一个包切换模式,也限制结构何时更新,正是为了避免解释在任意时刻漂移。

没有世代的结构表,不是事实,只是猜测模板。

Picture ID 的价值来自它的局部性

PID 是七位或十五位的运行编号,可以随机起步,会回绕,也允许在会话中改变宽度。七位升到十五位时补零,十五位降到七位时截断。接收方不得假设位宽永久不变。

同一时刻的所有空间层帧共享 PID。即使某个 VP9 画面设置 show_frame=0、只用于更新参考而不输出,它也要占用独立 PID。这个编号描述传输中的画面单位,而不是人的观看次数。

选择性转发设备还可以合法删除部分画面。因此接收端 PID 不连续不一定意味着丢包;连续也不意味着一帧内部的 RTP 分片齐全。一个计数器无法同时承担画面身份、传输完整性和屏幕输出三种职责。

调查时必须把 PID 与会话、SSRC、RTP timestamp、位宽、层号、模式世代和观察点绑定。脱离这些坐标后,同一个数字会在回绕、截断和多路流中反复出现。

看见结束,不等于看见全部

一帧 VP9 可以跨多个 RTP 包。B=1 标记开头,E=1 标记结尾。RTP Marker 位标记该画面最高空间层的最后一个包,也就是整个画面的末端。如果转发器剥离更高空间层,就必须把 Marker 改到实际发送目标层的末包。

这些标志给组装器画边界,不给丢失的字节做担保。中间序列号缺失时,E 仍可能到达。一个要求 D=1 的高层帧可能齐全,而同一画面的低层帧不齐。转发器可以正确改写 Marker,却引用了此前已经丢失的时间帧。

RFC 9628 没有为 VP9 帧提供更细粒度的部分访问机制。所谓完整,必须额外证明从 B 到 E 的 RTP 序列覆盖、timestamp 一致、空间层顺序正确,以及经过删层后的 Marker 正确。结束标记是边界证据,不是内容收据。

引用图看不见概率表

VP9 跨帧保存的不只有参考画面,还有熵编码和树编码使用的概率表。error_resilient_mode 会重置这部分附加状态。可伸缩流若允许某个前置帧被合法删除,编码器就必须确保后续帧不再偷偷依赖它留下的状态。

这一点划出头部分析的上限。P_DIFF 全部闭合、Picture Group 相位正确,并不必然说明码流可独立解码。如果基层没有在应当恢复时启用错误韧性,选择性删除之后可能留下结构图之外的断链。

VP9 码流规范 定义负载内部的语义,真实解码器则给出运行结果。RTP 描述器、码流检查和解码输出是三层证据,不能由第一层代理后两层签字。

层级字段只授予有限推断

TID 与 SID 指明时间层和空间层。D 表示当前空间层是否依赖同一画面中紧邻的低一层。U 表示一个上切点:后续更高时间层不再依赖当前点之前、且高于当前时间层的画面。Z 则说明更高空间层不依赖当前帧,允许面向高层的接收者安全丢弃它。

这些字段很适合做转发决策,却不是体验分数。U=1 不证明切换点到达。Z=1 不证明丢弃符合用户想要的质量。SID=2 不自动对应某个绝对分辨率。D=0 只排除一条同画面空间依赖,不能排除跨画面引用。

转发器必须为自己的动作留下记录:目标层、命中的规则、保留和丢弃的包、Marker 改写以及拥塞依据。若只保存允许动作的字段,不保存实际动作,故障后便只剩一份无责任人的许可。

RPSI 更接近结果,但仍没有到达屏幕

接收方可以通过 RPSI 返回已经正确解码的 golden frame 或 altref frame 的 Picture ID,也可以在丢失后建议一个可靠参考。这比单纯解析发送端 PID 更强,因为它来自尝试过解码的一侧。

它仍然只对特定参考画面作陈述。它不证明所有空间层都可用,不证明应用选择了该画面进入播放队列,也不证明在用户期限内完成呈现。FIR 请求全量状态刷新;RFC 9627 的 LRR 请求局部层刷新。命令与反馈各有对象,不能叠成一个泛化的“视频已恢复”。

完整链条要分开记录:结构发出、包被运送、引用被保留、码流被接受、画面被解码、输出被呈现。

协商是描述器的字典

SDP 为 VP9 绑定动态 payload type 和 90 kHz 时钟。profile-id 是需要对称匹配的媒体配置;缺失时推断为 Profile 0。max-fr 和 max-fs 是接收能力声明,不是实际流量测量。

规范还承认,预编码内容或选择性转发场景可能没有完全落在声明上限内的可用版本。一次成功的 Offer/Answer 因而只证明双方交换了配置,不证明后来发送的画面匹配能力,更不证明解码成功。

证据链应从协商开始:offer、answer、payload 映射、profile、能力、SSRC 以及每次重新协商。没有这本字典,后续每个字节即使可解析,也可能按错误的会话解释。

来源