使用 Live555 開發 RTSP 客戶端時,收到 SDP 信息後創建 MediaSession,隨後仍需遍歷創建 MediaSubsession 並對每個發送 SETUP,不理解兩者關係與必要性。
| 概念 | 對應實體 | 作用 | SDP 中體現 |
|---|---|---|---|
| MediaSession | 一個完整的媒體會話/演示 | 聚合多個媒體流的控制入口,管理整體會話狀態 | 整個 SDP 文本(v=0 到末尾) |
| MediaSubsession | 單一媒體軌道 | 代表具體的音視頻軌道,處理獨立的 SETUP/PLAY/TEARDOWN | 每個 m= 行及其屬性(a=rtpmap 等) |
一個 RTSP 會話通常包含多個媒體流:
每個軌道需要獨立的傳輸參數協商:
因此 每個軌道 = 一個 Subsession = 一次獨立的 SETUP 交互。
客戶端 服務端
| |
|---- DESCRIBE --------->|
|<--- 200 OK (SDP) ------| ← 解析 SDP 創建 MediaSession
| |
|---- SETUP (video) ---->| ← 為視頻 Subsession 發送 SETUP
|<--- 200 OK (ports) ----|
| |
|---- SETUP (audio) ---->| ← 為音頻 Subsession 發送 SETUP
|<--- 200 OK (ports) ----|
| |
|---- PLAY (aggregate) -->| ← 聚合播放整個 Session
|<--- 200 OK ------------|
| |
|==== RTP/RTP 流數據 ====>| ← 多路流並行傳輸
| |
// 1. 解析 SDP 創建會話
MediaSession* session = MediaSession::createNew(env, sdpDescription);
// 2. 遍歷每個 Subsession(即每個 m= 行)
MediaSubsessionIterator iter(*session);
MediaSubsession* subsession;
while ((subsession = iter.next()) != NULL) {
// 3. 為每個 Subsession 創建源/接收器
subsession->initiate(); // 內部會發送 SETUP
// 4. 可選:設置接收回調、緩衝區等
// subsession->sink = MySink::createNew(...);
}
// 5. 聚合播放整個會話
session->startPlaying(); // 發送 PLAY (aggregate)
MediaSession
├── sessionName (SDP s=)
├── sessionDescription (SDP i=)
├── subsessions (MediaSubsession 列表)
│ ├── MediaSubsession (視頻)
│ │ ├── mediumName = "video"
│ │ ├── codecName = "H264"
│ │ ├── rtpPayloadFormat = 96
│ │ ├── rtpTimestampFrequency = 90000
│ │ ├── controlPath = "track1" ← SETUP URL 相對路徑
│ │ ├── rtpChannel, rtcpChannel ← SETUP 協商得到的端口
│ │ └── sink (MediaSink) ← 數據接收器
│ │
│ └── MediaSubsession (音頻)
│ ├── mediumName = "audio"
│ ├── codecName = "AAC"
│ ├── controlPath = "track2"
│ └── sink ...
│
└── aggregateControl (PLAY 聚合控制 URL)
MediaSession = 整個會話容器,MediaSubsession = 單一媒體軌道。SDP 中每個 m= 行對應一個 Subsession,客戶端必須遍歷所有 Subsession 逐個發送 SETUP 協商傳輸參數,最後對 Session 發送聚合 PLAY 開始接收多路流數據。