如何去使用GObject去构建一个所谓的“对象”呢?GObject中每个类要定义两个结构体,假设你要定义的类型为People,那么你要定义两个结构分别名为People和PeopleClass,估计刚接触的人会有些晕,一般的C++啊,JAVA什么的都是直接一个class了事儿了。但记住C本身并没有面向对象的机制,这里这样做也仅仅是为了模拟。 名为PeopleClass的结构是表示类的结构,而名为People的结构则是这个类的实例,可能这么说一般人还会有点摸不着头脑,切记,这是一种模拟。
下面这段话摘自《快速上手Gobject 》:
在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》这两个文档,网页链接为:
http://www.cnblogs.com/pingf/archive/2009/11/20/1606742.html
https://segmentfault.com/a/1190000003861212
或者查看《快速上手Gobject.pdf》中有关这一部分的讲解,链接如下:
http://blog.csdn.net/acs713/article/details/7778051
在上一篇文章中讲到了使用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* (*get_caps) (GstBaseSrc *src, GstCaps *filter);
/* decide on caps */
gboolean (*negotiate) (GstBaseSrc *src);
/* called if, in negotiation, caps need fixating */
GstCaps * (*fixate) (GstBaseSrc *src, GstCaps *caps);
/* notify the subclass of new caps */
gboolean (*set_caps) (GstBaseSrc *src, GstCaps *caps);
/* setup allocation query */
gboolean (*decide_allocation) (GstBaseSrc *src, GstQuery *query);
/* start and stop processing, ideal for opening/closing the resource */
gboolean (*start) (GstBaseSrc *src);
gboolean (*stop) (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 (*query) (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 (*create) (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文件就是来实现这些函数指针的具体内容。