对RTP实现的一点理解

对RTP实现的一点理解 风清扬 2023-06-26 10:39:35 562

文章目录

说明

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

例程中使用位字段表示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的区别

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 点赞 收藏 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
关于作者
风清扬

风清扬

暂无个性签名~

原创10
阅读1.1w
收藏1
点赞1
评论0
打赏用户 0
我要创作
分享技术经验,可获取创作收益
分类专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
风清扬
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区