NuPlayer播放框架解析RTCP包

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值