VLC媒体播放器系统结构
VLC是免费和 开源跨平台多媒体播放器,一个可以播放大部分多媒体格式如DVD,CD,VCD和各种媒体流协议的框架。从技术来讲,只是一个处理计算机和网络上的媒体数据的软件包。VLC提供一套直观的API和模块化框架,可以很容易地添加新的编解码器,容器格式和传输协议。
源代码由C语言方面的专家编写。有时非常难以理解。要完全解释VLC播放器工作原理的话,可能需要编写一本书。我将努力用最少的语言文字来说明我对VLC源代码的研究的一些结果。让我们从最顶层的系统结构开始吧。
VLC由一个运行核和很多功能模块组成(200-400不等),VLC不能没有功能模块,很多功能都有由模块提供。主要有下面几个模块:
- libVLCcore是框架的中心模块。提供一个面向对象的层给c语言写成的模块,进行模块装载,对多媒体操作的功能抽象,如:输入,复用,解复,语音输出,视频输出。
- modules实现livVLCcore具体的功能,模块按照不同的能力进行分类。如输入管理模块(文件,网络),解码模块(mp3,dirvx),用户接口模块(文本,网页,telnet,等)
- Externallibraries外部依靠文件,通常为具体的功能模块服务。
- vlc (主要程序)–播放器主要入口,初始化libVLC,建立和显示用户接口(播放器窗口)。
功能模块
没有功能模块,VLC实际上是没有任何用处的。功能模块可以根据其能够提供的功能分类。每个具体的功能可以认为是类似面向对象的接口。模块装载简单解释如下:当VLC需要一个特别功能的模块(如解码功能)的时候,将打开所有的模块,然后找到一个最合适的。打开的时候按照匹配的分数按照从大到小进行排列,调用模块的Open()函数,如果返回OK,VLC就使用该模块。(更多详情,可以参考”VLC如何装载模块”一文)。下图说明一些主要模块的功能。
VLC 提供不同风格的用户界面。每个界面也是一个模块。与VLC一起发布的界面有基于QT的用于rWindows/Linux的界面, 基于Cocoa/Objective-C用于OSX的界面,基于Ncurses用于DOS控制台的界面和用于Maemo设备的界面等。
下图说明功能模块代码在文件系统中存放结构,以modules/audio_output为例。
audio_output目录下实现了不同语音技术的输出:ALSA,Pulse 和OSS,DirectX(Windows平台)
面向对象的层 – VLC对象
VLC定义了一些管理模块的层,这些层使用面向对象开发方法。VLC对象的定义如下:
/*****************************************************************************
* The vlc_object_t type. Yes, it's that simple :-)
*****************************************************************************/
/** The main vlc_object_t structure */
struct vlc_object_t
{
VLC_COMMON_MEMBERS
};
VLC_COMMON_MEMBERS is a C“#define” coded as the following:
/* VLC_COMMON_MEMBERS : members common to all basic vlc objects */
#define VLC_COMMON_MEMBERS \
/** \name VLC_COMMON_MEMBERS \
* these members are common for all vlc objects \
*/ \
/**@{*/ \
const char *psz_object_type; \
\
/* Messages header */ \
char *psz_header; \
int i_flags; \
\
/* Object properties */ \
volatile bool b_die; /**< set by the outside */ \
bool b_force; /**< set by the outside (eg. module_need()) */ \
\
/* Stuff related to the libvlc structure */ \
libvlc_int_t *p_libvlc; /**< (root of all evil) - 1 */ \
\
vlc_object_t * p_parent; /**< our parent */ \
\
/**@}*/
在 src/misc/objects.c里面,定义了VLC对象的创建,销毁和查找方法:vlc_custom_create(..)
,vlc_object_destroy(...)
,vlc_object_find(...)
.例如,下面的例子创建一个心的VLC对象:
p_libvlc = vlc_custom_create( (vlc_object_t *)NULL,
sizeof (*priv),VLC_OBJECT_GENERIC, "libvlc" );
LibVLC 实例
VLC库的根类是libvlc实例,该类定义如下:
struct libvlc_instance_t
{
libvlc_int_t *p_libvlc_int;
libvlc_vlm_t libvlc_vlm;
unsigned ref_count;
int verbosity;
vlc_mutex_t instance_lock;
struct libvlc_callback_entry_list_t *p_callback_list;
};
这里 libvlc_int_t
为一个vlc对象。在初始化过程中,指向一个类型为libvlc_priv_t
的对象。
这个
libvlc_priv_t
类型包含库主要的数据和结构。如
playlist_t
(src/playlist/)为一个主要的抽象结构,表示媒体播放清单。vlm_t
是服务器运行核的根对象,允许多个媒体流同时运行。
VLC可以有不同的用户界面,这些界面模块功过p_intf
变量指向的对象进行访问。
/**
* Private LibVLC instance data.
*/
typedef struct libvlc_priv_t
{
. . . . . .
. . . . . .
bool playlist_active;
/* Messages */
msg_bank_t *msg_bank; ///< The message bank
/* Singleton objects */
module_t *p_memcpy_module; ///< Fast memcpy plugin used
playlist_t *p_playlist; //< the playlist singleton
vlm_t *p_vlm; ///< the VLM singleton (or NULL)
vlc_object_t *p_dialog_provider; ///< dialog provider
httpd_t *p_httpd; ///< HTTP daemon (src/network/httpd.c)
. . . . . .
. . . . . .
/* Interfaces */
struct intf_thread_t *p_intf; ///< Interfaces linked-list
} libvlc_priv_t;
模块抽象和通信
正如当前谈到的,VLC是有一套功能模块组成。在开发过程中,主要分两个方面:
- 初始化。调用
open()
和
close()
函数。
- 实现功能。实现每个具体的功能需要的方法。
例如,语音输出类模块需要实现下面的方法:
..................
int aout_VolumeDown( vlc_object_t * p_object, int i_nb_steps,audio_volume_t * pi_volume )
int aout_VolumeUp( vlc_object_t * p_object, int i_nb_steps, audio_volume_t * pi_volume )
int aout_VolumeGet( vlc_object_t * p_object, audio_volume_t * pi_volume )
int aout_ToggleMute( vlc_object_t * p_object, audio_volume_t * pi_volume )
..................
视频输出类的模块需要实现下面的方法:
....................
picture_t *vout_CreatePicture( vout_thread_t *p_vout, bool b_progressive,bool b_top_field_first,
unsigned int i_nb_fields )
void vout_DisplayPicture( vout_thread_t *p_vout, picture_t *p_pic )
void DestroyPicture( vout_thread_t *p_vout, picture_t *p_picture )
void vout_DropPicture( vout_thread_t *p_vout, picture_t *p_pic )
....................
另外一个VLC的基本概念是:按照“察看者/可察看“结构开发,这样可以降低模块之间的耦合度。这个结构通过VLC变量的这种概念实现。
所谓VLC变量是一个可以设置为任何VLC对象的值。令人感兴趣的是,这些变量可以激活回调函数。下面说明变量如何工作的:
- 首先创建变量,使用
var_Create(p_libvlc, name, VLC_VAR_INTEGER )
。
p_libvlc为指向vlc对象的指针,name为变量名称,VLC_VAR_INTEGER为类型。 - 当需要设置变量的时候,调用
var_SetInteger(p_libvlc, name, temp_value)
之类的函数。
temp_value是需要设置的值。对应地,可以使用var_getInteger
将数据读取回来。
var_AddCallback(vlc_object_t *p_this, const char *psz_name,vlc_callback_tpf_callback, void *p_data )
可以设置回调函数,
psz_name为变量名称,pf_callback为函数指针,p_data为通用的函数指针,指向用户数据。- 当改变一个变量的值的时候,
TriggerCallback(..)
方法将会被调用,这样变量的察看者将会获得通知。
这个机制如何具体实现的呢?让在VLC的ncurses文本用户界面模块里面有一段代码:var_AddCallback(p_playlist, "playlist-item-append", PlaylistChanged, p_intf);
该代码的作用是:当 “playlist-item-append”变量发生变化时,PlaylistChanged()
回调函数将会被调用。
这就是”察看者/可察看”结构的复杂执行的执行机制。