• 沒有找到結果。

解多工器與解碼器模組之間的資料傳遞機制

第四章 VLC 解多工器與解碼器模組

4.3 解多工器與解碼器模組之間的資料傳遞機制

else while( (p_pic = p_dec->pf_decode_video( p_dec, &p_block )) ) 圖4.14 影像解碼的 callback function

4.3 解多工器與解碼器模組之間的資料傳遞機制

在 4.2 節我們已經知道 VLC 找到適合的解碼器模組後就會開始產生一個 decoder thread 並開始進行解碼程序,此時在 input thread 下的解多工器模組會不斷開始對影音檔 案進行解多工程序,被分離出來的 audio stream 和 video stream 會被放入不同的 FIFO buffer 中,然後影像和聲音解碼器就會到各自的 FIFO buffer 中去讀取資料,但是在 VLC

中的解多工器模組和解碼器模組是在不同的thread 下運作,所以解多工器和解碼器模組

之間的資料傳遞機制就變得很重要,在這一節會以開啟MPEG-2 影音檔案為例子探討解

碼器模組是如何存取解多工好的的資料封包來解碼,並討論其中的存取機制。

ps.c 解多工器模組在分析完封包的 header 後,就會找到 libmpeg2.c 解碼器模組並創 造一個decoder thread,ps.c 解多工器模組會呼叫函數 es_out_Send() 將解多工好的 audio stream 和 video stream 不斷傳送到不同的 FIFO buffer 中,而 es_out_Send()函數是在 Init() 函數中所設定好傳送資料的函數;es_out_Send()函數則會判定是否有找到適合的解碼器 模組,若已有適合的解碼器模組便會呼叫 block_FifoPut()函數來將解多工器模組所分離 出的audio stream 和 video stream 資料進行 linked list。在說明 block_FifoPut()函數之前,

我們要先說明在尋找適合的解碼器模組時,如圖 4.15 所示會先呼叫 block_FifoNew()函

47

數來創造一個解碼器模組和解多工器模組之間傳遞資料的FIFO buffer。下面會詳細說明 FIFO buffer 中的資料是如何進行 linked list。

if( ( p_dec->p_owner->p_fifo = block_FifoNew( p_dec ) ) == NULL ) linked list 資料的長度,而 block_fifo_t 結構內的成員型態 block_t 的指標變數 p_first 和雙 重指標變數pp_last 標示 linked list 資料的起始和結束點。如圖 4.16 所示在程式中我們可 以看到p_first 會先指向 NULL,然後 pp_last 指向 p_first 指標在記憶體位置,形成下面 的圖4.17 關係。

block_fifo_t *__block_FifoNew( vlc_object_t *p_obj ) {

48 料。在執行*p_fifo->pp_last = p_block 後由於 pp_last 是存放 p_first 的記憶體位址,所以

*p_fifo->pp_last = p_block 會改變 p_first 所指向的位址,變成指向傳入的 p_block 所指向 的位置;p_block 是指向由解多工器模組解析好的資料在記憶體中的位置。此時指標關係

49

如圖 4.19 所示當執行 p_fifo->pp_last = &p_block->p_next 時,p_first 就會固定的指 向linked list 的第一個資料。然後 pp_last 存放著 p_block->p_next 記憶體位址,所以在 第二次進入迴圈後就 p_block->p_next 會指向第二個傳入的資料,在第一次迴圈結束後 其指標關係如圖4.20,其 linked list 關係如圖 4.21。

address5 address6 pp_last block_t *p_next address3 address5 NULL

圖4.20 記憶體位置關係示意圖三

p_first

p_block 1 NULL NULL

*pp_last

圖4.21 第一次迴圈結束其 linked list 關係圖

在 第 二 次 進 入 迴 圈 會 傳 入 第 二 個 資 料 在 記 憶 體 中 的 位 址 , 然 後 執 行 完

*p_fifo->pp_last = p_block 時會將第一輪的 p_block->p_next 指向新的 block_t 結構並開始 linked 第二個 block_t 結構,其指標關係如圖 4.22。

50

address5 address6 members 傳入的第 2 個 p_block pp_last block_t *p_next p_block

address3 address5 address6 圖4.22 記憶體位置關係示意圖四

假設最後會傳入 n-1 個 block_t 結構,其 linked list 關係會變成圖 4.23。我們可以很 清楚地看到雙重指標pp_last 扮演了很重要的角色。我們可以發現只用*p_fifo->pp_last = p_block 和 p_fifo->pp_last = &p_block->p_next 這兩行程式就可以巧妙的建立了資料 linked list 的架構,這是一個相當特別的技巧。

p_first

p_block 1 p_block 2 … p_block n-1 NULL

*pp_last 圖4.23 第 n-1 次迴圈結束其 linked list 關係圖

在解多工器模組不斷用上述的方式將 audio stream 和 video stream 放入 FIFO buffer 的同時解碼器模組也會不斷的讀取 FIFO buffer 中的資料來進行解碼程序。解碼器模組 會不斷呼叫 block_FifoGet()函數來讀取 FIFO buffer 內的資料並進行解碼程序。前面我

們有提過由於解碼器模組和解多工器模組是在不同的thread 下運作而且解多工器模組是

要將影音資料分離給影像和聲音解碼器模組進行解碼程序,所以可能會有解多工器模組 擺放資料的速度趕不上解碼器模組讀取資料的速度的情形發生,在這種情形下解碼器模 組會停止讀取 FIFO buffer 的資料並進入等待的狀態,解多工器模組就有緩衝的時間將

51

資料放入FIFO buffer 中後,解碼器模組才會再次去讀取 FIFO buffer 中的資料。

VLC 的解多工器模組在分離好的影音資料後就會利用 Init()函數中所設定好的資

料傳送模組中的函數來傳送資料,同時會根據資料是影像或聲音的類型放入不同的FIFO

buffer 中,也就是說如圖 4.24 所示假設現在 ps.c 解多工器模組解析出 PES 中的 payload 是video bitstream,那麼呼叫 block _FifoPut()函數時就會將此 payload 放入影像的 FIFO buffer 中,所以當在進行影像解碼程序時,就會呼叫 block_FifoGet()函數在影像的 FIFO buffer 中取出 video stream 並由 libmpeg2.c 解碼器模組中所定義的解碼函數來進行影像的 解碼程序。

Video video FIFO buffer pkt pkt pkt .… pkt

Data stream 解多工程序 Audio audio FIFO buffer

pkt pkt pkt .… pkt 圖 4.24 傳送 bitstream 示意圖