![[GLib][GStreamer] 插件编写思路 —— 继承、覆写 和 虚函数,第1张 [GLib][GStreamer] 插件编写思路 —— 继承、覆写 和 虚函数,第1张](/aiimages/%5BGLib%5D%5BGStreamer%5D+%E6%8F%92%E4%BB%B6%E7%BC%96%E5%86%99%E6%80%9D%E8%B7%AF+%E2%80%94%E2%80%94+%E7%BB%A7%E6%89%BF%E3%80%81%E8%A6%86%E5%86%99+%E5%92%8C+%E8%99%9A%E5%87%BD%E6%95%B0.png)
每一个 GType 都有两个结构体 :instance struct 和 class struct ,二者作用和异同 见 [GLib][GStreamer] Glib 对象模型中的 instance struct 和 class struct_ykun089的博客-CSDN博客
继承:GLib 中的继承是通过在 instance struct 和 class struct 开始部分分别定义 parent instance struct 成结构体成员 和 parent class struct 结构体成员来实现的 (这也是OO语言的底层实现)。
函数覆写:那么函数覆写是如何实现的?下面以 GstbaseSrcClass 为例:
类的成员函数基本上都定义在 class struct中,因此以 GstbaseSrcClass 为例
struct _GstbaseSrcClass {
GstElementClass parent_class;
GstCaps* (*get_caps) (GstbaseSrc *src, GstCaps *filter);
gboolean (*negotiate) (GstbaseSrc *src);
GstCaps * (*fixate) (GstbaseSrc *src, GstCaps *caps);
gboolean (*set_caps) (GstbaseSrc *src, GstCaps *caps);
gboolean (*decide_allocation) (GstbaseSrc *src, GstQuery *query);
gboolean (*start) (GstbaseSrc *src);
gboolean (*stop) (GstbaseSrc *src);
void (*get_times) (GstbaseSrc *src, GstBuffer *buffer,
GstClockTime *start, GstClockTime *end);
gboolean (*get_size) (GstbaseSrc *src, guint64 *size);
gboolean (*is_seekable) (GstbaseSrc *src);
gboolean (*prepare_seek_segment) (GstbaseSrc *src, GstEvent *seek,
GstSegment *segment);
gboolean (*do_seek) (GstbaseSrc *src, GstSegment *segment);
gboolean (*unlock) (GstbaseSrc *src);
gboolean (*unlock_stop) (GstbaseSrc *src);
gboolean (*query) (GstbaseSrc *src, GstQuery *query);
gboolean (*event) (GstbaseSrc *src, GstEvent *event);
GstFlowReturn (*create) (GstbaseSrc *src, guint64 offset, guint size,
GstBuffer **buf);
GstFlowReturn (*alloc) (GstbaseSrc *src, guint64 offset, guint size,
GstBuffer **buf);
GstFlowReturn (*fill) (GstbaseSrc *src, guint64 offset, guint size,
GstBuffer *buf);
gpointer _gst_reserved[GST_PADDING_LARGE];
};
可以看到在 class struct 中有很多函数指针,那么这些指针是怎么用的呢?再随便找一个 GstbaseSrc 的内部实现:
static gboolean
gst_base_src_prepare_seek_segment (GstbaseSrc * src, GstEvent * event,
GstSegment * seeksegment)
{
GstbaseSrcClass *bclass;
gboolean result = FALSE;
bclass = GST_base_SRC_GET_CLASS (src);
//这里进行函数指针的调用
if (bclass->prepare_seek_segment)
result = bclass->prepare_seek_segment (src, event, seeksegment);
return result;
}
因此,如果向覆盖 GstbaseSrc 的 prepare_seek_segment 这个函数的默认实现,只要在自定义的 MyGstbaseSrcClass 中为 GstbaseSrcClass 成员的 prepare_seek_segment 指定自定义的函数即可:
static gboolean
gst_my_prepare_seek_segment (GstbaseSrc * src, GstEvent * event, GstSegment * segment);
static void gst_my_gst_src_init_class(GstbaseSrc* src,gpointer g_class)
{
...
parent_class->prepare_seek_segment = gst_my_prepare_seek_segment;
...
}
其实,上面的思路也是 c++ 语言的实现思路,只不过 c++ 编译器帮我们完成了语法到实现的转换。
一般情况下,某些类都会有一些默认的实现,比如 GstbaseSrc 的 class_init 函数中就用下面这些函数指定:
static void
gst_base_src_class_init (GstbaseSrcClass * klass)
{
//...
//同样地, GstbaseSrc 也会给它自己的 parent class GstElementClass 指定一下自定义动作用来
//覆盖 parent class 的默认动作
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_base_src_change_state);
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_base_src_send_event);
//指定默认动作,GST_DEBUG_FUNCPTR 里面包裹的函数都是定义在 GstbaseSrc.c 文件中的 static 函数
klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_src_default_get_caps);
klass->negotiate = GST_DEBUG_FUNCPTR (gst_base_src_default_negotiate);
klass->fixate = GST_DEBUG_FUNCPTR (gst_base_src_default_fixate);
klass->prepare_seek_segment =
GST_DEBUG_FUNCPTR (gst_base_src_default_prepare_seek_segment);
klass->do_seek = GST_DEBUG_FUNCPTR (gst_base_src_default_do_seek);
klass->query = GST_DEBUG_FUNCPTR (gst_base_src_default_query);
klass->event = GST_DEBUG_FUNCPTR (gst_base_src_default_event);
klass->create = GST_DEBUG_FUNCPTR (gst_base_src_default_create);
klass->alloc = GST_DEBUG_FUNCPTR (gst_base_src_default_alloc);
klass->decide_allocation = GST_DEBUG_FUNCPTR (gst_base_src_decide_allocation_default);
//...
}
虚函数:
结合上面的描述,如果 基类不提供默认动作,而继承类又不进行函数覆写,那么函数指针就是空指针,这个时候可以认为某个函数是一个纯虚函数。不过通常情况下都会进行二次包装,不会直接暴露成员函数给外部使用,比如GstbaseSrc就对外暴露了下面这些接口,可以认为这些接口是public接口:
GST_base_API
GType gst_base_src_get_type (void);
GST_base_API
GstFlowReturn gst_base_src_wait_playing (GstbaseSrc *src);
GST_base_API
void gst_base_src_set_live (GstbaseSrc *src, gboolean live);
GST_base_API
gboolean gst_base_src_is_live (GstbaseSrc *src);
GST_base_API
void gst_base_src_set_format (GstbaseSrc *src, GstFormat format);
GST_base_API
void gst_base_src_set_dynamic_size (GstbaseSrc * src, gboolean dynamic);
GST_base_API
void gst_base_src_set_automatic_eos (GstbaseSrc * src, gboolean automatic_eos);
GST_base_API
void gst_base_src_set_async (GstbaseSrc *src, gboolean async);
GST_base_API
gboolean gst_base_src_is_async (GstbaseSrc *src);
GST_base_API
gboolean gst_base_src_negotiate (GstbaseSrc *src);
GST_base_API
void gst_base_src_start_complete (GstbaseSrc * basesrc, GstFlowReturn ret);
GST_base_API
GstFlowReturn gst_base_src_start_wait (GstbaseSrc * basesrc);
GST_base_API
gboolean gst_base_src_query_latency (GstbaseSrc *src, gboolean * live,
GstClockTime * min_latency,
GstClockTime * max_latency);
GST_base_API
void gst_base_src_set_blocksize (GstbaseSrc *src, guint blocksize);
GST_base_API
guint gst_base_src_get_blocksize (GstbaseSrc *src);
GST_base_API
void gst_base_src_set_do_timestamp (GstbaseSrc *src, gboolean timestamp);
GST_base_API
gboolean gst_base_src_get_do_timestamp (GstbaseSrc *src);
GST_base_API
gboolean gst_base_src_new_seamless_segment (GstbaseSrc *src, gint64 start, gint64 stop, gint64 time);
GST_base_API
gboolean gst_base_src_new_segment (GstbaseSrc *src,
const GstSegment * segment);
GST_base_API
gboolean gst_base_src_set_caps (GstbaseSrc *src, GstCaps *caps);
GST_base_API
GstBufferPool * gst_base_src_get_buffer_pool (GstbaseSrc *src);
GST_base_API
void gst_base_src_get_allocator (GstbaseSrc *src,
GstAllocator **allocator,
GstAllocationParams *params);
GST_base_API
void gst_base_src_submit_buffer_list (GstbaseSrc * src,
GstBufferList * buffer_list);
而GstbaseSrcClass 中的那些指针则是 protected 的接口,因为只有继承类能够访问,同理可以通过在 GstbaseSrc.c 中定义Priv结构体来实现 private 接口,因为按照 c 语言的变成习惯,大家不会include c文件,这样就没法知道 Priv结构体 的内部细节。
如果想要更强制一点的制止不规范程序员试图 include c文件的话,那么可以约定一个规则,要求所有c文件都定义某个同名符号,这样在链接是就会报错重复符号,进而阻止 include c文件的行为。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)