VLC建立在很多独立的功能模块上面的,象很多媒体播放器系统框架一样,每个模块实现一个新的功能。在阅读本文之前,必须先行阅读“VLC运行核及功能模块”和“VLC如何装载功能模块”章节。本文叙述如何为VLC编写一个新的功能模块。
如果你打算将你的工作上传到VLC,首先请阅读”git简介”一节,除此以外,还要检查”发送补丁”页里面的内容。
首先在modules目录下,找到正确的存放代码的位置。
如果只是增加单文件模块到资源库,只需要添加该文件到moduels.am所在的目录。
如果需要添加比较大的模块到资源库:
创建一个新目录
在新目录下面创建一个新的Modules.am.
在configure.ac文件末尾添加对应的一行语句,用于m akefile。
例如, modules/video_filter/Modules.am文件告诉编译系统哪些源代码将加入videofilter模块。
注意: modules.am里面的缩进使用tab键(ASCII0x09),不是空隔键。
为了使模块可以编译,还需要在configure.ac添加VLC_ADD_PLUGIN或PKG_ENABLE_MODULES_VLC语句,以新模块的名称作为参数。还要参照其它模块编译和链结所需要的参数,进行相应的添加。
当修改configure.ac以后,必须重新运行./bootstrap&& ./configure
VLC 保留模块的缓冲区,用户不应该清除,但是,可以使用vlc–reset-plugins-cache来强行重启。
然后使用
vlc -vvv --color --list
检查插件是否可见。 也可以在QT开发的图形界面下查看。
下面是一个很小的例子,有助于更好的理解。
/***************************************************************************** * Preamble *****************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <vlc_common.h> #include <vlc_plugin.h> /***************************************************************************** * Local prototypes. *****************************************************************************/ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); /***************************************************************************** * Module descriptor *****************************************************************************/ vlc_module_begin() set_shortname( _("testmodule") ) set_description( _("testmodle plug-in") ) set_capability( "testing", 0 ) set_callbacks( Open, Close ) set_category( CAT_INTERFACE ) /* 定义模块分类,默认的一些类别在vlc/include/vlc_configuration.h 中定义*/ add_integer( "test-angle", 1, "DVD Angle", "Default DVD Angle", false) vlc_module_end () /***************************************************************************** * Open: 初始化接口 *****************************************************************************/ static int Open( vlc_object_t *p_this ) { } /***************************************************************************** * Close: 销毁接口 *****************************************************************************/ static void Close( vlc_object_t *p_this ) { }
VLC播放器模块必须包含一个自身的描述表,以及它可以接受的参数。
模块描述表使用vlc_module_begin()开始。
然后设置基本的信息:
set_shortname( _("DVD without menus") ); set_description( _("DVDRead Input") ); set_category( CAT_INPUT ); set_subcategory( SUBCAT_INPUT_ACCESS );
注意: _("")可以用来创建需要翻译的字符串。
能力是模块可以装载的关键 。它定义模块的类型,即是否是数据读取模块,解复模块,还是其它类型的模块。VLC如何装载以及VLC能力的主要类型详情,请参考”VLC如何装载功能模块”一文。
如果VLC装载具体模块名称,直接打开。
如果VLC需要装载某一类型的功能模块(如解码器),VLC将装载所有的具有该能力的模块,按照匹配分数降序排列,然后调用Open()函数,如果返回VLC_SUCCESS,结束。
分数是一个整数,是一个与同一类功能模块的相对值。分数为0的时候,一种特例。下面是一个定义实例:
set_capability( "testing", 10 )
该语句定义testing能力为10.
使用其中一个预定义的分类进行配置。分类和子类的配置决定功能模块的优先顺序。
配置分类包括:
CAT_INTERFACE
CAT_AUDIO
CAT_VIDEO
CAT_INPUT
CAT_SOUT
CAT_ADVANCED
CAT_PLAYLIST
同时,也应该使用某一个子类进行配置。所有的配置分类和子类的定义,参考include/vlc_configuration.h
如果需要给模块添加新的选项,按照下面的语句进行:
add_integer(name, value, text, longtext, advanced)
name是参数的标识,在命令行控制台里面可以用来设置参数的值
value参数的默认值。
text参数的简单描述,使用 _("")创建需要翻译的字符串。
longtext完整的参数描述,使用_("")可以创建需要翻译的文本。
advanced布尔值,如果为TRUE,对应的参数仅仅在—advanced选项的时候显示。
你可以添加下面的选项或参数到模块里面:
add_integer,
add_string,
add_float,
add_bool,
add_key,
add_file,
add_directory,
完整定义,请参看 include/vlc_plugin.h。
模块打开和关闭的函数(随后详细介绍)需要在模块描述表里面定义,这样VLC才可以与模块进行交互。
set_callbacks()宏可以定义两个参数:第一个参数是pf_activate回调函数,第二个是pf_deactivate回调函数。VLC当需要一个插件实例提供正确接口的时候,调用pf_activate回调函数, 像宏里面set_capability()制定的一样。
最重要的模块函数是打开函数:
static int Open ( vlc_object_t * );
VLC运行核尝试打开模块的时候,将调用该函数,进行模块装载。
在Open函数里面,设置各种结构,设备或I/O。可以正常打开的函数将返回VLC_SUCCESS。否则,将认为装载失败。
Open函数里面还进行私有数据的分配(如有),对私有结构进行设置。如果打开失败,需要用户自己清理结构里面的资源。
第二个最重要的函数是关闭函数,其声明如下:
static int Close ( vlc_object_t * );
VLC运行核尝试关闭或卸载模块的时候,调用Close函数。
在关闭函数里面,将清理一些数据结构,I/O或设备。同时,关闭函数也释放一些私有数据。
子模块,如使用add_submodule()语句进行添加
与模块工作原理一样,这写模块对父模块非常有用,可以在模块之间代码公用。子模块在缓冲区分开存放,装载方式与模块一样。
根据模块的能力的类别不同,需要实现的函数或方法也不一样,用户可以在相应的模块分类里面找到更多的信息。下面是一些主要的模块类别清单:
Access/获取
Demux/解复
Access_Demux / 获取并解复
Decoder/解码
Interface/用户界面
Videofilter / 视频滤波
Audiofilter /音频滤波
Audio output /语音输出
有些时候,模块可能不正常工作,这时,需要转入到VLC源代码树的顶层目录,做下面的一些工作。这里假设使用bashshell.
编译文件和环境准备
find . -name .deps -exec rm -rf \{\} \; ./bootstrap ./configure
rm-rf选项具有递归功能,删除在源代码树里面的依赖缓冲区。它可以保证编译器自动查找新的文件。
编译
./compile
最后运行:
./vlc --reset-plugins-cache
有时候,重设config.status里面的.deps缓冲区就可以了:
find . -name .deps -exec rm -rf \{\} \; ./config.status ./compile ./vlc --reset-plugins-cache
如果仅仅上面的几行不能凑效,请按照保险方法进行。
当上面的方法还不能解决问题,可以按照下面的方法进行:
find . -name .deps -exec rm -rf \{\} \; find . -name .libs -exec rm -rf \{\} \; ./bootstrap ./configure ./compile ./vlc --reset-plugins-cache
上面的过程同样删除库缓冲。在这个过程中必须运行configure,而不是config.status,后者在--no-create模式下运行configure,不会重新创建.lib缓冲。
为了能够避免编译整个的VLC源代码,可以单独对一些功能模块进行编译。这样在有些情况下非常有用的。例如新的模块由于只是版权问题不能随VLC一起发布。单独编译可以降低VLC的依赖,增加模块的独立性。
独立的模块不能使用VLC的主国际化域。需要添加自己的域。声明如下:
#define DOMAIN "vlc-myplugin" #define _(str) dgettext(DOMAIN, str) #define N_(str) (str) /* ... */ vlc_module_begin() set_text_domain (DOMAIN) set_description (N_("My plugin")) /* ... */ vlc_module_end()
或者完全禁止国际化:
#define _(str) (str) #define N_(str) (str)
为了保证VLC能装载模块,需要定义 __PLUGIN__关键字.这个可以使输出的符号类似下面的几行:(该处译者不甚明白,请参照原文。)
EXPORTS vlc_entry__1_1_0g vlc_entry_license__1_1_0g
同样,还可以定义__LIBVLC__关键字。
下面的Makefile是VLC的X264模块的例子。
libdir = $(shell pkg-config --variable=libdir vlc-plugin ) vlclibdir = $(libdir)/vlc all: libx264_plugin.so libx264_plugin.so: libx264_plugin.o gcc -shared -std=gnu99 $< `pkg-config --libs vlc-plugin x264` -Wl,-soname -Wl,$@ -o $@ libx264_plugin.o: x264.c gcc -c -std=gnu99 $< `pkg-config --cflags vlc-plugin x264` -D__PLUGIN__ -DMODULE_STRING=\"x264\" -o $@ clean: rm -f libx264_plugin.o libx264_plugin.so install: all mkdir -p $(DESTDIR)$(vlclibdir)/ install -m 0755 libx264_plugin.so $(DESTDIR)$(vlclibdir)/ install-strip: all mkdir -p $(DESTDIR)$(vlclibdir)/ install -s -m 0755 libx264_plugin.so $(DESTDIR)$(vlclibdir)/ uninstall: rm -f -- $(DESTDIR)$(vlclibdir)/libx264_plugin.so .PHONY: all clean install uninstall
如果使用automake和libtool,按照下面Makefile.am例子进行:
vlclibdir = $(libdir)/vlc vlclib_libx264_plugin_la_SOURCES = x264.c vlclib_libx264_plugin_la_CFLAGS = $(VLC_PLUGIN_CFLAGS) $(X264_CFLAGS) \ -D__PLUGIN__ -DMODULE_STRING=\"x264\" vlclib_libx264_plugin_la_LIBADD = $(VLC_PLUGIN_LIBS) $(X264_LIBS) vlclib_libx264_plugin_la_LDFLAGS = \ -avoid-version -module -export-symbol-regex ^vlc_entry