gstreamer---Gobject类对象

一、Gobject类定义

  Gstreamer框架是基于插件的,同时插件是可以动态的注册、创建,gstreamer基于Gobject开发,下面来了解一下gstreamer是如何通过Gobject完成自定义类的注册。

  在每个类的c文件中,都会有以下这样的一个宏定义:

G_DEFINE_TYPE (GstV4l2Allocator, gst_v4l2_allocator, GST_TYPE_ALLOCATOR);

  G_DEFINE_TYPE是一个宏定义,那么这个G_DEFINE_TYPE宏是如何完成向Gobject系统完成类的注册呢?
  将G_DEFINE_TYPE展开得到以下代码:

#define G_DEFINE_TYPE(TN, t_n, T_P)
/******* 其中
 *TN  ---> TypeName
 *t_n ---> type_name
 *T_P ---> TYPE_PARENT
 *_f_ ---> 0
 *_c_ ---> {}
******/
/*****   以下为宏展开   *****/
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;

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);
}

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;
  /* Prelude goes here */
  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)(void (*)(void)) type_name##_class_intern_init,
                                       sizeof (TypeName),
                                       (GInstanceInitFunc)(void (*)(void)) type_name##_init,
                                       (GTypeFlags) flags);
      { /* custom code follows */
            {_C_;}
        /* 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() */


二、向Gobject系统注册类

  G_DEFINE_TYPE定义如上,那么,最终它是如何向Gobject系统注册该类的呢?

   Gobject系统为什么知道你新添加了一个名叫TypeName的类,是因为你通过g_type_register_static_simple()函数告诉它,我这里有一个新类,你登记一下。

  g_type_register_static_simple()函数声明如下:

GLIB_AVAILABLE_IN_ALL
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);

  函数声明的前面GLIB_AVAILABLE_IN_ALL就是一个extern关键词,从函数声明我们可以了解到,向Gobject系统注册一个类,需要告诉Gobject系统,我现在需要注册一个新类,它父类的类型是parent_type,大小是class_size,类的初始化函数是class_init,类的实例大小以及初始化函数,还有这个类有什么flags,通过告诉Gobject,它就会将新类登记在线。

  通过G_DEFINE_TYPE宏的展开可以知道,在type_name##_get_type()函数中调用到g_type_register_static_simple()函数,那么,究竟是什么时候,程序会向Gobject系统注册该新类呢?

  比如我们是要注册一个名叫TestObject的类,那么就是通过TestObject_get_type()函数完成TestObject的注册登记。

  在我们需要创建一个TestObject的实例时,会通过调用g_object_new()函数完成,在调用g_object_new函数,需要传进相应的参数,这个时候,我们就将TestObject_get_type()函数的返回值传递给它,即演变成以下:

TestObject *testObject = (TestObject *)g_object_new (TestObject_get_type(), NULL);

  在创建TestObject实例对象的时候,将会调用TestObject_get_type()函数得到相应的类型,而在TestObject_get_type()函数中,将会先通过g_once_init_enter()函数检查TestObject_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系统注册自定义类的时候,就传进了相应的参数,包括类的初始化函数test_object_class_intern_init()以及类实例的初始化函数test_object_init(),它们两个共同的相当于TestObject类的构造函数。从宏定义G_DEFINE_TYPE的展开代码中发现以下函数声明以及test_object_class_intern_init()函数的定义:

static void     test_object_init              (TestObject      *self); 
static void     test_object_class_init        (TestObjectClass *klass); 

static void     test_object_class_intern_init (gpointer klass)
{
  test_object_parent_class = g_type_class_peek_parent (klass);
  if (test_object_private_offset != 0)
    g_type_class_adjust_private_offset (klass, &TestObject_private_offset);
  test_object_class_init ((TestObjectClass*) klass);
}

  从上述代码我们可以知道,在通过G_DEFINE_TYPE向Gobject系统注册类,还需要我们实现test_object_class_init()和test_object_init()函数的定义。test_object_class_init()函数是在第一次创建TestObject类实例对象的时候调用的,该函数只会调用一次,而test_object_init()函数则是每次创建TestObject类实例对象都会调用。


四、父子类的继承关系

  在G_DEFINE_TYPE的展开代码中,我们可以看到以下代码:

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()函数赋值的,type_name_parent_class变量代表着什么呢,它就是父类。一般的,会在该源文件新增一个宏,定义如下:

#define type_name##_parent_class parent_class

  这样就可以通过宏定义parent_class直接调用父类函数,而该父类,就是在通过宏定义G_DEFINE_TYPE向Gobject系统注册类时传进的第三个参数T_P。g_type_class_peek_parent()函数通过传进的子类指针,查找到注册时候的相应信息,得到父类的类型,而后通过父类类型得到父类信息并返回。


五、类的析构函数

  有了相应的构造函数,在构造函数中申请了内存、硬件等资源,自然的,也会类似C++的,有相应的析构函数负责资源的释放操作。那么,在Gobject系统中,析构函数又是什么回事呢?我们都知道,构造函数是从父类到子类,而析构函数是从子类到父类。在Gobject系统中的析构函数又是如何的呢?

  之前说到,在通过G_DEFINE_TYPE向Gobject系统,注册TestObject类的时候,需要定义test_object_class_init()和test_object_init()函数,而在类实例的初始化函数test_object_init()中,我们可能申请了一些内存等资源,我们需要在析构函数中释放这些资源,这个时候,需要我们在TestObject类初始化函数test_object_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_WITH_CODE也是实现与G_DEFINE_TYPE类似的功能,只不过是可以将一些函数内置在type_name##_get_type()函数中。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容