第三章 VLC播放流程架構介紹
3.3 播放影音程序
在PlayItem()函數中,主要是開啟 input thread 來開始整個影音播放程序,而 這支input thread 的主要運作流程就是圖 3.7 虛框所示的 Run()函數。我們可以看 到Run()函數裡主要呼叫了 vlc_thread_ready()函數、Init()函數、MainLoop()函數這 3 個函數,而接下來我們只針對 Init()函數及 MainLoop()函數內部分相關的函數流 程來討論。
PlayItem( )
Init ( )
vlc_thread_ready ( )
MainLoop( ) Run ( )
cre ate input thre ad Run( )
input thre ad
圖3.7:input thread Run()函數
在本章的一開始我們已經簡單的描述過VLC 播放流程架構:在我們選擇開啟
的資料來源之後,首先 VLC 所要進行的程序是從資料來源去讀取影音資料,並
利用解多工器將影音資料分離出audio bitstream 及 video bitstream。
但是在進行這個程序之前,VLC 必須要針對存取的資料來源找到合適的存取 模組,以及針對影音包裝格式找到合適解多工器模組,之後就會利用此存取模組 來讀取資料,並且利用此解多工器模組來解析影音資料。而要尋找合適的存取模 組以及解多工器模組的部分,就是在 Init()函數內所呼叫的 InputSourceInit()函數 來完成的。
下面圖3.8 就是一個 InputSourceInit()函數的基本程式流程,這個函數的主要 的作用是在於針對不同的資料來源,例如從電腦檔案、網路、DVD/VCD 等等,
要先找到合適的存取模組來存取影音資料;並且針對不同的影音包裝格式,例如 MPEG-2 program stream、MPEG-2 transport stream 等等,要找到合適解多工器模
組來解析影音資料,所以接下來我們就要來說明VLC 如何找到並使用合適的存
取模組以及解多工器模組。
圖 3.8:InputSourceInit()函數程式流程 if( *psz_demux == '\0' ) in->p_demux=
demux2_New( )
access2_New( )
stream_AccessNew( ) if( in->p_demux )
demux2_New( ) true
true false
false
Init( ) InputSourceInit( ) MRLSplit( )
InputSourceInit( ) End
3.3.1 分解 MRL(Media Resource Locator)
VLC 定義了所謂的 Media Resource Locator(簡稱 MRL),也就是我們所選擇的 影音資料來源 (如前面圖 3.5 所示),例如 C:\test.mpg 就是一個 MRL,而 MRL 的 架構為access/demux://path。所以在 InputSourceInit()函數內會呼叫 MRLSplit()函 數先分解MRL 的 access、demux、path 這 3 個部分,並分別儲存在 psz_access、
psz_demux、psz_path 這 3 個字串指標(如圖 3.9),而分解 MRL 的主要目的,就是 要利用所分解出來的字串來尋找合適的存取模組。
psz_dup 變數儲存所選取的 MRL
圖3.9:呼叫 MRLSplit()函數分解 MRL
例如:若MRL 為 C:\test.mpg,則會被拆解成
Îpsz_access=””,psz_demux=””,psz_path=C:\test.mpg 若MRL 為 rtsp://140.113.13.84/test.mpg,則會被拆解成 Îpsz_access=rtsp,psz_demux=””,psz_path=140.113.1.84/test.mpg
若MRL 為 http://140.113.13.84:1234,則會被拆解成 Îpsz_access=http,psz_demux=””,psz_path=140.113.1.84:1234
若MRL 為 udp://@224.0.0.1:1235,則會被拆解成 Îpsz_access=udp,psz_demux=””,[email protected]:1235
3.3.2 尋找合適的存取模組
在分解完 MRL 之後,就會先判斷 psz_demux 是否為空字串,一般情況下,
我們所選擇的MRL 是沒有 demux 這個部分,因此 psz_demux 大部分都是為空字
串,所以程式流程會進到demux2_New()函數內,並呼叫 module_Need()函數來尋 找是否有合適的access_demux module(下圖 3.10)來存取我們所選擇的資料來源及 解多工影音資料,其中psz_module 參數就是前面所分解出來的 psz_access。例如 當我們選擇rtsp 串流連線方式時,就會找到 live555 module 來存取串流資料。假 使成功在 demux2()函數內找到合適的 access_demux module,那麼就會回傳一個 指標位址給in->p_demux 結構指標並結束 InputsourceInit()函數。
圖3.10:呼叫 module_Need()尋找合適的 access_demux module
如 果 找 不 到 合 適 的 access_demux module , 那 麼 就 會 回 傳 NULL 給 in->p_demux 結構指標並進到 access2_New()函數內,然後呼叫 module_Need()函 數來尋找是否有合適的 access2 module 來存取我們所選擇的資料來源(如下圖 3.11),其中 psz_module 參數一樣是前面所分解出來的 psz_access。例如當我們選 擇開啟電腦的影音資料時,就會在這裡找到file module 來讀取檔電腦案資料,而 假使在這裡還是找不到合適的 module 來存取資料,那就表示我們所選擇的資料 來源是有問題,VLC 也就無法從這個來源存取資料,當然後面的程序也就無法完 成了。
圖3.11:呼叫 module_Need()尋找合適的 access2 module
而 在 找 到 合 適 的 access2 module 來 存 取 影 音 資 料 後 , 接 下 來 所 呼 叫 stream_AccessNew()函數,主要是初始化一些結構變數以及開始先讀取一部分影 音資料暫存到buffer 內,而讀取的方式是利用讀取函數指標來讀取資料,而這個
讀取函數指標就是指向前面所找到的合適的access2 module 內的讀取函數。
因為在找到合適的access2 module 的同時,就會指定此 access2 module 內的讀取 函數給圖3.13 紅框內的函數指標。
p_access->pf_read 是一個讀取函 數指標,指向某個合適的 access2 module 內的讀取函數。
圖3.12:讀取影音資料
在 file module 內指定 Read 函數給讀取函數指標
圖3.13:file module 內所指定的讀取函數
所以當尋找合適的解多工器的時候,就會讀取buffer 內的資料先確認影音包 裝格式是什麼,例如MPEG-2 program stream、MPEG-2 transport stream 等等。
3.3.3 尋找合適的解多工器模組
那麼VLC 是如何確認來源資料影音包裝格式呢?在經過前面所描述的程序之 後,接下來會在進到demux2_New()函數內,並呼叫 module_Need()函數來尋找是 否有合適的demux2 module 可以解多工所讀取的影音資料(如下圖 3.14),其中 psz_module 參數是前面所分解出來的 psz_demux。
圖3.14:呼叫 module_Need()尋找合適的 demux2 module
舉例來說,假如影音資料的包裝格式為MPEG-2 program stream,那麼在這裡 就會找到ps module 來解多工所存取的影音資料;如果影音包裝格式為 MPEG-2 transport stream,那麼就會找到 ts module 來解多工所存取的影音資料。所以 VLC 是利用module_Need()函數來找到合適的解多工器模組,而如何利用此函數在這 些解多工器模組當中找到合適的模組,在第四章我們會舉例來說明。
3.3.4 進行解多工程序
在經過前面3.3 節所描述的 InputSourceInit()函數內運作流程及重要性之後,
接下來我們要描述的是解多工的程序。首先我們直接來看Run()函數內所呼叫的 MainLoop()函數。在 MainLoop()函數裡主要是利用了 while 迴圈不斷的呼叫解多 工函數(如下圖 3.15),利用此解多工函數將所讀取的影音資料分離出 audio bitstream 及 video bitstream,而此解多工函數其實就是前面所找到合適的 access_demux module 或者是 demux2 module 內所指定的解多工函數。
p_input->input.p_demux->pf_demux 是一個解多工函數指標,指向某個合 適的 demux2 module 或 access_demux 內的解多工函數。
圖 3.15:MainLoop()函數部分程式碼
舉例來說,假如影音資料的包裝格式為MPEG-2 program stream,那麼在前面 確認來源資料影音包裝格式的時候,就會找到ps module 來解多工所存取的影音 資料,而找到ps module 的同時,就會指定此 module 內的解多工函數給的解多工 函數指標(如圖 3.16),所以在 while 迴圈裡所呼叫的解多工函數,就等同於呼叫了 此ps module 內的解多工函數來進行解多工動作。
ps module 內指定 Demux 函數給解多工函數指標
圖3.16:ps module 內所指定的解多工函數
3.3.5 尋找合適的解碼器模組
在前面討論完解多工的程序之後,現在我們要討論的是如何進行到解碼的程 序,而在進入到解碼程序之前,要先尋找到合適的解碼模組。下面圖3.17 是在說 明開啟不同的影音資料來源,尋找合適的解碼器模組的時機會不太一樣。例如虛 框的部分是表示如果是開啟rtsp 串流或開啟 dvd 光碟時,是在 Init()函數內去尋找 合適的audio 和 video decoder module 和開啟 audio 和 video decoder thread;若是 開啟電腦檔案或http 等連線方式時,會在 demuxer function 內去尋找合適的 audio 和video decoder module 和開啟 audio 和 video decoder thread。
Run( ) Init( ) InputSourceInit( )
MainLoop( )
demuxer function while-loop
look for suitable audio and video decoder module and
create audio and video decoder thread rtsp,dvd
local file,unicast/multicast,http
look for suitable audio and video decoder module and
create audio and video decoder thread
圖3.17:尋找合適的 decoder module 程序
圖3.17 中的 demuxer function 就是前面圖 3.16 紅框所表示的解多工函數。在 前面有提過,解多工函數主要的作用就是將讀取的影音資料分解成video bistream 和audio bistream,然後再分別由 video decoder 及 audio decoder 來進行解碼,但 是VLC 是如何知道 audio 及 video 的編碼格式,以及尋找並使用合適的解碼器來 進行解碼呢?
在進行解多工的程序當中,一般都會去解析來源資料的影音包裝格式,最後 分解出video bistream 和 audio bistream。例如 mpeg-2 program
stream 是由許多 pack 所組成,每個 pack 是由一個 pack header 再加上多個 PES(Packetized Elementary Stream)而形成的(如下圖 3.18 所示),每一個 PES 所代 表的就是一個audio PES 或者 video PES 等其他類型 PES。所以解多工函數就會一 層層的的解析出audio PES 和 video PES,並再從 audio PES 和 video PES 當中取 出 PES payload,而這些 PES paylaod 就是前面所說的 video bistream 或 audio bistream,最後這些 bistream 就會由解碼器來解碼。
pack 1 pack 2 program code pack n end
pack 3 . . . . .
pack
header PES 1 PES 2 PES 3 . . . . . PES n
PES
header PES payload
圖3.18:mpeg-2 program stream
所以VLC 就是在解析來源資料的影音包裝格式的時候,會找出 audio 與 video 的編碼格式,然後將 audio 與 video 的編碼格式分別以四字元碼型式來表示,並 且會呼叫module_Need()函數來尋找合適的 audio decoder module 和 video decoder module,而如何找到合適的 decoder module 就是跟四字元碼有關。所以有關 audio 與video 四字元碼型式為何以及如何找到合適的 decoder module 這部分在第四章 則會再詳細的說明。