[GLib][GStreamer] 插件编写思路 —— 继承、覆写 和 虚函数

[GLib][GStreamer] 插件编写思路 —— 继承、覆写 和 虚函数,第1张

[GLib][GStreamer] 插件编写思路 —— 继承、覆写 和 虚函数

每一个 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文件的行为。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5690737.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存