RTMP(Real-Time Messaging Protocol)是基于 TCP 的协议,其底层传输的数据实际上封装了 FLV(Flash Video)格式的 Tag。在 RTMP 流中,数据被切分成一个个 Chunk(块)进行发送。
为了让你深入理解,我将模拟抓取一个真实场景:一个主播正在推流,内容包含一段 H.264 视频和 AAC 音频。
以下是详细的分析:
1. RTMP Chunk Header(包头)分析
无论音频还是视频,数据包的最外层都是 RTMP Chunk Header。
- 假设数据 (Hex):
03 00 00 00 00 00 04 05 00 00 00 - 详细解析 :
03: Format (2 bit) + Chunk Stream ID (6 bit) 。- Format=0(表示这是一个完整的 Chunk Header,包含时间戳、长度、类型等全部信息)。
- CSID=3(表示块流 ID 为 3)。
00 00 00: Timestamp (3 bytes)。时间戳为 0(通常是流的第一个包)。00 00 04: Message Length (3 bytes)。消息体长度为 4 字节(这是一个极小的包,通常用于发送握手后的配置信息)。05: Message Type ID (1 byte) 。类型为 5,表示 Audio Data(如果是 9 则是 Video Data)。00 00 00: Stream ID (4 bytes)。流 ID 为 0。
2. 音频包详细分析
AAC 音频在 RTMP 中有两种主要的包类型:AAC Sequence Header (配置信息)和 AAC Raw Data(声音数据)。
示例场景:发送 AAC 配置信息
这是推流开始后发送的第一个音频包,解码器需要它来知道采样率、声道数等。
-
假设 Payload 数据 (Hex):
AF 00 11 90 56 E5- (注意:不包含上面的 Chunk Header,这里是 Tag Body 的内容)
-
详细格式解析:
-
AF: Sound Format & Info (1 byte)- 高 4 位
1010(10): Sound Format = AAC。 - 高 3-2 位
11(3): Sample Rate = 44 kHz。 - 高 1 位
1(1): Sample Size = 16-bit。 - 高 0 位
1(1): Sound Type = Stereo (立体声)。 - 总结: 44100Hz, 16位, 立体声 AAC。
- 高 4 位
-
00: AAC Packet Type (1 byte)00= AAC Sequence Header (AudioSpecificConfig)。01= AAC Raw Data (真正的音频帧)。- 分析 : 这里
00说明这是配置包,告诉播放器如何初始化解码器。
-
11 90 56 E5: AudioSpecificConfig Data (4 bytes)- 这是 AAC 的 ADTS(Audio Data Transport Stream)头部信息去掉同步字后的紧凑格式。
- 通常包含 Profile (如 LC)、Sampling Frequency Index、Channel Configuration 等信息。
- 播放器会将这部分数据提取出来,构建解码器上下文。
-
示例场景:发送真实的音频帧
- 假设 Payload 数据 :
AF 01 [后面跟随一长串 AAC 压缩数据]AF: 同上,音频格式信息。01: AAC Packet Type = 1。表示后面的数据是裸的 AAC ES (Elementary Stream) 数据,不包含 ADTS 头(因为 RTMP/FLV 已经在配置包里定义了格式,这里为了节省带宽不再重复发送头部)。
3. 视频包详细分析
H.264 视频在 RTMP 中也分两种:AVC Sequence Header (SPS/PPS) 和 AVC NALU (I帧/P帧/B帧)。
示例场景:发送 AVC 配置信息 (SPS/PPS)
这是推流的第一个视频包,没有画面,只有元数据。
-
假设 Payload 数据 (Hex):
17 00 00 00 00 01 67 42 ...- (注:为了演示清晰,简化了数据长度)
-
详细格式解析:
-
17: Frame Type & Codec ID (1 byte)- 高 4 位
0001(1): Frame Type = Keyframe (IDR 帧)。- 1: Keyframe (I帧)
- 2: Inter frame (P帧/B帧)
- 3: Disposable inter frame
- ...
- 低 4 位
0111(7): Codec ID = AVC。- 7 = H.264 (AVC)
- 12 = H.265 (HEVC)
- 含义: 这是一个关键帧的配置包。
- 高 4 位
-
00: AVCPacket Type (1 byte)00= AVC Sequence Header。01= AVC NALU unit。- 分析 : 这里
00表示这是包含 SPS 和 PPS 的配置包。
-
00 00 00: Composition Time (3 bytes)- 仅在 Packet Type 为 1 时有意义。这里因为是 Header,通常为 0。
-
01 ...: DecoderConfigurationRecord Data- 这里存储的是 ISO BMFF 格式的 AVCDecoderConfigurationRecord。
- 它包含:ConfigurationVersion、AVCProfileIndication、ProfileCompatibility、AVCLevelIndication、LengthSizeMinusOne(通常为 3,表示 NALU 长度用 4 字节表示)。
- 紧接着是 SPS (Sequence Parameter Set) 和 PPS (Picture Parameter Set) 的数据。
-
示例场景:发送关键帧数据 (I-Frame)
-
假设 Payload 数据 :
17 01 00 00 1D 00 00 00 01 67 42 ...- (注:实际长度字段取决于配置)
-
详细格式解析:
17: Frame Type = Keyframe (1), Codec ID = AVC (7)。01: AVCPacket Type = AVC NALU (1)。表示这是真正的视频画面数据。00 00 1D: Composition Time (3 bytes) 。- 表示解码时间戳与显示时间戳之间的偏移量 (PTS - DTS)。假设值为 29ms(0x001D),意味着这个帧应该比解码时间晚 29ms 显示(B帧常用,但在纯 I/P 帧流中通常为 0)。
00 00 00 01: NALU Start Code (4 bytes) (虽然 FLV 通常使用 Length 字段,但在分析底层 H.264 码流时常见 Start Code,但在 RTMP Tag Body 内部,通常是 [长度][数据] 的结构)。- 修正 :在 FLV 结构中,这里应该是 NALU Length。如果 LengthSizeMinusOne=3,这里就是 4 字节长度。
- 假设 Length =
00 00 00 1C(28 字节)。
67 ...: NALU Data 。67(十进制 103) 代表 NALU 类型为 SPS (非关键 IDR)。但这里如果是 I 帧,通常会包含 SPS/PPS(如果 IDR 周期性插入配置),或者类型65(IDR slice)。- 实际数据结构示例循环:
[4 Bytes: Length] [1 Byte: NALU Header] [N Bytes: Payload][4 Bytes: Length] [1 Byte: NALU Header] [N Bytes: Payload]
4. 总结对比表
| 字段位置 | 音频包 (AAC) | 视频包 |
|---|---|---|
| 第一个字节 | AF (Sound Format=AAC, 44k, Stereo, 16bit) |
17 (Frame Type=Keyframe, Codec=AVC) |
| 第二个字节 | 00 (Seq Header) 或 01 (Raw Data) |
00 (Seq Header) 或 01 (NALU) |
| 后续字节 | AAC AudioSpecificConfig 或 Raw ES Data | 3字节 CompTime + DecoderRecord 或 NALU Units |
核心点记忆:
- 配置包 是必须先发送的,包含了解码必需的表头信息。
- 音频 AAC :
00类型是头,01类型是去掉 ADTS头的裸流。 - 视频 AVC :
00类型是 SPS/PPS 头,01类型是 NALU 单元。 - Composition Time :只在视频
01类型中有意义,用于音画同步中的补正。
希望这个详细的数据包分析能帮你理解 RTMP 流的内部结构!