对RTP实现的一点理解

说明

本文性质为个人学习笔记。由于刚刚入门所以看法很浅,如有错误请指正。

例程中使用位字段表示RTP首部,没有问题吗?

位字段是一种通过结构来声明的、对变量进行的位操作方法。经过查阅资料,对位字段有如下论述:

《C Primer Plus(第六版)中文版》第505页:

…位字段是一个signed int或unsigned int类型变量中的一组相邻的位(C99和C11新增了_Bool类型的位字段)。…

《高质量嵌入式Linux C编程(第2版)》第153页:

…使用位段需注意以下几点:

位段的类型只能是int、unsigned int和signed int这3种类型,不能是char型或者浮点型。

然而在我所学习并查找到的RTP实现中,RTP首部通常被定义为以下的样子:

typedef struct
{
    unsigned char u4CSrcLen   :4;
    unsigned char u1Externsion:1;
    unsigned char u1Padding   :1;
    unsigned char u2Version   :2;
    unsigned char u7Payload   :7;
    unsigned char u1Marker    :1;
    unsigned short u16SeqNum;
    unsigned long u32TimeStamp;
    unsigned long u32SSrc;
}stRTPHeader;

可以看到,其中大量使用了unsigned char型的变量,这显然有悖于标准中的叙述。但是实际测试中发现这 并不影响编译和传输 。姑且如此。

为什么要使用位字段?

位字段的使用,方便了修改RTP首部中指定位时的操作,减少了移位带来的麻烦,另外一点是, RTP规定中,是以大端方式进行定义的。 使用位字段的操作,可以方便在大小端系统上进行移植,例如这样定义(此处引用来源见参考1):

struct MBRTPHeader
{
#ifdef RTP_BIG_ENDIAN
	unsigned char version:2;
	unsigned char padding:1;
	unsigned char extension:1;
	unsigned char csrccount:4;

	unsigned char marker:1;
	unsigned char payloadtype:7;
#else
	unsigned char csrccount:4;
	unsigned char extension:1;
	unsigned char padding:1;
	unsigned char version:2;

	unsigned char payloadtype:7;
	unsigned char marker:1;
#endif

	unsigned short sequencenumber;
	unsigned int timestamp;
	unsigned int ssrc;
};

通过在编译时开启不同的宏,达到增加可移植性的效果。

另外需要注意,对RTP首部(以及分片的指示首部)中的成员赋值时, 均需转换为网络字节序再进行传输

关于socket的使用

本部分内容引用自参考2。

Socket使用中,需要首先初始化一个sockaddr_in结构,其中的sin_family成员可以设置为AF_INET协议族或者PF_INET协议族。在Windows下,AF_INET与PF_INET完全一样.。而在Unix/Linux系统中,在不同的版本中这两者有微小差别。对于BSD,是AF,对于POSIX是PF。

理论上建立socket时是指定协议,应该用PF_xxxx,设置地址时应该用AF_xxxx。当然AF_INET和PF_INET的值是相同的,混用也 不会有太大的问题。也就是说你socket时候用PF_xxxx,设置的时候用AF_xxxx也是没关系的。

为什么要在应用层对RTP报文进行分片?

RTP分片并不是为了适配UDP报文长度,而是取了一个比较理想的值用以适配MTU大小,使一条报文传递到数据链路层后不会被分割成更小的包。

一般而言,当传输一个较大的UDP数据报时,数据被层层封包和分割,而后进行发送,在网络情况较好的时候这样做倒是没什么,但是如果路由或网络发生状况,分包的动作有可能会有延迟或者出错,将影响到RTP流的传输。

所以我所见的RTP实现,以一个理想的MTU大小为阈值对包在应用层进行分割,这样传到底层的包,路由只需要转发不需要额外的操作,对网络稳定性有了更好的容错。

实际上,对于H264视频流而言,进行稍微大一点的画面编码时,基本上只有IDR帧的SPS、PPS和SEI包可以在一个RTP报文中传输而不需要分片。

然而这样做的时候,如果传输中发生包的丢失,RTP依旧无法处理,只能丢弃,这也体现了RTP不保证服务质量这一特点。

另外,分片和不分片,对NALU头的处理方式是不同的。在不分片的情况下,一个RTP包格式是这样的:

RTP首部(12Bytes)+NALU(1Byte)+数据

在分片的时候,分片前端是这样的:

RTP首部(12Bytes)+FUA指示(1Byte)+FUA头(1Byte)+数据+…

这时,NALU头会被分为两个部分分别存放在FUA指示字节和FUA头中。这也是NALU头需要专门取出处理的原因之一。

编码的H264流的RTP时间戳应该如何设置?

我测试时使用的编码平台为Hi3516DV300,海思编码器的每个码流包中会给出一个时间戳信息,为无符号64位整型值,单位是us。RTP码流中视频时间戳是基于90KHz的,故其单位为1/90000,RTP首部中的时间戳要换算成该单位下的变量,方法是(以下计算忽略了变量范围): RTPTimeStamp=H264TimeStamp*90000/1000000

这样就借由编码器的时间戳获得RTP报文的时间戳,而且不需要再查询系统时间了。

参考

1.RTP头的解析及大小端处理的细节
2.关于PF_INET和AF_INET的区别

————2020-4-1 @燕卫博————

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值