RTP时间戳概念

2023-11-12

RTP协议不依赖于底层协议,由于UDP包的快速、时实性高的特点,它通常和UDP结合在一起,作为UDP的上层载体数据的形式传播。

 typedef struct _rtp_header_t
{
    uint32_t v:2;   /* protocol version */
    uint32_t p:1;   /* padding flag */
    uint32_t x:1;   /* header extension flag */
    uint32_t cc:4;   /* CSRC count */
    uint32_t m:1;   /* marker bit */
    uint32_t pt:7;   /* payload type */
    uint32_t seq:16; /* sequence number */
    uint32_t timestamp; /* timestamp */
    uint32_t ssrc; /* synchronization source */
} rtp_header_t;

这是一个RTP头,很简单,并没有你想象的那么复杂,对不对?我们来看几个主要的参数,他们也是RTP的灵魂:
(1)payload。payload表示了此RTP包的数据是那种类型的数据,不同的数值表示不同的类型。如0是PCMU,8是723,24是视频263等等。
(2)SSRC,这个东西并不常用,实际上它是一个随即生成的ID,表示了一个RTP连接。在应用的时候,确保这个ID唯一就可以了。
(3)sequence number。也就是序列号,它表示了当前包是第几个包。发送方每发送一个包,就把这个数值加一。接受放可以根据这个数值来重新组合包顺序,判断包是否丢失等操作。注意:它只是表示了包的先后顺序,它不能表示时间上的任何其它信息。这个请和后面的时间戳比较。
(4)timestamp。时间戳,它的概念稍微有点复杂,我用稍微通俗点的理解去解释它,虽然这样有点不太正确。时间戳顾名思义,它表示了一个数据产生的时间,和我们邮递的邮戳一样,它是个时间标记(至于这个时间干什么用,我后面会详细的说),通常表示RTP数据包中,第一个字节数据产生的时间(至于你是不是这么用就是你写程序的问题了)。
如果你上面理解了,那么我们更进一步:实际上,时间戳增加一并不是我们通常意义上的过了一个微秒,而是增加了一个采样间隔那么长的时间。举个例子来说。不同的采集有不同的采样频率,比如一般的音频是8K的采样频率,也就是一毫秒采集8次数据,也就是每次采样间隔是1/8MS,而timestamp增加1也就意味着增加了一个采样间隔。也就是过了1/8MS。换个例子,如果令一种编码的采样频率是16K,那么timestamp增加1也就意味着系统过了1/16MS。也就是说,再同一个系统中,对不同编码,虽然使用同一个时钟,但timestamp的增长速度是不同的,在这个例子中,采样频率是16K的编码要比8K的快两倍,请记住这个区别。

RTCP
RTCP协议是real-time transport control protocol的缩写,被设计来做RTP的控制,这个相对来说大家不怎么关心,我只介绍下它基本的东西。
RTCP实际上是RTP传输情况的反馈,通俗的说,它告诉另外一方,在一端时间内(5秒),它发送多少数据包给对方,接收到了多少对方的包。
另外,在RTCP中,还有两个比较重要的东西,一个64位的绝对时间戳和一个32位的相对时间戳。64 位时间戳也叫NTP时间戳,它的前32位是从1900 年1 月1 日0 时开始到现在的以秒为单位的整数部分,后32 位是此时间的小数部,因此,它可以肯定的表示了数据发送出去的绝对时间。32位的时间戳和RTP中的时间戳是一样的,没有任何区别
1、SSRC的作用。
SSRC相当于一个RTP传输session的ID,就象每个人都有一个名字一样,每一个RTP传输也都有一个名字。这个数字是随机产生,并且要保证唯一。当RTP session改变(如IP等)时,这个ID也要改变。
2、序列号字段是否可以作为流内的同步标时?
我在上面已经说过,序列号只表示了包发出的先后顺序,它表示不了任何时间上的其它概念,所有严格的说,序列号并不能作为流内的同步标志。但是,由于一般来说,包的发送时间都会有严格限制,比如音频包是每秒种发送30个数据包,也就是说,每个包间隔1000/30MS,而这个时间就可以作为一个同步时间来播放。也就是说,按照序列号,每1000/30MS间隔播放一个数据包,这样也能保证同步,但是这时候请考虑丢包问题。
3、绝对时间戳和相对时间戳在进行同步处理时有什么不同
当我们取得绝对时间后,我们就可以根据这个绝对时间来播放这些数据包。这个绝对时间,加上我们需要的延时(用于数据传输,解码等等的时间)就是我们的播放时间,这样我们可以保证时间的严格同步(相当于把对方的动作延时一段时间后原原本本的再现出来)。目前,在RTP中,能得到这个绝对时间的办法只有RTCP。
对于相对时间戳,我们更关心的是两个时间戳之间的时间间隔,依靠这个时间间隔,来确定两个包的播放时间间隔。
4、单个媒体内的同步和不同媒体流之间的同步在处理方式上有什么不同
应该说,不同媒体之间同步比单媒体同步要复杂得多,除了要保证本身的播放要和时间同步外,还要保证两个或多个媒体间同步(比如音视频的同步)。这种不同更关心的两个时间戳时间的换算统一,前面我已经说过,不同编码有不同的采样频率,那么时间戳的增长速度就不同。另外,两个时间戳也需要有一个标准时间来表示时间戳的同步。最简单的方法是两个媒体的第一个时间戳相同,表示两个流的采集开始时间统一。另外还可以通过RTCP来做不同流之间的同步,这在下个问题中会提到。
5、时间戳字段如何用于作为流间同步标识
在RTP协议中,我们取得时间戳的方法有两个:一个是RTP包中的时间戳,另外一个是RTCP包中的绝对时间戳和相对时间戳。绝对时间戳的概念上面我已经说了,它可以表示系统的绝对时间。而RTCP包中的相对时间就是RTP包中的时间。根据这两个时间,不同流都可以纠正自己播放时间和真正时间的偏差以达到和绝对时间同步的目的。反过来说,如果我们没有办法拿到这个绝对时间,只有RTP包中的相对时间,那怎我们需要确定两个流在某一时间点的时间戳的数值。通俗的说,就是在某个时间点,流A的timestamp是多少,B是多少,然后根据这个时间两个流播放的延时时间,以达到同步的目的。实现这个目的最简单的办法是在两个流开始的时候,使用相同的stamp,拿音视频来说,在某一绝对时刻,采集相应的数据,并打上相同的时间戳,以后的播放,都以这个时间做基准时间,以保证同步

三、RTP时间戳相关
通过RTSP建立好会话之后,就可以开始传输RTP数据和RTCP SR包了(用来同步音视频)。
这两者涉及到很重要的问题:时间戳。下面是《rtp_audio_and_video_for_the_internet》上的一个时间图。
TimeStamp的初始值是随即生成的,然后每一帧数据固定增加一个增量,客户端在接收到数据时,根据这个时间戳就能以正确的时间恢复(其中被分包的视频桢是没有时间戳增加的)。RTCP的SR包里面除了这个时间戳,还有一个NTP时间,这是距1900年1月1日的秒数,允许每个系统存在差异,只要同一个系统不同流的该值的生成方式相同就行。以时钟频率为90KHz的视频为例,若其帧率为30帧,则每一帧的时间戳增量为90000/30=3000;RTCP的SR包的时间戳也可以相应计算出来:增量=(现在时间-上一次RTP包发送时间)单位时间增量,其中单位时间增量=900001000000/(232),因为SR包中的微秒时间形式是NTP_frac,因此要做“/(232)”这样一个转化。

RFC中说时间戳增量需要满足线性增长,实际上没必要严格按照诸如3000增量来增长,我是按照实际的帧的时间间隔来打的这个时间戳:
时间戳 = 上一次时间戳 + 采样频率(典型值为90000)0.000001 * 两帧时间差(单位毫秒)**来计算的
**时间戳(timestamp) 32比特 时间戳反映了RTP数据包中第一个字节的采样时间。(采样时钟必须来源于一个及时的单调、线性递增时钟,以便允许同步和去除网络引起的数据包抖动。该时钟的分辨率必须满足理想的同步精度和测量数据包到来时的抖动的需要(一种典型的时钟分辨率不满足情况是每个视频帧仅一个时钟周期)时钟 频率依赖于负载数据的格式,并在描述文件(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。
RTP协议包头的格式:
  10~16 Bit为PT域,指的就是负载类型(PayLoad),负载类型定义了RTP负载的格式,协议原文说该域由具体应用决定其解释。
  目前,负载类型主要用来告诉接收端(或者播放器)传输的是哪种类型的媒体(例如G.729,H.264,MPEG-4等),这样接收端(或者播放器)才知道了数据流的格式,才会调用适当的编解码器去解码或者播放,这就是负载类型的主要作用。
时间戳单位:时间戳计算的单位不是秒之类的单位,而是由采样频率所代替的单位,这样做的目的就是为了是时间戳单位更为精准。比如说一个音频的采样频率为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。
【补充】:最近思考了一下,又有了新的体会和解释,可能对大家更容易地去理解这个时间戳增量会有所帮助,补充在下面吧:
其实,网络发送重点关注的是流量的平衡,即均匀地利用网络带宽,为了实现这一点,需要满足:数据采集的速率与数据网络传输的速率尽量保持一致。时间戳增量的设置影响的是RTP包的网络传输的速率,时间戳增量越小,发送速度越快。
  下面再进一步解释一下时间戳增量是怎么计算出来的:
对于PAL制式的视频而言,每秒摄像头会采集 25 帧 数据,那么,每采集到 1帧 耗时 1/25 s ,如果我们设计为1个RTP包只包含1帧数据,并且一次发送1帧,那么,要想网络流量均匀,则时间戳增量应该设计为 1/25 s . 而在一般的RTP协议的实现中,时间戳单位不是 秒(s),而约定为采样频率的倒数,由于一般视频的采样频率是 90000,故时间戳单位为 1/90000 s,因此,实际的时间戳增量 = 时间戳增量 ( 1/25 s ) / 时间戳单位(1/90000 s) = 3600

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RTP时间戳概念 的相关文章

  • Markdown语法简介

    Markdown是一种方便记忆 书写的纯文本标记语言 用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档 它目标是实现易读易写 Markdown的语法全由一些符号所组成 Markdown语法的目标是成为一种适用于网络的书写语言 M

随机推荐

  • java 重新加载spring_SpringBoot的reload加载器的方法

    背景 出现了相同类castException 分析 首先确定出现相同类的castException比如是由于classloader不同造成的 一个class是否相同取决于两个因素 classloader相同 class文件相同 即不同cla
  • Failed to execute goal org.apache .maven.plugins.maven-resounces-plugin:3.2.0:resounces

    1 在部署微服务系统的服务中心和服务提供者时 在打包的过程中 出现过如下的错误 Failed to execute goal org apache maven plugins maven resounces plugin 3 2 0 res
  • 谁说 AI 编程工具缺乏记忆和联想能力?简单琐碎的需求完全可以交给它

    Amazon CodeWhisperer 是一款 AI 编码配套应用程序 可在 IDE 中生成整行代码和完整的函数代码建议 以帮助您更快地完成更多工作 在本系列文章中 我们将为您详细介绍 Amazon CodeWhisperer 的相关信息
  • 如果虚函数在基类与子类名字相同,而参数类型不同不会进行迟后联编

    class Base public virtual void Show int x cout lt lt In Base class int x lt lt x lt lt endl class Derived public Base pu
  • 2020-08-27

    java 1 编译java程序的命令是javac 该命令的文件是javac exe 2 jsp表达式的写法 3 3 Math round 11 5 为 11 四舍五入是向数值大的方向入 4 float与int做除法运算时 会将int转化为f
  • 基础项目(2)二选一数据选择器的设计

    写在前面的话 数据选择器在数字电路设计中的应用尤为广泛 同时 作为基础的电路功能单元 也比较适合作为初学者的入门实验 现在梦翼师兄陪大家一起来设计一个最基础的数据选择器 项目需求 设计一个二选一数据选择器 然后用一路控制信号选择输出数据选通
  • 区块链中的全节点与轻量级节点

    在加密货币中 凡是连接到该网络的任何计算机 都被称为节点 在区块链中 存在一种冗余备份的现象 就是说 如果所有节点都需要保存全网的所有交易及其他数据信息 则不可避免的会出现一些弊端 比如 用户想创建一个自己的区块链节点进行项目开发 而不需要
  • 华为OD机试 - 返回矩阵中非1的元素个数(Java)

    题目描述 存在一个m n的二维数组 其成员取值范围为0 1 2 其中值为1的元素具备同化特性 每经过1S 将上下左右值为0的元素同化为1 而值为2的元素 免疫同化 将数组所有成员随机初始化为0或2 再将矩阵的 0 0 元素修改成1 在经过足
  • 微信小程序:setdata--数据如何输出到前台

    简介 这一部分我来为大家讲解一下啊 小程序前台wxml页面如何调用后台的数据 其中涉及到的一个方法 setdata 演示 前 wxml
  • Python二级--三国演义分词

    三国演义分词 1 题目一 概述 三国演义 是中国古典四大名著之一 曹操是其中主要人物 考生文件夹下文件data txt给出 三 国演义 简介 问题1 请编写程序 用Python语言中文分词第三方库jieba对文件data txt进行分词 并
  • Flutter SingleChildScrollView要点

    知识要点 SingleChildScrollView类似Android中的ScrollView 只接收一个子组件 SingleChildScrollView this scrollDirection Axis vertical 滚动方向 默
  • Springboot中如何打印sql信息和sql参数信息呢?

    转自 Springboot中如何打印sql信息和sql参数信息呢 下文笔者讲述SpringBoot中设置配置文件 使其可打印出sql信息及参数信息的方法分享 实现思路 springboot打印sql信息及参数 我们可采用修改applicat
  • python:openpyxl基础操作(一):创建.xlsx文件

    目录 前言 安装openpyxl 创建 xlsx文件 添加单元格内容 单个添加 多个添加 增删单元格 遍历表格中的值 合并拆分单元格 前言 本文仅供个人学习记录 复习所用 探索过程中难免出现差错和纰漏 如文中有错误 以及可以改进的地方 还请
  • python中的utils模块_python常用方法utils

    always 目录里主要封装了一些自己常用的方法 1 doc2txt 本函数主要目的是方便在写爬虫的时候遇到word附件 下载后直接提取出文本文件 注意 需要安装antiword mac下 brew install antiword 2 f
  • vue3组件通信方式一

    目录 props 父组件给子组件传递数据 子组件获取父组件传递数据 方式1 子组件获取父组件传递数据 方式2 案例 自定义事件 原生DOM事件 自定义事件 案例 全局事件总线 v model useAttrs 不管是vue2还是vue3 组
  • yolov3测试自己的数据

    yolov3测试自己的数据 前言 上一篇我已经介绍了利用yolov3预训练权重文件 只包含卷积层 并训练 只需要进行如下编译 darknet detector train cfg voc data cfg yolov3 cfg darkne
  • macOS 虚拟桌面黑屏(转)

    转自 macOS重置虚拟桌面 macOS 虚拟桌面黑屏 有几次出现如图的情况 以为是iTerm的问题 但是在关闭软件 重启之后 依旧无效 后面经过网友告知 才知道是虚拟桌面的问题 为了清理这个问题 有以下两种方法 法一 在终端输入以下命令
  • VC++闪退的两种解决方式(1然后选择“开始执行(不调试)“,也就是ctrl+F5;2system(“pause“);)

    这几天将开发集成环境换成了vs2010 刚开始用 有些地方不是很懂 通过查阅相关文档 现得以解决 就拿VS2010 调试窗口一闪而过解决方法如下 cpp view plain copy include
  • DAY2:leetcode977有序数组的平方 209 长度最小的子数组 59螺旋矩阵

    一 双指针法 O n 时间复杂度 有序数组的平方 一维数组初始化 vector
  • RTP时间戳概念

    RTP协议不依赖于底层协议 由于UDP包的快速 时实性高的特点 它通常和UDP结合在一起 作为UDP的上层载体数据的形式传播 typedef struct rtp header t uint32 t v 2 protocol version