首先,我们需要知道插件代码中一共有三个初始化函数,分别是:
- 插件的初始化函数plugin_init,完成类class的注册;
- 插件类的初始化函数gst_xxx_class_init,完成类的初始化,对应成员class_init;
- 对象的初始化函数gst_xxx_init,初始化对象本身,对应成员instance_init。
类比C++,gst_xxx_class_init是定义类,并创建类的静态成员,gst_xxx_init是创建类的具体对象,完成对象本身的初始化。所以,当我们多次gst_element_factory_create 来创建元素的时候,gst_xxx_class_init只会在第一次被调用创建该类型,gst_xxx_init会调用多次,创建多个对象。
至于为啥需要这么麻烦,弄这么多init函数,需要学习一下GObject的相关资料,这里就不扩散了。
1. 插件函数的声明和赋值
gstreamer的插件是如何被加载和初始化的呢?
在所有插件中,有一个plugin_init函数,这是插件运行的起点。
static gboolean
plugin_init (GstPlugin * plugin)
{
/* FIXME Remember to set the rank if it's an element that is meant
to be autoplugged by decodebin. */
return gst_element_register (plugin, "aerodecode", GST_RANK_NONE,
GST_TYPE_AERODECODE);
}
1.1 GST_PLUGIN_DEFINE 宏定义
plugin_init 函数为static函数,通过GST_PLUGIN_DEFINE 来声明描述信息,该信息非常重要,最后应用就是通过该描述信息的plugin_init来初始化插件的:
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
aerodecode,
"This is aero leaning codec.",
plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN)
1.2 G_DEFINE_TYPE_WITH_CODE(或G_DEFINE_TYPE) 宏定义
除此之外,头文件的gst_aerodecode_get_type函数,我们找不到定义。这就牵涉到另一个重量级的宏G_DEFINE_TYPE_WITH_CODE。它经过层层宏嵌套,定义了gst_aerodecode_get_instance_private、gst_aerodecode_get_type等函数。
在gst_aerodecode_get_type的代码中,将gst_aerodecode_init和gst_aerodecode_class_init等函数进行注册。因此我们可以看到生成的模板中,gst_aerodecode_class_init有定义,但是看不到明文调用,实际上调用是通过这个宏展开来实现的。
/* class initialization */
G_DEFINE_TYPE_WITH_CODE (GstAerodecode, gst_aerodecode, GST_TYPE_VIDEO_DECODER,
GST_DEBUG_CATEGORY_INIT (gst_aerodecode_debug_category, "aerodecode", 0,
"debug category for aerodecode element"));
G_DEFINE_TYPE_WITH_CODE会定义gst_aerodecode_class_intern_init 函数:
static void gst_aerodecode_class_intern_init (gpointer klass)
{
gst_aerodecode_parent_class = g_type_class_peek_parent (klass);
gst_aerodecode_class_init ((GstAerodecodeClass*) klass);
}
gst_aerodecode_get_type会调用g_type_register_static_simple来将gst_aerodecode_class_intern_init 设定给类初始化函数:
gst_aerodecode_get_type--->
g_type_register_static_simple--->
g_type_register_static--->
type_data_ref_Wm--->
type_data_make_W--->
data->instance.class_size = info->class_size;
data->instance.class_init_base = info->base_init;
data->instance.class_finalize_base = info->base_finalize;
data->instance.class_init = info->class_init;
data->instance.class_finalize = info->class_finalize;
data->instance.class_data = info->class_data;
data->instance.instance_init = info->instance_init;
可以看到,此时class_init和instance_init都会比插件中定义的函数多出一个指针参数,但是却是直接赋值的。从后面的内容也能看到,调用的时候也是传入了两个参数。
定义:
static void gst_aerodecode_class_intern_init (GstAerodecodeClass * klass)//class_init
static void gst_aerodecode_init (GstAerodecode *aerodecode)//instance_init
传值:
typedef void (*GClassInitFunc) (gpointer g_class, gpointer class_data);//多出一个class_data
typedef void (*GInstanceInitFunc) (GTypeInstance *instance, gpointer g_class);//多出一个g_class
1.3 总结:
在插件源码中,有两个重要的宏。
- GST_PLUGIN_DEFINE 将plugin_init函数进行传递;
- G_DEFINE_TYPE_WITH_CODE宏将class_init 和instance_init进行传递。
2. 从应用端看插件初始化
我们先了解程序是如如何运行到plugin_init的。
我们知道,Gstreamer创建element是通过如下函数:
decoder = gst_element_factory_make ("aerodecode", "aero_decoder");
gst_element_factory_make是gst_element_factory_find
和gst_element_factory_create个函数的结合:
factory = gst_element_factory_find ("aerodecode");
element = gst_element_factory_create (factory, "aero_decoder");
其中,gst_element_factory_create才是真正加载库并初始化插件的函数。照例分为两步来理一理代码。
2.1 gst_element_factory_find 工厂对象指针
gst_element_factory_find先在已注册的hash查找aerodecode元素工厂,调用流程:
gst_element_factory_find——>
gst_registry_find_feature——>
gst_registry_lookup_feature——>
gst_registry_lookup_feature_locked——>
g_hash_table_lookup--->
g_hash_table_lookup_node
在g_hash_table_lookup_node中,如找到对应键值,就返回该处的index。如果找不到,则会返回一个没用过(空的或者废弃)的节点,可以用来插入该新的键值。
不过,从代码上短时间内看不清楚新对象的处理方式。鉴于此时新的插件还并没有被加载,因此个人理解是没有找到的将被默认新建,之后再插入新值。
2.2 gst_element_factory_create 从工厂创建元素
gst_element_factory_create主要有两个函数:
- gst_plugin_feature_load,用来加载动态库并初始化插件;
- g_object_new 创建新元素;
GstElement *
gst_element_factory_create (GstElementFactory * factory, const gchar * name)
{
GstElement *element;
GstElementClass *oclass;
GstElementFactory *newfactory = GST_ELEMENT_FACTORY (gst_plugin_feature_load (GST_PLUGIN_FEATURE(factory)));
factory = newfactory;
element = g_object_new (factory->type, "name", name, NULL);//生成元素对象
oclass = GST_ELEMENT_GET_CLASS (element);//检查类型是否正确
return element;
通过GST_ELEMENT_FACTORY宏来生成一个工厂对象,并将gst_element_factory_find返回的factory 重新赋值,然后我们在插件中定义的GST_ELEMENT_GET_CLASS 会在这里被调用。
2.3 总结:
gst_element_factory_make 有两个主要流程:
- gst_element_factory_find
- gst_element_factory_create
其中插件的加载和初始化都是gst_element_factory_create中完成的。接下来,我们就主要看gst_element_factory_create函数是怎么调用的。
3.初始化函数的调用路径
上面的脉络需要在小本本上梳理一下,不然看起来衔接不好。接下来我们看看gst_element_factory_create是如何调用插件的上初始化函数的。
3.1 plugin_init 初始化
gst_plugin_feature_load (GST_PLUGIN_FEATURE(factory))是加载对应的模组,plugin_init隐藏的比较深,找了很久才找到。代码调用路径如下:
gst_element_factory_create--->
gst_plugin_feature_load --->
gst_plugin_load_by_name--->
gst_plugin_load_file--->
_priv_gst_plugin_load_file_for_registry--->//完成加载文件、初始化并注册
g_module_open--->
_g_module_open--->
dlopen//动态库加载
g_module_symbol--->
_g_module_symbol--->
dlsym//动态库符号链接
_gst_plugin_fault_handler_setup//设置SIGSEGV信号处理句柄函数
gst_plugin_register_func--->//注册库文件的函数符号
gst_plugin_desc_copy (&plugin->desc, desc);//拷贝描述信息
(desc->plugin_init)) (plugin, user_data))//最终在这里调用plugin_init函数
gst_registry_add_plugin--->//把plugin添加到注册表
g_signal_emit(signals[PLUGIN_ADDED]) //发出新参加添加信号
我们从上面可以看到,加载了动态库之后,通过描述信息的plugin_init函数,来调用了插件的初始化函数。至此,从应用到plugin_init的流程结束。
接下来,了解插件内部plugin_init,class_init等函数的调用流程。
3.2 class_init 插件类初始化
前面我们主要梳理了gst_element_factory_create 通过gst_plugin_feature_load 函数加载动态库并调用plugin_init。接下来就是gst_element_factory_create 通过g_object_new 创建新元素的过程,这时候plugin的各初始化函数都将被调用。
gst_element_factory_create--->
gst_plugin_feature_load //见上一节
g_object_new---> //创建新元素
g_object_new_valist-->//创建新实例并初始化属性
g_type_class_ref--->//增加该类引用计数,如该类不存在则新建
type_class_init_Wm--->
node->data->class.class_init (class, (gpointer) node->data->class.class_data);
从G_DEFINE_TYPE_WITH_CODE 的定义中,可以看到最终是把gst_aerodecode_class_intern_init 函数赋值给class.class_init。不过这里class_init 明显比gst_aerodecode_class_intern_init 多了一个指针参数,暂时没有理清。
3.3 instance_init 插件对象初始化
前面我们主要梳理了class_init的流程。接下来就是对象本身的初始化。
gst_element_factory_create--->
gst_plugin_feature_load //见上一节
g_object_new---> //创建新元素
g_object_new_valist-->//创建新实例并初始化属性
g_type_create_instance--->//创建对象
--->node->data->instance.instance_init (instance, class);
从G_DEFINE_TYPE_WITH_CODE 的定义中,可以看到最终是把gst_aerodecode_init函数赋值给class.instance_init 。不过这里instance_init 一样是比gst_aerodecode_init多了一个指针参数,暂时没有理清。
3.4 通过打印调用栈来辅助确认
- 插件加载和初始化:plugin_init in.
********* print_trace *********
There are 12 addresses:
/home/earo/gst/plugin/bin/libaero_decoder.so(print_trace+0x2e) [0x7fa5f5a39586]
/home/earo/gst/plugin/bin/libaero_decoder.so(+0x23af) [0x7fa5f5a3a3af]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(+0xb857b) [0x7fa5f7e7357b]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(+0xb9800) [0x7fa5f7e74800]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_plugin_load_file+0x25) [0x7fa5f7e739cb]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_plugin_load_by_name+0x119) [0x7fa5f7e75033]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_plugin_feature_load+0x1aa) [0x7fa5f7e76f71]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_element_factory_create+0x59) [0x7fa5f7e3ac39]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_element_factory_make+0x153) [0x7fa5f7e3b115]
../bin/plug_test(+0x1659) [0x5602d7249659]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7fa5f7433bf7]
../bin/plug_test(+0x10ca) [0x5602d72490ca]
********* print_trace end *********
- 类型初始化:gst_aerodecode_class_init in.
********* print_trace *********
There are 11 addresses:
/home/earo/gst/plugin/bin/libaero_decoder.so(print_trace+0x2e) [0x7fa5f5a39586]
/home/earo/gst/plugin/bin/libaero_decoder.so(+0x19f8) [0x7fa5f5a399f8]
/home/earo/gst/plugin/bin/libaero_decoder.so(+0x18b4) [0x7fa5f5a398b4]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(g_type_class_ref+0x442) [0x7fa5f7b93792]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(g_object_new_valist+0x4fb) [0x7fa5f7b76e4b]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(g_object_new+0x99) [0x7fa5f7b77039]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_element_factory_create+0x182) [0x7fa5f7e3ad62]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_element_factory_make+0x153) [0x7fa5f7e3b115]
../bin/plug_test(+0x1659) [0x5602d7249659]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7fa5f7433bf7]
../bin/plug_test(+0x10ca) [0x5602d72490ca]
********* print_trace end *********
- 对象初始化:gst_aerodecode_init in.
********* print_trace *********
There are 11 addresses:
/home/earo/gst/plugin/bin/libaero_decoder.so(print_trace+0x2e) [0x7fa5f5a39586]
/home/earo/gst/plugin/bin/libaero_decoder.so(+0x1bf0) [0x7fa5f5a39bf0]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(g_type_create_instance+0x1dc) [0x7fa5f7b968bc]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(+0x18e90) [0x7fa5f7b74e90]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(g_object_new_valist+0x161) [0x7fa5f7b76ab1]
/winstream/lib/x86_64-linux-gnu/libgobject-2.0.so.0(g_object_new+0x99) [0x7fa5f7b77039]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_element_factory_create+0x182) [0x7fa5f7e3ad62]
/winstream/lib/x86_64-linux-gnu/libgstreamer-1.0.so.0(gst_element_factory_make+0x153) [0x7fa5f7e3b115]
../bin/plug_test(+0x1659) [0x5602d7249659]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7fa5f7433bf7]
../bin/plug_test(+0x10ca) [0x5602d72490ca]
********* print_trace end *********