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對象系統的一些其它機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章