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的分组类型

类型 缩写表示 意义 200 SR(Sender Report) 发送端报告 201 RR(Receiver Roport) 接收端报告 202 SDES(Source Descripition Items) 源点 203 BYE 结束 204 APP(Application) 特定应用 205 TSFB (transport layer specific feedback) 运输层反馈信息 206 PSFB (payload specific feedback) 载荷反馈信息

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包。

1 0