# Payload Type 分配 · 一个数字的讲究

> RTP 包用 payload type 这个数字标明「负载是哪种编解码器」。它怎么分配竟大有讲究：动态范围 96-127、回退 35-63、避开 64-95，还有 BUNDLE 下的冲突消解。这一章读 g3doc/rtp.md 与 PayloadTypePicker，把这个小数字讲透。

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

# Payload Type 分配 · 一个数字的讲究

[RTP 包的 `PayloadType` 字段](https://daiw.org/manual/webrtc/rtp-packet)是个数字，指明负载用哪种[编解码器](https://daiw.org/manual/webrtc/media-session)。看似小事，分配起来却颇有讲究——范围、回退、避让、冲突消解，每一条都有原因。这一章依据 `g3doc/rtp.md`，把这个数字讲透。

## 静态 vs 动态

Payload type（PT）只有 7 位，取值 0-127。分两类（`g3doc/rtp.md`）：

- **静态分配** —— 一些老编解码器（如 PCMU）的 PT 由 [RFC 3551](https://datatracker.ietf.org/doc/html/rfc3551) **固定**规定，人人皆知。
- **动态分配** —— 其余编解码器的 PT 通过 [SDP 协商](https://daiw.org/manual/webrtc/media-session)时**临时指定**（`a=rtpmap:96 VP8/90000` 就是把 96 分给 VP8）。

`g3doc/rtp.md` 特意警告：**动态 PT 的分配不保证稳定**——同一个编解码器，不同通话、不同实现，可能分到不同的数字。所以代码里**永远不能把 PT 数字写死**，必须以协商结果为准。

## 范围：96-127，回退 35-63，避开 64-95

动态 PT 的取值范围有一套规矩（`g3doc/rtp.md`）：

<Callout type="info">
  **动态 payload type 优先用 96-127，用完了回退到 35-63，而 64-95 刻意避开。**

  - **96-127** —— 首选的动态范围。音视频编解码器默认在这里分配。
  - **35-63** —— 回退范围。当 96-127 用尽（编解码器 + 它们的[重传](https://daiw.org/manual/webrtc/nack-rtx) / [FEC](https://daiw.org/manual/webrtc/fec) 格式太多），就用这里。一些较新的格式（如 flexfec-03、AV1）**默认**就放这个低范围。
  - **64-95** —— **刻意不用**。因为这个范围会和 [RTCP](https://daiw.org/manual/webrtc/rtcp-reports) 的包类型混淆（[RFC 5761](https://datatracker.ietf.org/doc/html/rfc5761),[rtcp-mux](https://daiw.org/manual/webrtc/srtp-encryption) 让 RTP 和 RTCP 共用一个端口，靠这个字节区分）。避开它，才能在同一条流上区分「这是 RTP 媒体包还是 RTCP 反馈包」。

  一个小数字的分配，竟要照顾「和 RTCP 不冲突」「给新格式留位置」「兼容老版本 WebRTC」——[实时协议的工程](https://daiw.org/manual/webrtc/rtp-packet)，魔鬼全在这种细节里。`g3doc/rtp.md` 还提到，老版本 WebRTC 认不出低范围的 PT，所以新格式往低范围放也是一种「不打扰老版本」的照顾。
</Callout>

## 谁来分：PayloadTypePicker

具体分配由几个组件负责（`g3doc/rtp.md`）：

- **音频**：`PayloadTypePicker`（`call/payload_type_picker.h`）从一张表里分配，音频编解码器倾向用高范围 96-127（兼容老版本），而 RED 这类冗余格式放低范围 35-63（从 63 往下，减少和视频 PT 撞车）。
- **视频**：由媒体引擎的 `GetPayloadTypesAndDefaultCodecs`（`media/engine/webrtc_video_engine.cc`）分配，从 96 起**升序**分给视频编解码器及其[重传格式](https://daiw.org/manual/webrtc/nack-rtx)。

## BUNDLE 下的冲突消解

[BUNDLE](https://daiw.org/manual/webrtc/jsep-transport-controller)带来一个新麻烦：音频和视频**挤一条传输**，它们的 PT 必须**全局唯一**（否则收到一个包，PT=96 到底是音频还是视频？）。但音频、视频是各自分配 PT 的，难免撞车。

解法是 `PayloadTypeSuggester`（`call/payload_type.h`）接口：检测到冲突时，**从 127 往下重新分配**冲突的 PT，直到全局无重复（`g3doc/rtp.md`）。于是 BUNDLE 一条流上，每个 PT 都能唯一映射到一种编解码器，[分流](https://daiw.org/manual/webrtc/rtp-demuxer)才不会错。

## 一个副产品：探测用的 PT

顺带一提（`g3doc/rtp.md` 后半讲的），[带宽估计](https://daiw.org/manual/webrtc/source-map)有时要**主动发包探测**可用带宽（bitrate probing），用两种手段：纯 **padding 包**（RTP 填充，收端可丢弃）和 **[RTX](https://daiw.org/manual/webrtc/nack-rtx) 包**（重发旧包，兼做冗余）。它们也带 PT——padding/RTX 有各自的流和 PT。探测是[拥塞控制第 5 部分](https://daiw.org/manual/webrtc/source-map)的话题，这里知道「探测包也是 RTP 包、也占 PT」即可。

## 小结与下一章

**Payload type** 标明[编解码器](https://daiw.org/manual/webrtc/media-session)，分**静态**（RFC 3551 固定）和**动态**（[SDP 协商](https://daiw.org/manual/webrtc/sdp-and-jsep)，**不保证稳定，别写死**）。动态范围**优先 96-127、回退 35-63、避开 64-95**（避与 [RTCP](https://daiw.org/manual/webrtc/rtcp-reports) 混淆）。音频用 `PayloadTypePicker`、视频用引擎方法分配；[BUNDLE](https://daiw.org/manual/webrtc/jsep-transport-controller) 下的冲突由 `PayloadTypeSuggester` **从 127 往下**重分保证全局唯一。

包结构、扩展、PT 都清楚了。现在看**谁在收发这些包**——`modules/rtp_rtcp/` 的核心模块 `ModuleRtpRtcpImpl2`。下一章。👉 [RtpRtcp 模块](https://daiw.org/manual/webrtc/rtp-rtcp-module)。

## 参考资料

- [1] libwebrtc 源码.`g3doc/rtp.md`（PT 分配规则）、`call/payload_type_picker.h`、`call/payload_type.h`。
- [2] libwebrtc 源码.`media/engine/webrtc_video_engine.cc`（视频 PT 分配）。
- [3] IETF RFC 3551（静态 PT）、RFC 5761（避开 64-95）。
