Advertisement

网络协议---RTP---NALU打包成RTP

阅读量:

在h264 RTP STAP-A协议中,在单时间聚合时会比A包多包含一个DON字段

nal_unit_type:
1~23----NALU包类型
24~31----RTP打包类型

在这里插入图片描述

RTP数据包 = RTP包头 + 载荷

1、RTP包头

在这里插入图片描述
复制代码
    typedef struct 
    {
    /**//* 1byte (0) */
    unsigned char version:2;/* expect 2,版本号,固定为2 */
    unsigned char padding:1;/*expect 0 , 填充位,不用关心*/
    unsigned char extension:1;/* expect 1, see RTP_OP below ,扩展位*/
    unsigned char csrc_len:4;/* expect 0, csrc计数器,没啥用*/
    
    /**//* 1byte (1) */
    unsigned char payload:7;/*RTP_PAYLOAD_RTSP,负载类型 h264的PT=96*/
    unsigned char marker:1;
    /* expect 1,视频中:1尾包/0非尾包,不拆包的NALU也算尾包→Marker=1*/
    
    /**//* 2bytes (2, 3) */
    unsigned short seq_no;/* RTP包序号,比如100,101,102,类推 */ 
    /* 序列号只表示了包发出的先后顺序,它表示不了任何时间上的其它概念,所有严格的说,序列号并不能作为流内的同步标志。
    但是,由于一般来说,包的发送时间都会有严格限制,比如音频包是每秒种发送30个数据包,也就是说,每个包间隔1000/30MS,而这个时间就可以作为一个同步时间来播放。也就是说,按照序列号,每1000/30MS间隔播放一个数据包,这样也能保证同步,但是这时候请考虑丢包问题。*/   
      
    /**//* 4bytes (4-7) */
    unsigned long timestamp;/* 时间戳位 数据能以正确的时间顺序恢复的关键 */   
    /*发送方时间戳的时钟是连续、单调增长的,即使不发送or接受*/   
    /*会话的初始时间戳必须随机选择,但没有规定时间戳的单位,而是交由负载来确定→根据需要选择合适的输出计时精度*/  
    /*同一时间采样的多个分组的时间戳相同*/ 
    /* RTCP的一个关键作用就是能让接收方同步多个RTP流,例如:当音频与视频一起传输的时候,由于编码的不同,RTP使用两个流分别进行传输,这样两个流的时间戳以不同的速率运行,接收方必须同步两个流,以保证声音与影像的一致。为能进行流同步,RTCP要求发送方给每个传送一个唯一的标识数据源的规范名(Canonical Name),尽管由一个数据源发出的不同的流具有不同的同步源标识(SSRC),但具有相同的规范名,这样接收方就知道哪些流是有关联的。*/
      
    /**//* 4bytes (8-11) */
    unsigned long ssrc;
    /* 同步信源标识符,一个RTP传输session的ID,
    用于区分信号来源,在本RTP会话中全局唯一就行
    这个数字是随机产生,并且要保证唯一。*/
    /*比如  rtp_hdr->ssrc        = htonl(10);   //全局唯一就行*/
    // CSRC(4Bytes)不是RTP必须的,因此不定义它
    } RTP_FIXED_HEADER;

2、RTP3种打包方式(👈考虑到MUT大小)

当数据报长度超过最大传输单元时(具体为有线IP网络1500字节、无线及非IP网络254字节以下),若未经PCT/IP层处理而自行分割,则可能导致数据包丢失的概率增加

假设我们采用1500字节作为基准计算,则其中总数据长度为:
1500 = 89 + 89 + 89 + 89 + 89 + 89 + 89 + 89 + 77 + 77
其中各组成部分的具体数值如下:
信道编码冗余(CC)= C_{{\rm R}} = C_{{\rm E}} = C_{{\rm F}} = C_{{\rm G}} = C_{{\rm H}} = C_{{\rm I}} = C_{{\rm J}}
信道重传次数(AR)= A_{\text{r}}
信道重传次数(ER)= E_{\text{r}}
信道重传次数(FR)= F_{\text{r}}
信道重传次数(GR)= G_{\text{r}}
信道重传次数(HR)= H_{\text{r}}

①单一NAL单元模式
②组合封包模式
将多个NALU封装到一个RTP中,在实际应用中发现适用于较小规模的NALU场景时可采用这种打包策略以显著提升传输效率。具体而言,则是由多个NALU构成单个RTP包。
在STAP包中所有ATAL的时间戳均为相同值;而在MTAP配置下则赋予每个ATAL独立的时间戳值。
③分片封包模式FUs
相较于FU-A方案,在FU-B版本中多引入了一个DON(decoding order number)字段,并规定该字段采用网络字节序编码方式。值得注意的是,在 FU-B 方案下仅限于行间扫描的分片封包场景适用。

RTP 包等于 RTP 头信息与 RTP PAYLOAD 的组合。

由 PlayLoad 头信息及 RBSP 构成的 RTP Payload 单元组。

||
由 NAL 头信息及 RBSP 构成的 NAL 单元组。

拆包

如果未对NAK头进行拆分则采用NAL_head;
当对NAK头进行拆分时,则采用FU_Indicator和FU_header两种字段;
对于不分包的情况,则为[RTP HEADER]+[NAL_header]+[RBSP];
而当分包时,则是[RTP HEADER]+[FU_Indicator]+[FU_header]+[RBSP].

①NAL_header的前面3位:F和NRI 即FU_indicator的前3位
②NALU码流类型
NAL_header的后3位:TYPE 即FU_header的后5位
类型1~12
③FU_indicator的后5位(拓展类型)
TYPE表征打包类型(方式)
类型24-29
④FU_header的前三位
S:开始位 拆包的第一包则置位 否则0
E:结束位 拆包的最后一包则置位 否则0
R:预留,必须是0

RTP_FIXED_HEADER rtp_hdr;
rtp_hdr =(RTP_FIXED_HEADER
)&sendBuf[0];
rtp_hdr->payload = H264; //
rtp_hdr->version = 2;
rtp_hdr->marker = 0;
rtp_hdr->ssrc = htonl(10); //同步信源(SSRC)标识符使用同一个-用于区分信号来源
int k=0,l=0;
k = nalu_t->len/MAX_RTP_PKT_LENGTH; // 比如k为7,表示有7个完整RTP包+1个尾包
l = nalu_t->len%MAX_RTP_PKT_LENGTH; // 比如l为126,表示尾包的RBSP为126个字节
int t=0;
ts_current = ts_current+timestamp_increse; //时间戳
rtp_hdr->timestamp = htonl(ts_current);
while(t<=k)
{
rtp_hdr->seq_no = htons(seq_num ++); //序列号累加
if(!t) //第一包
{
rtp_hdr->marker=0; // 不是尾包
fu_ind =(FU_INDICATOR*)&sendBuf[12];
fu_ind->F=nalu_t->forbidden_bit;
fu_ind->NRI=nalu_t->nal_reference_idc>>5;
fu_ind->TYPE=28;

复制代码
        fu_hdr =(FU_HEADER*)&sendBuf[13];  
        fu_hdr->E=0;  
        fu_hdr->R=0;  
        fu_hdr->S=1;  
        fu_hdr->TYPE=nalu_t->nal_unit_type;  
    
        nalu_payload=&sendBuf[14];  
        memcpy(nalu_payload,nalu_t->buf+1,MAX_RTP_PKT_LENGTH);  
    
        bytes=MAX_RTP_PKT_LENGTH+14;  
        ::send( socketFd, sendBuf, bytes, 0 );  
        t++;  
    }
    else if(k==t)  // 尾包的情况
    {  
        rtp_hdr->marker=1;  // 尾包
        fu_ind =(FU_INDICATOR*)&sendBuf[12];  
        fu_ind->F=nalu_t->forbidden_bit;  
        fu_ind->NRI=nalu_t->nal_reference_idc>>5;  
        fu_ind->TYPE=28;  
    
        fu_hdr =(FU_HEADER*)&sendBuf[13];  
        fu_hdr->R=0;  
        fu_hdr->S=0;  
        fu_hdr->TYPE=nalu_t->nal_unit_type;  
        fu_hdr->E=1;  
    
        nalu_payload=&sendBuf[14];  
        memcpy(nalu_payload,nalu_t->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);  
        bytes=l-1+14;  
        ::send( socketFd, sendBuf, bytes, 0 );  
        t++;  
    }
     else if(t<k&&0!=t) // 注意,头包,中间包,尾包,fu_hdr->R,fu_hdr->S,fu_hdr->E 的值不一样
     {  
        rtp_hdr->marker=0;  
        fu_ind =(FU_INDICATOR*)&sendBuf[12];  
        fu_ind->F=nalu_t->forbidden_bit;  
        fu_ind->NRI=nalu_t->nal_reference_idc>>5;  
        fu_ind->TYPE=28;  
    
        fu_hdr =(FU_HEADER*)&sendBuf[13];  
        fu_hdr->R=0; 
        fu_hdr->S=0;  
        fu_hdr->E=0;  
        fu_hdr->TYPE=nalu_t->nal_unit_type;  
    
        nalu_payload=&sendBuf[14];  
        memcpy(nalu_payload,nalu_t->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH);  
        bytes=MAX_RTP_PKT_LENGTH+14;  
        ::send( socketFd, sendBuf, bytes, 0 );  
        t++;  
    }

}

全部评论 (0)

还没有任何评论哟~