VLC 架构初步分析

时间:2021-10-04 15:52:13

 

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