在webrtc系统中,关键帧的判断一直是一个重要的逻辑,不同的编解码器对关键帧的判断有不同的逻辑, 这里介绍一下VP8的关键帧判断逻辑.
VP8 RTP包结构
-
payload desc : 对VP8负载数据的描述
-
payload header : 非关键帧3个字节或者关键帧10个字节, 只有视频帧的第一个RTP包才会有此header
-
payload : 负载数据
payload descriptor
以上两种结构的区别在于PictureID的长度, 左侧结构中pic ID占7位, 右侧结构中pic ID占15位.
-
X : 扩展位, 如果置 ‘ 1 ’, 后面会紧跟一个字节的X项, 包括I、L、T、K、RSV
-
I : 置1 表示存在pic ID, 且其紧跟在扩展字节‘X’之后
-
pic ID域由M和Picture ID组成, M=1, picID占15位; M=0, picID占7位
-
L : 置1 表示存在TL0PICIDX, 他跟在Picture ID之后, 且T位必须置1
-
T : 置1 或(T=0 且K=1), TID/Y/KEYIDX字节存在, 否则不存在
-
K : 置1 表示TID/Y/KEYIDX字节存在; T=1且K=0, KEYIDX域被忽略; 否则TID/Y/KEYIDX字节不存在
-
TID = 0 : TL0PICIDX表示的是时间基础层帧的运行索引, 与SVC相关
-
TID > 0 : TL0PICIDX表示的是当前图像所依赖的基础层帧, 同样与SVC相关
-
TID/Y/KEYIDX域中, TID占2位, Y占1位, KEYIDX占5位
-
TID : 代表时间层, 基础层为0, 层级越高, 值越大
-
Y : 层同步位, 置1, 表示当前帧仅依赖基础层帧(TID0); 置0表示当前帧不依赖基础层帧
-
KEYIDX : key帧索引值
-
SRV : 保留位, 必须置0
-
R : 预留位, 设置成‘0’ 即可, 为以后使用,现在忽略
-
N : 是否非参考帧, 置 ‘1’ 表示是非参考帧, 这类帧在VP8中不重要, 丢弃时不影响其他将来或过去的帧
-
S : 表示这个包是否编码帧的第一个分片, 其他分片S位为0
-
PID : 表示分片的序号, 第一个分片为0, 后续分片PID可以相同, 也可以递增, 但不超过8
Payload Header
-
可以是3个字节 也可以是10个字节, 前3个字节表达的含义都相同
-
关键帧, 该域占10字节; 内部帧该域占3字节
-
P : 1位, 是否关键帧, 0 - key, 1 - interframe, 不同于其他结构中扩展位的含义
-
VER : 3位, 0-3定义了4种不同的解码复杂度的profile
-
H : 忽略
-
Size0/1/2 : 19位, 这个RTP包携带的视频帧第一个分片大小, 对于每一个视频帧来说只有第一个RTP包携带了payload header, 而那些没有payload header的RTP包本身所携带的就是负载数据的分片大小
-
并不是每个RTP包中都含有payload header, 只有在上面的S位置1且PID=0时才存在header, 也就是每个视频帧的第一个RTP包才存在header
-
3个通用字节后面的7个字节
-
3字节起始码 : 0x9d, 0x01, 0x2a
-
接下来的16位 : 14位表示帧的宽度, 另外2位表示水平范围
-
最后16位 : 14位表示帧的高度, 另外2位表示垂直范围
综上,VP8判断是否关键帧的逻辑为:
- 查找每个RTP种描述符的S位置1 且PID=0的包
- 拿到header, 如果P位为0 则为关键帧, P位为1 则为非关键帧