RTP包中timestamp的间隔问题
概述
近期在和同事调试G729的编解码库时碰到一个语音质量的问题,问题产生的原因和RTP包中的时间戳设置有关,特此记录下来。
问题现象,1001和1002账号注册在fs,媒体设置为G729并通过fs中转,1001终端使用eyebean,1002终端使用自己开发的sip终端,从1001-1002的语音正常,从1002-1001的语音卡顿异常。
环境
centos:CentOS release 7.0 (Final)或以上版本
freeswitch:v1.8.7
GCC:4.8.5
问题分析
freeswitch在正常的语音转发中没有发现过类似问题。
从语音质量的现象看,只有单边的语音卡顿,即从1002-1001方向的语音有问题。
1002的终端属于自研产品,其中G729模块也是刚刚接入,发生问题的概率较大。
有了基本的分析之后,还是要找到明确的证据支撑,这时候就需要抓包分析。
使用wireshark对SIP终端侧进行抓包,查看抓包的RTP流。如下图

其中蓝色的第1、3行,是出现问题的RTP流。
再打开RTP流分析的页面。
从1002-fs的RTP流如下图。可以看到stream0的包分析结果。

从fs-1001的RTP流如下图。可以看到stream1的包分析结果。
这个页面我们发现了一个问题,就是RTP流的发包中,Delta(ms)列的数据有一些异常,每隔5个包,就会出现1个40ms间隔的包,非常的规律。

对应到RTP流中,根据timestamp字段就会发现,每隔6个包,就会有1个包丢掉了。

至此,我们可以得到一个初步的分析结果,就是1002发送的RTP包,经过fs的转发后,被部分丢弃了,造成1001收到的语音质量问题。
RTP包timestamp字段
由于1002SIP终端的G729库是新接入的,所以对于RTP打包格式是首先要怀疑的。
RTP头格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
对于其他字段的介绍网上很多,直接略过,这里主要看一下timestamp字段。
timestamp,32比特。时间戳,反映了RTP数据包中第一个八位字节的采样时间。时间戳的初始值应当是随机的,类似序号。时钟频率依赖于负载数据格式,因此时间戳增量依赖于当前数据格式和策略。如果RTP数据包周期性产生,那么将使用采样时钟确定的标称采样时刻,而非读取系统时间。举例而言,对于固定采样率的音频,时间戳时钟可能会在每个采样周期增加1;如果音频应用程序从输入设备读取覆盖160个采样周期的块,则对于每个这样的块,时间戳将增加160,无论该块是在分组中传输还是作为静默丢弃。
上面这一段是官方的介绍,说实话我也看的稀里糊涂的,但是其中的关键是“时间戳将增加160”这一句,和我的认知有了冲突。
当我们看到timestamp字段的名字时,首先想到的是时间戳,既然是时间,按照正常的理解,应该是和打包时长ptime相对应,比如打包时长ptime是20ms,那么timestamp的间隔也应该是20。
但是事实上,RTP包中的timestamp字段并不是这样定义的。
在官方的RFC1889文档中,If an audio application reads blocks covering 160 sampling periods from the input device, the timestamp would be increased by 160 for each such block, regardless of whether the block is transmitted in a packet or dropped as silent.
下面翻译成我自己的理解。
timestamp字段和通常的时间没有关系。
timestamp字段是为了表示媒体流的采样长度和顺序。
timestamp字段的初始值是随机数。
timestamp字段的间隔的计算方法,根据媒体流协商的媒体类型来决定,具体由俩个指标,采样率和打包时长。
计算公式是(timestamp间隔=采样率*打包时长)。
举个栗子,媒体类型的协商结果是G729,其中采样率是8000,打包时长是20ms,那么timestamp的间隔 = 8000 * 0.02 = 160。
再举个栗子,媒体协商结果是iLBC,其中采样率是8000,打包时长是30ms,那么timestamp的间隔 = 8000 * 0.03 = 240。
再举最后一个栗子,媒体协商结果是opus,其中采样率是16000,打包时长是20ms,那么timestamp的间隔 = 16000 * 0.02 = 320。
总结
RTP流在VOIP和RTC通信中非常常见。
我们从一个问题出发,在分析解决的过程中,重新认识了RTP包格式,尤其是其中timestamp字段的定义,和平常的时间戳定义有区别。
碰到网络问题,wireshark抓包是非常好用的工具,可以解决90%的问题。
空空如常
求真得真
RTP包中timestamp的间隔问题的更多相关文章
- 得到RTP包中的timestamp
NTP------网络时间协议 PTP------精确时间协议 PTS,DTS的关系: http://www.cnblogs.com/qingquan/archive/2011/07/27/21189 ...
- Live555中RTP包的打包与发送过程分析
这里主要分析一下,live555中关于RTP打包发送的部分.在处理完PLAY命令之后,就开始发送RTP数据包了(其实在发送PLAY命令的response包之前,就会发送一个RTP包,这里传输就已经开始 ...
- RTP 包格式 详细解析
H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) NALU 头由一个字节组成, 它的语法如下: +---------------+ |0|1|2|3|4|5|6|7 ...
- H264 RTP包解析
1. 预备 视频: 由一副副连续的图像构成,由于数据量比较大,因此为了节省带宽以及存储,就需要进行必要的压缩与解压缩,也就是编解码. h264裸码流: 对一个图像或者一个视频序列进行压缩,即产生码流 ...
- NALU数据打RTP包流程详解
最近在看RTP发送H264数据的文章,感觉很乱,没有比较清晰易懂的教程,自己整理了一下各种资料,备忘! --------Part A ---- 先说说H264数据,H264在网络传输的是NALU(N ...
- RTP包的结构
live555中数据的发送最后是要使用RTP协议发送的,下面介绍一下RTP包格式. RTP packet RTP是基于UDP协议的,RTP服务器会通过UDP协议,通常每次会发送一个RTP packet ...
- FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法。。
[原创] RFC3984是H.264的baseline码流在RTP方式下传输的规范,这里只讨论FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法. 1.单个NAL包单元 12字节 ...
- (转)谈谈RTP传输中的负载类型和时间戳
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://ticktick.blog.51cto.com/823160/350142 最近被 ...
- rtp包格式
转载一篇帮助我了解h264 rtp的文档,地址http://www.cppblog.com/czanyou/archive/2009/12/25/67940.html 当packetization-m ...
随机推荐
- java运行时创建对象
有很多场景需要运行时创建对象,比如Copy对象到指定类型的对象中,比如根据指定的字段和值创建指定类型的对像.使用JDK自带的反射(java.lang.reflect)或者自省(java.beans.I ...
- elasticsearch之拼音搜索
拼音搜索在中文搜索环境中是经常使用的一种功能,用户只需要输入关键词的拼音全拼或者拼音首字母,搜索引擎就可以搜索出相关结果.在国内,中文输入法基本上都是基于汉语拼音的,这种在符合用户输入习惯的条件下缩短 ...
- 手把手教你分析解决MySQL死锁问题
在生产环境中如果出现MySQL死锁问题该如何排查和解决呢,本文将模拟真实死锁场景进行排查,最后总结下实际开发中如何尽量避免死锁发生. 一.准备好相关数据和环境 当前自己的数据版本是8.0.22 mys ...
- 【CSAPP】第三章 程序的机器级表示
目录 1. 数据的编码与存储 2. 汇编指令 2.1 数据传送指令 访存方式 数据传送指令 入栈出栈 2.2 算术/逻辑指令 2.3 过程控制指令 控制码 比较指令 跳转指令 条件设置指令 3. 程序 ...
- opencv 4.0 + linux + cuda静态编译
#下载最新的opencv git clone "https://github.com/opencv/opencv.git" git clone "https://gith ...
- ROS之face recongination(cbo_peopel_detection)
一准备 Ros的cbo_people_detection网站http://wiki.ros.org/cob_people_detection 某网站来自Amir:http://edu.gaitech. ...
- 羽夏看Win系统内核——进程线程篇
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...
- Go 结构体方法
#### Go 结构体方法本来今天有些事情忙的不准备更新内容了,后来提前完成了, 所以还是要更新了; 毕竟坚持本就是一件不容易的事情!加油,相信不管是大家还是我,都有一些事情想要做,那就坚持吧,剩下的 ...
- Filter的拦截路径
Filter的拦截路径 精确匹配 <url-pattern>/target.jsp</url-pattern> 以上配置的路径,表示请求地址必须为:http://ip.port ...
- python 小兵(5)参数
我们目前为止,已经可以完成一些软件的基本功能了,那么我们来完成这样一个功能:约x 1 2 3 4 5 pint("拿出手机") print("打开陌陌") pr ...