封装VP8格式的RTP包

封装VP8格式的RTP包封装 VP8 格式的 RTP 包整体结构 0123 0 V P X CC M PT

封装VP8格式的RTP包

整体结构

* 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | V |P|X| CC |M| PT | sequence number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | timestamp | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | synchronization source (SSRC) identifier | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | contributing source (CSRC) identifiers | * | …… | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | header extension | * | …… | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | vp8 payload descriptor (integer #octets) | * : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | : vp8 payload header(3 octets) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | vp8 pyld her : | * +-+-+-+-+-+-+-+-+ | * : octets 4..N vp8 payload : * | | * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | : optional RTP padding | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
RTP包头前12(通常是12个,可能有多个CSRC。或者加了扩展位)个固定字节解释:
  • V – Version. RTP版本号
  • P – Padding. 是否填充,如果设置为允许填充的话,在包的末尾填充一个或多个字节,这些填充的字节不是有效负载的一部分。
  • X – Extension bit. 扩充位,如果设置为允许的话,固定头结构后面(即包的12个字节后面,有效负载的前面)紧跟着一个扩展头结构,该结构是已定义的一种格式。(可以自己设计,为了支持多个扩展头,RFC5285以defined by profile进行了扩展。分为One-Byte Header和Two-Byte Header:==https://datatracker.ietf.org/doc/rfc5285/
  • CSRC count (CC) 数据源的个数(即源的个数),如果只有一个源(SSRC)那么此时的值为0
  • M – Marker. 标识,每帧的最后一个包设置为1,表示这一帧结束了,可以开始解码。不然就得等下一帧来了,才知道上一帧结束了。
  • Payload type 有效负载,RTP数据的有效负载(不包括头12个字节),由具体的应用程序来确定负载的格式和意义。官方文档里有表格说明,该表格显示了格式代码和具体格式的对应关系,附加的格式代码可能不在RTP协议里定义。
  • Sequence number 数据包序号,发送的RTP数据包序号,接收端可用它来检查丢失的数据包和确定保存数据包次序。
  • Timestamp 时间戳, 纪录了RTP数据包中第一个字节的采样时间(不是真实时间),采样时间必须源自一个时间增量且允许同步和计算。
  • SSRC – 同步标识, 是一个随机数,在同一个RTP会话中只有一个同步标识。
  • CSRC – Contributing source identifiers list. 标识此数据包中包含的有效负载的来源。

Payload Descriptor

结构如下(左右两图结构一样,只有PictureID长度不同)

 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ I: |X|R|N|S|R| PID |(REQUIRED) |X|R|N|S|R| PID |(REQUIRED) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ X: |I|L|T|K| RSV |(OPTIONAL) X: |I|L|T|K| RSV |(OPTIONAL) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ I: |M| PictureID |(OPTIONAL) I: |M| PictureID |(OPTIONAL) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ L: | TLOPICIDX |(OPTIONAL) | PictureID | +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ T/K:|TID|Y| KEYIDX |(OPTIONAL) L: | TLOPICIDX |(OPTIONAL) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ T/K: |TID|Y| KEYIDX |(OPTIONAL) +-+-+-+-+-+-+-+-+ 
必选
  • X:扩展控制位。如果为1,结构如图。如果为0,途中optional的行都没有。
  • R:保留位,必须设置为0.
  • N:不被参考的帧。1表示丢了这帧,不影响其他帧的解码。如果不清楚,设置0.
  • S:标识一个VP8 partition的开始。如果payload的第一个字节是一个新的VP8 partition的开始,则设为1.每帧的第一个包必须设置为1.
  • PID: Partition index.每帧的第一个partition必须是0.每个partition增加1,或者全保持为0.PID不超过7.如果多个包包含同一个PID,则除了第一个,其他的S位设置为0.
可选

X为1时,有以下部分:

  • I: PictureID 控制。如果设置为1,有图中I行。
  • L: TL0PICIDX 控制。如果设置为1,有图中L行。并且T必须设置为1.
  • T: TID 控制。如果设置为1,有T/K行。
  • K: KeyIDX 控制。如果设置为1,有T/K行。
  • RSV: 保留位。
  • M:0,PictureID长度7位(上左图);1,PictureID长度15位(上右图)。
  • PictureID:标识帧,随机值开始,每帧增加1。到达最大值后,回到0.(不要假设PictureID的长度保持不变)
  • TL0PICIDX:8 bits temporal level zero index. The field MUST be
    present if the L bit is equal to 1, and MUST NOT be present
    otherwise. TL0PICIDX is a running index for the temporal base
    layer frames, i.e., the frames with TID set to 0. If TID is
    larger than 0, TL0PICIDX indicates which base layer frame the
    current image depends on. TL0PICIDX MUST be incremented when TID
    is 0. The index SHOULD start on a random number, and MUST restart
    at 0 after reaching the maximum number 255.














  • TID:2bit,Temporal-layer index.如果T为0,必须被忽略。
  • Y:1layer sync bit.
  • KeyIDX:5bit,temporal key frame index.如果K为0,必须被忽略。开始于随机值,最大值31,达到最大值后变为0.关键帧如果改变了对后续解码很关键的参数信息,则+1,未改变参数的关键帧和非关键帧保持和前面的关键帧相同。如果收到一个P帧,而相同KeyIDX的关键帧没有收到,则不应该解码。如果不确定是否改变了关键信息,可以把关键帧都+1.或者把K设置为0。
Payload Header
 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ |Size0|H| VER |P| +-+-+-+-+-+-+-+-+ | Size1 | +-+-+-+-+-+-+-+-+ | Size2 | +-+-+-+-+-+-+-+-+ | Bytes 4..N of | | VP8 payload | : : +-+-+-+-+-+-+-+-+ | OPTIONAL RTP | | padding | : : +-+-+-+-+-+-+-+-+ 
  • P:类型,VP8只有两种,keyframe及interframe,分别为0和1.RFC6386中有定义。(也就是说,如果payload Header第一个字节是偶数,则说明这帧是keyframe)
  • VER:版本,内容如下:
 +---------+-------------------------+-------------+ | Version | Reconstruction Filter | Loop Filter | +---------+-------------------------+-------------+ | 0 | Bicubic | Normal | | | | | | 1 | Bilinear | Simple | | | | | | 2 | Bilinear | None | | | | | | 3 | None | None | | | | | | Other | Reserved for future use | | +---------+-------------------------+-------------+ 
  • H:显示位。0的时候,不显示,其实我觉得很奇怪,因为我这边抓到的报文,该位都是0,总不能都不显示吧,显然又是被解码器忽略了!
  • Size:首个partition的长度,19位,很奇怪的设定,计算方式是这样的1stPartitionSize = Size0 + 8 * Size1 + 2048 * Size2

VP8 payload

VP8编码数据

最后一个需要解析的是关于keyframe的了,只有是keyframe才带有keyframe header。这个头里面携带了图像的大小,以及起始的校验值0x9d012a(157 1 42)。rfc6386更是把代码直接贴出来了:头校验,不符合0x9d012a显然非VP8的keyframe了

==更多详情可阅读:http://elkpi.com/topics/2014/12/vp8-rtp-payload.html==

Optional RTP padding

RTP头的P为1才有这部分。

封包说明

  • RTP封包的时候,可以考虑VP8的partition边界,也可以不考虑。
  • 如果不考虑,则所有一个包可以包含多个partition,PID全为0.
  • 如果考虑,则一个包只能包含一个parition的片段,每个partition的包PID递增。

==参考资料:https://segmentfault.com/a/97934==

实践

解释下面这段VP8数据的RTP包:

[144 96 42 244 223 91 236 167 131 214 222 250 190 222 0 1 64 49 0 0 144 128 245 116 16 191 0 157 1 42 128 2 224 ... 226 72 169 244 51 91 89 71 39] //【首先是RTP Header】 144[]: V=2;P=0说明没有填充;X=1说明多了一个字节的扩展位;CC=0,说明没有CSRC。96[0] M=0说明这一帧还没结束,payloadType=96;42 244是序列号;223 91 236 167是时间戳;131 214 222 250是SSRC;190 222 0 1 64 49 0 0是扩展位(0xBE开头,是One-Byte Header)。 //【然后是payload Describetor】144[]:X=1说明有扩展位;R必须为0;N通常为0;S=1说明是一个新的VP8 partition的开始。因为X=1说明128[]是X的值,I=1说明有PictureID的信息;L=0、T=0、K=0说明这几行信息都没有;RSV保留为默认为0;245 116 :M=1说明PictureID有15位,后面15位都是PictureID。 //【然后是payload header】16[00010000]前三位为size0,就不去计算了,H=1说明要显示,作用其实也不大;VER=0;重要的是!P=0说明是KeyFrame,191 0分别是size1和size2;再后面三位是157 1 42可以验证前面P=0。 [144 96 42 245 223 91 236 167 131 214 222 250 190 222 0 1 64 49 0 0 128 128 245 116 248 225 248 55 217 186 2 31 214 ... 43 155 167 49 69 104 111 73 208 212 104 222 4 243 189 41 80 28 141 54 134 233 231 82 223 33] //【首先是RTP Header】 144[]: V=2;P=0说明没有填充;X=1说明多了一个字节的扩展位;CC=0,说明没有CSRC。96[0] M=0说明这一帧还没结束,payloadType=96;42 245是序列号;223 91 236 167是时间戳(因为是同一帧,所以和上一个包是一样的);131 214 222 250是SSRC;190 222 0 1 64 49 0 0是扩展位。 //【然后是payload Describetor】128[]:X=1说明有扩展位;R必须为0;N通常为0;S=0说明不是一个新的VP8 partition的开始,而是接着上一个包的。因为X=1说明128[]是X的值,I=1说明有PictureID的信息;L=0、T=0、K=0说明这几行信息都没有;RSV保留为默认为0;245 116 :M=1说明PictureID有15位,后面15位都是PictureID(也和上一个包相同)。 //【然后是payload数据】因为这是接着前一个包的中间的帧数据,所以没有header了,而是直接接着上一个尾巴的,详情看后面的Payload函数代码。 [144 224 42 246 223 91 236 167 131 214 222 250 190 222 0 1 64 49 0 0 128 128 245 116 18 152 229 126 72 231 2 118 76 213 252 86 88 89 ... 186 62 12 89 67 104 195 134 95 83 59 40 226 187 37 90 18 0] //【首先是RTP Header】 144[]: V=2;P=0说明没有填充;X=1说明多了一个字节的扩展位;CC=0,说明没有CSRC。224[] M=1说明这一帧到这个包就结束了,payloadType=96;42 246是序列号;(后面都和前两个包一样)223 91 236 167是时间戳;131 214 222 250是SSRC;190 222 0 1 64 49 0 0是扩展位。 //【然后是payload Describetor】128[]:X=1说明有扩展位;R必须为0;N通常为0;S=0说明不是一个新的VP8 partition的开始,而是接着上一个包的。因为X=1说明128[]是X的值,I=1说明有PictureID的信息;L=0、T=0、K=0说明这几行信息都没有;RSV保留为默认为0;245 116 :M=1说明PictureID有15位,后面15位都是PictureID(也和上一个包相同)。 //【然后是payload数据】因为这是接着前一个包的最后的帧数据,也是直接接着上一个尾巴的,没有header。 [144 224 42 247 223 91 249 79 131 214 222 250 190 222 0 1 64 49 0 0 144 128 245 117 49 51 0 228 224 175 188 243 87 167 71 167 216 133 23 242 73 96 55 216 63 252 111 125 121 180 94 67 58 11 ... 32 76 173 48 194 242 52 71 141 116 185 88 68 47 245 145 39 0] //【首先是RTP Header】 144[]: V=2;P=0说明没有填充;X=1说明多了一个字节的扩展位;CC=0,说明没有CSRC。224[0] M=1说明这一帧已经结束(一个包就包含了整个帧的数据,因为是P帧所以比较小,前面是I帧所以比较大),payloadType=96;42 247是序列号;223 91 249 79是时间戳;131 214 222 250是SSRC;190 222 0 1 64 49 0 0是扩展位。 //【然后是payload Describetor】144[]:X=1说明有扩展位;R必须为0;N通常为0;S=1说明是一个新的VP8 partition的开始。因为X=1说明128[]是X的值,I=1说明有PictureID的信息;L=0、T=0、K=0说明这几行信息都没有;RSV保留为默认为0;245 117 :M=1说明PictureID有15位,后面15位都是PictureID。 //【然后是payload header】49[00]前三位为size0,就不去计算了,H=1说明要显示;VER=0;重要的是!P=1说明是interframe,51 0分别是size1和size2;再后面三位不是157 1 42可以验证前面P=1,而是真实的VP8 payload了。 
// Payload fragments a VP8 packet across one or more byte arrays vp8HeaderSize := 1 //在这个封包方法中,只要了必选的VP8 payload Descriptor func (p *VP8Payloader) Payload(mtu int, payload []byte) [][]byte { maxFragmentSize := mtu - vp8HeaderSize//这里mtu=1200-12 payloadData := payload payloadDataRemaining := len(payload) payloadDataIndex := 0 var payloads [][]byte // Make sure the fragment/payload size is correct这个帧封完了 if min(maxFragmentSize, payloadDataRemaining) <= 0 { return payloads } for payloadDataRemaining > 0 { currentFragmentSize := min(maxFragmentSize, payloadDataRemaining) out := make([]byte, vp8HeaderSize+currentFragmentSize) if payloadDataRemaining == len(payload) { //如果是一个帧的第一个包,那么S位等于1,否则默认为0 out[0] = 0x10 } copy(out[vp8HeaderSize:], payloadData[payloadDataIndex:payloadDataIndex+currentFragmentSize]) //数据是直接分割vp8 payload的,然后统一加上了一个vp8 payload Descriptor。所以只有每帧的第一个包里面的payload包含了payload header。 payloads = append(payloads, out) payloadDataRemaining -= currentFragmentSize payloadDataIndex += currentFragmentSize } return payloads } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/217888.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 上午8:32
下一篇 2026年3月18日 上午8:32


相关推荐

  • 正则替换括号但保留内容(常用的正则表达式)

    正则表达式保留部分内容替换需求:把trim(ABC)替换成trim(replace(ABC,char(9),”)需要把ABC保留不变,替换其它的。实现:trim\(([^).]*)\)替换成trim\(replace\($1,char\(9\),”\)在查找的时候用括号括起来的代表一部分,在替换的时候可以用$1,$2…引用。注意:有写编…

    2022年4月10日
    118
  • php 最长回文子串

    php 最长回文子串最长回文子串由于case包含奇偶性,所以分两种情况讨论思路:找到以字符”x”为中心的最长回文子串从x的下标开始遍历,拆分为偶数对称情况和奇数对称情况 终止条件有2: 对称位置的字符不相同 循环右侧下标超出字符长度 结果:1364ms14.9MB主要难点是:计算下标代码:classSolution{/***@paramStr…

    2022年6月4日
    41
  • c++ 静态函数_c语言if结构格式

    c++ 静态函数_c语言if结构格式1、对象与对象之间的成员变量是相互独立的.要想共用数据,则需要使用静态成员或静态方法2、只要在类中声明静态成员变量,即使不定义对象,也可以为静态成员变量分配空间,进而可以使用静态成员变量.(因为静态成员变量在对象创建之前就已经被分配了内存空间)3、静态成员变量虽然在类中,但它并不是随对象的建立而分配空间的,也不是随对象的撤销而释放(一般的成员在对象建立时会分配空间,在对象撤销时会释放…

    2025年6月6日
    4
  • webpack基本配置项_webpack配置文件详解

    webpack基本配置项_webpack配置文件详解前言上篇我们已经配置好了本地开发服务器,但是配置的相对比较凌乱,一个文件中有些是开发时用到的配置,有些是生成时用到的配置,有些是开发和生成都要用到的配置,所以我们这里把环境分为3个环境webpac

    2022年7月31日
    8
  • 《VMware Virtual SAN权威指南(原书第2版)》一3.11 小结

    《VMware Virtual SAN权威指南(原书第2版)》一3.11 小结

    2022年3月5日
    58
  • linux服务器检查硬件配置,linux怎么查看服务器硬件配置

    linux服务器检查硬件配置,linux怎么查看服务器硬件配置你们知道怎么查看服务器的硬件配置信息吗 下面是学习啦小编带来的关于 linux 怎么查看服务器硬件配置的内容 欢迎阅读 linux 怎么查看服务器硬件配置 1 查看机器所有硬件信息 dmidecode moredmesg more 这 2 个命令出来的信息都非常多 所以建议后面使用 more 便于查看 2 查看 CPU 信息方法一 Linux 下 CPU 相关的参数保存在 proc cpuinfo 文件里 cat

    2026年3月26日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号