VLC player的大架構不難理解,難理解的是它的對象meta系統的作用,類的繼承機制,類的層次關系,以及消息傳遞路線。
Meta系統
VLC實現了對象的Meta系統,我初步認為其作用為:
• 輕松實現屬性永久化,與配置文件原生態結合。
• 動態屬性
• 很多流程或者說消息驅動機制由屬性的可觀查機制實現。
類(結構)的繼承
• 大部分類都是從vlc_object_t繼承的。
• vlc_object_t實現了對象之間的父子關系(不同於繼承)。
• 繼承的形式並不是像真正的類那樣或者是把vlc_object_t變量放在結構的最開始,而是用了一個宏:VLC_COMMON_MEMBERS放在了類的最開始處。
• 每個插件實現的私有數據保存在插件的公開類的帶有“sys”或“priv”字串的類型的變量中。
類的層次關系
• 對外的最高類是libvlc_instance_t,外部接口要使用的主要參數是libvlc_instance_t對象,由libvlc_new()創建。
• 內部操作的主要對象是libvlc_int_t,但其實就是libvlc_priv_t ,但更其實libvlc_priv_t和libvlc_int_t和libvlc_instance_t跟本就是一個對象。只不過為了實現封裝性搞出這麼多花樣,還不如都放一塊然後用注釋說明呢。
• libvlc_priv_t包含了很多一個instance中應包含的東西,比如播放列表,media_library,流媒體服務器對象,熱鍵映射動作列表,界面對象等。所以獲得了libvlc_int_t對象,就能獲得所有其它對象。
消息傳遞路線
• meta的可觀查機制是消息驅動的基礎。
• 舉個例子,playlist_t對象會觀查input_thread_t對象。input_thread_t對象代表一個輸入流。當input_thread_t對像的一些動態屬性發生變化時,就會通知playlist_t,而如果界面偵聽了playlist_t,就可以在界面上表現出這些變化。反之,如果playlist_t要操作input_thread_t,也是通過設置input_thread_t的動態屬性的值來做的。
流程粗析
QT消息與meta通知的配合
• 界面的主要作用一是提供用戶控制libvlc核心對象的用戶接口,二是舉核心對象的狀態顯示給用戶看。
• 要通過界面控制核心對象,只要在界面插件內部能獲取到libvlc_int_t,就能獲取到任何其它對象,然後利用現有函數或直接操作它的動態屬性即可。
• 要將核心對象的狀態反映到界面上來,就需要界面插件觀查這些對象的相關動態屬性。比如input_thread_t集中地通過一個動態屬性向觀查者通知事件的發生,這個屬性叫做:”intf-event”.
• input_event_type_e中定義了所有input所能發出的事件通知。
• Qt界面插件用一個全局函數InputEvent()作為“intf-event”動態屬性的回調函數,以響應”intf-event”事件。
• playlist對象管理input對象,因為每次播放playlist中的一個條目,都需要建立一個新的input對象。Qt界面插件用一個InputManager對象對應一個input對象,用一個MainInputManager對象對應一個(也是唯一的)playlist對象。
• InputManager對象負責從input對象中接收事件,通過InputEvent()把”intf-event”轉換為QT事件,然後post給自己的customEvent()函數,在其中又轉換為signal發出。但這個類不負責操作input對象,應該是playlist對象負責了。
• MainInputManager對象負責從playlist對象中接收事件,偵聽了playlist的多個屬性。在回調函數中把屬性變化轉換為QT事件,然後將事件郵寄給自己,在接收函數中把事件轉換為signal發出。同時,MainInputManager對象還負載操作playlist對象。
• 對鍵盤與響應函數的映射,在libvlc內部已做好了。調用者要做的就是把鍵值轉換為libvlc內部定義的鍵值,然後以libvlc_int_t對象的"key-pressed"屬性傳給libvlc內部。內部會調用此屬性的偵聽函數,對鍵盤事件做出正確的反映。
Qt界面插件解析
• 界面在libvlc初始化完成後調用libvlc_add_intf()時被添加。
• 在插件的Open()函數中創建和初始化私有數據:intf_sys_t。然後又創建了界面所在的線程。
• 界面線程中創建QApp實例,創建主界面窗口,進入消息循壞。
• 主窗口界面分為三部分:菜單欄,中心區,狀態欄,分別由VLCMenuBar::createMenuBar(),createMainWidget(),createStatusBar()三個函數創建。
• 中心區包含很多控件,有控制欄,輸入欄,高級控制欄,視頻顯示控件,背景控件,播放列表控件等。當然有些控件是互斥顯示的。
• 含有多個控件的控件,比如控制欄和輸入欄,高級控制欄等,它們內部的控件都是跟據配置文件中的條目依次創建的。配置文件中的每個控件都對應一個類型,這個類型直接指定了控件的功能。
• 所有包含多個控件的控件,都是從AbstractController派生,AbstractController中為了能對自己包含的所有控件的信號進行統一處理,以支持可編程性,使用了QSignalMapper。所有的控件都把自己的信號加入mapper中,將mapper的信號連接到ActionsManager對象,在ActionsManager的doAction()中處理所有的控件的信號。
關於如可實現同步播放的思路
• 擴展Qt界面插件
• 使用非阻塞式Qt socket:QTcpSocket。
• Socket處於界面所在線程中。
• 創建主界面窗口後立即創建tcp socket
• 在界面收到用戶輸入信號或libvlc信號時,向另一個端發出命令包。
• socket從另一端收到命令包後,跟據命令的內容,直接調用libvlc的相關函數。
如何向工程中添加新的界面
• 在src/modules/gui/qt4/dialogs下添加定義界面的頭文件和源碼文件。
• 修改src/modules/gui/qt4下的makefile.am文件。
• 向nodist_SOURCES_qt4增加項。
• 向SOURCES_qt4增加項。
• 向noinst_HEADERS增加項。
• 如果要鏈接新的QT庫,則修改src/configure.ac。比如增加鏈接QtNetwork庫,則找到行:PKG_CHECK_MODULES(QT4,[QtCore QtGui >= 4.6.0], [ 改為PKG_CHECK_MODULES(QT4, [QtCore QtGui QtNetwork >=4.6.0], [
• 執行autoreconf命令
• ./configure
• make
如何向工程中增加圖像資源
• 將圖像文件,比如a.png放到/modules/gui/qt4/pixmaps下。
• 在modules/gui/qt4/Modules.am中的DEPS_res區增加一項:pixmaps/a.png。
• 在modules/gui/qt4/Modules.am中的<qresource prefix="/">區增加一條:<filealias="a_icon">pixmaps/a.png</file>
• autoreconf,configure
• 在源文件中這樣使用:QIcon(“:a_icon”);
• make