3. init函式和class_init函式的講解
如何去使用GObject去構建一個所謂的“物件”呢?GObject中每個類要定義兩個結構體,假設你要定義的型別為People,那麼你要定義兩個結構分別名為People和PeopleClass,估計剛接觸的人會有些暈,一般的C++啊,JAVA什麼的都是直接一個class了事兒了。但記住C本身並沒有面向物件的機制,這裡這樣做也僅僅是為了模擬。 名為PeopleClass的結構是表示類的結構,而名為People的結構則是這個類的例項,可能這麼說一般人還會有點摸不著頭腦,切記,這是一種模擬。
在GObject世界裡,類是兩個結構體的組合,一個是例項結構體,另一個是類結構體。有點繞。類、物件、例項有什麼區別?可以這麼理解,類-
在GObject中一個物件的產生遵循如下原則,如果產生的是該類的第一個例項,那麼先分配Class結構,再分配針對該例項的結構。否則直接分配針對該例項的結構。也就是說在Class結構中所有的內容,是通過該類生成的例項所公有的。而例項獲每個物件時,為其單獨分配專門的例項用結構。
同時需要注意的是,每個結構中的第一項描述的是其父類相關的結構,這是為了模擬繼承的機制,比如,在gstimxv4l2src.h檔案中:
struct _GstImxV4l2Src { GstPushSrc videosrc; gchar *device; guint frame_plus; GstBufferPool *pool; GstAllocator *allocator; gpointer v4l2handle; GstCaps *probed_caps; GstCaps *old_caps; GList * gstbuffer_in_v4l2; guint w, h, fps_n, fps_d; guint v4l2fmt; guint actual_buf_cnt; GstVideoAlignment video_align; GstClockTime duration; GstClockTime base_time_org; gboolean stream_on; gboolean use_my_allocator; gboolean use_v4l2_memory; }; struct _GstImxV4l2SrcClass { GstPushSrcClass parent_class; };
這兩個結構體中的第一個元素都是與其父類相關的結構。這一點一定要注意。
有關這方面的理解,檢視《使用C語言進行面向物件的開發--GObject入門系列》中的《使用C語言進行面向物件的開發--GObject入門[3].pdf》和《藉助 C++ 來理解 GObject 的基本程式設計框架.pdf》這兩個文件,網頁連結為:
或者檢視《快速上手Gobject.pdf》中有關這一部分的講解,連結如下:
在上一篇文章中講到了使用G_DEFINE_TYPE 巨集來定義了幾個函式,其中有type_name##_init
函式和type_name##_class_init函式,這兩個函式分別對應例項初始化和類的初始化。
下面以gstimxv4l2src.c檔案為例來分析,先來看看gst_imx_v4l2src_init函式:static void
gst_imx_v4l2src_init (GstImxV4l2Src * v4l2src)
{
v4l2src->device = g_strdup (DEFAULT_DEVICE);
v4l2src->frame_plus = DEFAULT_FRAME_PLUS;
v4l2src->v4l2handle = NULL;
v4l2src->probed_caps = NULL;
v4l2src->old_caps = NULL;
v4l2src->pool = NULL;
v4l2src->allocator = NULL;
v4l2src->gstbuffer_in_v4l2 = NULL;
v4l2src->actual_buf_cnt = 0;
v4l2src->duration = 0;
v4l2src->stream_on = FALSE;
v4l2src->use_my_allocator = FALSE;
v4l2src->use_v4l2_memory = DEFAULT_USE_V4L2SRC_MEMORY;
v4l2src->base_time_org = GST_CLOCK_TIME_NONE;
gst_base_src_set_format (GST_BASE_SRC (v4l2src), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (v4l2src), TRUE);
g_print("====== IMXV4L2SRC: %s build on %s %s. ======\n", (VERSION),__DATE__,__TIME__);
}
先來看這個函式,這個函式完成的是例項的初始化,對比對應的.h標頭檔案,可以發現這個函式只是將GstImxV4l2Src這個結構體中的各個元素賦初值。
再來看看gst_imx_v4l2src_class_init函式:
static void
gst_imx_v4l2src_class_init (GstImxV4l2SrcClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
GstBaseSrcClass *basesrc_class;
GstPushSrcClass *pushsrc_class;
gobject_class = G_OBJECT_CLASS (klass);
element_class = GST_ELEMENT_CLASS (klass);
basesrc_class = GST_BASE_SRC_CLASS (klass);
pushsrc_class = GST_PUSH_SRC_CLASS (klass);
gobject_class->finalize = (GObjectFinalizeFunc) gst_imx_v4l2src_finalize;
gobject_class->set_property = gst_imx_v4l2src_set_property;
gobject_class->get_property = gst_imx_v4l2src_get_property;
gst_imx_v4l2src_install_properties (gobject_class);
gst_element_class_add_pad_template (element_class, \
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, \
gst_imx_v4l2src_get_all_caps ()));
basesrc_class->start = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_start);
basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_stop);
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_get_caps);
basesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_fixate);
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_set_caps);
basesrc_class->query = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_query);
basesrc_class->decide_allocation = \
GST_DEBUG_FUNCPTR (gst_imx_v4l2src_decide_allocation);
pushsrc_class->create = GST_DEBUG_FUNCPTR (gst_imx_v4l2src_create);
gst_element_class_set_static_metadata (element_class, \
"IMX Video (video4linux2) Source", "Src/Video", \
"Capture frames from IMX SoC video4linux2 device", IMX_GST_PLUGIN_AUTHOR);
GST_DEBUG_CATEGORY_INIT (imxv4l2src_debug, "imxv4l2src", 0, "Freescale IMX V4L2 source element");
}
這個函式類似於C++裡面的建構函式,該初始化過程只進行一次。
對於這個函式的理解,首先需要理解物件的分層結構圖:
可以看出GstPushSrcClass是按照GstObject--->GstElement--->GstBaseSrc--->GstPushSrc這樣繼承下來的,所以使用:
gobject_class =G_OBJECT_CLASS (klass);
element_class =GST_ELEMENT_CLASS (klass);
basesrc_class =GST_BASE_SRC_CLASS (klass);
pushsrc_class =GST_PUSH_SRC_CLASS (klass);
的方法,分別獲取它們對應的父類。然後分別實現這些類中的虛擬函式,也就是這些結構體裡面的那些函式指標。
這裡之所以使用 klass 而不是 class,是因為class 是 c++ 語言的關鍵字,如果使用 class,那麼類如是被 C++ 程式呼叫,那麼程式編譯時就杯具了。
對於structGObjectClass:struct GObjectClass {
GTypeClass g_type_class;
/* seldom overidden */
GObject* (*constructor) (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
/* overridable methods */
void (*set_property) (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
void (*get_property) (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
void (*dispose) (GObject *object);
void (*finalize) (GObject *object);
/* seldom overidden */
void (*dispatch_properties_changed) (GObject *object,
guint n_pspecs,
GParamSpec **pspecs);
/* signals */
void (*notify) (GObject *object,
GParamSpec *pspec);
/* called when done constructing */
void (*constructed) (GObject *object);
};
上面標紅的就是在這個函式中實現的函式指標。需要注意的是,這個是基礎的物件,如果設定好這幾個函式指標以後,就需要設定這個物件的屬性,需要使用g_object_class_install_property函式,這個函式封裝在gst_imx_v4l2src_install_properties函式裡面。來看看g_object_class_install_property函式的解釋:
至此,structGObjectClass的初始化就算完成。
struct GstElementClass {
GstObjectClass parent_class;
/* the element metadata */
gpointer metadata;
/* factory that the element was created from */
GstElementFactory *elementfactory;
/* templates for our pads */
GList *padtemplates;
gint numpadtemplates;
guint32 pad_templ_cookie;
/* virtual methods for subclasses */
/* request/release pads */
/* FIXME 2.0 harmonize naming with gst_element_request_pad */
GstPad* (*request_new_pad) (GstElement *element, GstPadTemplate *templ,
const gchar* name, const GstCaps *caps);
void (*release_pad) (GstElement *element, GstPad *pad);
/* state changes */
GstStateChangeReturn (*get_state) (GstElement * element, GstState * state,
GstState * pending, GstClockTime timeout);
GstStateChangeReturn (*set_state) (GstElement *element, GstState state);
GstStateChangeReturn (*change_state) (GstElement *element, GstStateChange transition);
void (*state_changed) (GstElement *element, GstState oldstate,
GstState newstate, GstState pending);
/* bus */
void (*set_bus) (GstElement * element, GstBus * bus);
/* set/get clocks */
GstClock* (*provide_clock) (GstElement *element);
gboolean (*set_clock) (GstElement *element, GstClock *clock);
/* query functions */
gboolean (*send_event) (GstElement *element, GstEvent *event);
gboolean (*query) (GstElement *element, GstQuery *query);
gboolean (*post_message) (GstElement *element, GstMessage *message);
void (*set_context) (GstElement *element, GstContext *context);
};
而對於struct GstElementClass來說,這個結構體可以看作一個pipeline中每一個元件所對應的結構體,所以,對於這個結構體主要是設定它的Pad等,需要使用gst_element_class_add_pad_template函式,函式解釋如下:
關於這兩個函式以後再具體分析。設定完Pad後,就需要設定metadata,使用gst_element_class_set_static_metadata函式來完成:
這個函式也是需要在class_init函式中完成的,具體的分析檢視《外掛編寫指南》。
對於struct GstBaseSrcClass:
struct GstBaseSrcClass {
GstElementClass parent_class;
/* virtual methods for subclasses */
/* get caps from subclass */
GstCaps* (*<span style="color:#FF0000;">get_caps</span>) (GstBaseSrc *src, GstCaps *filter);
/* decide on caps */
gboolean (*negotiate) (GstBaseSrc *src);
/* called if, in negotiation, caps need fixating */
GstCaps * (*<span style="color:#FF0000;">fixate</span>) (GstBaseSrc *src, GstCaps *caps);
/* notify the subclass of new caps */
gboolean (<span style="color:#FF0000;">*set_caps</span>) (GstBaseSrc *src, GstCaps *caps);
/* setup allocation query */
gboolean (*<span style="color:#FF0000;">decide_allocation</span>) (GstBaseSrc *src, GstQuery *query);
/* start and stop processing, ideal for opening/closing the resource */
gboolean (*<span style="color:#FF0000;">start</span>) (GstBaseSrc *src);
gboolean (*<span style="color:#FF0000;">stop</span>) (GstBaseSrc *src);
/* given a buffer, return start and stop time when it should be pushed
* out. The base class will sync on the clock using these times. */
void (*get_times) (GstBaseSrc *src, GstBuffer *buffer,
GstClockTime *start, GstClockTime *end);
/* get the total size of the resource in the format set by
* gst_base_src_set_format() */
gboolean (*get_size) (GstBaseSrc *src, guint64 *size);
/* check if the resource is seekable */
gboolean (*is_seekable) (GstBaseSrc *src);
/* Prepare the segment on which to perform do_seek(), converting to the
* current basesrc format. */
gboolean (*prepare_seek_segment) (GstBaseSrc *src, GstEvent *seek,
GstSegment *segment);
/* notify subclasses of a seek */
gboolean (*do_seek) (GstBaseSrc *src, GstSegment *segment);
/* unlock any pending access to the resource. subclasses should unlock
* any function ASAP. */
gboolean (*unlock) (GstBaseSrc *src);
/* Clear any pending unlock request, as we succeeded in unlocking */
gboolean (*unlock_stop) (GstBaseSrc *src);
/* notify subclasses of a query */
gboolean (*<span style="color:#FF0000;">query</span>) (GstBaseSrc *src, GstQuery *query);
/* notify subclasses of an event */
gboolean (*event) (GstBaseSrc *src, GstEvent *event);
/* ask the subclass to create a buffer with offset and size, the default
* implementation will call alloc and fill. */
GstFlowReturn (*create) (GstBaseSrc *src, guint64 offset, guint size,
GstBuffer **buf);
/* ask the subclass to allocate an output buffer. The default implementation
* will use the negotiated allocator. */
GstFlowReturn (*alloc) (GstBaseSrc *src, guint64 offset, guint size,
GstBuffer **buf);
/* ask the subclass to fill the buffer with data from offset and size */
GstFlowReturn (*fill) (GstBaseSrc *src, guint64 offset, guint size,
GstBuffer *buf);
};
上面標紅的就是在這個函式中實現的函式指標。
對於struct GstPushSrcClass:
struct GstPushSrcClass {
GstBaseSrcClass parent_class;
/* ask the subclass to create a buffer, the default implementation
* uses alloc and fill */
GstFlowReturn (*<span style="color:#FF0000;">create</span>) (GstPushSrc *src, GstBuffer **buf);
/* allocate memory for a buffer */
GstFlowReturn (*alloc) (GstPushSrc *src, GstBuffer **buf);
/* ask the subclass to fill a buffer */
GstFlowReturn (*fill) (GstPushSrc *src, GstBuffer *buf);
};
上面標紅的就是在這個函式中實現的函式指標。
至此,就簡單分析完class_init函式,整個.c檔案就是圍繞這個class_init函式來構建的,或者說,整個.c檔案就是來實現這些函式指標的具體內容。