此文为系列文章,此系列主要讲解RTSP客户端的拉流及播放,文章持续更新,会从rtsp的基本协议讲起,如何一步步实现音视频的拉流过程,包括一系列涉及到的协议,rtsp,sdp, rtp(本系列文章的核心内容会放在rtp协议,会重点介绍讲解rtp负载部分), rtcp, 从rtp解析aac,h264数据帧,得到帧后如何交给解码库(ffmpeg,libVLC,live555等)进行解码,音视频同步并播放音频和视频,如果内容涉及到TCP和UDP网络通信内容,可以参考:
【Linux编程】一个基于 C++ 的 TCP 客户端异步(epoll)框架(一))
【Linux编程】TcpServer 类的设计与实现:构建高性能的 TCP 服务器(二)
【Linux编程】C++ UDP的UdpClient 类详解与网络通信实现(三))
文章目录
- 1.RTP协议基础
-
- [1.1 RTP协议概述](#1.1 RTP协议概述)
- [1.2 RTP头部结构](#1.2 RTP头部结构)
- [2. RTP负载:音频AAC封装模式](#2. RTP负载:音频AAC封装模式)
-
- [2.1 单帧模式](#2.1 单帧模式)
- [2.2 多帧模式](#2.2 多帧模式)
- [2.3. 分片模式](#2.3. 分片模式)
- 3.RTP负载:音频AAC封装格式
-
- [3.1 AU-headers-length](#3.1 AU-headers-length)
- [3.2 AU-headers](#3.2 AU-headers)
-
- [3.2.1 AU-size](#3.2.1 AU-size)
- [3.2.2 AU-index/AU-index-delta](#3.2.2 AU-index/AU-index-delta)
- [3.2.3 CTS-flag](#3.2.3 CTS-flag)
- [3.2.4 CTS-delta](#3.2.4 CTS-delta)
- [3.2.5 DTS-flag](#3.2.5 DTS-flag)
- [3.2.6 DTS-delta](#3.2.6 DTS-delta)
- [3.2.7 RAP-flag](#3.2.7 RAP-flag)
- [3.2.8 Stream-state](#3.2.8 Stream-state)
- [3.3 AAC音频数据](#3.3 AAC音频数据)
- [4. ADTS](#4. ADTS)
-
- [4.1 AAC与ADTS的关系](#4.1 AAC与ADTS的关系)
- [4.2 ADTS 结构](#4.2 ADTS 结构)
-
- [4.2.1 ADTS 头部详解](#4.2.1 ADTS 头部详解)
本篇文章是介绍关于RTSP拉流过程内容,如果涉及到推流协议的了解,请阅读:
基于FFmpeg进行rtsp推流协议分析过程(详细教程)(全息讲解)
1.RTP协议基础
1.1 RTP协议概述
RTP(Real-time Transport Protocol)是一种用于网络上传输实时数据(如音频、视频等)的协议,广泛应用于流媒体、视频会议、VoIP等场景。RTP协议的核心目标是为实时数据的传输提供时间戳、序列号等信息,以便接收端能够正确地重组数据流,保证数据的实时性和顺序性。RTP协议不保证数据的可靠传输,通常与RTCP(RTP Control Protocol)配合使用,以实现流量控制、拥塞控制等功能。
1.2 RTP头部结构
RTP头在前面的文章中讲解了,RTP头部是RTP协议的关键组成部分,它包含了多个字段,用于描述RTP数据包的特征和传输状态。RTP头部的结构如下表所示:
![](https://i-blog.csdnimg.cn/direct/014faa36a0dc4a36b291bbb7e58e7b1e.jpeg)
2. RTP负载:音频AAC封装模式
根据 RFC 3640 的定义,RTP 封装 AAC 数据时主要有以下三种方式:一个 RTP 包携带一个 AU、一个 RTP 包携带多个 AU、一个 RTP 包携带一个 AU 的片段。
2.1 单帧模式
一个 RTP 数据包中只携带一个完整的 Access Unit(AU),即一个音频帧。 每个 RTP 数据包只包含一个完整的音频帧,不会进行分片。适用于音频帧较小且网络 MTU 足够大的情况。RTP 数据包的 Marker 位(M 位)通常设置为 1,表示这是一个完整的音频帧。
2.2 多帧模式
一个 RTP 数据包中携带多个完整的 Access Unit(AU),即多个音频帧。 一个 RTP 数据包可以包含多个完整的音频帧,以提高传输效率。每个音频帧的大小可以通过 AU-header 中的 AU-size 字段指定。RTP 数据包的 Marker 位(M 位)通常设置为 1,表示这是最后一个音频帧。
2.3. 分片模式
一个 RTP 数据包中只携带一个 AU 的一个片段,多个 RTP 数据包共同组成一个完整的 AU。一个 AU 被分割成多个片段,每个片段携带在不同的 RTP 数据包中。所有分片的 RTP 数据包具有相同的时间戳,但序列号不同。最后一个分片的 RTP 数据包的 Marker 位(M 位)设置为 1,表示这是该 AU 的最后一个分片。适用于音频帧较大且网络 MTU 较小的情况,避免因音频帧过大而导致的 IP 碎片化。
3.RTP负载:音频AAC封装格式
3.1 AU-headers-length
AU-headers-length 是在 RTP 封装 AAC 音频数据时的关键字段。它位于 RTP 载荷的起始位置,紧跟在 RTP 头部之后。该字段的长度固定为 2 字节,其值以 bit 为单位,表示后续所有 AU-header(Audio Unit Header,音频单元头)的总长度。由于每个 AU-header 的长度固定为 16 bit,因此 AU-headers-length 的值总是 16 的倍数。这一特性使得接收端能够快速准确地解析出后续的 AU-header 数量和位置。
例如,当一个 RTP 包中只包含一个 AU-header 时,AU-headers-length 的值为 16;若包含两个 AU-header,则其值为 32,以此类推。通过这种方式,接收端可以根据 AU-headers-length 的值,轻松计算出后续 AU-header 的数量,即 AU-headers-length 的值除以 16。这种设计不仅提高了数据解析的效率,还确保了数据传输的灵活性,允许一个 RTP 包中可以包含多个音频单元(Audio Unit),从而适应不同的音频编码和传输需求。在实际应用中,AU-headers-length 字段的值对于接收端正确解析 RTP 包中的音频数据至关重要。接收端首先读取该字段的值,确定后续 AU-header 的总长度,进而可以准确地提取出每个 AU-header 的内容。每个 AU-header 包含了音频单元的大小、索引等信息,这些信息对于后续音频数据的解码和播放具有重要意义。因此,AU-headers-length 字段在 RTP 封装 AAC 数据的过程中,起到了桥梁的作用,连接了 RTP 头部和音频数据,确保了数据传输的完整性和准确性。
3.2 AU-headers
AU-headers(Access Unit Headers)是用于音频编码数据流中的头部信息,主要在音频数据的传输和解析过程中发挥重要作用。在音频编码标准中,AU-headers通常用于标识音频数据的起始位置、长度、时间戳等关键信息,以便接收端能够正确地解析和播放音频流。AU-headers是音频数据流中的一个组成部分,它位于音频帧的前面,用于描述音频帧的相关信息。根据不同的音频编码标准,AU-headers的结构和内容可能会有所不同。例如,在AAC(Advanced Audio Coding)编码中,AU-headers通常包含音频帧的长度、索引等信息,这些信息需要SDP内容来确定。
在此之前放一个SDP示例:
cpp
v=0
o=- 0 0 IN IP4 127.0.0.1
s=Stream
c=IN IP4 0.0.0.0
t=0 0
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzZQHgGf58BagICAoAAAAMAgAAAGQeMGMs=,aO+CPLA=; profile-level-id=640028
a=control:trackID=0
m=audio 0 RTP/AVP 97
a=rtpmap:97 mpeg4-generic/48000/2
a=fmtp:97 profile-level-id=1; mode=AAC-hbr; sizelength=13; indexlength=3; indexdeltalength=3; config=119056e500
a=control:trackID=1
注: SDP来自于RTSP请求信息的DESCRIBE
方法。
3.2.1 AU-size
表示音频帧的长度(以字节为单位)。该字段的长度由SDP(Session Description Protocol)中的sizeLength
参数决定,sizeLength
的单位为bit。例如,sizeLength
= 13,表示AU-size字段占用13位,能够表示的最大音频帧长度为8191字节。这一字段对于接收端正确读取音频帧的大小至关重要。
3.2.2 AU-index/AU-index-delta
用于标识 序列号。AU-Index 出现在第一个 AU-header 中,而 AU-Index-delta 出现在后续的 AU-header 中,表示相对于前一个 AU 的序列号差值
AU-index表示音频帧在整个音频流中的序号。该字段的长度由SDP中的indexLength
参数决定。在某些情况下,AU-index字段可能用于标识音频帧的顺序,特别是在音频流中包含多个音频帧的情况下。例如,当一个RTP包中包含多个音频帧时,AU-index字段可以帮助接收端对音频帧进行排序和管理。例如,indexlength
= 3 时,表示AU-index字段占用3位数据长度。
AU-index-delta表示音频帧序号的增量。该字段的长度由SDP中的indexDeltaLength
参数决定。在某些音频编码标准中,AU-index-delta字段用于表示当前音频帧序号与前一个音频帧序号的差值。这种表示方式可以节省一些空间,特别是在音频帧序号变化较小的情况下。
3.2.3 CTS-flag
表示是否包含 CTS-delta 字段。值为 1 表示存在 CTS-delta 字段,值为 0 表示不存在,该字段由SDP 中的 CTSDeltaLength
参数定义了 CTS-delta 字段的位数,如果 CTSDeltaLength
为 0或不存在,则表示 CTS-delta 字段不存在。
3.2.4 CTS-delta
表示 Composition Time Stamp (CTS) 的偏移量,相对于 RTP 头部的时间戳,该字段的存在与否由 CTS-flag 字段决定,而 CTS-flag 的存在与否由 CTSDeltaLength
参数决定。
3.2.5 DTS-flag
表示是否包含 DTS-delta 字段。值为 1 表示存在 DTS-delta 字段,值为 0 表示不存在, 该字段由SDP 中的 DTSDeltaLength
参数定义了 DTS-delta 字段的位数。如果 DTSDeltaLength
为 0,则表示 DTS-delta 字段不存在。
3.2.6 DTS-delta
表示 Decoding Time Stamp (DTS) 的偏移量,相对于 CTS,该字段的存在与否由 DTS-flag 字段决定,而 DTS-flag 的存在与否由 DTSDeltaLength
参数决定。
3.2.7 RAP-flag
表示该 Access Unit 是否为随机访问点。值为 1 表示是随机访问点,值为 0 表示不是,该字段由SDP 中的 randomAccessIndication
参数定义了 RAP-flag 字段的存在与否。值为 1 表示存在 RAP-flag 字段,值为 0 表示不存在。
3.2.8 Stream-state
表示 MPEG-4 系统流的状态,每个状态由一个模数计数器的值标识。该字段由SDP 中的 streamStateIndication
参数定义了 Stream-state 字段的位数。如果 streamStateIndication
为 0或不存在,则表示 Stream-state 字段不存在。
3.3 AAC音频数据
之后就是裸的AAC音频压缩流了,RTP负载中封装AAC音频数据主要使用两种格式:ADTS和LATM。ADTS格式较为简单,但封装开销较大,而LATM格式更加高效,适合低延迟和低带宽的实时传输应用。在实际应用中,根据网络带宽、延迟要求和音频质量需求的不同,选择适当的封装格式是关键。但是在封装RTP协议时,需要特别注意的是,在封装AAC音频时,往往去掉了ADTS信息的,解析时得到的是裸的AAC音频压缩流,我们在解码时需要将ADTS还原。
4. ADTS
4.1 AAC与ADTS的关系
ADTS(Audio Data Transport Stream,音频数据传输流)是AAC音频的一种封装格式,主要用于音频数据的传输。ADTS为每帧AAC音频数据添加了一个头部信息,该头部包含了音频数据的编码参数和帧的长度等信息。
在实际应用中,AAC音频数据通常被封装在ADTS格式中进行传输。发送端将AAC音频数据编码成ADTS格式,接收端则解析ADTS头部信息,提取AAC音频数据进行解码。这种封装方式使得AAC音频能够在各种网络环境中高效、可靠地传输。
4.2 ADTS 结构
ADTS(Audio Data Transport Stream)是AAC(Advanced Audio Coding)的一种常见传输流格式,通常用于封装AAC音频数据,使其适合在网络上传输。ADTS帧由两部分组成:
- ADTS头部(Header):包含音频的采样率、声道数、帧长度等信息。
- AAC音频数据(Payload):实际的AAC音频编码数据。
4.2.1 ADTS 头部详解
ADTS头部通常为7个字节(当存在CRC校验时为9个字节)。千字文,不如表格来的直接:
![](https://i-blog.csdnimg.cn/direct/212de63b5297418eab84849d5c25269e.jpeg)