2、RTP会话建立:SDP协议简介、Offer/Answer模型、媒体协商流程

聊完RTP的包头和基本传输模型,咱们得面对一个实际问题:两个端点怎么知道对方支持什么编码、用什么端口、发什么格式的数据?

说白了,RTP只管把媒体数据打包扔出去。但在这之前,双方得先“握手”——告诉对方自己能干什么、想干什么。这个握手的过程,就是SDP协议配合Offer/Answer模型完成的。

我刚开始接触RTP开发时,总觉得SDP就是个文本描述文件,没什么技术含量。直到有一次在项目中排查兼容性问题,发现对方把a=fmtp参数写错了,导致视频花屏。嗯,从那以后我再也不敢小看这几行文本了。

2.1 SDP协议:不只是个文本描述

SDP的全称是Session Description Protocol,会话描述协议。它不负责传输数据,只负责描述媒体会话的“长相”。

你想想看,两个设备要通话,至少得知道:

  • 用什么编码?H.264还是VP8?
  • 码率多少?分辨率多大?
  • RTP往哪个IP和端口发?
  • 音频和视频是分开还是混在一起?

SDP就是干这个的。它用一组键值对来描述这些信息,每一行以单个字母开头,后面跟着等号和具体值。

SDP的核心行类型

行标识 含义 示例
v= 协议版本 v=0
o= 会话发起者信息 o=- 123456 2 IN IP4 192.168.1.1
s= 会话名称 s=RTP Session
c= 连接信息 c=IN IP4 0.0.0.0
t= 会话有效时间 t=0 0
m= 媒体描述 m=video 49170 RTP/AVP 96
a= 属性行(扩展描述) a=rtpmap:96 H264/90000

我个人习惯把SDP分成两部分看:会话级和媒体级。会话级描述全局信息(版本、发起者、连接地址),媒体级描述每个媒体流的具体参数。一个SDP里可以有多个m=行,分别对应音频、视频、字幕等。

小技巧:调试时如果看到c=IN IP4 0.0.0.0,别慌。这通常表示地址还没确定,等ICE协商完成后会更新。我在项目中遇到过好几次,一开始以为是Bug,后来发现是标准做法。

2.2 Offer/Answer模型:你一句我一句

SDP只是描述格式,那谁来发起、谁来响应?这就是Offer/Answer模型要解决的问题。

这个模型很简单:

  1. Offer方(通常是呼叫方)生成一个SDP,描述自己支持的媒体能力。
  2. Answer方(被叫方)收到后,根据自己的能力生成一个SDP回复。
  3. 双方各自解析对方的SDP,确定最终的媒体参数。

说白了,就是“我告诉你我能做什么,你告诉我你能做什么,然后我们取交集”。

举个例子:

// Offer(呼叫方)
v=0
o=- 123456 2 IN IP4 192.168.1.10
s=-
c=IN IP4 192.168.1.10
t=0 0
m=audio 5004 RTP/AVP 0 8 101
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
m=video 5006 RTP/AVP 96 97
a=rtpmap:96 H264/90000
a=rtpmap:97 VP8/90000
// Answer(被叫方)
v=0
o=- 654321 1 IN IP4 192.168.1.20
s=-
c=IN IP4 192.168.1.20
t=0 0
m=audio 6004 RTP/AVP 0 101
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
m=video 6006 RTP/AVP 96
a=rtpmap:96 H264/90000

你看,Answer方只选择了自己支持的编码:音频选了PCMU(payload type 0),视频选了H.264(payload type 96)。VP8被跳过了,因为Answer方不支持。

注意:Offer/Answer模型要求Answer方不能随意添加Offer方没提到的编码。我曾经见过一个实现,Answer方自作主张加了个m=application行,结果Offer方直接报错。记住,Answer只能“选”,不能“加”。

2.3 媒体协商流程:从SDP到RTP流

光有Offer/Answer还不够,实际项目中媒体协商是一个多步骤的流程。我画了一张图,帮你理清整个过程:

RTP会话建立:媒体协商完整流程 1. 应用层发起 2. 生成Offer SDP 3. 传输Offer 4. 接收方解析 5. 生成Answer SDP 6. 传输Answer 7. 双方解析Answer 8. 确定媒体参数 9. 启动RTP流 关键说明 • 步骤1-3由Offer方完成,步骤4-6由Answer方完成 • 实际项目中常与ICE、DTLS等协议并行协商 • 媒体参数确定后,双方开始发送RTP包

这个流程看起来简单,但实际项目中坑不少。我挑几个常见的说说:

2.4 避坑指南:媒体协商中的常见问题

我曾经在做一个跨平台项目时,发现Android端和iOS端视频通话一直黑屏。排查了半天,问题出在a=fmtp参数上。Android端在SDP里写了a=fmtp:96 packetization-mode=1;profile-level-id=42e01f,但iOS端解析时把profile-level-id当成了可选参数,直接忽略了。结果H.264的profile不匹配,解码失败。

从那以后,我要求团队在SDP解析时,对a=fmtp做严格校验,不匹配就降级或报错。

另一个常见问题是Payload Type冲突。RTP标准里,0-95是静态分配的类型(比如0=PCMU,8=PCMA),96-127是动态类型。但不同厂商对动态类型的理解不一样。我见过一个设备把H.264映射到96,另一个设备把VP8映射到96,结果两边都以为对方说的是自己的编码。

建议:在实现Offer/Answer时,尽量使用a=rtpmap明确声明编码名称和时钟频率。不要依赖静态Payload Type的默认映射。这样即使对方用了不同的动态类型,也能通过编码名称匹配上。

2.5 媒体协商与编解码协同

回到课程主题——流控与编解码协同。媒体协商阶段其实已经埋下了协同的种子。

举个例子,Offer方在SDP里声明了a=fmtp:96 max-br=2000,意思是“我最高支持2Mbps的码率”。Answer方如果网络条件差,可以在Answer里回复a=fmtp:96 max-br=500,把码率限制到500Kbps。这样在RTP流启动前,双方就已经对码率上限达成了共识。

我个人习惯在SDP里额外携带以下编解码参数:

参数 含义 示例
max-br 最大码率(kbps) a=fmtp:96 max-br=2000
max-fs 最大帧尺寸(宏块数) a=fmtp:96 max-fs=8160
max-fps 最大帧率 a=fmtp:96 max-fps=30
packetization-mode H.264分包模式 a=fmtp:96 packetization-mode=1

这些参数在协商阶段定下来后,后续的流控算法就有了边界。比如码率不能超过max-br,分辨率不能超过max-fs对应的尺寸。说白了,SDP就是给流控画了个“天花板”。

总结一下:SDP + Offer/Answer模型是RTP会话建立的基石。它不复杂,但细节多。你只要记住三点:

  • SDP描述“我能做什么”
  • Offer/Answer模型完成“我们做什么”
  • 媒体协商的结果直接影响后续的流控和编解码行为

下一章我们会深入RTP的传输控制,看看在流启动后,如何动态调整码率和编码参数。嗯,到时候你会回来感谢SDP里那些参数的。