plugin插件加载和初始化流程详解

首先,我们需要知道插件代码中一共有三个初始化函数,分别是:

  • 插件的初始化函数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 *********
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值