vlc內部執行機制以及架構分析
vlc架構剖析
1. VideoLan簡介
1.1 videolan組成
Videolan有以下兩部分組成:
VLC:一個最主要的部分,它可以播放各種型別的媒體檔案和流媒體檔案,並且可以創造媒體
流並儲存成各種格式的媒體檔案,這些檔案的質量要比沒儲存前的件好。videolan作為客戶
端可以播放本地檔案,httP://,rtsp://。
VLS:是一種流伺服器,專門用來解決流的各種問題,它也具有一些VLC的特徵。videolan作
為伺服器可以輸出httP,rtP,rtsp的流。
1.2 VLC優點
VLC是一種跨平臺的媒體播放器和流媒體伺服器,最初為videolan的客戶端,它是一種
非常簡便的多媒體播放器,它可以用來播放各種各樣的音
2、MPEG- 4、DivX、WMV、mp3、OGG、Vorbis、AC3、AAC等等)流媒體協議,最具特色的
功能是可以邊下載邊觀看Divx媒體檔案,並可以播放不完全的AVI檔案。並且支援介面的
更改。VLC支援多種的作業系統,linux(rh9,Debian,Mandrake,Gentoo),BSD,windows,
Mac OS X,Be OS,Solaris等等。支援帶選單的VCD,SVCD,和DVD,數字衛星頻道、數字
地球電視訊道(digital terrestrial television channels),在這些作業系統下通過寬頻IPv4、IPv6
網路播放線上影片。此軟體
了多平臺的支援,可以用於播放網路流媒體及本機多媒體檔案,特別是它能直接播放未下載
完整的多媒體檔案。
下圖表示出了VideoLan的解決方案:
VideoLan Client是VideoLan專案(一個完整的MPEG-2客戶/伺服器解決方案)的一個組成
部分。不過VideoLan Client也可以作為一個獨立的程式來播放來自硬碟或者DVDROM的
MPEG資料流。它目前支援GTK+、GNOME、KDE和QT,並且可以使用X11、Xvideo、SDL
或者DirectX作為視訊輸出。對於聲音,VideoLan Client支援OSS、ALSA和ESD。要訪問DVD,
VideoLan Client使用的是Libdvdcss庫。它是一個簡單的專為DVD訪問設計的庫。它可以像
訪問塊裝置一樣訪問DVD,而不用考慮解密問題。
2. VLC整體架構分析
2.1 LibVLC
LibVLC是VLC的核心部分。它是一個提供介面的庫,比如給VLC提供些功能介面:流的
接入,音訊和視訊輸出,外掛管理,執行緒系統。所有的LibVLC原始碼位於src\及其子目錄:
Interface/:包含與使用者互動的程式碼如按鍵和裝置彈出。
Playlist/:管理播放列表的互動,如停止,播放,下一個,或者隨機播放。
Input/:開啟一個輸入元件,讀包,解析它們並且將被還原的基本流傳遞給解器。
Video_output/:初始化video顯示器,從解碼器得到所有的圖片和子圖片(如subtitles)。隨意
將它們轉換為其它格式(如:YUV到RGB)並且播放。
Audio_output/:初始化音訊mixer(混合器)。如:發現正確的播放頻率,然後重新制作從解碼器
接收過來的音訊幀。
Stream_output/:類似Audio_output。
Misc/:被libvlc其它部分使用的雜項,如執行緒系統,訊息佇列,CPU探測,物件查詢系統,
或者特定平臺程式碼。
2.2 VLC
VLC是一個純粹圍繞著LibVLC寫成的程式。它是非常小的,但是功能很齊全的媒體播放
器,歸功於LibVLC的動態元件支援。
2.3 元件
元件位於modules\子目錄,在執行時被載入。每一個元件提供不同的特徵適應特定的
檔案的環境。另外,大量的不斷編寫的可移植功能位於audio_output\,vidco_output\和
interface\元件,以支援新的平臺(如:BeoS Mae OS X)。
元件中的外掛被位於src\misc\modules.c和include\modules*.h中的函式動態載入和卸
載。寫元件的API描述如下,共3種:
(l)元件描述巨集:宣告元件具有哪種優先順序的能力(介面,demux2等等),還有GUI元件的
實現引數,特定元件的配置變數,快捷方式,子元件等等;
(2)Open(vlc_objeet_t*p_object):被VLC呼叫初始化這個元件,它被元件描述巨集賦值給了
結構體module_t中的pf_activate函式指標,被Module_Need呼叫;
(3)Close(vlc_objeet_t*p_object):被VLC呼叫負初始化這個元件,保證消耗Open分配的所
有資源。它被元件描述巨集賦值給了結構體module_t中的pf_deactivate函式指標,被
Module_Unneed呼叫。
用LibVLC寫的元件能夠直接被編譯進VLC,因為有的OS不支援動態載入程式碼。被靜態
編譯進VLC的元件叫做內建元件。
2.4 執行緒分析
(l)執行緒管理:
VLC是一個密集的多執行緒應用。由於解碼器必須預先清空和播放工序必須預先做好流程
(比如說解碼器和輸出必須被分開使用,否則無法保證在要求的時間裡播放檔案),因此VLC
不採用單執行緒方法。目前不支援單執行緒的客戶端,多執行緒的解碼器通常就意味著更多的開銷
(各執行緒共享記憶體的問題等),程序間的通訊也會比較複雜。
VLC的執行緒結構基於pthreads執行緒模型。為了可移植的目的,沒有直接使用pthreads
函式,而是做了一系列類似的包裹函式:vlc_thread_create,vlc_thread_exit,vlc_thread_join,
vlc_mutex_init,vlc_mutex_lock,vlc_mutex_unlock,vlc_mutex_destroy,vlc_cond_init,
vlc_cond_signal,vlc_cond_broadcast,vlc_cond_wait,vlc_cond_destroy和類似結
構:vlc_thread_t,vlc_mutex_t,and vlc_cond_t。
(2)執行緒同步:
VLC的另一個關鍵特徵就是解碼和播放是非同步的:解碼由一個解碼器執行緒工作,播放由音
頻輸出執行緒或者視訊輸出執行緒工作。這個設計的主要目的是不會阻塞任何解碼器執行緒,能夠
及時播放正確的音訊幀或者視訊幀。這樣實現也導致產生了在介面,輸入,解碼器和輸出之
間的一個複雜的通訊結構。
雖然當前介面並不允許,但是讓若干個輸入和視訊輸出執行緒在同一時刻讀取多個檔案是
可行的(這是VLC未來改進的主要方向)。現在的客戶端就是用這種思想實現的,這就意味著
如果沒有用到全域性鎖的話那麼一個不能重入的庫是不能被使用的(尤其是liba52庫)。
VLC輸出的流裡包含時間戳,被傳遞給解碼器,所有有時間戳標記的流也均被記錄,這
樣輸出層可以正確及時的播放這些流。時間mtime_t是一個有符號的64-bit整形變數,單位
是百萬分之一秒,是從1970年7月1日以來的絕對時間。
當前時間能夠被mdate()函式恢復。一個執行緒可以被阻塞到mwait(mtime_t date)等到一
個確定的時間才被執行。也可以用msleep(mtime_t delay)休眠一段時間。如果有重要的事情
要處理的話,那麼應該在正常時間到來之前被喚醒(如色度變換)。例如在
modules\codec\mpeg_vldeo\synchro.c中,通常的解碼時間被記錄,保證影象被即時解碼。
3. VLC介面技術分析
3.1 VLC執行過程
通過對相關資料和自己的分析,VLC的執行過程如下:
ELF(Linux下可執行檔案的格式)先被動態載入,然後主執行緒就變成了介面執行緒並且在
src/interface/interface.c中開始。它執行下列步驟:
1.cpu探測:什麼型號?所有能力(MMX,MMXEXT,3DNow,AltiVec等等)
2.訊息介面初始化;
3.命令列選項解析元件
4.建立播放列表
5.倉庫初始化
6.載入所有內建和動態元件
7.開啟介面
8.安裝訊號處理器:SIGHUP,SIGINT和SIGQUIT(捕獲一個,忽略後來的並退出)。
9.派生音訊輸出執行緒;
10.派生視訊輸出執行緒;
11.主迴圈:事件管理;
下圖表示了這些步驟的執行過程:
VLC的執行過程圖
由於printf()函式不是執行緒安全的,因此在呼叫printf()函式時一個執行緒的執行將會受到
干擾,當這個執行緒被另一個函式所呼叫時就會其狀態被破壞而退出程式。所以VLC構造了自
己的執行緒安全的訊息介面。
VLC的執行緒安全的訊息介面有兩種實現方式:如果在config.h裡定義了INTF_MSG_QUEUE
的話,每一個類似printf()的函式將會把排隊的訊息放到連結串列裡,這個連結串列將會在事件迴圈
中被執行緒介面用紅色標記的方式打印出來。如果INTF_MSG_QUEUE沒被定義的話,呼叫線
程將會獲得一個print lock(用來防止在同一時刻有兩個printf操作被執行)同時直接打印出消
息(預設操作)。
以下為VLC執行緒安全訊息的API:
QueueMsg:新增一條訊息到訊息佇列,如果訊息佇列滿了,先列印所有的訊息;
FlushMsg:列印所有在訊息佇列裡的訊息,特別的,訊息佇列必須被提前加鎖,因為該
函式不檢查鎖。
PrintMsg:列印一條訊息到stderr,可以列印彩色訊息。
3.3 命令列選項
VLC用GNU的getopt解析命令列選項。Getopt結構定義在src\extras\getopt.h裡。所有
的配置也可以用環境變數改變:呼叫函式main_Put*Variable和main_Get*Variable。所
以,.\vlc--height=240和 .\vic_height=240./vlc(這種方式用於所有地方,包括外掛)是一樣的。
但是為了執行緒安全的考慮,當第二個執行緒派生了,main_Put*Variable便不能被使用了。