NuPlayer播放框架解析RTCP包
来源:互联网 发布:网络编辑岗位职责 编辑:程序博客网 时间:2024/06/05 13:32
1.ARTPConnection::parseRTCP源码
下面贴出安卓N版本ARTPConnection::parseRTCP源码:
status_t ARTPConnection::parseRTCP(StreamInfo *s, const sp<ABuffer> &buffer) { if (s->mNumRTCPPacketsReceived++ == 0) { sp<AMessage> notify = s->mNotifyMsg->dup(); notify->setInt32("first-rtcp", true); notify->post(); } const uint8_t *data = buffer->data(); size_t size = buffer->size(); while (size > 0) { if (size < 8) { // Too short to be a valid RTCP header return -1; } if ((data[0] >> 6) != 2) { // Unsupported version. return -1; } if (data[0] & 0x20) { // Padding present. size_t paddingLength = data[size - 1]; if (paddingLength + 12 > size) { // If we removed this much padding we'd end up with something // that's too short to be a valid RTP header. return -1; } size -= paddingLength; } size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4; if (size < headerLength) { // Only received a partial packet? return -1; } switch (data[1]) { case 200: { parseSR(s, data, headerLength); break; } case 201: // RR case 202: // SDES case 204: // APP break; case 205: // TSFB (transport layer specific feedback) case 206: // PSFB (payload specific feedback) // hexdump(data, headerLength); break; case 203: { parseBYE(s, data, headerLength); break; } default: { ALOGW("Unknown RTCP packet type %u of size %zu", (unsigned)data[1], headerLength); break; } } data += headerLength; size -= headerLength; } return OK;}
2.RTCP报文结构
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| v=2 (0~1) | p (2) | reserved (3~7)| PT (8 bits)| legnth (16 bits)| SSRC (32bits)| report blocks |
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
关于RTCP报文结构推荐阅读文章:
https://en.wikipedia.org/wiki/RTP_Control_Protocol
https://tools.ietf.org/html/rfc3611#page-7
可以看出一个完整的一个RTCP报文头部部分由8个字节的固定部分和可变部分组成。
关于各个字段的解释:
version (V): 2 bits
识别 RTP 版本。RTP 数据包中的该值与 RTCP 数据包中的一样。当前规范定义的版本值为 2。
padding (P): 1 bit
间隙(Padding)。设置时, RTCP 数据包包含一些其它 padding 八位位组,它们不属于控制信息。Padding 的最后八位是用于计算应该忽略多少间隙八位位组。一些加密算法中需要计算固定块大小时也可能需要使用 Padding 字段。在一个复合 RTCP 数据包中,只有最后的个别数据包中才需要使用 padding ,这是因为复合数据包是作为一个整体来加密的。
reserved: 5 bits
保留位。通常被设置为0,并且被接收者忽略。
packet type (PT): 8 bits
RTCP的分组类型
length: 16 bits
RTCP 数据包的大小(32 位字减去 1),包含头和任意间隙 。
SSRC: 32 bits
同步信源标识符(SSRC)
2.1RTCP的分组类型
SR(Sender Report)
发送端报告分组SR用来使发送端周期性地向所有接收端用多播方式进行报告。发送端每发送一个RTP流就要发送一个发送端报告分组SR。
RR(Receiver Roport)
接收端报告分组RR用来使接收端周期性地向所有的点用多播方式进行报告。接收端每收到一个RTP流(一次会话包含多个RTP流)就产生一个接收端报告分组RR。
SDES(Source Descripition Items)
源点描述分组SDES给出会话中参加者的描述,它包含参加者的规范名CNAME(Canonical NAME)。规范名是参加者的电子邮件地址的字符串。
BYE|结束
结束分组BYE表示关闭一个数据流。
APP(Application)
特定应用分组APP使应用程序能够定义新的分组类型。
3.解析RTCP包
3.1处理接收到的第一个RTCP包
if (s->mNumRTCPPacketsReceived++ == 0) { sp<AMessage> notify = s->mNotifyMsg->dup(); notify->setInt32("first-rtcp", true); notify->post(); }
如果s->mNumRTCPPacketsReceived的值为0,说明当前收到的RTCP包为第一个RTCP包,发送消息”first-rtcp”进行相应的异步处理,并且将该值自增1。如果不为0,则只将该值自增1,不发送消息进行异步处理。
3.2得到RTCP数据包的起始地址
const uint8_t *data = buffer->data(); size_t size = buffer->size();
3.3解析RTCP包
由于接收到的可能是一个复合包,所以采用了一个循环,依次解析每一个包
while (size > 0) { if (size < 8) { // Too short to be a valid RTCP header return -1; } if ((data[0] >> 6) != 2) { // Unsupported version. return -1; } if (data[0] & 0x20) { // Padding present. size_t paddingLength = data[size - 1]; if (paddingLength + 12 > size) { // If we removed this much padding we'd end up with something // that's too short to be a valid RTP header. return -1; } size -= paddingLength; } size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4; if (size < headerLength) { // Only received a partial packet? return -1; } switch (data[1]) { case 200: { parseSR(s, data, headerLength); break; } case 201: // RR case 202: // SDES case 204: // APP break; case 205: // TSFB (transport layer specific feedback) case 206: // PSFB (payload specific feedback) // hexdump(data, headerLength); break; case 203: { parseBYE(s, data, headerLength); break; } default: { ALOGW("Unknown RTCP packet type %u of size %zu", (unsigned)data[1], headerLength); break; } } data += headerLength; size -= headerLength; }
3.3.1判断当前协议的版本号
if (size < 8) { // Too short to be a valid RTCP header return -1; } if ((data[0] >> 6) != 2) { // Unsupported version. return -1; }
一个RTCP包的头部部分固定的大小是8个字节,如果小于这个数值则不是一个完成的RTCP包。
一个RTCP包头部部分的第一个字节的前两位标识的是协议的版本号,所以data[0] >> 6即第一个字节向右移动6位,则标识协议版本号的两位则被移动到了低两位,高六位被填充0.即得到了协议版本号的值。当前的规范的协议版本号为2.
3.3.2判断填充位
if (data[0] & 0x20) { // Padding present. size_t paddingLength = data[size - 1]; if (paddingLength + 12 > size) { // If we removed this much padding we'd end up with something // that's too short to be a valid RTP header. return -1; } size -= paddingLength; }
填充位P在第一个字节的第三位,(data[0] & 0x20) 即 (data[0] & 0010 0000) 就得到了填充位的值。如果该值为1,则说明存在填充位,如果为0,则说明不存在填充位。当存在填充位的时候,整个RTP包的最后一个字节即data[size-1]的值表示填充的长度(以字节为单位,包括data[size-1])。所以存在填充位的时候,在解析需要将该填充长度丢弃掉。通过size -= paddingLength;限定size的范围来丢弃该填充位。
注:即使是符合包也只会在最后一个包添加填充位来进行加密算法,因为加密算法可能需要某个数字的整数倍的bit位数,所以只需要在最后一个RTCP包进行填充就好了。
3.3.3计算一个RTCP包的长度
size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
计算得到的headerLength值是界定一个完整的单个RTCP包的范围。一个RTCP包的第3,第4个字节所表示的整数值反应了一个RTCP包的大小。data[2] << 8 | data[3]计算技巧得到这个值,经过线性变换4 * (data[2] << 8 | data[3]) + 4;得到一个一个RTCP包的大小。
3.3.4处理不同的分组类型PT
switch (data[1]) { case 200: { parseSR(s, data, headerLength); break; } case 201: // RR case 202: // SDES case 204: // APP break; case 205: // TSFB (transport layer specific feedback) case 206: // PSFB (payload specific feedback) // hexdump(data, headerLength); break; case 203: { parseBYE(s, data, headerLength); break; } default: { ALOGW("Unknown RTCP packet type %u of size %zu", (unsigned)data[1], headerLength); break; } }
分组类型PT是RTCP包第二个字节所表示的一个8bit整数,即data[1]。可以看出针对不同的分组类型对应不同的处理,传入的参数是界定一个单独的RTCP的参数。
3.3.4解析下一个RTCP包
data += headerLength; size -= headerLength;
移动data指针,进入下一个循环,解析下一个RTCP包。
- NuPlayer播放框架解析RTCP包
- Android NuPlayer播放框架
- Android NuPlayer播放框架
- NuPlayer播放框架RTP数据包获取和解析
- ③NuPlayer播放框架之类NuPlayer源码分析
- ④NuPlayer播放框架之Renderer源码分析
- ⑤NuPlayer播放框架之GenericSource源码分析
- Android中基于Nuplayer的RTSP框架解析
- nuplayer播放流程分析
- ②NuPlayer播放框架之ALooper-AHandler-AMessage底层机制分析
- Nuplayer
- RTCP包结构
- ⑥NuPlayer播放源码分析之DecoderBase分析 NuPlayer播放源码分析之DecoderBase分析
- RTP/RTCP协议解析
- RTCP TMMBR字段解析
- RTP/RTCP协议解析
- RTP/RTCP协议解析
- 分析jrtplib收发RTCP包
- IAR for STM8 为何可以不配置时钟?
- HLS学习(一)HLS介绍
- (1)Oracle 11g之安装数据库
- 命令格式
- 在数组中找出三个不重叠的固定长度的子数组,要求这三个子数组的和最大
- NuPlayer播放框架解析RTCP包
- Java反射机制
- Tree 笨方法实现
- 腾讯的应用拉活套路
- Android N多窗口支持介绍
- elasticsearch查询原理
- 九度OJ Graduate Admission
- Android-解决ScrollView和ListView嵌套的问题
- [Spring boot] web应用返回jsp页面