1. 程式人生 > >3. init函式和class_init函式的講解

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的初始化就算完成。

對於structGstElementClass:
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檔案就是來實現這些函式指標的具體內容。