Gstreamer基础之Gobject


GStreamer是依附于GLib 2.0对象模型的,采用了信号与对象属性的机制,所有的GStreamer对象都采用GObject继承的方法进行扩展,是所有GStreamer对象的基类,因此有必要学习一下Gobject是怎么回事。

Gobject简介

GObject是一个程序库,它可以让我们使用C语言编写面向对象的程序。面向对象只是一种编程思想,不是一种编程语言;GObject 告诉我们;使用 C 语言编写程序时,也可以用面向对象的编程思想。

在GObject系统中,对象由三个部分组成:

  • 对象的ID标识(唯一,无符号长整型,所有此类对象共同的标识);
  • 对象的类结构(唯一,结构型,由对象的所有实例共同拥有);
  • 对象的实例(多个,结构型,对象的具体实现)。

GObject 类具有以下功能:

  • 基于引用计数的内存管理
  • 实现对象的构造函数与析构函数
  • 可设置对象属性的 set/get 函数
  • 易于使用的信号机制

GObject中如何实现类定义

在 GObject 世界里,类是两个结构体的组合,一个是实例结构体,另一个是类结构体。
例如:MyObject 是实例结构体,MyObjectClass 是类结构体,它们合起来便可以称为 MyObject类

#include <glib-object.h>
typedef struct _MyObjectClass 
{
	GObjectClass parent_class;
} MyObjectClass;

typedef struct _MyObject
{
	GObject parent_instance;
} MyObject;

G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT);

在GObject中一个对象的产生遵循如下原则:

  • 如果产生的是该类的第一个实例,那么先分配Class结构体,再分配针对该实例的结构体;否则直接分配针对该实例的结构;也就是说类结构体中的内容,是该类生成的所有实例对象所公有的;而实例化每个对象时,为其单独分配专门的实例对象结构体。
  • MyObject类的实例结构体第一个成员是GObject 结构体;类结构体第一个成员是GObjectClass 结构体。GObject结构体与 GObjectClass 结构体分别是 GObject类实例结构体与类结构体;当它们分别作为 MyObject类的实例结构体与类结构体的第一个成员时;这意味着 MyObject类类继承自 GObject类
  • 要使用Gobject对象系统,必须在自定义的类结构体与实例结构体中,第一个成员分别是GObjectClass 结构体与GObject 结构体
  • 类结构体初始化函数只被调用一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。这意味着,所有对象共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中。

注册自定义GObject类

要向GObject系统中注册自定义GObject类,GObject向我们提供了一个简单的宏来完成这个任务:G_DEFINE_TYPE。如上例所示的:G_DEFINE_TYPE (MyObject, my_object, G_TYPE_OBJECT)
次宏定义如下:

/**
 * G_DEFINE_TYPE:
 * @TN: The name of the new type, in Camel case.
 * @t_n: The name of the new type, in lowercase, with words 
 *  separated by '_'.
 * @T_P: The #GType of the parent type.
 * 
 * A convenience macro for type implementations, which declares a class
 * initialization function, an instance initialization function (see #GTypeInfo
 * for information about these) and a static variable named `t_n_parent_class`
 * pointing to the parent class. Furthermore, it defines  a *_get_type() function.
 * See G_DEFINE_TYPE_EXTENDED() for an example.
 * 
 * Since: 2.4
 */
#define G_DEFINE_TYPE(TN, t_n, T_P)			    G_DEFINE_TYPE_EXTENDED (TN, t_n, T_P, 0, {})

#define G_DEFINE_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_)	    _G_DEFINE_TYPE_EXTENDED_BEGIN (TN, t_n, T_P, _f_) {_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 (const 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() */

G_DEFINE_TYPE宏是怎么完成向GObject系统中注册自定义类

G_DEFINE_TYPE(MyObject, my_object, G_TYPE_OBJECT)为例,将宏定义展开如下:

static void my_object_init(MyObject * self);
static void my_object_class_init(MyObjectClass * klass);
static gpointer my_object_parent_class = ((void *) 0);
static void my_object_class_intern_init(gpointer klass)
{
	my_object_parent_class = g_type_class_peek_parent(klass);
	my_object_class_init((MyObjectClass *) klass);
}
GType my_object_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(((GType) ((20) << (2))),
													g_intern_static_string("MyObject"),
													sizeof(MyObjectClass),
													(GClassInitFunc) my_object_class_intern_init,
													sizeof(MyObject),
													(GInstanceInitFunc) my_object_init,
													(GTypeFlags) 0);
	}
	return g_define_type_id__volatile;
};

  • GObject 类型系统之所以能够接受 MyObject 这个类型,完全拜my_object_get_type函数所赐;因为my_object_get_type函数调用了g_type_register_static_simple函数,此函数由GObject类型系统提供,其主要职责就是为 GObject 类型系统注册新的类型。
  • my_object_get_type 第一次被调用时,会向 GObject 类型系统注册 MyObject 类型,然后对 MyOject 类进行实例化;产生实例结构体对象,最后返回对象的数据类型的 ID;从my_object_get_type第二次被调用开始,它就只进行 MyOject 类的实例化,不会再重复向 GObject 类型系统注册类型。
  • my_object_get_type 会被 g_object_new 调用,所有 GObject 类派生的类型,皆可由g_object_new函数进行实例化,例如:MyObject *my_obj = g_object_new(my_object_get_type(), NULL)
  • 由上面的代码中可以看出来,如果在.c文件中声明一个G_DEFINE_TYPE宏的,就会自动生成一个my_object_get_type (void)函数,,因此需要在.h头文件中声明。G_DEFINE_TYPE 声明了两个函数,但是并没有实现,需要定义对象的用户自己去实现,这两个函数是:
    static void my_object_init(MyObject * self);
    static void my_object_class_init(MyObjectClass * klass);
    这两个函数是对象的初始化函数,相当于C++中的构造函数,第一个函数在每个对象创建的时候都会被调用,第二个函数只有在第一次创建对象的时候才会被调用。

要完成自定义的 GObject 类,还必须遵循GObject的一些规范,创建一些宏定义

//返回GType关联的对象类型
#define MY_OBJECT_TYPE (my_object_get_type ())

//返回一个指向MY_OBJECT类型的指针。这个宏用于必要时安全地强制转换一个静态类型。运行环境检查时,同样也是安全地执行动态类型。
#define MY_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_OBJECT_TYPE, MyObject))

//它以类结构的动态类型检查来进行静态转换,并返回一个指向MY_OBJECT_CLASS这个类型的类结构的指针。
#define MY_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_OBJECT_TYPE, MyObjectClass))

// 这个宏用于判断输入的对象实例是否是是MY_OBJECT_TYPE类型
#define IS_MY_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_OBJECT_TYPE))

//输入的类型指针是否是MY_OBJECT_TYPE类型
#define GST_IS_MY_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_OBJECT_TYPE))

C代码中实现Gobject类步骤

  • 在 .h 文件中包含 glib-object.h
  • 在 .h 文件中构建实例结构体与类结构体,并分别将 GObject 类的实例结构体与类结构体置于第一个成员;
  • 在 .h 文件中定义 PREFIX_OBJECT_TYPE宏,并声明 prefix_object_get_type函数,prefix为自定义类名
  • 在 .h 文件中继续完成上面那5个宏定义;
  • 在 .c 文件中调用 G_DEFINE_TYPE 宏产生类型注册代码。

自定义GObject类构造函数

G_DEFINE_TYPE 宏为我们声明了类的类型初始化函数与类的实例的初始化函数:
static void my_object_init(MyObject * self)
static void my_object_class_init(MyObjectClass * klass)

  • my_object_class_init:是类的类型初始化函数,作用是使得用户能够在类的类型初始化阶段插入一些额外的功能。
  • my_object_init:是类的实例的初始化函数,可以将它理解为 C++ 对象的构造函数。

在通过G_DEFINE_TYPE向Gobject系统注册类,还需要我们自行实现my_object_class_initmy_object_init函数。

  • my_object_class_init:函数是在第一次创建MyObject类实例对象的时候调用的,该函数只会调用一次,
  • test_object_init:函数则是每次创建TestObject类实例对象都会调用。

自定义GObject类析构函数

在通过G_DEFINE_TYPE向Gobject系统,注册MyObject类的时候,需要定义my_object_class_init()和my_object_init()函数,而在类实例的初始化函数my_object_init()中,我们可能申请了一些内存等资源,我们需要在析构函数中释放这些资源,这个时候,需要我们在MyObject类初始化函数test_object_class_init中覆盖从父类继承的析构函数,具体代码如下:

static void
my_object_dispose (GObject * object)
{
    MyObject *myobject = My_OBJECT (object);

    /*  资源释放*/

    /*  调用父类的dispose 函数 */
    G_OBJECT_CLASS (parent_class)->dispose (object);
}

static void my_object_finalize (MyObject * myobject)
{
    g_free(myobject->mem);

    /*  调用父类的finalize 函数 */
    G_OBJECT_CLASS (parent_class)->finalize (myobject);
}

static void my_object_init(MyObject * self)
{
    self->mem = g_malloc (1);
}
static void my_object_class_init(MyObjectClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->dispose = my_object_dispose;
    object_class->finalize = my_object_finalize;
    ...
}
  • MyObject的初始化的时候,将会覆盖从父类继承而来的析构函数,同时在析构函数中释放类实例初始化时占用的资源,同时还有递归调用父类的析构函数。
  • dispose函数主要是将在类中占用的资源释放,而finalize函数则是有点类似真正的析构函数,将构造函数申请的资源进行释放回收。

析构函数何时调用呢?
Gobject的引用计数方式大致如下

  • 使用g_object_new()函数进行实例化的时候,对象的引用计数为1;
  • 使用g_object_ref()函数进行引用对象的时候,对象的引用计数加1;
  • 使用g_object_unref()函数解除引用的时候,对象的引用计数减1;

在调用g_object_unref()函数进行解引用的时候,如果发现对象的引用计数为0,将会先后调用该对象的dispose()函数和finalize函数。
为什么需要在my_object_class_init()函数中覆盖从父类继承过来的析构函数呢?
因为在g_object_unref()函数中调用dispose()函数和finalize函数是通过宏定义G_OBJECT_GET_CLASS取得OBJECT_CLASS类之后,再调用它的dispose()函数和finalize函数,所以需要在MyObject的类初始化函数中,对这两个函数指针进行覆盖,而在MyObject类的dispose()函数和finalize函数再通过G_OBJECT_CLASS (parent_class)取得父类指针,调用父类的析构函数。

感觉写的不怎么样,可能我的理解还不够深刻吧,后续在学习GObject对象系统的一些其它机制。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值