Live555 streaming server

LIVE555 Streaming Media
This code forms a set of C++ libraries for multimedia streaming, using open standard protocols (RTP/RTCP, RTSP, SIP). The libraries are already being used to implement applications such as "the LIVE555 Media Server" (a RTSP server application), "liveCaster" and "playRTPMPEG" (for streaming MP3 audio using RTP/RTCP), and "vobStreamer" (for streaming DVD content using RTP/RTCP/RTSP). The libraries can also be used to stream, receive, and process MPEG, H.263+ or JPEG video, and several audio codecs.

They can easily be extended to support additional (audio and/or video) codecs, and can also be used to build basic RTSP or SIP clients and servers, and have been used to add streaming support to existing media player applications, such as "VLC" and "MPlayer". (For some specific examples of how these libraries can be used, see the test programs below.)

Install LIVE555 Streaming Media Server
先下載 Source Code。
tar xzvf live555-latest.tar.gz
    ./genMakefiles linux
    make
    cp -r live /usr/lib
將檔案(.mp3, .mpg, .m4e)複製到 /usr/lib/live/mediaServer/ 下。

cd /usr/lib/live/mediaServer
    ./live555MediaServer
開啟 VLC Player 播放串流檔案。例如:rtsp:///demo.mpg 播放。

「轉貼 Live555 Source Code 分析

1. RTSP 連結建立

RTSPServer Class 是用來建立一個 RTSP Server,在內部定義了一個 RTSPClientSession Class,用來處理單一的 Client Session。

首先建立 RTSP Server (具体實現 Class是 DynamicRTSPServer),在建立過程中,先建立 Socket 在 TCP 的 554 port,然後把 RTSPServer::incomingConnectionHandler and socket Id傳給 taskScheduler。

taskScheduler 把 socket Id 放入 select 中( fReadSet ), 接著主程序開始進入 taskScheduler  的doEventLoop,使用 select 等待網路連結。

當 RTSP Client 連接到Server 時,select return 對應的 scoket,進而根據 incomingConnectionHandler 中建立了 RTSPClientSession,開始對該 Client 進行處理。

2. DESCRIBE 處理過程

RTSP Server 收到 Client 的 DESCRIBE 請求後,根據請求 URL(rtsp://192.168.0.1/1.mpg),找到對的 streaming resource,返回應訊息。Live555 中的 ServerMediaSession Class 用來處理 Session 中描述,它包含多個 ServerMediaSubsession。

當 Client 發出 RTSP 請求後,Server主 Loop 中的 select 返回,根據 socket id 找到對應的incomingRequestHandler,開始消息處理。先進行消息的解析,如果發現請求是 DESCRIBE 則進入 handleCmd_DESCRIBE 函數。

根據 Client 請求 URL (如1.mpg),調用DynamicRTSPServer::lookupServerMediaSession 找尋對應的 streaming 信息 ServerMediaSession。如果 ServerMediaSession 不存在,但是 local 存在  1.mpg 文件,則建立一個新的ServerMediaSession。在建立 ServerMediaSession 過程中,根據文件 .mpg,
建立 MPEG-1 or 2 的解碼器( MPEG1or2FileServerDemux )。再由 MPEG1or2FileServerDemux 建立一各MPEG1or2DemuxedServerMediaSubsession。最後由 ServerMediaSession 完成回應消息中的 SDP ,然後將回應訊息發給 Client,完成一次消息傳遞。

SDP消息組裝過程:

ServerMediaSession 負責產生 Session 公共描述信息,子 Session 描述由MPEG1or2DemuxedServer
MediaSubsession 產生。 MPEG1or2DemuxedServerMediaSubsession 在其父 Class 成員函數
 OnDemandServerMediaSubsession::sdpLines() 中生成 Session 描述信息。
在 sdpLines() 裡面建立一各 dummy 的 FramedSource (具体實現 Class 為 MPEG1or2
AudioStreamFramer and MPEG1or2VideoStreamFramer ) 和 RTPSink ( 具体實現 Class 為
 MPEG1or2AudioRTPSink and MPEG1or2VideoRTPSink ),最後調用 setSDPLinesFromRTPSink(...)成員函數生成子 Session 描述。

3. SETUP 處理過程

RTSPClientSession Class 用於處理單獨的 Client Session。其 Class 成員函數 handleCmd_SETUP() 負責處理 Client 的 SETUP 請求。調用 parseTransportHeader() 對 SETUP 請求的作解析,調用子Session(具体實現 Class 為 OnDemandServerMediaSubsession )的 getStreamParameters() 函數擷取 Stream 發送傳輸參數。將這些參數組裝成回應消息,返回給 Client。

擷取發送傳輸參數的過程:調用子 Session (具体實現 Class MPEG1or2DemuxedServer
MediaSubsession) 的 createNewStreamSource(...)  建立MPEG1or2VideoStreamFramer,選擇發送傳輸參數,並調用子 Session 的 createNewRTPSink(...)  建立 MPEG1or2VideoRTPSink。同時將這些信息保存在 StreamState Class 中,用來記錄 Stream 的狀態。

Client 發送兩個 SETUP 請求,分别用來建立 Audio and Video 的 RTP 接收。

4. PLAY 處理過程

RTSPClientSession Class 成員函數 handleCmd_PLAY() 處理 Client 的播放請求。首先調用子 Session 的 startStream() ,內部調用 MediaSink::startPlaying(...),然後是MultiFramedRTPSink::continuePlaying(),接着調用 MultiFramedRTPSink::buildAndSendPacket(...)。buildAndSendPacket 先設置 RTP packet header,再調用 MultiFramedRTPSink::packFrame() 。

packFrame() 內部通過 FramedSource::getNextFrame(), 接著MPEGVideoStreamFramer::
doGetNextFrame(),再接著經過 MPEGVideoStreamFramer::continueReadProcessing(),
FramedSource::afterGetting(...),MultiFramedRTPSink::afterGettingFrame(...),
MultiFramedRTPSink::afterGettingFrame1(...)等一系列繁瑣調用,最後到了
MultiFramedRTPSink::sendPacketIfNecessary(),這裡才是真正發送 RTP packet。

然後是計算下一個 packet 發送時間,把 MultiFramedRTPSink::sendNext(...) 函數傳給 taskScheduler,作為一個延時事件調度。在主 Loop中,當 MultiFramedRTPSink::sendNext() 被調度時,又會開始調用 MultiFramedRTPSink::buildAndSendPacket(...) 開始新的發送 packet 過程,這樣 Client 就可以源源不斷的收到 Server 傳來的 RTP packet 了。

Send RTP packet 的間隔計算方法:

Update the time at which the next packet should be sent, based on the duration of the frame that we just packed into it.

這個其實是整個 Server 的關鍵點!之前都覺得別人的 Server 寫的不好,當自己深入瞭解才知道其實作  Server 不簡單,也是有不少訣竅!

==================================================================

RTCP(Real Time Transport Control Protocol)將傳輸資料與控制資料分離。

RTCP port((為奇數) = RTP port (為偶數)+ 1

RTCP : Bandwidth Scaling

如果每個接收端都會週期性地產生 RTCP 封包,則全部的 RTCP 封包傳送速率,可能會遠超過傳送端的 RTP 封包傳送速率。傳送給群播數的 RTP 流量並沒有隨著接收端數量的增加而改變。

為了解決這個擴充性的問題,RTCP 會修改參與者傳送 RTCP 封包給群播樹的速率,而該速率成為會談參與者數量的函數。此外,因為每位參與者會傳送控制封包給其他的參與者,所以每位參與者可以估算會談中參與者的總數。

RTCP 會嘗試將它的流量限制在會談頻寬的 5%,舉例來說,假設傳送端以 2Mbps 的速率來傳送視訊,則 RTCP 會嘗試加它的流量控制在 2Mbps 的 5%,也就是 100kbps,方法如下:

這個協定提供接收端該速率的75%,也就是75kbps;並將該速率剩餘的25%指定給傳送端(25kbps),提供給接收端的75kbps,是由所有的接收端所共享。因此,如果有 R 個接收端,則每個接收端可以使用 75/R kbps 的速率來傳送 RTCP 流量,而傳送端會以 25kbps 的速率來傳送 RTCP 流量。參與者(傳送或接收端)會藉著動態計算平均 RTCP 封包大小,來判斷 RTCP 封包的傳送週期,並且以它所分配的速率來分割 RTCP 封包的平均大小。
傳送端的 RTCP 封包傳送週期是:

T=(傳送端的數目/0.25*0.05*會談的頻寬) * (平均RTCP封包大小)

接收端的RTCP封包傳送週期是:

T=(接收端的數目/0.75*0.05*會談的頻寬) * (平均RTCP封包大小)

==================================================================

RTP(Real Time Protocol):

RTP 基本上是在 UDP 之上的 Protocol,因為TCP 內定之流量管制與重傳機制並不適用於多媒體之即時傳輸,故採用 UDP 。當要傳送資料時,傳送端就會將 media 封包在一個RTP packet 裡面,然後再把 packet 封包在一個 UDP 的 segment 中,再傳到接收端。而RTP只會存放media的資料 或是內容,並不會做其他的控制,也就是說,RTP不會提供任何的技巧去確保資料遞送的時間或是提供其他服務品質的保證。

RTP packet 也有提供不同的功能:
(1) Support Mixer(media streams)and Translator.

(2) Intra-media synchronization. 提供媒體內外部同步機制。

(3) 封包遺失偵測,用來判斷傳輸品質。

(4) 數據包(packet)連續編號(packet sequence numbering)
由於UDP表頭並未有序號之記載,所以必須由應用層協定來提供。

(5) Support QOS(Quality of Service).

(6) RTP 提供了分割區段(segment)與重組(resseembly)之功能,而位元檢查之功能則由 UDP負責。

(7) 提供 jitter 偵測。由於網路流量的狀況訊息萬變,我們並不能保證資料可以保持一定的流量到達接收端,接收端接收之資料常有時快有時慢的狀況。提供jitter的訊息可讓接收端視情況增加 buffer 的容量以消除 jitter 所造成的影響。

RTP Packet Header 的欄位:
RTP 標頭的四個主要欄位分別為承載資料類型欄位(payload type)、序號欄位(sequence number)、時戳欄位(timestamp)、和來源端識別碼欄位(source identifier)。

負載類型(Payload type):
此欄位的長度為7個位元,指定封包資料的編碼方式。

序號欄位(Sequence number filed):
此欄位的長度為16個位元,每傳送一個 RTP 封包,序號的數值就會加1,而接收端可以用它來偵測封包遺失和恢復封包順序。

時戳欄位(Timestamp):
此欄位長度為32個位元。它代表對 RTP 資料封包第一個位元組取樣的瞬間時間。以 Sample 為單位遞增。Sample 為 audio 的最小單位(video 以frame 為最小單位),一個 RTP 封包通常傳送 20ms 的 audio 資料,也就是 160 各Samples.

同步來源端識別碼(Synchronization source indentifier,SSRC):
此欄位長度為32個位元,它可以識別RTP串流的來源端。

CSRC(Contributing source identiffiers)[長度不固定]:
Mixer 會在RTP 表頭中插入資料流之SSRC串列。


參考資料:
RFC3550 - RTP 中文版
RTSP 通訊協定介紹
C++學習筆記

0 Responses

Say something :)