二、 連線建立與 RTSP 信令溝通
2.4 Server 與 Client 互動之流程
2.4.2 Live555 RTSP 動作流程
2.4.2.4 RTSP PLAY
Client 接收 server 回傳的 SETUP 訊息後便會知道 server 端所使用的 RTP/RTCP 阜 號資訊,故 VLC player(Client)在完成用戶端接收模組的初始化動作後,便發出 PLAY 訊息告知伺服器可開始傳輸 RTP 封包。VLC player 發送的 PLAY 信令內容包括了多媒 體檔案的起始時間、終止時間等資訊,如下圖 2.21 所示,起始時間為 0.000 即是片頭一 開始,而終止時間未著名表示請 Live RTSPserver 播放到最後。
(VLC client 發送 PLAY 訊息給 Live RTSPServer,請求傳送 RTP 封包資料)
圖 2.21 client RTSP PLAY request
下圖2.22所示的是Live RTSPserver的PLAY回應封包內容,伺服器如果完成串流傳輸 的準備,則將下一個RTP封包的序號(sequence number)先傳給client做為確認之用。
(Live RTSPserver 的 PLAY 封包內容,傳回給 VLC client player)
圖 2.22 server RTSP PLAY response
2.4.2.5 client RTSP PAUSE request
Client 要求暫停 session 傳送但不釋放伺服器端資源,如下圖 2.23 所示。
圖 2.23 client RTSP PAUSE request
如果Server接受暫停指令,則回傳一個封包給client確認之,並暫停串流資料的傳 輸,server針對RTSP PAUSE回應內容如下圖2.24所示。
(Live RTSPServer告知VLC player已暫停封包的傳送)
圖 2.24 server RTSP PAUSE response
2.4.2.6 client RTSP TEARDOWN request
Client 要求將整個串流傳輸停止,如下圖 2.25 所示。
圖 2.25 client RTSP TEARDOWN request
當伺服器接收到此訊息後便關閉此串流傳輸,釋放連線的所有資源,並回應確認訊 息給使用者,Server針對RTSP TEARDOWN回應內容如下圖2.26所示,可以看到Live RTSPServer傳回確認訊息給VLC player後,便是TCP連線結束的handshaking。
連線結束 TCP handshaking
圖 2.26 server RTSP TEARDOWN response
要注意的是 client 端的程式設計在完成 SDP 格式的 coding 後,最後記得都要呼叫 sendRequest()將封包傳送之(也就是 BSD API 的 send())。
2.4.3 client getResponse()
在 client 端呼叫 BSD send()來發送 RTSP 命令後,server 端即回應相對應的 RTSP response 給 client,這時候 client 必須呼叫 getResponse()來接收 server 端所回應的 RTSP 訊息。
如下面的 getResponse()函數呼叫圖所示,當 client 接收到 server 的 RTSP response 後,需再呼叫五個函數,功能如下所述:
a.getLine()是一行一行地去讀取 SDP 的文字內容。
b.envir()函數主要是傳回一個 UsageEnvironment 的地址參照,如我們前面所介紹過的,
UsageEnvironment 這個類別的功用是負責接收串流程式內部所產生的訊息,並且把這 些訊息傳達給使用者知道。
c.getResponse1()
再去呼叫 BSD 的 recvfrom()來接收 server 所回應的 RTSP 訊息。
BSD soclet API recvfrom()如下所標示部分。
recvfrom():
經由 clientsocket (connected socket)讀取資 料,並儲存資料來源(對方) 的位址。
d.parseResponseCode()
這邊開始讀取 responseCode 的資料,解析 Server 傳回的狀態碼,比如說狀態碼 200 是 OK 的意思,RTSP status code 的意思如下表 2.3 所示。
表 2.3 RTSP status code table
在第二章最後,我們用以下 8 個步驟來說明 Live555 Client-Server 間傳送接收 RTSP 的『溝通協調』命令順序:
1.Client 發送 RTSP 命令給 Server。
2.Server 接受、解析、判斷、執行 RTSP,並產生適當的回應訊息。
3.Client 接收到 Server 回應訊息,並解析其回應碼(Response code)。
4.Client 依回應碼決定相對應的處理函數。
5.Client 在處理函數中更進一步地去解析重要的串流資訊:
5.1 Transport Header(During RTSP『SETUP』)。
5.2 RTP 表頭資訊( During RTSP『PLAY』)。
5.3 Scale Header 播放範圍表頭( During RTSP『PLAY』)。
6.Client 把 RTP payload 的內容傳給上 decoder 應用程式去執行播放
(During PLAY)。
第三章 封包包裝及傳送
到目前為止我們已經把一個串流伺服器的『連線建立的標準程序』以及『溝通協調』
部分論述完畢,在第三章要更去探討的地方有下面幾點:
1.在client發送RTSP『PLAY』給server接收後,server將會採取什麼動作?
2.在server開始傳送RTP封包之前,這些媒體封包的來源訊框(frame)是如何由一個媒體檔 案的資料流中取得的?
3.在分離媒體檔案資料流中的表頭(header)以及訊框後,我們便把訊框打包進一個封包之 中,而當然一個封包不可能裝進所有的訊框資料,因此衍生出來的切割封裝的演算法 與所處網路環境的對應關係也是值得去瞭解的問題,因為這些問題牽動著串流媒體的 效能表現。 (註.詳細的封裝演算法在第四章才會討論之,第三章主要以瞭解Live555 的程式流程為主)。
而在第二章中我們提到了Live555 server在收到RTSP『PLAY』後便會去執行
handleCmd_PLAY(),所以我們接下來便由該行程式出發,以求瞭解在這個階段串流伺 服器彙整了哪些功能來完成影音的串流傳輸。
3.1 RTSP PLAY信令的執行-handleCmd_PLAY()
如上紅框內所示,handleCmd_PLAY()呼叫了九個函數,以下先對這九個函數做 一個簡單介紹:
1.dateHeader():
傳回系統儲存時間的相關資訊。
2.parseRangeHeader():
RangeHeader 是 client 用來告知 server 它所需的 data 範圍,也就是 Range header 會將 streaming media 的起始及結束的範圍告知 Server,所以 server 端需用這個函數來解析 client 所傳過來的 RTSP『PLAY』中的表頭資訊。
3.parseScaleHeader():
ScaleHeader 是 client 用來告知 server 它所需的影片播放速率,所以 server 端需用這個 函數來解析 client 所傳過來的 RTSP『PLAY』中的表頭資訊。
9.strDup():主要用途在 Buffer 中 string 的複製(scaleHeader = strDup(buf))。
上述函數中我們針對 RTSPServer.rtspURL()函數做特別說明,該函數會先呼叫
ourSourceAddressForMulticast(envir()),最後會再去呼叫下圖紅框內的十個函數,而我們 在此只論述到比較重要的三個函數,說明如下:
1.setupDatagramSocket():建立一個 UDP Socket,如下圖 3.1 所示。
2.readSocket():server 讀取 client 端所傳輸的資料,比如說 RTCP/UDP。
3.writeSocket():server 傳送資料給 client 接收,比如說 RTP/UDP。
1.setupDatagramSocket()會
完成建立 UDP Socket()以及
Bind()的動作。
2.readSocket()會呼叫
recvfrom()經由 client
socket 讀取資料,並儲存資料
來源的位址。
3.writeSocket()會呼叫
sendto()把 server 的資料傳
至 client 端的 socket。
圖 3.1 Create RTP/RTCP socket after RTSP PLAY
而 UDP socket 和 TCP socket 的使用差別就是因為 UDP socket 是非連接導向
(Connectionless),故不需要 connect()函數,其餘的函數使用方法大致上同於我們第二 章的介紹。 (註.UDP socket API 函數流程圖可以參考圖 2.4)
3.1.1 伺服器媒體連線會談(ServerMediaSession)
而建立好 UDP socket 之後,串流伺服器便具有能力把 RTP 封包傳送到網路上,但 是 RTP 封包資料從哪裡來呢?所以我們接下來繼續看 Live555 的下一行程式碼。
#4 ServerMediaSession* sms = ServerMediaSession::createNew(
*env, streamName, streamName,descriptionString);
在前面 2.2.4 章節中(SDP)中我們約略介紹到 Live555 所提供『ServerMediaSession』
類別的用途,它是 Live555 用來表示一個『伺服器連線會談的資料結構』,而 createNew() 會產生一個 ServerMediaSession 的建構子,該建構子用來建立以及初始化一個多媒體『連 線會談』的相關資訊,比如說有:
1.streamName(串流名稱)~此即為 client 端輸入 URL suffix 的連線代號,需特別注意此變 數的處理,因為 Live555 是以連線名稱來決定所要播放的檔案。
2.info (Live 版本的資訊)。
3.description= "LIVE.COM Streaming Media v"(軟體開發公司的 title)。
4.gettimeofday(取得系統的時間資訊)
而這也就是我們下面所看到的 User-Agent 欄位資訊。
3.1.2 伺服器的媒體底層連線會談(ServerMediaSubSession)
ServerMediaSession(伺服器媒體連線會談)類別的功用是幫助伺服器建立一些基本 的訊息;而接下來的程式如#5 所示:
#5 sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew
(*env, inputFileName, reuseFirstSource));
上述#5 程式開始便進入媒體資料切割打包的部分,而
『MPEG4VideoFileServerMediaSubsession』這部分是我們所要串流的影音格式是什麼,
即套用該影音格式的對應建構子,舉例來說:
由 ServerMediaSession 建構子所完成的 User-Agent 資訊,包含串流名稱以 及軟體版本資訊
『MPEG4VideoFile』+『ServerMediaSubsession』=『MPEG4VideoFileServerMediaSubsession』,或是
『MP3AudioFile』+『ServerMediaSubsession』=『MP3AudioFileServerMediaSubsession』
,也就是套用特定影音格式的 createNew 部分,這是因為每一種影音格式都有特定的 RTP 包裝方式,並且規定於不同的 RFC 文件中。因此,Live555 目前支援影音格式共有九種,
每一種建構子都會對應到特定影音格式的解析以及封包演算法,以下列舉的是該九種建 構子的程式碼。
1.A MPEG-4 video elementary stream::
sms->addSubsession(MPEG4VideoFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
2. MPEG-1 or 2 audio+video program stream (elementary stream):
MPEG1or2FileServerDemux* demux = MPEG1or2FileServerDemux::createNew(*env, inputFileName, reuseFirstSource);
sms->addSubsession(demux->newVideoServerMediaSubsession(iFramesOnly));
sms->addSubsession(demux->newAudioServerMediaSubsession());
3.mp3:
sms->addSubsession(MP3AudioFileServerMediaSubsession::createNew
(*env,inputFileName,reuseFirstSource,useADUs,interleaving));
4.wav:
sms->addSubsession(WAVAudioFileServerMediaSubsession::createNew
(*env, inputFileName, reuseFirstSource, convertToULaw));
5.amr:
sms->addSubsession(AMRAudioFileServerMediaSubsession::createNew
(*env, inputFileName, reuseFirstSource));
6.vob:
sms->addSubsession(demux->newVideoServerMediaSubsession(iFramesOnly));
sms->addSubsession(demux->newAC3AudioServerMediaSubsession());
7.ts:
sms->addSubsession(MPEG2TransportFileServerMediaSubsession::createNew
(*env, inputFileName, reuseFirstSource));
8.m4v:
sms->addSubsession(MPEG4VideoFileServerMediaSubsession::createNew
(*env, inputFileName, reuseFirstSource));
9.aac
sms->addSubsession(ADTSAudioFileServerMediaSubsession
::createNew(*env, inputFileName, reuseFirstSource));
所以在 Live555 的『testOnDemandRTSPServer』這個程式所實作的串流伺服器具 有在『同一時間』串流『九種媒體格式』的能力,也就是說在最大 connected socket 限 制數目範圍內,串流伺服器可以隨意傳送這九種媒體格式的媒體檔案給用戶端接收。
*在此特別注意inputFileName是一個指向媒體檔案名稱的指標,預設會播放同一目錄 下所設定的檔案(如 test.mpg)。如果想建置一個 Live Capture 即時影像傳送的串流伺服 器,只要把即時抓取所儲存的檔案名稱設置成 test.mpg 即可,但注意即時抓取影像所儲 存的影音格式要和支援的格式相符。
接下來我們便以 MPEG4VideoFileServerMediaSubsession::createNew()來做說明。
如下紅框內所示,執行 createNew 之後便會傳回一個 MPEG4VideoFileServerMediaSubsession 的建構子。
而該 MPEG4VideoFileServerMediaSubsession 的建構子主要會去做 FileServerMediaSubsession 的預設值設定。
而在 FileServerMediaSubsession 的建構子中會去做 OnDemandServerMediaSubsession 的 預設值設定。
OnDemandServerMediaSubsession.cpp 這一個程式主要功能便是由 sdpLines()函數
去呼叫『createNewStreamSource()』以及『createNewRTPSink()』,如下所示:
createNewStreamSource()函數功用主要負責開啟串流檔案,並從該檔案取得訊框
(frame),這部分我們會在接下來的 3.2 章節(開啟串流檔案來源 , MPEG4VideoFileServerMediaSubsession.cpp)中論述。
而 createNewRTPSink()主要負責對這些訊框做打包(pack)的動作,並且在這些封包 之中加入 RTP header 的資訊,這部分我們會在接下來的 3.4.3 章節(影像訊框的切割與 包裝,MultiFramedRTPSink.cpp)中論述。
(MPEG4VideoFileServerMediaSubsession 類別的繼承關係圖與函數呼叫圖)
3.2 訊框的切割、包裝以及傳送
在論述 Live555 訊框(frame)的切割
、
包裝以及傳送的方式之前,我們在此先對 3.4 章節的六個小節做個預覽說明,以便接下來能夠更有條理地分析 Live555 的MPEG4VideoFileServerMediaSubsession.cpp 程式的功能模組。
小節 標題(主要函數) 簡要說明
3.2.5 RTP Payload format for MPEG4 (createNewRTPSink())
主要是對於 RTP 協定以及 RFC3016 的說明。
3.2.6 取得各種影音格式的訊框:
(doGetNextFrame())
特定媒體格式訊框的解析。
3.2.1 IP(Internet Protocol)、Fragment and RTCP
IP 通訊協定定義於 RFC 791,位於下圖 3.2(a)OSI model 的第三層,是網際網路所 使用的網路層通訊協定。IP 負責傳送資料到指定位址,但並不確認資料是否正確傳達,
是一種無連結(Connectionless)的通訊協定,而 IP 主要負責以下三點:
1.Packet 路徑選擇(Routing) 2.Packet 分割(Fragmentation) 3.Packet 重組 (Re-assembly)
- by Packet’s Fragment ID。
圖 3.2(a) OSI 7-layer model
通常網路上傳輸的封包大小都有限制(如下表 3.1 所示),比如說在 Ethernet 上 MTU 的限制為 1500 Bytes,而 1500 Bytes 便是 MTU(Maximum Transfer Unit,最大傳輸單 位);MTU 也就是 Data-link layer 中對資料(payload)傳輸的最大限制,而 Network layer 會因 Data-link layer 的 MTU 而對 frame 做切割的動作。
表 3.1 MTU limited table 以下為兩個 IP fragmentation 的例子:
範例一:假設原始 IP 封包大小為 4520Bytes,IP 表頭為 20Bytes,故 IP Payload 為 4500 Bytes;
若必須經由乙太網路傳送,在切割後每個 IPfragment 最大長度為 1500 Bytes,其中 IP 表
頭的長度為 20 Bytes,每一個 IP Payload 最大為 1480 Bytes,因此,會產生如下圖 3.2(b) 的 4 個新 IP fragment:
圖 3.2(b) IP fragmentation
下圖 3.3 為串流伺服器傳送 RTP 以及 RTSP 封包的內容資訊,因此以 MTU 為
1500Bytes 來說,最大的 RTP 封包 payload 便只能是 1460Bytes,如果串流伺服器故意設 定 payload 長度為 1470Bytes 將會造成系統額外的負擔,因為 Network layer 會把一個
1470Bytes 的訊框切割成二個封包,除了造成傳送端的麻煩,亦會造成接收端在封包重
1470Bytes 的訊框切割成二個封包,除了造成傳送端的麻煩,亦會造成接收端在封包重