说明:本篇文章是对上篇文章 The architecture of VLC media framework 的翻译。翻译内容来自GOOGLE,我只是搬运工~~~
VLC是一个免费的开源跨平台多媒体播放器和框架,可播放大多数多媒体文件以及 DVD,音频CD,VCD和各种流媒体协议。从技术上讲,它是一个软件包,可以处理计算机和网络上的媒体。它提供直观的 API和模块化架构,可轻松添加对新编解码器,容器格式和传输协议的支持。来自 VLC的大多数已知软件包是Windows,Linux和 OSX上常用的播放器。
根据Videolan网站,这个项目很难理解:
VLC媒体播放器是一个庞大而复杂的软件。它还使用大量依赖项。
作为开源,VLC开发可以从全球范围内的大型开发人员社区中受益。
但是,对于新开发人员来说,进入VLC媒体播放器等项目可能会很复杂。
代码由专家C黑客编写,有时很难理解。可能需要一本书来解释VLC的工作原理。我会用几句话来总结我在采矿中发现的东西。让我们从高级架构开始:
VLC有一个核心和很多模块(200到400之间取决于构建)。没有模块,VLC无法做很多事情,因为模块提供了您期望的大部分功能。有以下模块:
如前所述,没有模块的VLC是无用的。模块可以根据其功能进行分类。一个能力应该被看作是类似于一个面向对象接口的东西。模块加载很容易解释:当VLC需要具有特定功能的模块时(例如,当需要“解码器”时),它会打开该功能的所有模块,直到匹配为止。它以降低的分数顺序打开它们(首先是较大的分数,最后是较小的分数),并运行Open()
模块的功能。当一个模块返回OK时,VLC使用该模块。你可以在这里进一步阅读。下图显示了VLC中实现的主要模块功能
VLC可以具有不同风格的用户界面。每种接口都是一个模块(在这里你可以找到一个基本的声音设计原则:“将用户逻辑与用户界面分开”)。在实际分配有基于用户界面QT适用于Windows / Linux的,在可可/ Objective-C中的OSX上ncurses的控制台界面和一个用于Maemo操作设备。
只是为了了解模块的物理结构,如果你打开modules/audio_output
目录,你会发现一些看起来像的东西
其中有几种音频输出技术的实现:ALSA,用于Linux的Pulse和OSS,用于Windows的DirectX
VLC定义了一个面向对象的层,用于管理模型。在此基础上有一个VLC对象,定义如下:
/ ******************* ****************************
* vlc_object_t类型。是的,就这么简单:-)
************************************************** *************************** /
/ **主要的vlc_object_t结构* /
struct vlc_object_t
{
VLC_COMMON_MEMBERS
};
VLC_COMMON_MEMBERS是C“#define”,编码如下:
/ * VLC_COMMON_MEMBERS:所有基本vlc对象共有的成员* /
#define VLC_COMMON_MEMBERS \
/ ** \ name VLC_COMMON_MEMBERS \
*这些成员对于所有vlc对象都是通用的
* / \
/ ** @ {* / \
const char * psz_object_type; \
\
/ *邮件标题* / \
char * psz_header; \
int i_flags; \
\
/ *对象属性* / \
挥发性bool b_die; / ** <由外部设置* / \
bool b_force; / ** <由外部设置(例如.module_need())* / \
\
/ *与libvlc结构相关的东西* / \
libvlc_int_t * p_libvlc; / ** <(万恶之源) - 1 * / \
\
vlc_object_t * p_parent; / ** <我们的父母* / \
\
/ ** @} * /
如果您习惯使用面向对象语言,那么您会发现这种结构非常天真。在SRC /其它/ 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实例,定义如下:
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
包含库的主要数据和结构的类型。例如playlist_t
(src / playlist /)是一个中心抽象,它代表一个音乐播放列表。vlm_t
是服务器核心的根对象,允许同时流式传输多个媒体。VLC可以具有不同的用户界面(图形,文本,基于irc,......)。它们由p_intf
变量建模,指向用户界面根对象。
/ **
*私有LibVLC实例数据。
* /
typedef struct libvlc_priv_t
{
。。。。。。
。。。。。。
bool playlist_active;
/ *消息* /
msg_bank_t * msg_bank; /// <消息库
/ *单身对象* /
module_t * p_memcpy_module; /// <使用快速memcpy插件
playlist_t * p_playlist; // <播放列表单身
vlm_t * p_vlm; ///
如前所述,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中还有另一个基本概念,即基于“Observer-Observable”模式构建,它有助于解耦模块之间的通信。它是VLC变量的概念。甲VLC变量是可以被分配给任何一个值VLC对象。有趣的是,变量可以向感兴趣的观众发送__callbacks_。让我们看看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_t pf_callback, void *p_data )
其中psz_name是变量名,pf_callback是函数指针和P_DATA是传递给函数指针作为参数的通用数据。当您更改变量的值时,将TriggerCallback(..)
调用方法并通知变量的所有观察者。这是我在生活中看到的Observer / Observable模式中最复杂的实现。
如何具体应用这个概念?如果我们查看VLC的ncurses文本用户界面,有一段代码表明: 这意味着当“playlist-item-append”变量被更改时,会调用一个回调方法(这是用户界面的本地方法) 。var_AddCallback( p_playlist, "playlist-item-append", PlaylistChanged, p_intf );
PlaylistChanged()
VLC变量是用于解耦模块之间通信的机制。