H264 on rtp

18
H.264 H.264 分层结构由五层组成,分别是序列参数集、图像参数集、片( Slice )、和宏块和子块。 参数集是一个独立的数据单位,不依赖于参数集外的其它句法元素。图 2 描述了参数集与参 数集外的句法元素之间的关系。 一个参数集不对应某一个特定的图像或序列,同一序列参数集可以被多个图像参数集引用 同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时 才会发出新的参数集。 H.264 中,图像以序列为单位进行组织。一个序列的第一个图像叫做 IDR 图像, IDR 图像 都是 I 帧, H.264 引入 IDR 图像为了解码的同步,当解码器解码到 IDR 图像时,立即将参 考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这

Transcript of H264 on rtp

H.264

H.264 分层结构由五层组成,分别是序列参数集、图像参数集、片(Slice)、和宏块和子块。参数集是一个独立的数据单位,不依赖于参数集外的其它句法元素。图 2 描述了参数集与参数集外的句法元素之间的关系。

一个参数集不对应某一个特定的图像或序列,同一序列参数集可以被多个图像参数集引用,同理,同一个图像参数集也可以被多个图像引用。只在编码器认为需要更新参数集的内容时,才会发出新的参数集。

在 H.264 中,图像以序列为单位进行组织。一个序列的第一个图像叫做 IDR 图像,IDR 图像都是 I 帧,H.264 引入 IDR 图像为了解码的同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这

样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR 图像之后的图像永远不会使用 IDR 之前的图像的数据来解码。IDR 是 I 帧,但 I 帧不一定是 IDR。I 帧之后的图像有可能会使用 I 帧之前的图像做运动参考。

编码器将每个 NAL 各自独立、完整地放入一个分组(NALU),因为分组都有头部,解码器可以方便地检测出 NAL 的分界,并依次取出 NAL 进行解码。每个 NAL 前有一个起始码 0x000001,解码器检测每个起始码,作为一个 NAL 的起始标识,当检测到下一个起始码时,当前 NAL 结束。同时 H.264 规定,当检测到 0x000000 时,也可以表征当前 NAL 的结束。对于 NAL 中数据出现 0x000001 或 0x000000 时,H.264 引入了防止竞争机制,如果编码器检测到 NAL 数据存在 0x000001 或 0x000000 时,编码器会在最后个字节前插入一个新的字节 0x03,这样:

0x000000->0x00000300

0x000001->0x00000301

0x000002->0x00000302

0x000003->0x00000303

解码器检测到 0x000003 时,把 03 抛弃,恢复原始数据。解码器在解码时,首先逐个字节读取 NAL 的数据,统计 NAL 的长度,然后再开始解码。

NALU 头由一个字节组成, 它的语法如下:

+---------------+

|0|1|2|3|4|5|6|7|

+-+-+-+-+-+-+-+-+

|F|NRI| Type |

+---------------+

F: 1 个比特.

forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.

NRI: 2 个比特.

nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像的回放. 不过一般情况下不太关心这个属性.

Type: 5 个比特.

nal_unit_type. 这个 NALU 单元的类型. 简述如下:

0 没有定义 1-23 NAL 单元 单个 NAL 单元包.

24 STAP-A 单一时间的组合包 25 STAP-B 单一时间的组合包 26 MTAP16 多个时间的组合包 27 MTAP24 多个时间的组合包 28 FU-A 分片的单元 29 FU-B 分片的单元 30-31 没有定义.

24...29 的类型在 RTP 传输 H.264 数据时使用了.

实际 DVR 抓包数据,

00 00 01 67 SPS

00 00 01 68 PPS

00 00 01 06 SEI

00 00 01 65 IDR Slice.这是抓取的 HI DVR 的一个 IDR 帧的开头RTP 传输 H.264

RTP 头

负载类型 Payload type (PT): 7 bits

序列号 Sequence number (SN): 16 bits

时间戳 Timestamp: 32 bits

对于 H.264 来说说 PT = 96

H.264 Payload 格式定义了三种不同的基本的负载 (Payload)结构. 接收端可能通过 RTP

Payload 的第一个字节来识别它们. 这一个字节类似 NALU 头的格式, 而这个头结构的 NAL

单元类型字段则指出了代表的是哪一种结构,

这个字节的结构如下, 可以看出它和 H.264 的 NALU 头结构是一样的.

+---------------+

|0|1|2|3|4|5|6|7|

+-+-+-+-+-+-+-+-+

|F|NRI| Type |

+---------------+

字段 Type: 这个 RTP payload 中 NAL 单元的类型. 这个字段和 H.264 中类型字段的区别是,

当 type

的值为 24 ~ 31 表示这是一个特别格式的 NAL 单元, 而 H.264 中, 只取 1~23 是有效的值.

24 STAP-A 单一时间的组合包

25 STAP-B 单一时间的组合包

26 MTAP16 多个时间的组合包

27 MTAP24 多个时间的组合包

28 FU-A 分片的单元

29 FU-B 分片的单元

30-31 没有定义

可能的结构类型分别有:

1. 单一 NAL 单元模式

即一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的

H.264 的 NALU 头类型字段是一样的.

对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式.对于一个原始的

H.264 NALU 单元常由 [Start Code] [NALU Header] [NALU Payload] 三部分组成, 其中 Start

Code 用于标示这是一个 NALU 单元的开始, 必须是 "00 00 00 01" 或 "00 00 01", NALU 头仅

一个字节, 其后都是 NALU 单元内容.打包时去除 "00 00 01" 或 "00 00 00 01" 的开始码, 把其

他数据封包的 RTP 包即可

如有一个 H.264 的 NALU 是这样的:

[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]

这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开

始的数据是 NALU 内容.

封装成 RTP 包将如下:

[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]

即只要去掉 4 个字节的开始码就可以了.

上面截图是抓包软件抓的 SPS 的单一封包.第 12 个直接是 0x67 SPS

PPS 的单一封包. 第 12 个字节=0x68,表示是 PPS

2. 组合封包模式

即可能是由多个 NAL 单元组成一个 RTP 包. 分别有 4 种组合方式: STAP-A, STAP-B,

MTAP16, MTAP24.那么这里的类型值分别是 24, 25, 26 以及 27.

其次, 当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.在实际的码流

中没有使用

3. 分片封包模式

用于把一个 NALU 单元封装成多个 RTP 包. 存在两种类型 FU-A 和 FU-B. 类型值分别是

28 和 29.而当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包. 也称为

Fragmentation Units (FUs).

The FU indicator octet has the following format:

+---------------+

|0|1|2|3|4|5|6|7|

+-+-+-+-+-+-+-+-+

|F|NRI| Type |

+---------------+

The FU header has the following format:

+---------------+

|0|1|2|3|4|5|6|7|

+-+-+-+-+-+-+-+-+

|S|E|R| Type |

+---------------+

实际抓的编码器的码流.一秒钟 15 帧,可以看到间隔了 65ms,这是两个 P 帧,数据量不大.

打开 P 帧的一个包看.第 12 字节为 FU_Indicator: Type= 0x1c =28,分片

第 13 字节:FU_Header.S =1 ,分片开始.Type = 0x01, 非 IDR 片.

抓的一个 I 帧的码流. 长度为 69 和 64 是 SPS 和 PPS,另外看到一帧数据量大有很多个数据包.

IDR 帧数据起始 Fragment FU_Indicator=0x7c ,type= 0x1c=28,分片.

FU_Header= 0x85 S=1,起始 Fragment.Type=0x05.IDR Slice

S=1

中间 Fragment

FU_Indicator=0x7c ,type= 0x1c=28,分片.

FU_Header= 0x08 S=0,E=0,中间 Fragment,Type=0x08(这个值似乎不对,应该是 0x05)

结尾 Fragment.

FU_Indicator=0x7c ,type= 0x1c=28,分片.

FU_Header= 0x45 S=0 ,E=1,结束 Fragment.Type=0x05.IDR Slice

3. SDP 参数

下面描述了如何在 SDP 中表示一个 H.264 流:

. "m=" 行中的媒体名必须是 "video"

. "a=rtpmap" 行中的编码名称必须是 "H264".

. "a=rtpmap" 行中的时钟频率必须是 90000.

. 其他参数都包括在 "a=fmtp" 行中.

如:

m=video 49170 RTP/AVP 98

a=rtpmap:98 H264/90000

a=fmtp:98 profile-level-id=42A01E; sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==

下面介绍一些常用的参数.

3.1 packetization-mode:

表示支持的封包模式.

当 packetization-mode 的值为 0 时或不存在时, 必须使用单一 NALU 单元模式.

当 packetization-mode 的值为 1 时必须使用非交错(non-interleaved)封包模式.

当 packetization-mode 的值为 2 时必须使用交错(interleaved)封包模式.

这个参数不可以取其他的值.

3.2 sprop-parameter-sets:

这个参数可以用于传输 H.264 的序列参数集和图像参数 NAL 单元. 这个参数的值采用

Base64 进行编码. 不同的参数集间用","号隔开.

3.3 profile-level-id:

这个参数用于指示 H.264 流的 profile 类型和级别. 由 Base16(十六进制) 表示的 3 个字节.

第一个字节表示 H.264 的 Profile 类型, 第

三个字节表示 H.264 的 Profile 级别:

3.4 max-mbps:

这个参数的值是一个整型, 指出了每一秒最大的宏块处理速度.

1.1. RTP 是什么

RTP 全名是 Real-time Transport Protocol(实时传输协议)。它是 IETF提出的一个标准,对应的 RFC文档为 RFC3550(RFC1889 为其过期版本)。RFC3550 不仅定义了 RTP,而且定义了配套的相关协议 RTCP(Real-time Transport Control Protocol,即实时传输控制协议)。RTP 用来为 IP网上的语音、图像、传真等多种需要实时传输的多媒体数据提供端到端的实时传输服务。RTP 为 Internet上端到端的实时传输提供时间信息和流同步,但并不保证服务质量,服务质量由 RTCP 来提供。

1.2. RTP 的应用环境

RTP 用于在单播或多播网络中传送实时数据。它们典型的应用场合有如下几个。简单的多播音频会议。语音通信通过一个多播地址和一对端口来实现。一个用于音频数

据(RTP),另一个用于控制包(RTCP)。音频和视频会议。如果在一次会议中同时使用了音频和视频会议,这两种媒体将分别在

不同的 RTP 会话中传送,每一个会话使用不同的传输地址(IP 地址+端口)。如果一个用户同时使用了两个会话,则每个会话对应的 RTCP 包都使用规范化名字CNAME(Canonical Name)。与会者可以根据 RTCP 包中的 CNAME 来获取相关联的音频和视频,然后根据 RTCP 包中的计时信息(Network time protocol)来实现音频和视频的同步。翻译器和混合器。翻译器和混合器都是 RTP级的中继系统。翻译器用在通过 IP 多播不能

直接到达的用户区,例如发送者和接收者之间存在防火墙。当与会者能接收的音频编码格式不一样,比如有一个与会者通过一条低速链路接入到高速会议,这时就要使用混合器。在进入音频数据格式需要变化的网络前,混合器将来自一个源或多个源的音频包进行重构,并把重构后的多个音频合并,采用另一种音频编码进行编码后,再转发这个新的 RTP 包。从一个混合器出来的所有数据包要用混合器作为它们的同步源(SSRC,见 Error: Reference

source not foundRTP 的封装)来识别,可以通过贡献源列表(CSRC 表,见 Error: Reference

source not foundRTP 的封装)可以确认谈话者。

1.3. 相关概念

1.3.1. 流媒体

流媒体是指 Internet上使用流式传输技术的连续时基媒体。当前在 Internet上传输音频和视频等信息主要有两种方式:下载和流式传输两种方式。

下载情况下,用户需要先下载整个媒体文件到本地,然后才能播放媒体文件。在视频直播等应用场合,由于生成整个媒体文件要等直播结束,也就是用户至少要在直播结束后才能看到直播节目,所以用下载方式不能实现直播。流式传输是实现流媒体的关键技术。使用流式传输可以边下载边观看流媒体节目。由于

Internet 是基于分组传输的,所以接收端收到的数据包往往有延迟和乱序(流式传输构建在UDP上)。要实现流式传输,就是要从降低延迟和恢复数据包时序入手。在发送端,为降低延迟,往往对传输数据进行预处理(降低质量和高效压缩)。在接收端为了恢复时序,采用了接收缓冲;而为了实现媒体的流畅播放,则采用了播放缓冲。

使用接收缓冲,可以将接收到的数据包缓存起来,然后根据数据包的封装信息(如包序号和时戳等),将乱序的包重新排序,最后将重新排序了的数据包放入播放缓冲播放。

为什么需要播放缓冲呢?容易想到,由于网络不可能很理想,并且对数据包排序需要处理时耗,我们得到排序好的数据包的时间间隔是不等的。如果不用播放缓冲,那么播放节目会很卡,这叫时延抖动。相反,使用播放缓冲,在开始播放时,花费几十秒钟先将播放缓冲填满(例如 PPLIVE),可以有效地消除时延抖动,从而在不太损失实时性的前提下实现流媒体的顺畅播放。

到目前为止,Internet上使用较多的流式视频格式主要有以下三种:RealNetworks公司的RealMedia ,Apple公司的 QuickTime 以及Microsoft公司的 Advanced Streaming Format

(ASF)。上面在谈接收缓冲时,说到了流媒体数据包的封装信息(包序号和时戳等),这在后

面的 RTP封装中会有体现。另外,RealMedia 这些流式媒体格式只是编解码有不同,但对于RTP 来说,它们都是待封装传输的流媒体数据而没有什么不同。

第 2章. RTP详解

2.1. RTP 的协议层次

2.1.1. 传输层的子层

RTP(实时传输协议),顾名思义它是用来提供实时传输的,因而可以看成是传输层的一个子层。图 1给出了流媒体应用中的一个典型的协议体系结构。

图 1 流媒体体系结构从图中可以看出,RTP 被划分在传输层,它建立在 UDP上。同 UDP协议一样,为了实

现其实时传输功能,RTP 也有固定的封装形式。RTP 用来为端到端的实时传输提供时间信息和流同步,但并不保证服务质量。服务质量由 RTCP 来提供。这些特点,在第 4章可以看到。

2.1.2. 应用层的一部分

不少人也把 RTP归为应用层的一部分,这是从应用开发者的角度来说的。操作系统中的 TCP/IP等协议栈所提供的是我们最常用的服务,而 RTP 的实现还是要靠开发者自己。因

此从开发的角度来说,RTP 的实现和应用层协议的实现没不同,所以可将 RTP 看成应用层协议。

RTP 实现者在发送 RTP 数据时,需先将数据封装成 RTP 包,而在接收到 RTP 数据包,需要将数据从 RTP 包中提取出来。

2.2. RTP 的封装

一个协议的封装是为了满足协议的功能需求的。从前面提出的功能需求,可以推测出RTP封装中应该有同步源和时戳等字段,但更为完整的封装是什么样子呢?请看图 2。

图 2 RTP 的头部格式版本号(V):2 比特,用来标志使用的 RTP版本。填充位(P):1 比特,如果该位置位,则该 RTP 包的尾部就包含附加的填充字节。扩展位(X):1 比特,如果该位置位的话,RTP固定头部后面就跟有一个扩展头部。CSRC 计数器(CC):4 比特,含有固定头部后面跟着的 CSRC 的数目。标记位(M):1 比特,该位的解释由配置文档(Profile)来承担.

载荷类型(PT):7 比特,标识了 RTP 载荷的类型。序列号(SN):16 比特,发送方在每发送完一个 RTP 包后就将该域的值增加 1,接收

方可以由该域检测包的丢失及恢复包序列。序列号的初始值是随机的。时间戳:32 比特,记录了该包中数据的第一个字节的采样时刻。在一次会话开始时,

时间戳初始化成一个初始值。即使在没有信号发送时,时间戳的数值也要随时间而不断地增加(时间在流逝嘛)。时间戳是去除抖动和实现同步不可缺少的。

同步源标识符(SSRC):32 比特,同步源就是指 RTP 包流的来源。在同一个 RTP 会话中不能有两个相同的 SSRC 值。该标识符是随机选取的 RFC1889推荐了 MD5随机算法。贡献源列表(CSRC List):0~15项,每项 32 比特,用来标志对一个 RTP混合器产

生的新包有贡献的所有 RTP 包的源。由混合器将这些有贡献的 SSRC 标识符插入表中。SSRC

标识符都被列出来,以便接收端能正确指出交谈双方的身份。

2.3. RTCP 的封装

RTP 需要 RTCP 为其服务质量提供保证,因此下面介绍一下 RTCP 的相关知识。RTCP 的主要功能是:服务质量的监视与反馈、媒体间的同步,以及多播组中成员的标

识。在 RTP 会话期 间,各参与者周期性地传送 RTCP 包。RTCP 包中含有已发送的数据包的数量、丢失的数据包的数量等统计资料,因此,各参与者可以利用这些信息动态地改变传输

速率,甚至改变有效载荷类型。RTP 和 RTCP配合使用,它们能以有效的反馈和最小的开销使传输效率最佳化,因而特别适合传送网上的实时数据。从图 1 可以看到,RTCP 也是用 UDP 来传送的,但 RTCP封装的仅仅是一些控制信息,

因而分组很短,所以可以将多个 RTCP 分组封装在一个 UDP 包中。RTCP 有如下五种分组类型。

类型 缩写表示 用途200 SR(Sender Report) 发送端报告201 RR(Receiver Report) 接收端报告202 SDES ( Source Description

Items)源点描述

203 BYE 结束传输204 APP 特定应用

表 1 RTCP 的 5 种分组类型上述五种分组的封装大同小异,下面只讲述 SR 类型,而其它类型请参考 RFC3550。发送端报告分组 SR(Sender Report)用来使发送端以多播方式向所有接收端报告发送

情况。SR 分组的主要内容有:相应的 RTP流的 SSRC,RTP流中最新产生的 RTP 分组的时间戳和 NTP,RTP流包含的分组数,RTP流包含的字节数。SR 包的封装如图 3所示。

图 3RTCP 头部的格式版本(V):同 RTP 包头域。

填充(P):同 RTP 包头域。接收报告计数器(RC):5 比特,该 SR 包中的接收报告块的数目,可以为零。包类型(PT):8 比特,SR 包是 200。长度域(Length):16 比特,其中存放的是该 SR 包以 32 比特为单位的总长度减一。同步源(SSRC):SR 包发送者的同步源标识符。与对应 RTP 包中的 SSRC 一样。NTP Timestamp(Network time protocol)SR 包发送时的绝对时间值。NTP 的作用是同

步不同的 RTP媒体流。RTP Timestamp:与 NTP 时间戳对应,与 RTP 数据包中的 RTP 时间戳具有相同的单位

和随机初始值。Sender’s packet count:从开始发送包到产生这个 SR 包这段时间里,发送者发送的 RTP

数据包的总数. SSRC改变时,这个域清零。Sender`s octet count:从开始发送包到产生这个 SR 包这段时间里,发送者发送的净荷

数据的总字节数(不包括头部和填充)。发送者改变其 SSRC 时,这个域要清零。同步源 n 的 SSRC 标识符:该报告块中包含的是从该源接收到的包的统计信息。丢失率(Fraction Lost):表明从上一个 SR 或 RR 包发出以来从同步源 n(SSRC_n)来的

RTP 数据包的丢失率。累计的包丢失数目:从开始接收到 SSRC_n 的包到发送 SR,从 SSRC_n 传过来的 RTP 数

据包的丢失总数。收到的扩展最大序列号:从 SSRC_n 收到的 RTP 数据包中最大的序列号,

接收抖动(Interarrival jitter):RTP 数据包接受时间的统计方差估计上次 SR 时间戳(Last SR,LSR):取最近从 SSRC_n 收到的 SR 包中的 NTP 时间戳的

中间 32 比特。如果目前还没收到 SR 包,则该域清零。上次 SR 以来的延时(Delay since last SR,DLSR):上次从 SSRC_n 收到 SR 包到发送

本报告的延时。

2.4. RTP 的会话过程

当应用程序建立一个 RTP 会话时,应用程序将确定一对目的传输地址。目的传输地址由一个网络地址和一对端口组成,有两个端口:一个给 RTP 包,一个给 RTCP 包,使得RTP/RTCP 数据能够正确发送。RTP 数据发向偶数的 UDP 端口,而对应的控制信号 RTCP 数据发向相邻的奇数 UDP 端口(偶数的 UDP 端口+1),这样就构成一个 UDP 端口对。 RTP

的发送过程如下,接收过程则相反。1) RTP协议从上层接收流媒体信息码流(如 H.263),封装成 RTP 数据包;RTCP

从上层接收控制信息,封装成 RTCP控制包。2) RTP 将 RTP 数据包发往UDP 端口对中偶数端口;RTCP 将 RTCP控制包发往UDP 端口对中的接收端口。

第 3章. 相关的协议

3.1. 实时流协议 RTSP

实时流协议 RTSP(Real-Time Streaming Protocol)是 IETF提出的协议,对应的 RFC

文档为 RFC2362。从图 1 可以看出,RTSP 是一个应用层协议(TCP/IP网络体系中)。它以 C/S 模式工作,

它是一个多媒体播放控制协议,主要用来使用户在播放流媒体时可以像操作本地的影碟机一样进行控制,即可以对流媒体进行暂停/继续、后退和前进等控制。

3.2. 资源预定协议 RSVP

资源预定协议 RSVP(Resource Reservation Protocol)是 IETF提出的协议,对应的 RFC

文档为 RFC2208。从图 1 可以看出,RSVP工作在 IP 层之上传输层之下,是一个网络控制协议。RSVP 通

过在路由器上预留一定的带宽,能在一定程度上为流媒体的传输提供服务质量。在某些试验性的系统如网络视频会议工具 vic 中就集成了 RSVP。

第 4章. 常见的疑问

4.1. 怎样重组乱序的数据包

可以根据 RTP 包的序列号来排序。

4.2. 怎样获得数据包的时序

可以根据 RTP 包的时间戳来获得数据包的时序。

4.3. 声音和图像怎么同步

根据声音流和图像流的相对时间(即 RTP 包的时间戳),以及它们的绝对时间(即对应的 RTCP 包中的 RTCP),可以实现声音和图像的同步。

4.4. 接收缓冲和播放缓冲的作用

如 1.3.1所述,接收缓冲用来排序乱序了的数据包;播放缓冲用来消除播放的抖动,实现等时播放。

第 5章. 实现方案

ID Protocol Captured contentsAccount password Local

telephonenumber

OpponentsTelephone

Number

audio login logout

36 Rtp √

表 2 协议分析要求表 2给出了协议分析要求。容易看出要获取 RTP音频包中的音频信息很容易,直接将

RTP 包的包头去掉即可。当然,要成功地播放解码获取到的音频流,需要知道其编码,这可从 RTP 包包头的有效载荷类型字段(PT)获得。

RTP 传输中的负载类型和时间戳.

首先,看 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 的时间戳有比较深刻地理解,我刚开始就是因为没搞清楚,随时设置时间戳增量导致传输一直有问题,困扰我好久。 好了,关于 RTP 的负载类型和时间戳的介绍就到这里了,这次通过解决 RTP 传输中的问题学到了不少知识,在此分享希望对大家有用。有说得不正确的地方欢迎高手指教,也可以来信交流: