# RTP 包 · 实时媒体的载体

> RTP 是音视频在网络上传输的基本载体。这一章读 modules/rtp_rtcp/source/rtp_packet.h，讲清 RTP 包的头部字段——SSRC、序号、时间戳、payload type、marker，它们各自解决什么问题，以及 RtpPacketToSend 与 RtpPacketReceived 两个方向的封装。

- 作者：David（道雾轩）
- 专栏：音视频与 WebRTC 详解（https://daiw.org/manual/webrtc.md）
- 最后更新：2026-07-14
- 原文：https://daiw.org/manual/webrtc/rtp-packet
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

# RTP 包 · 实时媒体的载体

[安全通道建好了](https://daiw.org/manual/webrtc/sframe)，媒体终于要在上面流动。承载音视频的基本单位，是 **RTP 包（RFC 3550）**。第 4 部分进入 `modules/rtp_rtcp/`，而一切从这个包的结构讲起——它的每个头部字段，都是为「实时媒体过不可靠网络」量身设计的。

## RTP 包 = 头 + 负载

一个 RTP 包很简单：一个**头部**加一段**负载（payload）**。负载是[编码后](https://daiw.org/manual/webrtc/source-map)的媒体数据（一段音频、一片视频）；头部则是一组精心设计的字段。`modules/rtp_rtcp/source/rtp_packet.h` 里，`RtpPacket` 类把这些字段暴露成访问器：

```cpp
// modules/rtp_rtcp/source/rtp_packet.h(节选)
class RtpPacket {
  bool Marker() const;              // 标记位(视频:帧结束)
  uint8_t PayloadType() const;      // 负载类型(哪种编解码器)
  uint16_t SequenceNumber() const;  // 序号(每包 +1)
  uint32_t Timestamp() const;       // 时间戳(采样时刻)
  uint32_t Ssrc() const;            // 同步源(哪条流)
};
```

## 五个关键字段，五个问题

这几个字段，每个都解决实时传输的一个具体问题：

<Callout type="info">
  **RTP 头部的字段，是「在乱序、丢包的网络上重建媒体」所需信息的最小集合：**

  - **`Ssrc`（同步源）** —— 一个 32 位随机数，标识**这是哪一条流**。一次通话有多条流（你的音频、你的视频、对方的……），全靠 SSRC 区分。[BUNDLE](https://daiw.org/manual/webrtc/jsep-transport-controller)把多条流挤一条传输后，接收端正是靠 SSRC [分流](https://daiw.org/manual/webrtc/rtp-demuxer)。
  - **`SequenceNumber`（序号）** —— 每发一个包 +1。它让接收端能**发现丢包**（序号跳了）和**乱序**（序号乱了）——[UDP](https://daiw.org/manual/webrtc/what-is-webrtc) 不保证顺序和送达，序号是重建秩序的钥匙，也是 [NACK 重传](https://daiw.org/manual/webrtc/nack-rtx)、[FEC](https://daiw.org/manual/webrtc/fec) 的基础。
  - **`Timestamp`（时间戳）** —— 媒体的**采样时刻**（不是发送时刻）。它决定这段媒体该在**什么时候播放**，以及[音视频怎么同步](https://daiw.org/manual/webrtc/source-map)（音频、视频各有时间戳，按它对齐口型）。注意它按媒体采样率走（如音频 48kHz），不是墙上时钟。
  - **`PayloadType`（负载类型）** —— 一个数字，指明负载**用哪种[编解码器](https://daiw.org/manual/webrtc/media-session)**（如 111=Opus、96=VP8）。接收端据此选对解码器。这个数字[怎么分配](https://daiw.org/manual/webrtc/payload-types)，下一章有讲究。
  - **`Marker`（标记位）** —— 一个比特，含义随媒体而定。视频里它常标记**一帧的最后一个包**（一帧可能拆成多个 RTP 包），接收端据此知道「这帧收全了，可以[组装](https://daiw.org/manual/webrtc/source-map)了」。
</Callout>

**这五个字段合起来，就够接收端在混乱的网络之上，把媒体重新拼回有序、可播放、可同步的样子。** RTP 的精妙，在于用极简的头部，给出了对抗真实网络所需的全部元信息。

## 两个方向：ToSend 与 Received

`RtpPacket` 是基类，收发两侧各有一个子类（`modules/rtp_rtcp/source/`）：

| 类 | 方向 | 特点 |
| --- | --- | --- |
| `RtpPacketToSend` | **发送** | 除基础字段外，带发送侧元数据（如是否重传、[分配](https://daiw.org/manual/webrtc/source-map)信息、原始序号） |
| `RtpPacketReceived` | **接收** | 带接收侧元数据（到达时间等，供[抖动](https://daiw.org/manual/webrtc/source-map)、[反馈](https://daiw.org/manual/webrtc/transport-cc)计算） |

分开是因为收发两侧关心的元数据不同：发送侧要记「这个包发出去了没、是不是重传」，接收侧要记「这个包什么时候到的」。`RtpPacketToSend` 还有个细节：重传时它的传输序号可能[和原始 `SequenceNumber()` 不同](https://daiw.org/manual/webrtc/nack-rtx)（[RTX](https://daiw.org/manual/webrtc/nack-rtx) 用独立的序号空间）。

## 头部之外：头扩展

标准 RTP 头字段是固定的那几个，但实时通信常需要**更多**元数据——比如「这个包发送时的绝对时间」「音量大小」「视频旋转角度」。这些通过 **RTP 头扩展（header extensions）** 携带，是一套可扩展机制。它太重要（拥塞控制、音视频同步都靠它），值得下一章专讲。

## 小结与下一章

**RTP 包 = 头 + 负载**，是实时媒体的载体（`RtpPacket`，`modules/rtp_rtcp/source/rtp_packet.h`）。五个关键头字段各解决一个问题：**`Ssrc`** 标识哪条流、**`SequenceNumber`** 发现丢包 / 乱序、**`Timestamp`** 定播放时刻与[音视频同步](https://daiw.org/manual/webrtc/source-map)、**`PayloadType`** 指明[编解码器](https://daiw.org/manual/webrtc/media-session)、**`Marker`** 标记[帧边界](https://daiw.org/manual/webrtc/source-map)。收发两侧分别用 `RtpPacketToSend` / `RtpPacketReceived`，携带各自的元数据。

标准头之外，更多元数据靠**头扩展**携带——拥塞控制、音视频同步都离不开它。下一章专讲这套机制。👉 [RTP 头扩展](https://daiw.org/manual/webrtc/rtp-header-extensions)。

## 参考资料

- [1] libwebrtc 源码.`modules/rtp_rtcp/source/rtp_packet.h`、`rtp_packet_to_send.h`、`rtp_packet_received.h`。
- [2] libwebrtc 源码.`g3doc/rtp.md`（RTP 概览）。
- [3] IETF RFC 3550「RTP」§ 5（包格式）。
