Gstreamer框架是基于插件的,同时插件是可以动态的注册、创建,gstreamer基于Gobject开发,下面来了解一下gstreamer是如何通过Gobject完成自定义类的注册。
在每个类的c文件中,都会有以下这样的一个宏定义:
/* class initialization */
#define DEBUG_INIT \
GST_DEBUG_CATEGORY_INIT (gst_omx_h264_dec_debug_category, "omxh264dec", 0, \
"debug category for gst-omx video decoder base class");
G_DEFINE_TYPE_WITH_CODE (GstOMXH264Dec, gst_omx_h264_dec,
GST_TYPE_OMX_VIDEO_DEC, DEBUG_INIT);
G_DEFINE_TYPE_WITH_CODE
是一个宏定义,那么这个G_DEFINE_TYPE_WITH_CODE
宏是如何向Gobject系统完成类的注册呢?
将G_DEFINE_TYPE_WITH_CODE
展开得到以下代码:
#define G_DEFINE_TYPE_WITH_CODE(TN, t_n, T_P, _C_) _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, 0) {_C_;} _G_DEFINE_TYPE_EXTENDED_END()
#define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \
\
static void type_name##_init (TypeName *self); \
static void type_name##_class_init (TypeName##Class *klass); \
static gpointer type_name##_parent_class = NULL; \
static gint TypeName##_private_offset; \
\
_G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
\
G_GNUC_UNUSED \
static inline gpointer \
type_name##_get_instance_private (TypeName *self) \
{ \
return (G_STRUCT_MEMBER_P (self, TypeName##_private_offset)); \
} \
\
GType \
type_name##_get_type (void) \
{ \
static volatile gsize g_define_type_id__volatile = 0; \
if (g_once_init_enter (&g_define_type_id__volatile)) \
{ \
GType g_define_type_id = \
g_type_register_static_simple (TYPE_PARENT, \
g_intern_static_string (#TypeName), \
sizeof (TypeName##Class), \
(GClassInitFunc) type_name##_class_intern_init, \
sizeof (TypeName), \
(GInstanceInitFunc) type_name##_init, \
(GTypeFlags) flags); \
{ /* custom code follows */
#define _G_DEFINE_TYPE_EXTENDED_END() \
/* following custom code */ \
} \
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
} \
return g_define_type_id__volatile; \
} /* closes type_name##_get_type() */
#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38
#define _G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
static void type_name##_class_intern_init (gpointer klass) \
{ \
type_name##_parent_class = g_type_class_peek_parent (klass); \
if (TypeName##_private_offset != 0) \
g_type_class_adjust_private_offset (klass, &TypeName##_private_offset); \
type_name##_class_init ((TypeName##Class*) klass); \
}
#else
#define _G_DEFINE_TYPE_EXTENDED_CLASS_INIT(TypeName, type_name) \
static void type_name##_class_intern_init (gpointer klass) \
{ \
type_name##_parent_class = g_type_class_peek_parent (klass); \
type_name##_class_init ((TypeName##Class*) klass); \
}
#endif /* GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 */
从上面宏G_DEFINE_TYPE_WITH_CODE
的展开式中,我们可以看出,实际是定义了几个函数,如下所示:
static void gst_omx_h264_dec_init (GstOMXH264Dec *self);
static void gst_omx_h264_dec_class_init (GstOMXH264DecClass *klass);
static gpointer gst_omx_h264_dec_parent_class = NULL;
static gint GstOMXH264Dec_private_offset;
static void gst_omx_h264_dec_class_intern_init (gpointer klass)
{
gst_omx_h264_dec_parent_class = g_type_class_peek_parent (klass);
if (GstOMXH264Dec_private_offset != 0)
g_type_class_adjust_private_offset (klass, &GstOMXH264Dec_private_offset);
gst_omx_h264_dec_class_init ((GstOMXH264DecClass*) klass);
}
G_GNUC_UNUSED
static inline gpointer
gst_omx_h264_dec_get_instance_private (GstOMXH264Dec *self)
{
return (G_STRUCT_MEMBER_P (self, GstOMXH264Dec_private_offset));
}
GType gst_omx_h264_dec_get_type (void)
{
static volatile gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile))
{
GType g_define_type_id =
g_type_register_static_simple (TYPE_PARENT,
g_intern_static_string (#GstOMXH264Dec),
sizeof (GstOMXH264DecClass),
(GClassInitFunc) gst_omx_h264_dec_class_intern_init,
sizeof (GstOMXH264Dec),
(GInstanceInitFunc) gst_omx_h264_dec_init,
(GTypeFlags) flags);
{ /* custom code follows */
/* following custom code */
}
g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
}
return g_define_type_id__volatile;
} /* closes gst_omx_h264_dec_get_type() */
G_DEFINE_TYPE_WITH_CODE
定义如上,那么,最终它是如何向Gobject系统注册该类的呢?
Gobject系统为什么知道你新添加了一个名叫TypeName
的类,是因为你通过g_type_register_static_simple(...)
函数告诉它,我这里有一个新类,你登记一下。
g_type_register_static_simple()
函数实现如下:
GType
g_type_register_static_simple (GType parent_type,
const gchar *type_name,
guint class_size,
GClassInitFunc class_init,
guint instance_size,
GInstanceInitFunc instance_init,
GTypeFlags flags)
{
GTypeInfo info;
/* Instances are not allowed to be larger than this. If you have a big
* fixed-length array or something, point to it instead.
*/
g_return_val_if_fail (class_size <= G_MAXUINT16, G_TYPE_INVALID);
g_return_val_if_fail (instance_size <= G_MAXUINT16, G_TYPE_INVALID);
info.class_size = class_size;
info.base_init = NULL;
info.base_finalize = NULL;
info.class_init = class_init;
info.class_finalize = NULL;
info.class_data = NULL;
info.instance_size = instance_size;
info.n_preallocs = 0;
info.instance_init = instance_init;
info.value_table = NULL;
return g_type_register_static (parent_type, type_name, &info, flags);
}
从该函数实现我们可以了解到,向Gobject系统注册一个类,需要告诉Gobject系统,我现在需要注册一个新类,它父类的类型是parent_type
,类型名是type_name
,大小是class_size
,类的初始化函数是class_init
,类的实例大小以及初始化函数,还有这个类有什么flags
,通过告诉Gobject,它就会将新类登记在线。
通过G_DEFINE_TYPE_WITH_CODE
宏的展开可以知道,在type_name##_get_type()
函数中调用到g_type_register_static_simple()
函数,从而完成了新类向Gobject系统的注册登记。
在我们需要创建一个TestObject实例对象时,会通过调用g_object_new()
函数完成,在调用g_object_new函数时,需要传进相应的参数,这个时候,我们就将type_name##_get_type()
函数的返回值传递给它,即演变成以下:
TestObject *testObject = (TestObject *)g_object_new (type_name##_get_type(), NULL);
而在type_name##_get_type()
函数中,将会先通过g_once_init_enter()
函数检查type_name##_get_type()
中的静态变量g_define_type_id__volatile
是否为0,如果是,则通过g_type_register_static_simple()
函数向Gobject系统登记TestObject类,同时返回object ID
,如果g_define_type_id__volatile
不为0,则说明已经向Gobject系统注册TestObject类,直接返回object ID
,这样,即完成了TestObject的注册登记。
学习C++我们都知道,类是有构造函数的,在创建类实例的时候,会自动调用该类的构造函数,那么,在Gobject中,又是怎么调用类的构造函数呢?
以TestObject为例,在上面说到通过g_type_register_static_simple()
函数向Gobject系统注册自定义类的时候,就传进了相应的参数,包括类的初始化函数type_name##_class_intern_init()
以及类实例的初始化函数type_name##_init()
,它们两个共同的相当于TestObject类的构造函数。从宏定义G_DEFINE_TYPE_WITH_CODE
的展开代码中我们可以知道,在通过G_DEFINE_TYPE_WITH_CODE
向Gobject系统注册类时,还需要我们实现type_name##_class_init()
和type_name##_init()
函数的定义。type_name##_class_init()
函数是在第一次创建TestObject类实例对象的时候调用的,该函数只会调用一次,而type_name##_init()
函数则是每次创建TestObject类实例对象都会调用。
在G_DEFINE_TYPE_WITH_CODE
的展开代码中,我们可以看到以下代码:
static gpointer type_name##_parent_class = NULL; \
static void type_name##_class_intern_init (gpointer klass) \
{ \
type_name##_parent_class = g_type_class_peek_parent (klass); \
if (TypeName##_private_offset != 0) \
g_type_class_adjust_private_offset (klass, &TypeName##_private_offset); \
type_name##_class_init ((TypeName##Class*) klass); \
}
在这个宏中可以看到,定义了一个静态的全局指针变量type_name##_parent_class
,而type_name##_parent_class
变量是通过g_type_class_peek_parent (klass)
函数赋值的,type_name##_parent_class
变量代表着什么呢,它就是父类。一般的,会在该源文件新增一个宏,定义如下:
#define type_name##_parent_class parent_class
这样就可以通过宏定义parent_class
直接调用父类函数,而该父类,就是在通过宏定义G_DEFINE_TYPE_WITH_CODE
向Gobject系统注册类时传进的第三个参数T_P。g_type_class_peek_parent()
函数通过传进的子类指针,查找到注册时候的相应信息,得到父类的类型,而后通过父类类型得到父类信息并返回。
有了相应的构造函数,在构造函数中申请了内存、硬件等资源,自然的,也会类似C++的,有相应的析构函数负责资源的释放操作。那么,在Gobject系统中,析构函数又是什么回事呢?我们都知道,构造函数是从父类到子类,而析构函数是从子类到父类。在Gobject系统中的析构函数又是如何的呢?
之前说到,在通过G_DEFINE_TYPE_WITH_CODE
向Gobject系统,注册TestObject类的时候,需要定义type_name##_class_init()
和type_name##_init()
函数,而在类实例的初始化函数type_name##_init()
中,我们可能申请了一些内存等资源,我们需要在析构函数中释放这些资源,这个时候,需要我们在TestObject类初始化函数type_name##_class_init()
覆盖从父类继承的析构函数,具体代码如下:
static void
test_object_dispose (GObject * object)
{
TestObject *testobject = TEST_OBJECT (object);
/* 资源释放*/
/* 调用父类的dispose 函数 */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
test_object_finalize (TestObject * testobject)
{
g_free(testobject->mem);
/* 调用父类的finalize 函数 */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void test_object_init(TestObject * self)
{
self->mem = g_malloc (1);
}
static void test_object_class_init(TestObjectClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = test_object_dispose;
object_class->finalize = test_object_finalize;
...
}
由上述代码我们可以知道,在TestObject的初始化的时候,将会覆盖从父类继承而来的析构函数,同时在析构函数中释放类实例初始化时占用的资源,同时还有递归调用父类的析构函数。dispose
函数主要是将在类中占用的资源释放,而finalize
函数则是有点类似真正的析构函数,将构造函数申请的资源进行释放回收。
既然析构函数也已经有了,析构函数又会是什么时候调用呢?
JAVA使用的是垃圾回收的机制,而Gobject则是使用引用计数的方式。当每个对象创建的时候,将会对其引用计数加一,如果期间被其他对象进行引用,也都会将它的引用计数增加;而当对象被解除引用的时候,引用计数将会减一,当引用计数减为0的时候,将会调用对象的析构函数,进行资源的回收。
Gobject的引用计数方式大致如下:
g_object_new()
函数进行实例化的时候,对象的引用计数为1;g_object_ref()
函数进行引用对象的时候,对象的引用计数加1;g_object_unref()
函数解除引用的时候,对象的引用计数减1;g_object_unref()
函数进行解引用的时候,如果发现对象的引用计数为0,将会先后调用该对象的dispose()
函数和finalize()
函数。而为什么在test_object_class_init()
函数中覆盖从父类继承过来的析构函数呢?
因为在g_object_unref()
函数中调用dispose()
函数和finalize()
函数是通过宏定义G_OBJECT_GET_CLASS
取得OBJECT_CLASS
类之后,再调用它的dispose()
函数和finalize()
函数,所以需要在TestObject的类初始化函数对这两个函数指针进行覆盖,而在TestObject类的dispose()
函数和finalize()
函数再通过G_OBJECT_CLASS (parent_class)
取得父类指针,调用父类的析构函数。
在Gobject系统中,设置了很多方便的宏,在使用对象的时候可以更加的方便,在相应的头文件,一般会有如下宏定义:
typedef struct _GstTestObject TestObject;
typedef struct _GstTestObjectClass GstTestObjectClass;
/* 获取类型 */
#define GST_TYPE_TEST_OBJECT (test_object_get_type())
/* 类实例类型判断 */
#define GST_IS_TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_TEST_OBJECT))
/* 类结构判定 */
#define GST_IS_TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_TEST_OBJECT))
/* 获取obj的类型,同时将其转换为GST_TYPE_TEST_OBJECT,并返回指向GstTestObjectClass的指针 */
#define GST_TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_TEST_OBJECT, GstTestObjectClass))
/* 检查obj是否是GST_TYPE_TEST_OBJECT类型,如果是,则将返回指向obj成员变量TestObject的指针 */
#define GST_TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_TEST_OBJECT, TestObject))
/* 检查klass是不是GST_TYPE_TEST_OBJECT类型,如果是,则将返回指向klass成员变量GstTestObjectClass的指针 */
#define GST_TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_TEST_OBJECT, GstTestObjectClass))
/* 实例结构转换 */
#define GST_TEST_OBJECT_CAST(obj) ((TestObject*)(obj))
struct _GstTestObject {
GstObject object;
gchar *mem;
...
}
/* 类定义 */
struct _GstTestObjectClass {
GstObjectClass object_class;
...
}
就是通过上述的宏定义,可以方便的将各种类以及对象进行转换,在子类中可以调用父类的函数等操作,同时,在gstreamer中,还有一些属性设置函数等,进行多样化的类管理。
另外的,宏定义G_DEFINE_TYPE
也是实现与G_DEFINE_TYPE_WITH_CODE
类似的功能,G_DEFINE_TYPE_WITH_CODE
可以将一些函数内置在type_name##_get_type()
函数中。