多媒体通信同步方法,主要有时间戳同步法、同步标记法、多路复用同步法三种。下面主要讨论时间戳同步法,特别是RTP时间戳同步。内容包括RTP媒体间同步的实现,为什么需要RTCP的NTP时间来实现媒体间同步?没有RTCP,能实现RTP媒体间的同步吗?DirectShow时间戳和RTP时间戳的区别,MPEG2-TS流的时间戳等。本文只简单讨论时间戳同步的原理,不涉及具体的实现方法,如音频帧和视频帧时间戳的计算方法,怎样根据时间戳去做音视频的呈现等。
根据RTP规范,不同的RTP媒体流是分开传输的,且使用各自独立的时间戳进行同步。假设在一次视频点播中,传输两路RTP媒体流,一路视频,一路音频。根据视频帧时间戳,可以实现视频流内同步,这很好理解,通过视频帧时间戳可以计算出相邻视频帧的时间间隔,也就是视频帧之间的相对时间关系很容易通过时间戳来确定,按照这个间隔去呈现视频,就可以获得较好的效果。同理,音频流也可以实现自身的同步。
那么音频和视频这两路媒体间如何实现同步呢?我们只使用音视频的RTP时间戳,看能否实现媒体间的同步。音视频的RTP时间戳的增长速率一般是不同的,但没关系,知道了具体的单位后,两者是可以通过单位换算联系起来的。如下图:
现在来看,这种方法好像可以实现同步,因为音视频被映射到同一个时间轴上了,音频和视频帧间的相对关系很清楚。慢着,RTP规范要求时间戳的初始值应该是一个随机值,那么假设音频帧时间戳的初始值是随机值1234,视频帧时间戳的初始值是随机值5678,看起来应该是下面这样:
这么做合适吗?我们把音频帧时间戳1234和视频帧时间戳5678对应到绝对时间轴的0上,我们这么做的理由是什么?你可能会说,因为那是第一个音频帧和第一个视频帧,所以可以对应到同一个点上,在第一幅图中我们就是这么做的,把音频帧时间戳0和视频帧时间戳0对应到绝对时间轴的0上。但是RTP规范并没有规定第一个视频帧的时间戳和第一个音频帧的时间戳必须或者应该对应到绝对时间轴的同一个点上,从整个RTP规范中不能直接得出这样的结论,也推导不出这样的结论。
我们上面两幅图所做的转换是不正确的,为什么呢?因为在做转换时,隐含了一个假设,我们想当然地认为这个假设是成立的,实际上它并不总是成立。这个假设就是第一个视频帧和第一个音频帧的时间戳应该对应到同一个点上,即无论它们时间戳是多少,都应该在同一时间播放。
仅仅使用RTP时间戳是无法实现媒体间同步的,根本的原因是音频时间轴和视频时间轴是完全独立的,通过音频帧和视频帧的时间戳,无法确定一个视频帧和一个音频帧的相对时间关系,也就是无法把它们都准确定位在绝对时间轴上,只能准确定位一个。
要实现RTP媒体间同步,需要借助于RTCP,在RTCP的SR包中,包含有<NTP时间,RTP时间戳>对,音频帧RTP时间戳和视频帧RTP时间戳通过<NTP时间,RTP时间戳>对,都可以准确定位到绝对时间轴NTP上,音频帧和视频帧的相对时间关系就可以确定下来了。
上面提到,我们的那个隐含的假设并不总是成立,那就是说它有成立的时候。那是不是说当它成立时,我们就可以不用RTCP来做媒体间同步了?答案是,基本上可以这么认为。
例如,对于RTP实时流,在发送端媒体间就同步的很好,在接收端只需做少许处理,不需要RTCP,就可以实现媒体间同步。当然,这只是少数例外。因为RTP规范并不包括这个假设,所以我们还是按照RTP规范来做吧。
下面说一下DirectShow和MPEG2-TS的时间戳。DirectShow中的时间戳和RTP中的时间戳,除了单位不一样,计算方法不一样外,本质的区别就是DirectShow中的音频帧和视频帧时间戳使用的是同一个时间轴,所以不需要借助其他的东西,仅仅使用音频帧时间戳和视频帧时间戳就可以实现媒体间同步。MPEG2-TS流中也有时间戳,它的时间戳和RTP及DirectShow的时间戳都不同,TS流中的音频帧和视频帧时间戳使用的也是同一个时间轴,TS流中的音频和视频是复用的,这在一定程度上就起到了同步的作用,所以它并不是在每个帧上都打时间戳,比如它的PTS时间戳就是每隔0.1秒一个,缺失的时间戳是通过其他时间戳插值计算出来的。
http://www.rosoo.net/a/201101/10776.html
http://hi.baidu.com/fairygardenjoy/blog/item/e56c5cca95829e37b600c88e.html
H264关于RTP协议的实现:http://www.rosoo.net/a/201108/14896.html
RTP协议包头的格式:
10~16 Bit为PT域,指的就是负载类型(PayLoad),负载类型定义了RTP负载的格式,协议原文说该域由具体应用决定其解释。
目前,负载类型主要用来告诉接收端(或者播放器)传输的是哪种类型的媒体(例如G.729,H.264,MPEG-4等),这样接收端(或者播放器)才知道了数据流的格式,才会调用适当的编解码器去解码或者播放,这就是负载类型的主要作用。
就ORTP库而言,负载类型定义如下:
每一种负载类型都有着其独特的参数,这里基本上涵盖了当前主流的一些媒体类型,例如pcmu 、g.729、h.263(很奇怪,竟然没有定义h.264)、mpeg-4等等。Jrtplib库应该也有相类似的定义,你可以去找找源码,在此我就不再赘述了。
在ORTP库和JRTplib库中,都提供了设置RTP负载类型的函数,千万要记得根据实际的应用进行设置,我就是当时没有注意,使用ORTP默认的pcmu音频的负载类型,传输H.264编码的视频数据,结果传输中一直有问题,困扰我好久好久。
好了,再说说RTP的时间戳吧。
基本概念:
时间戳单位:时间戳计算的单位不是秒之类的单位,而是由采样频率所代替的单位,这样做的目的就是为了是时间戳单位更为精准。比如说一个音频的采样频率为8000Hz,那么我们可以把时间戳单位设为1 / 8000。
时间戳增量:相邻两个RTP包之间的时间差(以时间戳单位为基准)。
采样频率: 每秒钟抽取样本的次数,例如音频的采样率一般为8000Hz
帧率: 每秒传输或者显示帧数,例如25f/s
再看看RTP时间戳课本中的定义:
RTP包头的第2个32Bit即为RTP包的时间戳,Time Stamp ,占32位。
时间戳反映了RTP分组中的数据的第一个字节的采样时刻。在一次会话开始时的时间戳初值也是随机选择的。即使是没有信号发送时,时间戳的数值也要随时间不断的增加。接收端使用时间戳可准确知道应当在什么时间还原哪一个数据块,从而消除传输中的抖动。时间戳还可用来使视频应用中声音和图像同步。
在RTP协议中并没有规定时间戳的粒度,这取决于有效载荷的类型。因此RTP的时间戳又称为媒体时间戳,以强调这种时间戳的粒度取决于信号的类型。例如,对于8kHz采样的话音信号,若每隔20ms构成一个数据块,则一个数据块中包含有160个样本(0.02×8000=160)。因此每发送一个RTP分组,其时间戳的值就增加160。
官方的解释看懂没?没看懂?没关系,我刚开始也没看懂,那就听我的解释吧。
首先,时间戳就是一个值,用来反映某个数据块的产生(采集)时间点的,后采集的数据块的时间戳肯定是大于先采集的数据块的。有了这样一个时间戳,就可以标记数据块的先后顺序。
第二,在实时流传输中,数据采集后立刻传递到RTP模块进行发送,那么,其实,数据块的采集时间戳就直接作为RTP包的时间戳。
第三,如果用RTP来传输固定的文件,则这个时间戳就是读文件的时间点,依次递增。这个不再我们当前的讨论范围内,暂时不考虑。
第四,时间戳的单位采用的是采样频率的倒数,例如采样频率为8000Hz时,时间戳的单位为1 / 8000 ,在Jrtplib库中,有设置时间戳单位的函数接口,而ORTP库中根据负载类型直接给定了时间戳的单位(音频负载1/8000,视频负载1/90000)
第五,时间戳增量是指两个RTP包之间的时间间隔,详细点说,就是发送第二个RTP包相距发送第一个RTP包时的时间间隔(单位是时间戳单位)。
如果采样频率为90000Hz,则由上面讨论可知,时间戳单位为1/90000,我们就假设1s钟被划分了90000个时间块,那么,如果每秒发送25帧,那么,每一个帧的发送占多少个时间块呢?当然是 90000/25 = 3600。因此,我们根据定义“时间戳增量是发送第二个RTP包相距发送第一个RTP包时的时间间隔”,故时间戳增量应该为3600。
在Jrtplib中好像不需要自己管理时间戳的递增,由库内部管理。但在ORTP中每次数据的发送都需要自己传入时间戳的值,即自己需要每次发完一个RTP包后,累加时间戳增量,不是很方便,这就需要自己对RTP的时间戳有比较深刻地理解,我刚开始就是因为没搞清楚,随时设置时间戳增量导致传输一直有问题,困扰我好久。
===========================================================================
在使用JRTPLIB的发送数据的时候需要设置时间戳单位(timestamp)和时间戳增量(timestampincrement)。看了网上一些文章,细细想来现在才想通这个问题。
RFC3550对时间戳的描述是:
时间戳(timestamp) 32比特 时间戳反映了RTP数据包中第一个字节的采样时间。(采样时钟必须来源于一个及时的单调、线性递增时钟,以便允许同步和去除网络引起的数据包抖动(见章节6.4.1)。该时钟的分辨率必须满足理想的同步精度和测量数据包到来时的抖动的需要(一种典型的时钟分辨率不满足情况是每个视频帧仅一个时钟周期)时钟频率依赖于负载数据的格式,并在描述文件(profile)中或者是在负载格式描述中(payload format speci_cation)进行静态描述。也可以通过非RTP方法(non-RTP means)对负载格式动态描述。
如果RTP包是周期性产生的,那么将使用由采样时钟决定的名义上的采样时刻,而不是读取系统时间。例如,对一个固定速率的音频,采样时钟(时间戳时钟)将在每个周期内增加1。如果一个音频从输入设备中读取含有160个采样周期的块,那么对每个块,时间戳的值增加160,而不考虑该块是否用一个包传递或是被丢弃。
时间戳的初始值应当是随机的,就像序号一样。几个连续的RTP包如果(逻辑上)是同时产生的,如:属于同一个视频帧的RTP包,将有相同的序列号。如果数据并不是以它采样的顺序进行传输,那么连续的RTP包可以包含不是单调递增(或递减)的时间戳(RTP包的序列号仍然是单调变化的)。
根据一些文章我自己推敲了一下几个概念如下:
时间戳单位:时间戳计算的单位不为秒之类的单位,而是由采样频率所代替的单位,这样做的目的就是为了是时间戳单位更为精准。比如说一个音频的采样频率为8000HZ,那么我们可以把时间戳单位设为1/8000。
时间戳增量:相邻两个RTP包之间的时间差(以时间戳单位为基准)。
如何设定时间戳之间的增量呢?
按照刚才时间戳单位来看,1秒钟按照时间戳单位就是8000,那么一秒钟如果可以播放20帧,也就是发送30帧(帧率),那么可以求出相邻两帧之间的时间差,也就是时间戳增量,那么显而易见是用8000/20,那么这个时间戳增量就为400.
网上大多数列举的一个例子是:例如MPEG,每帧20ms,采样频率8000Hz,设定时间戳单位1/8000,而每个包之间就是160的增量
这里又该如何理解呢?可以轻易地看出增量是直接8000与20ms相乘的结果,我们可以知道这里两帧之间的时间为20ms,也就是0.02s,这个单位是以秒来衡量的,那么我们要用时间戳单位来表示那么就是8000*0.02=160.所以时间戳增量为160.
还有一点为什么一般都用90000作为视频采样频率呢?
90k是用于视频同步的时间尺度(TimeScale),就是每秒90k个时钟tick。为什么采用90k呢?目前视频的帧速率主要有25fps、29.97fps、30fps等,而90k刚好是它们的倍数,所以就采用了90k。
下文转自:http://blog.csdn.net/denny_233/article/details/6733121
再看看RTP时间戳课本中的定义:
RTP包头的第2个32Bit即为RTP包的时间戳,Time Stamp ,占32位。
时间戳反映了RTP分组中的数据的第一个字节的采样时刻。在一次会话开始时的时间戳初值也是随机选择的。即使是没有信号发送时,时间戳的数值也要随时间不断的增加。接收端使用时间戳可准确知道应当在什么时间还原哪一个数据块,从而消除传输中的抖动。时间戳还可用来使视频应用中声音和图像同步。
在RTP协议中并没有规定时间戳的粒度,这取决于有效载荷的类型。因此RTP的时间戳又称为媒体时间戳,以强调这种时间戳的粒度取决于信号的类型。例如,对于8kHz采样的话音信号,若每隔20ms构成一个数据块,则一个数据块中包含有160个样本(0.02×8000=160)。因此每发送一个RTP分组,其时间戳的值就增加160。
官方的解释看懂没?没看懂?没关系,我刚开始也没看懂,那就听我的解释吧。