RTP(Real-time Transport Protocol)
作用
RTP 用于传输实时媒体流(如音频、视频),它不提供可靠传输,而是关注低延迟、高实时性。
报文结构
整体结构
RTP 报文由以下部分组成:
RTP Header (12 字节起始) + 可选扩展头 + Payload(负载部分)+ 可选 Padding
如果启用了扩展或 CSRC,则头部长度会超过 12 字节。
RTP Header
Bit位 | 名称 | 说明 |
---|---|---|
0-1 | V (Version) | RTP 版本号,占2位,当前为2 |
2 | P (Padding) | 是否有填充位,最后一个字节表明填充长度 |
3 | X (Extension) | 是否有扩展头 |
4-7 | CC (CSRC Count) | CSRC 的数量(最多15个),每个4字节 |
8 | M (Marker) | 标志位,具体含义由应用定义(如帧结束标志) |
9-15 | PT (Payload Type) | 有效负载类型,7位,如 96 表示动态类型 |
16-31 | Sequence Number | 序列号,每发一个 RTP 包加1,接收方可用于重排和丢包检测 |
32-63 | Timestamp | 时间戳,标识采样时刻,用于同步和延时计算 |
64-95 | SSRC | 同步源标识符,用于区分不同源(一个媒体流) |
96-… | CSRC List | 0~15个,标识贡献源 |
图示(前12字节):
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|V=2|P|X| CC |M| PT | sequence number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| timestamp |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| synchronization source (SSRC) identifier |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| contributing source (CSRC) identifiers (可选, 每个4字节) |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
字段说明:
字段 | 说明 |
---|---|
V(Version) | 当前为 2,必须正确解析 |
P(Padding) | 若为 1,表明数据末尾有填充,用于加密对齐 |
X(Extension) | 若为 1,RTP header 后跟扩展头 |
CC(CSRC Count) | 表示后续跟随几个 CSRC,每个 4 字节 |
M(Marker) | 由应用定义,如视频帧结束、音频边界等 |
PT(Payload Type) | 表示负载的类型,动态类型在 96~127 |
Sequence Number | 每发一个 RTP 包 +1,接收方可检测丢包、重排 |
Timestamp | 用于同步,单位依赖编码格式(如 AAC 是 90kHz) |
SSRC | 同步源的唯一 ID,通常为随机值 |
CSRC | 多个贡献者源(如会议中多个语音参与者) |
RTP Payload(负载部分)
- 这是实际承载音频/视频数据的部分,格式由 Payload Type 指定(如 H.264、AAC 等)。
- RTP 仅负责传输,不解析负载内容。
- 不同的编码格式有不同的 RTP 负载封装规则(如 H.264 的 FU-A、STAP-A 等)。
静态 Payload Type(0~95)
编号 | 编码格式 | 采样率 | 备注 |
---|---|---|---|
0 | PCMU (G.711 μ-law) | 8000 Hz | 音频 |
8 | PCMA (G.711 A-law) | 8000 Hz | 音频 |
3 | GSM | 8000 Hz | 音频 |
10 | L16 | 44100 Hz | 立体声音频 |
26 | JPEG | - | 视频 |
31 | H.261 | - | 视频 |
32 | MPEG Audio | - | 音频 |
33 | MPEG Video | - | 视频 |
动态 Payload Type(96~127)
动态类型必须在 SDP (Session Description Protocol) 或信令(如 SIP INVITE)中协商说明。常见映射:
PT值 | 编码格式 | 描述 |
---|---|---|
96+ | H.264 | RFC 6184 定义 |
97 | H.265 | RFC 7798 定义 |
98 | AAC | RFC 3640 定义 |
99 | OPUS | 音频,动态 |
100 | VP8 | Google 实现 |
101 | VP9 | Google 实现 |
可选扩展头(Extension Header)
如果 X=1,表示存在扩展头。格式如下:
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| defined by profile | length |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| extension data... |
-
“defined by profile”:扩展头类型(如 WebRTC 扩展)
-
“length”:以 32 位字(4 字节)为单位的扩展长度,不含自身头部。
-
后续紧跟扩展数据内容
工作流程
发送端:
- 将音视频编码成帧
- 每帧按照 RTP 规则进行分片(如 H.264 的 FU-A)
- 打包 RTP Header + Payload,发送到目标地址(UDP)
- 控制信息通过 RTCP 发送,如丢包率、RTT、抖动等
接收端:
- 接收 RTP 报文并解析头部
- 按 SSRC 区分媒体流
- 按序列号排序、重组
- 利用时间戳做同步(音视频同步)
- 解码播放
RTP Header解析流程(c++)
解析函数
#include <iostream>
#include <cstdint>
#include <cstring>
#include <arpa/inet.h> // ntohs, ntohl#pragma pack(push, 1)
struct RtpHeader {uint8_t vpxcc; // Version(2) | Padding(1) | Extension(1) | CC(4)uint8_t mpt; // Marker(1) | PayloadType(7)uint16_t seq; // Sequence Numberuint32_t timestamp; // Timestampuint32_t ssrc; // SSRC
};
#pragma pack(pop)#pragma pack(push, 1)
struct RtpExtensionHeader {uint16_t profile_defined; // Profile-specific IDuint16_t length; // 以 32bit 为单位的长度
};
#pragma pack(pop)// 解析 RTP 头部
bool parseRtpHeader(const uint8_t* data, size_t len, RtpHeader& header) {if (len < sizeof(RtpHeader)) return false;std::memcpy(&header, data, sizeof(RtpHeader));// 网络字节序转主机字节序header.seq = ntohs(header.seq);header.timestamp = ntohl(header.timestamp);header.ssrc = ntohl(header.ssrc);return true;
}// 解析扩展头,返回扩展负载指针及长度
bool parseRtpExtension(const uint8_t* data, size_t len,uint8_t cc, bool extension,const uint8_t** ext_payload, size_t* ext_len) {if (!extension) return false;size_t header_offset = 12 + cc * 4; // 基础头 + CSRC 区if (len < header_offset + sizeof(RtpExtensionHeader)) return false;RtpExtensionHeader ext_hdr;std::memcpy(&ext_hdr, data + header_offset, sizeof(RtpExtensionHeader));ext_hdr.profile_defined = ntohs(ext_hdr.profile_defined);ext_hdr.length = ntohs(ext_hdr.length);size_t ext_payload_len = ext_hdr.length * 4;if (len < header_offset + sizeof(RtpExtensionHeader) + ext_payload_len) return false;*ext_payload = data + header_offset + sizeof(RtpExtensionHeader);*ext_len = ext_payload_len;std::cout << "RTP Extension Header:" << std::endl;std::cout << " Profile-defined ID: 0x" << std::hex << ext_hdr.profile_defined << std::dec << std::endl;std::cout << " Extension length (32-bit words): " << ext_hdr.length << std::endl;std::cout << " Extension payload size: " << ext_payload_len << " bytes" << std::endl;return true;
}// 打印 RTP 头信息
void printRtpHeader(const RtpHeader& h) {uint8_t version = (h.vpxcc >> 6) & 0x03;uint8_t padding = (h.vpxcc >> 5) & 0x01;uint8_t extension = (h.vpxcc >> 4) & 0x01;uint8_t csrcCount = h.vpxcc & 0x0F;uint8_t marker = (h.mpt >> 7) & 0x01;uint8_t payloadType = h.mpt & 0x7F;std::cout << "RTP Header:" << std::endl;std::cout << " Version: " << (int)version << std::endl;std::cout << " Padding: " << (int)padding << std::endl;std::cout << " Extension: " << (int)extension << std::endl;std::cout << " CSRC Count: " << (int)csrcCount << std::endl;std::cout << " Marker: " << (int)marker << std::endl;std::cout << " Payload Type: " << (int)payloadType << std::endl;std::cout << " Sequence Number: " << h.seq << std::endl;std::cout << " Timestamp: " << h.timestamp << std::endl;std::cout << " SSRC: " << h.ssrc << std::endl;
}void handleRtpPacket(const uint8_t* data, size_t len) {if (len < 12) {std::cerr << "Packet too short for RTP" << std::endl;return;}RtpHeader header;if (!parseRtpHeader(data, len, header)) {std::cerr << "Failed to parse RTP header" << std::endl;return;}printRtpHeader(header);uint8_t cc = header.vpxcc & 0x0F;bool extension = (header.vpxcc >> 4) & 0x01;const uint8_t* ext_payload = nullptr;size_t ext_len = 0;if (parseRtpExtension(data, len, cc, extension, &ext_payload, &ext_len)) {std::cout << " Extension Payload (hex, first up to 16 bytes): ";for (size_t i = 0; i < std::min(ext_len, size_t(16)); ++i) {printf("%02X ", ext_payload[i]);}std::cout << std::endl;} else if (extension) {std::cerr << "Invalid RTP extension header or length" << std::endl;return;}size_t payload_offset = 12 + cc * 4 + (extension ? 4 + ext_len : 0);if (payload_offset > len) {std::cerr << "Payload offset exceeds packet length" << std::endl;return;}size_t payload_len = len - payload_offset;const uint8_t* payload = data + payload_offset;std::cout << "RTP Payload size: " << payload_len << " bytes" << std::endl;// 这里你可以继续解析 payload 内容,例如 H264 NALU、音频帧等
}int main() {// 构造一个测试 RTP 包(含扩展头)uint8_t rtpPacket[] = {0x90, 0x60, 0x12, 0x34, // V=2,P=0,X=1,CC=0 | M=0,PT=96 | Seq=0x12340x00, 0x00, 0x00, 0x01, // Timestamp = 10x12, 0x34, 0x56, 0x78, // SSRC = 0x12345678// Extension header: profile=0x1000, length=1 (4 bytes extension)0x10, 0x00, 0x00, 0x01,// Extension payload (4 bytes)0xAA, 0xBB, 0xCC, 0xDD,// RTP payload (示例4字节)0x01, 0x02, 0x03, 0x04};size_t packet_len = sizeof(rtpPacket);handleRtpPacket(rtpPacket, packet_len);return 0;
}
运行结果
RTP Header:Version: 2Padding: 0Extension: 1CSRC Count: 0Marker: 0Payload Type: 96Sequence Number: 4660Timestamp: 1SSRC: 305419896
RTP Extension Header:Profile-defined ID: 0x1000Extension length (32-bit words): 1Extension payload size: 4 bytesExtension Payload (hex, first up to 16 bytes): AA BB CC DD
RTP Payload size: 4 bytes
SRTP(Secure RTP)
作用
项目 | 说明 |
---|---|
全称 | Secure Real-time Transport Protocol |
定义 | 为 RTP 提供 加密(confidentiality)、完整性(integrity) 和 重放保护(replay protection) 的协议 |
标准 | RFC 3711 |
传输对象 | RTP 和 RTCP 报文 |
应用场景 | WebRTC、SIP 电话、视频会议、安全监控、国标 GB28181(国密版本)等 |
-
设计目标
-
加密 RTP Payload,保护媒体内容
-
校验报文完整性(Message Authentication)
-
防止重放攻击(Replay Protection)
-
不增加过多开销(适配实时场景)
-
-
特点
-
轻量级、低延迟(适用于 VoIP/实时视频)
-
只加密有效载荷(Payload),头部可解析
-
不改变 RTP 报文格式(兼容性好)
-
报文结构
SRTP 报文 = RTP 报文 + 加密/认证后的扩展字段
+-----------------------------+
| RTP Header (未加密) |
+-----------------------------+
| RTP Payload (加密) |
+-----------------------------+
| RTP padding (可选, 加密) |
+-----------------------------+
| Authentication Tag (可选) |
+-----------------------------+
RTP Header(12 字节及可选扩展)
未加密。用于 HMAC 认证计算。
RTP Payload(加密)
- SRTP 对 负载部分(即 RTP header 后面的媒体数据)进行 AES 加密,有两种方式:
- AES Counter Mode(AES-CTR):只加密,不使用 MAC(快速)
- AES f8 模式(已不常用)
- AES-CM with HMAC-SHA1(推荐方式):加密 + 完整性保护
RTP Padding(可选)
如果 RTP 报文设置了 Padding 位,那么最后会有若干字节的填充用于对齐。SRTP 也会对填充部分进行加密。
Authentication Tag(鉴权标签)
- 是 HMAC-SHA1 计算的一个 MAC(Message Authentication Code)
- 默认长度为 10 字节(可配置为 4~10 字节)
- 用于确保 RTP 报文未被篡改
- 计算时覆盖:
- RTP Header + Payload(加密后) + 累加器(包括 SSRC 和 Sequence Number)
核心算法与参数
类型 | 说明 |
---|---|
加密算法 | AES-CTR 或 AES-f8 |
认证算法 | HMAC-SHA1(默认) |
MAC 长度 | 默认 80bit(10字节),也可选 32bit(4字节) |
密钥派生 | 使用 主密钥 + Master Salt 派生出多种密钥(加密、认证、RTCP密钥) |
密钥派生机制
使用主密钥(Master Key)和主盐值(Master Salt)派生以下密钥:
- RTP 加密密钥
- RTP 认证密钥
- RTP 会话盐值
- RTCP 加密密钥
- RTCP 认证密钥
- RTCP 会话盐值
每个密钥长度根据加密算法而定,典型 AES-128 对应 128-bit。
Replay Protection(重放保护)
- 基于 RTP Sequence Number + ROC(rollover counter) 实现
- 接收端保存窗口(例如 64 个序列号)检测重复报文
- 每当序列号溢出,ROC +1
RTCP 的安全扩展(SRTCP)
RTCP 也有对应的安全扩展,结构如下:
SRTCP = RTCP 原始报文 + 加密字段 + SRTCP Index + Auth Tag
- 支持选择性加密
- 使用 Auth Tag 来保证完整性
- SRTCP Index 替代 RTP 的 ROC
SRTP 密钥协商方式
SRTP 不定义密钥协商方式,但常见有:
1. SDES(Session Description Protocol Security Descriptions)
-
将密钥写入 SDP 协议中
-
示例:
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:MTIzNDU2Nzg5MDEyMzQ1Ng==
-
优点:简单,易实现
-
缺点:明文传输,安全性差,已不推荐使用
2. DTLS-SRTP(Datagram TLS)
- 使用 DTLS 握手协商密钥(如 WebRTC)
- 端到端加密认证,符合现代安全要求
- WebRTC 和新版 SIP(使用 ICE+DTLS)都采用它
3. MIKEY(Multimedia Internet KEYing)
- 早期设计用于 SRTP,现较少使用
WebRTC中RTP/SRTP工作流程
整体流程
[Media Capture] → [Encoder] → [RTP Packetization] → [SRTP 加密] → [网络传输]↓[网络接收] ← [SRTP 解密] ← [RTP 解析] ← [Decoder] ← [Render]
工作流程:
+-------------+ +-----------+Media Input | Encoder | RTP | RTP Stack |(Camera/Mic)+----+--------+ ---> +-----------+| | RTP Packetizationv | SRTP 加密(libsrtp)+------+--------+ || DTLS Handshake | <---++------+--------+|vSRTP Key Derivation
核心模块
模块 | 作用 |
---|---|
RTP/RTCP | 传输媒体帧和反馈信息(如丢包、带宽) |
SRTP | RTP 加密传输(使用 AES/HMAC) |
DTLS | 用于协商 SRTP 密钥(DTLS-SRTP) |
ICE/STUN/TURN | 建立点对点连接、穿透 NAT |
SCTP/DTLS | 传输数据通道(非音视频) |
处理流程(发送端)
1. 媒体采集与编码
- 摄像头/麦克风采集音视频
- 使用 VP8/VP9/H264 等视频编码器,Opus/G.711 等音频编码器编码为压缩帧
2. RTP 封装
- 编码帧被分片打包为 RTP 包
- 每个 RTP 包头包括:
- SSRC(同步源)
- Sequence Number(顺序)
- Timestamp(采样时间)
- Payload Type(编码类型)
3. SRTP 加密
- RTP Payload(负载)被加密
- 附加 HMAC 鉴权标签
- 使用密钥来自 DTLS 握手派生的 SRTP 密钥
4. 通过 UDP 网络发送(可能走 STUN/TURN 服务器)
SRTP 加密流程(WebRTC 默认)
密钥协商流程(DTLS-SRTP)
- 在 SDP 协商阶段使用
a=setup
,a=fingerprint
等字段建立 DTLS 连接 - 建立 DTLS 通道后双方使用
Exporter
从 DTLS 会话中导出密钥(RFC 5705) - 导出的密钥用于 SRTP 加密解密(分别用于加密/认证)
SRTP 加密细节
项目 | 内容 |
---|---|
加密算法 | AES-CTR (默认),也可用 AES-GCM |
完整性保护 | HMAC-SHA1(默认10字节) |
支持重放保护 | 是(基于序列号窗口) |
SRTP 不加密字段 | RTP Header(但用于 HMAC) |
处理流程(接收端)
- 从网络接收 RTP 报文(可能经过 TURN relay)
- 使用协商好的 SRTP 密钥进行解密 + 验签
- 解析 RTP Header(获取序列号、时间戳等)
- RTP 组帧(重排序、丢包重传等)
- 解码器处理后播放音视频
RTCP 辅助流程(反馈通道)
WebRTC 使用 RTCP 提供控制反馈:
RTCP 类型 | 用途 |
---|---|
SR (Sender Report) | 提供 RTP 发送统计,用于同步 |
RR (Receiver Report) | 丢包率、抖动、延迟等统计 |
PLI (Picture Loss Indication) | 请求关键帧(视频重传) |
FIR (Full Intra Request) | 强制全帧编码 |
NACK | 指定丢失的 RTP 序号,建议重传 |
RTCP 包通常也通过 SRTCP 进行加密和认证。
传输通道建立流程
SDP Offer/Answer → ICE 候选收集 → DTLS 握手 → SRTP 密钥协商 → 开始传输 RTP/SRTP
各协议协同如下:
协议 | 功能 |
---|---|
SDP | 协商能力、媒体参数、加密 fingerprint |
ICE | NAT 穿透、连接检测 |
STUN/TURN | 发现公网地址 / 中继 |
DTLS | 端到端加密协商 |
SRTP | 媒体数据加密传输 |
RTP | 媒体传输 |
RTCP | 控制反馈与统计 |