数字视频编码和RTSP视频推拉流(三)

一、RTP协议

RTP包由RTP头部和RTP荷载构成: RTP Packet = RTP Header + RTP payload.

1.1 RTP头部(RTP Header)

在这里插入图片描述

版本号(V):2Bit,用来标志使用RTP版本

填充位§:1Bit,如果该位置位,则该RTP包的尾部就包含填充的附加字节

扩展位(X):1Bit,如果该位置位,则该RTP包的固定头部后面就跟着一个扩展头部

CSRC技术器(CC):4Bit,含有固定头部后面跟着的CSRC的数据

标记位(M):1Bit,该位的解释由配置文档来承担

载荷类型(PT):7Bit,标识了RTP载荷的类型

序列号(SN):16Bit,发送方在每发送完一个RTP包后就将该域的值增加1,可以由该域检测包的丢失及恢复

​ 包的序列。序列号的初始值是随机的

时间戳(timestamp):32比特,记录了该包中数据的第一个字节的采样时刻, H264/HEVC统一采用90kHz采样时钟,如果使用帧率fps来设置时间戳,则递增数值为90000/fps。

同步源标识符(SSRC):32比特,同步源就是RTP包源的来源。在同一个RTP会话中不能有两个相同的SSRC值

贡献源列表(CSRC List):0-15项,每项32比特,这个不常用

1.2 RTP荷载(RTP Payload)

RTP Payload结构一般分为3种:

  • 单NALU分组(Single NAL Unit Packet): 一个分组只包含一个NALU。
  • 聚合分组(Aggregation Packet): 一个分组包含多个NALU。
  • 分片分组(Fragmentation Unit):一个比较长的NALU分在多个RTP包中。

二、H.264的RTP打包

2.1 H.264格式

H.264由一个一个的NALU组成,每个NALU之间使用00 00 00 0100 00 01分隔开

每个NALU的第一次字节都有特殊的含义,其内容如下

描述
bit[7] 必须为0
bit[5-6] 标记该NALU的重要性
bit[0-4] NALU单元的类型

好,对于H.264格式了解这么多就够了,我们的目的是想从一个H.264的文件中将一个一个的NALU提取出来,然后封装成RTP包,下面介绍如何将NALU封装成RTP包

2.2 H.264的RTP打包方式

H.264可以由三种RTP打包方式

  • 单NALU打包

    一个RTP包包含一个完整的NALU

  • 聚合打包

    对于较小的NALU,一个RTP包可包含多个完整的NALU

  • 分片打包

    对于较大的NALU,一个NALU可以分为多个RTP包发送

注意:这里要区分好概念,每一个RTP包都包含一个RTP头部和RTP荷载,这是固定的。而H.264发送数据可支持三种RTP打包方式

比较常用的是单NALU打包分片打包,本文也只介绍这两种

单NALU打包

所谓单NALU打包就是将一整个NALU的数据放入RTP包的载荷中

这是最简单的一种方式,无需过多的讲解

分片打包

每个RTP包都有大小限制的,因为RTP一般都是使用UDP发送,UDP没有流量控制,所以要限制每一次发送的大小,所以如果一个NALU的太大,就需要分成多个RTP包发送,如何分成多个RTP包,下面来好好讲一讲

首先要明确,RTP包的格式是绝不会变的,永远多是RTP头+RTP载荷

在这里插入图片描述

RTP头部是固定的,那么只能在RTP载荷中去添加额外信息来说明这个RTP包是表示同一个NALU

如果是分片打包的话,那么在RTP载荷开始有两个字节的信息,然后再是NALU的内容

在这里插入图片描述

  • 第一个字节位FU Indicator,其格式如下
    在这里插入图片描述
    高三位:与NALU第一个字节的高三位相同

    Type:28,表示该RTP包一个分片,为什么是28?因为H.264的规范中定义的,此外还有许多其他Type,这里不详讲

  • 第二个字节位FU Header,其格式如下

    在这里插入图片描述

    S:标记该分片打包的第一个RTP包

    E:比较该分片打包的最后一个RTP包

    Type:NALU的Type

2.3 H.264 RTP打包的sdp描述

sdp文件有什么用?

sdp描述着媒体信息,当使用vlc打开这个sdp文件后,会根据这些信息做相应的操作(创建套接字…),然后等待接收RTP包

这里给出RTP打包H.264的sdp文件,并描述每一行是什么意思

m=video 9832 RTP/AVP 96 
a=rtpmap:96 H264/90000
a=framerate:25
c=IN IP4 127.0.0.1

这个一个媒体级的sdp描述,关于sdp文件描述详情可看从零开始写一个RTSP服务器(一)不一样的RTSP协议讲解

  • m=video 9832 RTP/AVP 96

    格式为 m=<媒体类型> <端口号> <传输协议> <媒体格式 >
    媒体类型:video,表示这是一个视频流

    端口号:9832,表示UDP发送的目的端口为9832

    传输协议:RTP/AVP,表示RTP OVER UDP,通过UDP发送RTP包

    媒体格式:表示负载类型(payload type),一般使用96表示H.264

  • a=rtpmap:96 H264/90000

    格式为a=rtpmap:<媒体格式><编码格式>/<时钟频率>

  • a=framerate:25

    表示帧率

  • c=IN IP4 127.0.0.1

    IN:表示internet

    IP4:表示IPV4

    127.0.0.1:表示UDP发送的目的地址为127.0.0.1

特别注意:这段sdp文件描述的udp发送的目的IP为127.0.0.1,目的端口为9832

type type value
vps 32
sps 33
pps 34
SEI 39
IDR 19或者20
常用P帧,后置图像帧 1

H264 的RTP打包代码

int rtpSendH264Frame(int socket, const char* ip, int16_t port,
                            struct RtpPacket* rtpPacket, uint8_t* frame, uint32_t frameSize)
{
   
    uint8_t naluType; // nalu第一个字节
    int sendBytes = 0;
    int ret;

    naluType = frame[0];

    if (frameSize <= RTP_MAX_PKT_SIZE) // nalu长度小于最大包场:单一NALU单元模式
    {
   
        /*
         *   0 1 2 3 4 5 6 7 8 9
         *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         *  |F|NRI|  Type   | a single NAL unit ... |
         *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */
        memcpy(rtpPacket->payload, frame, frameSize);
        ret = rtpSendPacket(socket, ip, port, rtpPacket, frameSize);
        if(ret < 0)
            return -1;

        rtpPacket->rtpHeader.seq++;
        sendBytes += ret;
        if ((naluType & 0x1F) == 7 || (naluType & 0x1F) == 8) // 如果是SPS、PPS就不需要加时间戳
            goto out;
    }
    else // nalu长度小于最大包场:分片模式
    {
   
        /*
         *  0                   1                   2
         *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         * | FU indicator  |   FU header   |   FU payload   ...  |
         * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         */

        /*
         *     FU Indicator
         *    0 1 2 3 4 5 6 7
         *   +-+-+-+-+-+-+-+-+
         *   |F|NRI|  Type   |
         *   +---------------+
         */

        /*
         *      FU Header
         *    0 1 2 3 4 5 6 7
         *   +-+-+-+-+-+-+-+-+
         *   |S|E|R|  Type   |
         *   +---------------+
         */

        int pktNum = frameSize / RTP_MAX_PKT_SIZE;       // 有几个完整的包
        int remainPktSize = frameSize % RTP_MAX_PKT_SIZE; // 剩余不完整包的大小
        int i, pos = 1;

        /* 发送完整的包 */
        for (i = 0; i < pktNum; i++)
        {
   
            rtpPacket->payload[0] = (naluType & 0x60) | 28;
            rtpPacket->payload[1] = naluType & 0x1F;

            if (i == 0) //第一包数据
                rtpPacket->payload[1] |= 0x80; // start
            else if (remainPktSize == 0 && i == pktNum - 1) //最后一包数据
                rtpPacket->payload[
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值