• 沒有找到結果。

第四章 多媒體串流系統與 RTP 封裝演算法

4.5 Pack frame in RTP packet

4.5.1 packFrame ( )

4.5.1.1 parse( )

在本文中一直提及的解析器是交由parse( )所執行;parse( )是一個 總稱,從3.2 節中可以知道不同的檔案類型會啟動不同的解析器對檔案 進行處理,parse( )所執行的任務如下:

檔案內容讀取

當解析器經由table4.1 中三至七項的函式讀取輸入緩衝區 (fCurBank)資料流時,若 fCurBank 是無資料的狀態或是解析器所 要求的資料數量大於 fCurBank 的資料量時,則會呼叫

ensureValidBytes1( )進行讀檔的動作,也就說檔案內容真正的讀取 是由解析器所啟動。

位元解析與訊框產生

每種編碼格式都有其對應的處理程序,在3.2 節中說明了 M4V 檔案與 MPEG-2 檔案解析的方式,在此以 M4V 檔為例解 說明MPEG4VideoStreamFramer::parse ( )對整個檔案資料處

理的方式,而詳細位元解析意義請參考 3-2 節的說明。

參考figure4.6 所示,在 parse( )中可以依 MPEG-4 的編 碼結構區分成六個區塊:

parseVisualObjectSequence( ) parseVisualObject( )

parseVideoObjectLayer( )

parseGroupOfVideoObjectPlane( ) parseVideoObjectPlane( )

parseVisualObjectSequenceEndCode( )

Figure4.6 parse ( ) model

Table4.5 fCurrentParseState 與 process 對映表 Value State / function

0 PARSING_VISUAL_OBJECT_SEQUENCE parseVisualObjectSequence( )

1 PARSING_VISUAL_OBJECT parseVisualObject( )

2 PARSING_VIDEO_OBJECT_LAYER parseVideoObjectLayer( )

3 PARSING_GROUP_OF_VIDEO_OBJECT_PLANE parseGroupOfVideoObjectPlane()

4 PARSING_VIDEO_OBJECT_PLANE parseVideoObjectPlane( )

5 PARSING_VISUAL_OBJECT_SEQUENCE_END_CODE parseVisualObjectSequenceEndCode( )

每一個區塊為一個小型的處理程序,而

fCurrentParseState 代表著目前解析器要進行的處理程序的狀 態,table4.5 為 fCurrentParseState 的狀態值與六個區塊程序

的對映關係,在最初解析器被啟動的時候,fCurrentParseState 被預設為“PARSING_VISUAL_OBJECT_SEQUENCE",因 此開始進入parse( )時,會先進入 parseVisualObjectSequence( ) 裡。在parseVisualObjectSequence( )中一開始會從輸入緩衝區 (fCurBank)中讀取 4 bytes 來辨別是否為 Visual Object

Sequence start code:000001B0,由於是第一次進入到此程序 中,此時fCurBank 中並沒有任何的資料,因此會由

ensureValidBytes1( )先進行讀檔的動作,把檔案資料放入緩衝 區中後,再繼續回到程序中進行解析。

在parseVisualObjectSequence( )中,解析器會不斷的讀入 4 bytes 來做判別,直到看見 Visual Object Sequence start code 後才會停止,並會先將Visual Object Sequence start code 存入 輸出緩衝區(fBuf)中,才會繼續對之後的位元流進行分析與儲 存的動作,解析過程中所讀入的位元流的byte 數的多寡是依 照編碼的語法結構而決定,當解析器看見Visual Object start code:000001B5 時會結束 parseVisualObjectSequence( )的運 作,並將fCurrentParseState 設定成下一個處理程序的狀態 值:“PARSING_VISUAL_OBJECT"。

當結束parseVisualObjectSequence( )後,會先進入到 afterGettingFrame1 ( )中檢查 fBuf 的 frame 狀態,由於此時 fBuf 中的 frame 並沒有 VOP 的 start code:000001B6 的存在,

因此不符合封包傳送的標準,所以又會再回到parse( )中繼續 進行檔案資料的解析及產生新的frame,此時會依據

fCurrentParseState 來決定進入那一個程序中。

上述為parseVisualObjectSequence( )的執行模式,其它程 序也都依循這樣的模式進行運作,關於fCurrentParseState 詳 細的設定順序可以參考3.2.1 小節 M4V 檔案解析以及

figure3.3 的說明。

另外Visual Object Sequence 、Visual Object 以及 Video Object Layer 為 MPEG-4 檔案的組態資訊,是伺服端在回送 RTSP 信息時須要傳送的資訊,因此這三個區段的資料都會由 appendToNewConfig( )另外存入到一個暫存記憶體的空間中 以供handleCmd_DESCRIBE ( )使用。

當進入parseVideoObjectPlane( ),會先解析 7 bytes 的 header 資訊,第一次讀入 4 bytes 為 start code,第二次讀入 1 bytes,辨認 vop_coding_type,之後再讀入 2bytes,找出時間 資訊的參數 modulo_time_base 與 vop_time_increment。解 析 vop_coding_type、modulo_time_base 和

vop_time_increment 是為了計算 presentation time。

解析header 後,會從 fCurBank 中不斷的讀入 4 bytes 的 資料並判別其是否為其他程序的 start code,每一次都從 fCurBank 中讀取 4 bytes 是因為 start code 這個特殊編碼值的 長度在MPEG-4 編碼語法中都是 4 bytes 的緣故;如果讀入的 位元值不是 start code 就會存入 fBuf 中,假設是的話,表示已 經讀到一張 VOP 的底端,伺服端會把 fPictureEndMarker 這 個旗標設為 true 的狀態,表示一張 VOP 的結束,並會依所解 析的start code 設定 fCurrentParseState 的狀態。在解析器進

入parseVisualObjectSequenceEndCode ( )這個程序時,也會 對 fPictureEndMarker 這個旗標的設定,此時表示了一個場景 的結束。而在此階段中會依解析得到的時間參數執行

computePresentationTime( )計算 presentation time。

由於每執行完一個區塊程序即會執行afterGettinFrame1( ) 來審視fBuf 中 frame 的狀態,檢視 frame 中有無 VOP 的 start code,如果有 VOP start code 的存在時,便會開始進行封包的 包裝與傳送,因此除了串流媒體檔案開頭的組態資訊外,解析 器每次解析的資料量大小單位為一張 VOP 的大小。

4.5.2 afterGettingFrame1 ( )

afterGettingFrame1 ( )最主要是將 fBuf 中的 frame 裝載成一個 RTP 封包,並呼叫 sendPacketIfNecessary ( )進行傳送。其運作機制可 分成三個階段如下所述:

Stage 1-fBuf 資料量檢查

先檢查fBuf 裡的資料量(RTP header +specialHeader +frame) 大小是否有超過預設封包的最大值(fMax=1448 bytes),如果有超 過,表示有溢位情況發生,則會對frame 進行切割;如果 fBuf 的 資料量大小沒有大於 fMax,則再檢查 fCurFragmentationOffset 這個變數是否有值,fCurFragmentationOffset 是紀錄 frame 經過 切割後在fBuf 中的偏移量,如果 fCurFragmentationOffset 大於 零,則表示目前在fBuf 中的 frame,是經過切割後的 frame 的尾 端部份,因此會設定旗標fPreviousFrameEndedFragmentation 為 true 表示在 fBuf 中的資料是 frame 末端了。

Frame 切割方式說明:

frame 的切割是由 numOverflowByte ( )所執行,參考 figure4.7 所示,伺服端首先會計算出 fBuf 中 frame 溢位資料 (overflowBytes)的大小,是由下式完成:

overflowBytes= fCuroffset+framesize-fMax fCuroffset 是紀錄目前 fBuf 中尚為送出的 frame 長度,

framesize 為每次解析器處所產生的新的 frame 的大小值,所 以 fCuroffset+framesize 為 fBuf 中資料的總大小值, fMax 為 預設封包最大的長度 1448 bytes。

接著會計算要傳送封包的長度:

fCuroffset= framesize- overflowBytes

重新計算 fCuroffset 的值,也就是對 frame 進行切割,fBuf 前1448 bytes 為所要傳送的封包。而在進行切割後會使用 setOverflowData( )紀錄 fBuf 中相關的資訊,包含了封包的起 始與結束位址,被切割後剩餘的frame 的起始位址以及剩餘 frame 大小的資料量(overflowBytes)…等。

fBuf

RTP header

parse() →frame

Packet →fMax=1448 bytes sendpacket(fPacketStart,fCuroffset) fPacketStart

Figure4.7 fragment Frame Stage 2-doSpecialFrameHandling ( )

doSpecialFrameHandling ( )主要是檢視 fBuf 中的 frame 狀 態,並依據 frame 的狀態更新 RTP header 中 Maker 和

Timestamp 的欄位。

(1.) 如果在 fBuf 中的 frame 是沒有經過切割的,也就 是 fCurFragmentationOffset 為零時,會檢查 frame 中是否有 VOP start code,並把結果紀錄在旗標 fVOPIsPresent 中。

(2.) 如果在 fBuf 中,已經沒有溢位狀態存在,也就是 overflowBytes 為零,旗標 fPictureEndMarker 也為 true,則會把 RTP header 欄位中的 Maker bit 重新 設定為1。

(3.) 設定 RTP header 欄位的 Timestamp。

Timestamp 的產生和 presentation time 息息相 關,也就是說timestamp 的值是經由對 presentation time 做一特定計算而來。當解析器產生一張 VOP 的 frame 同時,伺服端也會得到此 VOP 的 presentation time,因為每兩張 VOP 之間的 presentation time 增 加的時間間隔是固定的,都是以固定的毫秒增加,

所以 Timestamp 的值也會隨著 presentation time 的 變化而線性增加。當客戶端收到的 RTP 封包中

Timestamp 欄位的值都相同時表示這些封包裝載的 都是同一張 VOP 的位元流。

Timestamp 計算由 convertToRTPTimestamp( ) 執行,其計算方式是由VOP 的 presentation time 乘 上90000 再加上一個初始值 fTimestampBase 而 來。90000 是 encoding type 的 clock rate,初始值 fTimestampBase 是由 random ( )產生。

Stage 3-packet 傳送與否檢查

在這一階段中會根據前兩階段中對 fBuf 中的 frame 所做的分 析與設定來判定是否可以進行 RTP 封包傳送。如果以下四個條件 其中一項成立便會執行 sendPacketIfNecessary ( )來進行傳送。

滿足預設的封包大小,預設大小值在1000~1448 間。

沒有超過最大的封包大小值,最大值為1448。

如果fPreviousFrameEndedFragmentation 為 true,表示 封包中所裝載為frame 的末端資料流,且這個封包中並沒 有包含另一張frame 的資料流,也可以進行傳送。

如果是單獨的一張圖片或是一段音訊流,也就是

fVOPIsPresent 值為 true 時,則未達到預設的封包大小設 定也可以傳送。

如果上述條件都不成立,就會再回到 packFrame( )中運行,繼續產 生新的frame。

4.6 Send RTP packet

在這階段中表示輸出緩衝區(fBuf)已經有包裝完成的封包可以傳 送,因此伺服端會啟動sender 來傳送封包並且判斷檔案中是否還有資 料要包裝成封包進行傳送。

4.6.1 sendPacketIfNecessary ( )

sendPacketIfNecessary ( )負責傳送 RTP 封包。由於輸出緩衝區 (fBuf)只有一個,也就是說已經建制好的 RTP 封包和剩餘尚未包裝的 frame 都放在同一個緩衝區中,因此必須要告知伺服端的 sender 要傳 送的RTP 封包的起始位址以其所要傳送的範圍;當每送出一個 RTP 封

包時,則Sequence Number 便會接著加一,且會再次檢查 fBuf 的狀態,

當fBuf 中還存有尚未被裝入 RTP 封包的 frame 時,則伺服端會重新設 定下一個封包起始的位置,如figure4.8 所示;也就是在 fBuf 剩餘的資 料前插入新的 RTP header,並呼叫 sendNext ( )建置下一個 RTP 封包 傳送。

下一個封包起始位址的設定的方式是將 fBuf 中剩餘 frame 的起始 位置往前挪動RTP header 加上 specialHeader 大小的空間,並且重新 紀錄封包的起始位置,也就是說先在剩餘的frame 前留下 12 或 16 bytes 的空位,等待 buildAndSendPacket( )來重新設定 RTP header 與

specialHeader 的欄位值。

Figure4.8 Set up Next Packet

4.6.2 sendNext ( )

當sendNext ( )運作時,表示要建立一個新的 RTP 封包,因此會呼 叫buildAndSendPacket( ),重新進行整個建置過程,包含 RTP header 設定,payload 裝載…等。

而在新建一個RTP 封包的過程中,如果 output buffer 沒有資料,

則會再parse 新的資料,此時,會先檢查 input buffer 有沒有資料,如 果沒有會進行讀檔動作,如果檔案指標已到檔案底部,表示整個檔案都 被讀出,則會將fNoFramesLeft 設成 true,表示已沒有資料要傳送。

會再由parse()回到 sendPacketIfNecessary ( ),進入 singlestep()等待 下一個狀態。

4.6.3 SingleStep ( )

當整個串流的媒體檔案被完整傳輸到客戶端後,伺服端會離開

「RTP 封包包裝及傳送」這一階段,回到 SingleStep ( )中等待下一個 指令的指示,有可能是 user 要求要重新播放或者是關閉連線…等。

第五章 Experimental Results

在本章中以Live555 做為伺服端,而客戶端使用 VideoLanClient (VLC)進行實驗,以下將各別呈現當伺服端與客戶端進行連線時,伺服 端各部運作的實驗數據,串流媒體檔案為test.m4v。

5.1 RTSP 信息溝通協調

在以下將列出Live555 伺服端的「RTSP 信息溝通協調」模組中每 個Command function 在接收到客戶端的相對應的請求時所回應的內 容,如伺服端收到“SETUP”這個請求時會利用 handleCmd_SETUP ( ) 做出回應。

5.1.1 handleCmd_OPTION ( )

客戶端利用 OPTION 來詢問伺服端支援那一些功能,伺服端則會 將它所支援的功能送出,如figure5.1 中的綠框所示。

Figure5.1 OPTION - response information

5.1.2 handleCmd_DESCRIBE ( )

客戶端用“DESCRIBE”請求伺服端以何種描述方式來描述播放資 訊,描述的方式有三種:application/sdl、application/rtsl、

application/mheg,例如客戶端請求伺服端以 application/sdp 的方式 來描述:

rtsp://140.113.13.82/test.m4v 則客戶端的請求會如figure5.2 所示。

Figure5.2 Client request- OPTION

Figure5.2 Client request- OPTION