通过了解Gstreamer框架,我们可以知道,每个处理单元都是一个plugin,那么,plugin在gstreamer是怎么注册到系统中的呢,gstreamer又是如何知道,现在有哪些plugin,同时具备某一功能的plugin又有哪些,下面,我们来通过阅读源码,一起分析一下。
通过gstreamer的一些文档,我们可以了解到,插件,直接就是通过plugin_init()函数注册到Gstreamer中的,每个plugin都是在plugin_init()函数中通过gst_element_register()函数将plugin的相应信息注册到gstreamer中。如下:
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_element_register (plugin, "mssdemux",
GST_RANK_PRIMARY, GST_TYPE_MSS_DEMUX))
return FALSE;
return TRUE;
}
plugin_init()函数只有一个参数,这个参数就是GSTPlugin指针类型的plugin指针,现在我们不知道它具体是干嘛的,但是我们可以先猜测一下,这个应该是在gstreamer核心在进行注册plugin的时候,通过传进指针得到plugin的相应信息,而后方便管理和查找,我们后续验证一下,看看这个函数调用的时候,是否如同我们猜测的那样。
在plugin_init()函数中,通过gst_element_register()函数将plugin的相应信息注册到gstreamer中,gst_element_register()函数声明如下:
/**
* gst_element_register:
* @plugin: (allow-none): #GstPlugin to register the element with, or %NULL for
* a static element.
* @name: name of elements of this type
* @rank: rank of element (higher rank means more importance when autoplugging)
* @type: GType of element to register
*
* Create a new elementfactory capable of instantiating objects of the
* @type and add the factory to @plugin.
*
* Returns: %TRUE, if the registering succeeded, %FALSE on error
*/
gboolean
gst_element_register (GstPlugin * plugin, const gchar * name, guint rank, GType type);
从gst_element_register()的函数声明我们可以了解到,通过该函数,可以创建一个名称为name、优先级为rank的type类型elementfactory,并将elementfactory添加到plugin。在gstreamer中,每个plugin都应对应的名称,也都会有相应的信息说明它具备什么功能,但是很有可能在gstreamer中具备相同功能的plugin有多个呢,这个时候它又如何知道,该选那个了呢。这时就是rank起作用了,每个plugin都具备相应的功能,同时在注册plugin的时候也都会通过rank说明plugin的优先级,在遇到相同功能的plugin,gstreamer会优先选择rank数值大的plugin,rank在gstreamer系统中,默认的plugin rank最大值也就是GST_RANK_PRIMARY(256),所以当你自己编写一个插件,希望gstreamer在使用你插件所具备的功能,在注册plugin的时候,将rank的值设置为比GST_RANK_PRIMARY大即可,这样就将会优先选择你的plugin。
在上一节gstreamer学习笔记—Gobject类对象,我们说到过,在系统第一次调用某类型的type_name##_get_type()函数时,将会向gobject系统注册该种新的类型,那么,类型的第一次调用,是在什么时候呢,就是在plugin_init()函数中,将type_name##_get_type()作为函数参数type传递给gst_element_register()函数,从而完成类型向gobject系统的注册,只不过大多数的时候,type_name##_get_type()函数都被封装为一个宏定义GST_TYPE_xxx。
下面我们继续来分析一下gst_element_register()函数都干了什么。gst_element_register()代码有点多,就没有直接贴代码,下面通过流程图介绍一下它的工作与步骤。
通过阅读代码最后我们知道,之前我们的猜测是错误的,传进的plugin指针,只是通过它获取到plugin的一些描述信息,并将factory与plugin建立弱连接。而factory与gstreamer的联系,真正的都是在全局指针变量_gst_registry_default中。一般的,padtemplates信息是在plugin的类初始化函数xxx_class_init()通过gst_element_class_add_static_pad_template()函数添加到plugin。
接着我们继续分析,在gst_element_register()函数最后,调用了gst_registry_add_feature()函数,该函数又是如何操作的,函数声明如下:
/**
* gst_registry_add_feature:
* @registry: the registry to add the plugin to
* @feature: (transfer floating): the feature to add
*
* Add the feature to the registry. The feature-added signal will be emitted.
*
* @feature's reference count will be incremented, and any floating
* reference will be removed (see gst_object_ref_sink())
*
* Returns: %TRUE on success.
*
* MT safe.
*/
gboolean
gst_registry_add_feature (GstRegistry * registry, GstPluginFeature * feature)
在gst_registry_add_feature()函数中,很简单,就是将传进来的feature保存到全局变量的_gst_registry_default的priv->features成员中,同时根据feature的name生成相应的hash保存(在查找feature时就是根据name的hash快速查找),这里的feature,就是gst_element_register()
函数传进来的name命名的element,同时它的caps将会说明element所支持的功能。最后会通过g_signal_emit (registry, gst_registry_signals[FEATURE_ADDED], 0, feature)
函数发送一个添加了feature的信号,这个信息是谁来进行接收呢,我们再继续看代码。
通过上述的代码阅读,我们知道,gstreamer的plugin注册是通过在plugin_init()函数中调用gst_element_register()函数完成的,那么plugin_init()函数又是什么时候调用呢?
细心的童鞋会发现,其实每个plugin_init()函数定义都是个静态函数,也就证明该函数只是在本文件起作用,同时,在同一个C文件中有通过GST_PLUGIN_DEFINE这样的一个宏定义对plugin_init()进行修饰,而GST_PLUGIN_DEFINE这个宏是干嘛的呢,我们来看一下它的定义。
GST_PLUGIN_DEFINE详细定义在gstplugin.h,将其实现展开如下:
#define GST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origin)
/* 文本中的name都将使用GST_PLUGIN_DEFINE传进的name替换 */
GST_PLUGIN_EXPORT const GstPluginDesc * gst_plugin_name_get_desc (void);
GST_PLUGIN_EXPORT void gst_plugin_name_register (void);
static const GstPluginDesc gst_plugin_desc = {
major,
minor,
G_STRINGIFY(name),
(gchar *) description,
init,
version,
license,
PACKAGE,
package,
origin,
__GST_PACKAGE_RELEASE_DATETIME,
GST_PADDING_INIT
};
const GstPluginDesc *
gst_plugin_name_get_desc (void)
{
return &gst_plugin_desc;
}
void
gst_plugin_name_register (void)
{
gst_plugin_register_static (major, minor, G_STRINGIFY(name),
description, init, version, license,
PACKAGE, package, origin);
}
从GST_PLUGIN_DEFINE的展开我们可以看到,其实它就是定义了两个函数,分别是
GST_PLUGIN_EXPORT const GstPluginDesc * gst_plugin_name_get_desc (void);
GST_PLUGIN_EXPORT void gst_plugin_name_register (void);
gst_plugin_name_get_desc()函数将会返回一个描述plugin的结构体,里面包含了一系列plugin详细信息,主要有plugin编译的gstreamer-core版本号、插件名称、描述信息、传进来作为初始化函数的plugin_init()函数、版本号、许可证等。
gst_plugin_name_register()函数则是将通过gst_plugin_register_static()函数完成plugin的静态注册登记。gst_plugin_register_static()函数主体实现如下:
gboolean
gst_plugin_register_static (gint major_version, gint minor_version,
const gchar * name, const gchar * description, GstPluginInitFunc init_func,
const gchar * version, const gchar * license, const gchar * source,
const gchar * package, const gchar * origin)
{
GstPluginDesc desc = { major_version, minor_version, name, description,
init_func, version, license, source, package, origin, NULL,
};
GstPlugin *plugin;
gboolean res = FALSE;
/* 一系列的输入参数检查以及gstreamer core初始化检查 */
GST_LOG ("attempting to load static plugin \"%s\" now...", name);
plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
if (gst_plugin_register_func (plugin, &desc, NULL) != NULL) {
GST_INFO ("registered static plugin \"%s\"", name);
res = gst_registry_add_plugin (gst_registry_get (), plugin);
GST_INFO ("added static plugin \"%s\", result: %d", name, res);
}
return res;
}
在该函数中,将会先通过gst_plugin_register_func()函数进行plugin的注册,gst_plugin_register_func()将会检查版本之类的信息,然后拷贝关于plugin的描述信息,在通过函数指针desc->plugin_init真正的完成plugin的注册(desc->plugin_init函数指针指向的就是上述说到的plugin_init()函数),这样,就调用到了plugin_init()。接着,在gst_plugin_register_static()函数中还调用了gst_registry_add_plugin()函数,gst_registry_add_plugin()函数与gst_registry_add_feature()函数类似,gst_registry_add_feature()是将feature保存到全局变量的_gst_registry_default的priv->features成员,而gst_registry_add_plugin()函数则是将plugin保存到_gst_registry_default的priv->plugins成员。
通过分析宏定义GST_PLUGIN_DEFINE之后,我们知道了plugin_init()函数是被gst_plugin_name_register()函数调用的,那么gst_plugin_name_register()函数,又将会是被谁调用呢,我们接着往下看。
上面一节我们说到gst_plugin_name_register()函数是被谁调用,在GST_PLUGIN_DEFINE的展开我们可以看到,gst_plugin_name_register()函数是被GST_PLUGIN_EXPORT修饰的,但是,通过查看gstconfig.h中GST_PLUGIN_EXPORT的解析我们可以知道,这个宏只是在Windows导出符号给DLL,其他平台更多的是为空操作。那么是不是说我们刚才的上面一节的分析是不是都是错误的呢?嗯,是的,起码在Linux平台我们的分析是有点问题了,上述描述的更多的是静态加载,在使用过程中,更多的是动态加载,但是先别急,接着往下看。
在gstreamer编程中,都会在main()函数首先调用gst_init(NULL, NULL)
函数,插件的注册登记是否会和它有关呢,看看它的实现。通过阅读代码,我们可以看到gst_init()函数的与插件注册相关的函数调用关系,我们重点关注plugin注册的部分,函数调用如下:
在1:gst_init_get_option_group()
函数中,会设置post_parse_func函数指针指向init_post()
,这个最终对在2:处进行调用。而在init_post()
函数,会先准备gstreamer-core环境,检查当前环境有没有库更新,如果有,将会执行更新操作,这部分就是在3:处进行的。在4:ensure_current_registry()
函数中,将会检查一系列的环境变量,确认搜索库的路径,然后会在5:gst_registry_scan_path_level()
进行循环,搜索相应的库进行加载,最后是在gst_registry_scan_plugin_file()
中,通过_priv_gst_plugin_loader_funcs.load
成员函数plugin_loader_load()
进行plugin加载。
接下来,我们将会详细的解析plugin_loader_load()函数的操作,看看gstreamer究竟是如何进行plugin加载的。
所谓的plugin加载,并不是什么动态库加载,动态库的加载过程,在程序一开始运行的时候就已经加载了,我们所说的plugin注册过程(或者说加载过程),只是从机器端的相应路径搜索gstreamer的plugin库,同时将相应的plugin信息保存下来,方便后续在查找使用的时候,可以加快这一个过程。
大家都知道,什么搜索啊,查找啊,都是比较耗时的,所以gstreamer将plugin的库都放到同一个路径下,比如/usr/lib/gstreamer-1.0,而一些并没有plugin信息的库,则是和平常的库没有什么区别,放到/usr/lib/目录下,通过这样的方式,减少搜索的范围。同时,gstreamer还通过创建子进程的方式,让子进程来帮忙获取plugin的信息。详细是怎么做的呢,我们接下来看看plugin_loader_load()
函数是如何获取plugin相关信息。plugin_loader_load()
声明如下:
static gboolean
plugin_loader_load (GstPluginLoader * loader, const gchar * filename,
off_t file_size, time_t file_mtime)
函数也就四个参数,一个是loader,将会通过它保存plugin的信息,filename则是需要检查的库路径,file_size则是这个库的大小,file_mtime则是库的更新时间。通过函数声明的参数来看,通过库的路径获取库,从而获取plugin信息,同时保存库的大小以及最后的修改时间,以确定是否需要更新。在plugin_loader_load()
函数中,将会通过gst_plugin_loader_spawn (loader)
函数创建子进程,该函数的具体实现在这里就不再述说了,它的主要流程大概如下:
gst-plugin-scanner
;GST_PLUGIN_SCANNER_INSTALLED
;fork()
创建子进程运行gst-plugin-scanner,父进程将从plugin的库路径搜索库,并检查有效性,而子进程则是负责从相应的库中获取plugin信息并反馈; 可能有童鞋会想,是通过创建一个子进程帮忙获取plugin信息的,那么他们父子进程是怎么通信的呢,这个正是通过linux系统的管道进行通信的,详细的创建过程,有兴趣可以看看代码的实现。
父进程,也就是我们的主体运行程序,在找到相应的库的时候,将会通过下面的那种形式,将库的相关信息通过管道发送到子进程gst-plugin-scanner。
entry = g_slice_new (PendingPluginEntry);
entry->tag = loader->next_tag++;
entry->filename = g_strdup (filename);
entry->file_size = file_size;
entry->file_mtime = file_mtime;
loader->pending_plugins_tail =
g_list_append (loader->pending_plugins_tail, entry);
if (loader->pending_plugins == NULL)
loader->pending_plugins = loader->pending_plugins_tail;
else
loader->pending_plugins_tail = g_list_next (loader->pending_plugins_tail);
len = strlen (filename);
put_packet (loader, PACKET_LOAD_PLUGIN, entry->tag,
(guint8 *) filename, len + 1);
通过上面的代码,我们可以清晰的看到,在调用put_packet()
函数将消息push到管道之前,会有个tag标记现在是第几个plugin,以及文件的路径、大小、更新时间等信息,同时会通过一个链表保存这些信息,最后发送一个PACKET_LOAD_PLUGIN消息到管道,接下来,就是子进程gst-plugin-scanner表演的时候了。
上述已经说了,在父进程通过管道将PACKET_LOAD_PLUGIN类型的消息到子进程gst-plugin-scanner,那么子进程又将会进行什么操作呢,下面我们来看看。gst-plugin-scanner的源码是gstreamer-1.xx.0/libs/gst/helpers目录下的gst-plugin-scanner.c:
int
main (int argc, char *argv[])
{
gboolean res;
char **my_argv;
int my_argc;
/* We may or may not have an executable path */
if (argc != 2 && argc != 3)
return 1;
if (strcmp (argv[1], "-l"))
return 1;
my_argc = 2;
my_argv = g_malloc (my_argc * sizeof (char *));
my_argv[0] = argv[0];
my_argv[1] = (char *) "--gst-disable-registry-update";
#ifndef GST_DISABLE_REGISTRY
_gst_disable_registry_cache = TRUE;
#endif
if (argc == 3)
_gst_executable_path = g_strdup (argv[2]);
res = gst_init_check (&my_argc, &my_argv, NULL);
g_free (my_argv);
if (!res)
return 1;
/* Create registry scanner listener and run */
if (!_gst_plugin_loader_client_run ())
return 1;
return 0;
}
通过上面可以看到,就是通过检查运行时候的输入参数,参数一般为-l + 库的查找根路径,而后在_gst_plugin_loader_client_run ()
中循环等待父进程发过来的消息并处理。而在_gst_plugin_loader_client_run ()
函数中设置好管道的接收和发送端口之后,就是通过以下循环进行接收处理操作的。
/* Loop, listening for incoming packets on the fd and writing responses */
while (!l->rx_done && exchange_packets (l));
而在exchange_packets()中,将会检查,是否有数据可以进行接收或者发送,如果有,将会进行相应的处理。现在,我们假设,父进程已经发送PACKET_LOAD_PLUGIN
类型的数据过来,接下来,子进程gst-plugin-scanner将会在exchange_packets()函数中,见过一系列的检查之后,通过read_one()
函数进行处理。而在read_one()
函数中,按照协商好的协议,读取packet,再通过以下函数处理:
return handle_rx_packet (l, l->rx_buf[0], tag,
l->rx_buf + HEADER_SIZE, packet_len);
而在handle_rx_packet()函数中,根据pack_type的类型,即l->rx_buf[0]
的值进行相应的操作,比如加载plugin是PACKET_LOAD_PLUGIN
,将会调用do_plugin_load()函数进行处理。
case PACKET_LOAD_PLUGIN:{
if (!l->is_child)
return TRUE;
/* Payload is the filename to load */
res = do_plugin_load (l, (gchar *) payload, tag);
break;
}
在do_plugin_load()函数中,先通过gst_plugin_load_file()
加载plugin并将相应信息反馈父进程。
static gboolean
do_plugin_load (GstPluginLoader * l, const gchar * filename, guint tag)
{
GstPlugin *newplugin;
GList *chunks = NULL;
GST_DEBUG ("Plugin scanner loading file %s. tag %u", filename, tag);
/* 通过库的文件路径,搜索库并得到相应的plugin数据 */
newplugin = gst_plugin_load_file ((gchar *) filename, NULL);
if (newplugin) {
guint hdr_pos;
guint offset;
/* 将plugin信息保存到chunks */
/* Now serialise the plugin details and send */
if (!_priv_gst_registry_chunks_save_plugin (&chunks,
gst_registry_get (), newplugin))
goto fail;
/* Store where the header is, write an empty one, then write
* all the payload chunks, then fix up the header size */
hdr_pos = l->tx_buf_write;
offset = HEADER_SIZE;
/* 发送PACKET_PLUGIN_DETAILS类型消息 */
put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
if (chunks) {
GList *walk;
for (walk = chunks; walk; walk = g_list_next (walk)) {
GstRegistryChunk *cur = walk->data;
/* 发送external deps、plugin features、element desc等信息 */
put_chunk (l, cur, &offset);
_priv_gst_registry_chunk_free (cur);
}
g_list_free (chunks);
/* Store the size of the written payload */
GST_WRITE_UINT32_BE (l->tx_buf + hdr_pos + 4, offset - HEADER_SIZE);
}
gst_object_unref (newplugin);
} else {
put_packet (l, PACKET_PLUGIN_DETAILS, tag, NULL, 0);
}
return TRUE;
}
通过上述的do_plugin_load()函数实现,了解到子进程gst-plugin-scanner将会把plugin的外部依赖、支持的features、以及element desc等信息反馈到父进程,所以我们大概可以了解到gst_plugin_load_file()
函数的操作。
gst_plugin_load_file()函数也是通过_priv_gst_plugin_load_file_for_registry()
函数完成插件信息的获取,函数主体调用流程如下:
/* Note: The return value is (transfer full) although we work with floating
* references here. If a new plugin instance is created, it is always sinked
* in the registry first and a new reference is returned
*/
GstPlugin *
_priv_gst_plugin_load_file_for_registry (const gchar * filename,
GstRegistry * registry, GError ** error)
{
if (registry == NULL)
registry = gst_registry_get ();
g_mutex_lock (&gst_plugin_loading_mutex);
/* 在registry中检查,该路径的库是否已经注册,以及会进行一系列的文件检查操作 */
...
flags = G_MODULE_BIND_LOCAL;
/* 通过g_module_open打开库并获取相应的句柄 */
module = g_module_open (filename, flags);
/* 通过extract_symname()函数获取plugin的gst_plugin_name_get_desc函数名称, */
/* 然后通过g_module_symbol()函数调用dlsym()从库中获取函数地址 */
symname = extract_symname (filename);
ret = g_module_symbol (module, symname, &ptr);
if (ret) {
/* 调用第二节中说到的GST_PLUGIN_DEFINE展开的gst_plugin_name_get_desc()函数 */
GstPluginDesc *(*get_desc) (void) = ptr;
ptr = get_desc ();
} else {
GST_DEBUG ("Could not find symbol '%s', falling back to gst_plugin_desc",
symname);
ret = g_module_symbol (module, "gst_plugin_desc", &ptr);
}
g_free (symname);
desc = (const GstPluginDesc *) ptr;
/* 保存库的更新时间、大小、文件名称等信息到plugin */
if (new_plugin) {
plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
plugin->file_mtime = file_status.st_mtime;
plugin->file_size = file_status.st_size;
plugin->filename = g_strdup (filename);
plugin->basename = g_path_get_basename (filename);
}
plugin->module = module;
if (new_plugin) {
/* check plugin description: complain about bad values and fail */
/* this is where we load the actual .so, so let's trap SIGSEGV */
_gst_plugin_fault_handler_setup ();
_gst_plugin_fault_handler_filename = plugin->filename;
/* 在这里,调用了gst_plugin_register_func()函数 */
if (!gst_plugin_register_func (plugin, desc, NULL)) {
...
}
/* remove signal handler */
_gst_plugin_fault_handler_restore ();
_gst_plugin_fault_handler_filename = NULL;
GST_INFO ("plugin \"%s\" loaded", plugin->filename);
if (new_plugin) {
gst_object_ref (plugin);
/* 调用gst_registry_add_plugin()函数 */
gst_registry_add_plugin (registry, plugin);
}
g_mutex_unlock (&gst_plugin_loading_mutex);
return plugin;
}
不知道童鞋有没有发现,在_priv_gst_plugin_load_file_for_registry()
函数中,先后调用了gst_plugin_register_func()
和gst_registry_add_plugin()
函数,这两个函数,不正是第二节说到的宏定义GST_PLUGIN_DEFINE
实现展开中gst_plugin_name_register()
函数中所调用的gst_plugin_register_static()
函数中的具体实现吗?原来,在相应的plugin注册函数中的相应函数,只是使用了gst_plugin_name_get_desc()
函数,而gst_plugin_name_register()函数则是没有调用,而是在子进程进行插件注册的时候,直接调用了更底层的函数动态完成plugin注册的这一过程。
让我们来回忆一下,gst_plugin_register_func()
函数和gst_registry_add_plugin()
函数都干了什么。
通过gst_plugin_register_func()函数进行plugin的注册,gst_plugin_register_func()
将会检查版本之类的信息,然后拷贝关于plugin的描述信息,在通过函数指针desc->plugin_init真正的完成plugin的注册(desc->plugin_init函数指针指向的就是上述说到的plugin_init()
函数),这样,就调用到了plugin_init()。gst_registry_add_plugin()
函数与gst_registry_add_feature()函数类似,gst_registry_add_feature()是将feature保存到全局变量的_gst_registry_default的priv->features成员,而gst_registry_add_plugin()函数则是将plugin保存到_gst_registry_default的priv->plugins成员。
我们再回到do_plugin_load()
函数,在通过gst_plugin_load_file()函数获取库中的plugin信息之后,子进程将会把相应的信息通过管道发送给父进程,至此,子进程的协助工作就相当于是完成了一次,下面我们将切换回父进程,看看父进程又是如何处理这些信息的。
前面已经说到,当父进程在指定的路径下搜索到相应的库时,将相应的信息发给子进程之后,将会运行到以下程序:
if (!exchange_packets (loader)) {
if (!plugin_loader_replay_pending (loader))
return FALSE;
}
父进程发送完消息之后,也将会通过exchange_packets()
函数处理消息,管道消息的真正发送也是在这里的,前面的put_packet()函数只是封装消息packet。当子进程完成plugin信息的注册时将会发送PACKET_PLUGIN_DETAILS
类型的消息,下面我们来看看,父进程是如何处理该消息的。
case PACKET_PLUGIN_DETAILS:{
gchar *tmp = (gchar *) payload;
PendingPluginEntry *entry = NULL;
GList *cur;
/* 在我们发送PACKET_LOAD_PLUGIN消息给子进程的时候,
* 我们就已经将plugin的一些信息保存到l->pending_plugins,
* 所以现在我们得到反馈,也应该检查一下,是哪个plugin */
cur = l->pending_plugins;
while (cur) {
PendingPluginEntry *e = (PendingPluginEntry *) (cur->data);
if (e->tag > tag)
break;
if (e->tag == tag) {
entry = e;
break;
} else {
cur = g_list_delete_link (cur, cur);
g_free (e->filename);
g_slice_free (PendingPluginEntry, e);
}
}
l->pending_plugins = cur;
if (cur == NULL)
l->pending_plugins_tail = NULL;
if (payload_len > 0) {
GstPlugin *newplugin = NULL;
/* 通过_priv_gst_registry_chunks_load_plugin()函数解析数据包 */
if (!_priv_gst_registry_chunks_load_plugin (l->registry, &tmp,
tmp + payload_len, &newplugin)) {
return FALSE;
}
GST_OBJECT_FLAG_UNSET (newplugin, GST_PLUGIN_FLAG_CACHED);
newplugin->registered = TRUE;
/* We got a set of plugin details - remember it for later */
l->got_plugin_details = TRUE;
} else if (entry != NULL) {
/* Create a blacklist entry for this file to prevent scanning every time */
plugin_loader_create_blacklist_plugin (l, entry);
l->got_plugin_details = TRUE;
}
/* Remove the plugin entry we just loaded */
cur = l->pending_plugins;
if (cur != NULL)
cur = g_list_delete_link (cur, cur);
l->pending_plugins = cur;
if (cur == NULL)
l->pending_plugins_tail = NULL;
break;
}
从上面我们可以看到,具体的数据包解析是在_priv_gst_registry_chunks_load_plugin()
函数中,需要注意的,该函数的第一个参数,传进的是l->registry,实质上就是通过gst_registry_get ()
函数获取到的静态全局变量指针_gst_registry_default
,所以显然,plugin的信息是保存到_gst_registry_default
指向的数据区域。
在_priv_gst_registry_chunks_load_plugin()函数中,除了保存plugin的更新时间、文件大小、desc信息以及将plugin添加到_gst_registry_defaultwait,还会将plugin features以及相应的外部依赖保存到_gst_registry_defaultwait。至此,plugin注册完成,相应信息保存到全局变量_gst_registry_default中,可通过gst_registry_get()函数获取该指针。需要注意的,在gst_registry_chunks_load_feature()中,还根据不同的Factory类型进行不同的数据保存的,这个需要区分,但是它们都是通过同一个plugin保存到_gst_registry_default。
就是这样,通过两个进程的协作,完成plugin库的相关信息搜索、保存,当plugin都查找完之后,将会给子进程发送退出信号,至此,子进程功成身退。
前面说到的,在gst_registry_add_feature()函数中,最后会通过g_signal_emit (registry, gst_registry_signals[FEATURE_ADDED], 0, feature)
发出信号,这个信号最后是谁接收的,我们现在还不知道,后续学习过程中,如果发现这个点,再回头进程论述。
在plugin注册的过程,我们还有很多是没有提到的,比如,上面提到的,会保存plugin的外部依赖等信息,是在plugin_init()函数中,通过gst_plugin_add_dependency()
函数设置的,通过设置该函数,可以使plugin随硬件的变换而具备不同的属性。同时,通过保存plugin的库更新时间以及大小,是为了下一次进行plugin库扫描的时候,可以将没有修改的库舍弃,减少扫描时间。以及,我们上面所述说的,plugin的信息都是保存到静态全局变量_gst_registry_default
中,如果说,我们的程序退出了相应的信息也就没有了,但是大家都会发现,我们只有第一次运行gstreamer的时候,启动速度很慢,接下来的启动都是以比较快的速度启动gstreamer的,原因就在于,在扫描完plugin库的信息之后,会将_gst_registry_default保存的数据信息通过priv_gst_registry_binary_write_cache()
函数保存为二进制文件,在下一次启动gstreamer的时候,可以直接读取文件获得plugin的信息而不用扫描。
gstreamer plugin加载过程,简单的说,就是将plugin的库放在固定路径,库中都会通过宏GST_PLUGIN_DEFINE定义了gst_plugin_name_get_desc(),然后在gstreamer启动过程,通过获取库的gst_plugin_name_get_desc()函数取得plugin信息,这样gstreamer core就知道,它自己具备什么plugin,可以实现什么功能,在需要的时候就可以进行调用。