1 GstPad基本概念

  • GstElement通过“pads”与其他元素连接,这些 pads 是极其轻量级的通用连接点。

  • Pads具有GstPadDirection,source pads 产生数据,sink pads 消耗数据。Pads通常是从GstPadTemplate创建的,可以使用 gst_pad_new_from_template 创建,然后添加到GstElement中。这通常发生在元素创建时,但也可以根据元素处理的数据或应用程序请求的 pads 动态发生。没有 pad 模板的 pads 可以使用 gst_pad_new 创建,它接受方向和名称作为参数。如果名称为NULL,则会分配一个确保唯一的名称。

  • 创建 pad 的 GstElement 通常会使用各种 gst_pad_set_*_function() 调用为 pads 注册事件、查询或数据流的回调函数。

  • gst_pad_get_parent 将检索拥有该 pad 的 GstElement。

  • 通过 gst_element_get_static_pad 从元素中检索到两个 pad 后,可以使用 gst_pad_link 将这两个 pad 连接起来(对于快速连接,您也可以使用 gst_element_link,它会为您创建直接的连接)。可以使用 gst_pad_unlink 再次取消连接 pads。gst_pad_get_peer 可用于检查 pad 连接到了什么。

  • 在 pads 上实现数据流之前,它们需要使用 gst_pad_set_active 激活。

  • gst_pad_query 和 gst_pad_peer_query 可用于查询 pad 和流的各种属性。

  • 要在 pad 上发送 GstEvent,请使用 gst_pad_send_event 和 gst_pad_push_event。某些事件将在 pad 上保持粘性,这意味着它们在通过 pad 后,稍后可以使用 gst_pad_get_sticky_event 和 gst_pad_sticky_events_foreach 进行查询。gst_pad_get_current_caps 和 gst_pad_has_current_caps 是方便的函数,用于查询 pad 上当前粘性CAPS事件。

  • GstElements 将使用 gst_pad_push 和 gst_pad_pull_range 推出或拉入缓冲区。

  • 在 pad 上发生的数据流dataflow、事件events和查询queries可以通过安装 gst_pad_add_probe 的探测器probes进行监听。gst_pad_is_blocked 可用于检查 pad 上是否安装了block probe。gst_pad_is_blocking 检查阻塞监听器blocking probe是否当前阻塞了 pad。gst_pad_remove_probe 用于移除先前安装的probe,并解除阻止探测器的阻塞(如果有)。

  • Pad 具有偏移量,可以使用 gst_pad_get_offset 检索该偏移量。此偏移量将应用于通过 pad 的所有数据的运行时间running time。gst_pad_set_offset 可用于更改偏移量。

  • 存在一些方便的函数,用于通过 gst_pad_start_task、gst_pad_pause_task 和 gst_pad_stop_task 启动、暂停和停止 pad 上的任务。

alt text

1.1 GstPad继承结构

GObject
    ╰──GInitiallyUnowned
        ╰──GstObject
            ╰──GstPad
                ╰──GstProxyPad

2 GstPad类型结构

2.1 GstPad类型注册宏定义

/* filename: gstpad.h */
#define GST_TYPE_PAD			(gst_pad_get_type ())

/* filename: gstpad.c */
G_DEFINE_TYPE_WITH_CODE (GstPad, gst_pad, GST_TYPE_OBJECT,
    G_ADD_PRIVATE (GstPad) _do_init);

2.2 GstPad类型相关枚举

2.2.1 GstPadDirection

/* filename: gstpad.h */

typedef enum {
  GST_PAD_UNKNOWN,
  GST_PAD_SRC,
  GST_PAD_SINK
} GstPadDirection;

2.2.2 GstPadMode

/* filename: gstpad.h */
/* GstPad数据流的状态。
 * 在激活Pad之后,当parent element从Ready变化到Pause状态,会根据定义的GstPadMode运行。
 */
typedef enum {
  GST_PAD_MODE_NONE, /* Pad将不会处理数据流 */
  GST_PAD_MODE_PUSH, /* Pad以下游推送模式处理数据流 */
  GST_PAD_MODE_PULL /* “Pad在上游拉取模式处理数据流 */
} GstPadMode;

2.2.3 GstPadLinkReturn

/* filename: gstpad.h */

/**
 * gst_pad_link或者相关函数返回值
 */
typedef enum {
  GST_PAD_LINK_OK               =  0, /* 链接成功 */
  GST_PAD_LINK_WRONG_HIERARCHY  = -1, /* 没有公共的grandparent, 要把所有Element放入到同一个bin */
  GST_PAD_LINK_WAS_LINKED       = -2, /* 已经被链接 */
  GST_PAD_LINK_WRONG_DIRECTION  = -3, /* 链接方向错误 */
  GST_PAD_LINK_NOFORMAT         = -4, /* 没有共同的格式format */
  GST_PAD_LINK_NOSCHED          = -5, /* 无法在调度模式合作  push或者pull */
  GST_PAD_LINK_REFUSED          = -6 /* 由于某种原因被拒绝 */
} GstPadLinkReturn;

2.2.4 GstFlowReturn

/* filename: gstpad.h */

/**
 * GstFlowReturn:
 * @GST_FLOW_OK:		 Data passing was ok.
 * @GST_FLOW_NOT_LINKED:	 Pad is not linked.
 * @GST_FLOW_FLUSHING:	         Pad is flushing.
 * @GST_FLOW_EOS:                Pad is EOS.
 * @GST_FLOW_NOT_NEGOTIATED:	 Pad is not negotiated.
 * @GST_FLOW_ERROR:		 Some (fatal) error occurred. Element generating
 *                               this error should post an error message using
 *                               GST_ELEMENT_ERROR() with more details.
 * @GST_FLOW_NOT_SUPPORTED:	 This operation is not supported.
 * @GST_FLOW_CUSTOM_SUCCESS:	 Elements can use values starting from
 *                               this (and higher) to define custom success
 *                               codes.
 * @GST_FLOW_CUSTOM_SUCCESS_1:	 Pre-defined custom success code (define your
 *                               custom success code to this to avoid compiler
 *                               warnings).
 * @GST_FLOW_CUSTOM_SUCCESS_2:	 Pre-defined custom success code.
 * @GST_FLOW_CUSTOM_ERROR:	 Elements can use values starting from
 *                               this (and lower) to define custom error codes.
 * @GST_FLOW_CUSTOM_ERROR_1:	 Pre-defined custom error code (define your
 *                               custom error code to this to avoid compiler
 *                               warnings).
 * @GST_FLOW_CUSTOM_ERROR_2:	 Pre-defined custom error code.
 *
 */
/* 将数据传递给pad的结果 */
typedef enum {
  /* custom success starts here */
  GST_FLOW_CUSTOM_SUCCESS_2 = 102, /* 元素可以使用从此值开始(及更高)的值来定义自定义成功代码。 */
  GST_FLOW_CUSTOM_SUCCESS_1 = 101, /* 预定义的自定义成功代码(将您的自定义成功代码定义为此以避免编译器警告)。 */
  GST_FLOW_CUSTOM_SUCCESS = 100, /* 预定义的自定义成功代码。 */

  /* core predefined */
  GST_FLOW_OK		  =  0, /* 数据传递正常 */
  /* expected failures */
  GST_FLOW_NOT_LINKED     = -1, /* pad没有被链接 */
  GST_FLOW_FLUSHING       = -2, /* pad正在flushing */
  /* error cases */
  GST_FLOW_EOS            = -3, /* Pad已经是EOS */
  GST_FLOW_NOT_NEGOTIATED = -4, /* Pad没有进行协商 */
  GST_FLOW_ERROR	  = -5, /* 发生了一些(致命)错误。生成此错误的元素应使用 GST_ELEMENT_ERROR() 发布具有更多详细信息的错误消息。 */
  GST_FLOW_NOT_SUPPORTED  = -6, /* 不支持此操作。 */

  /* custom error starts here */
  GST_FLOW_CUSTOM_ERROR   = -100, /* 元素可以使用从此值开始(及更低)的值来定义自定义错误代码。 */
  GST_FLOW_CUSTOM_ERROR_1 = -101, /* 预定义的自定义错误代码(将您的自定义错误代码定义为此以避免编译器警告)。 */
  GST_FLOW_CUSTOM_ERROR_2 = -102 /* 预定义的自定义错误代码。 */
} GstFlowReturn;

2.2.6 GstPadLinkCheck

/* filename: gstpad.h */

typedef enum {
  /* 不检查层次结构体或者Caps兼容性 */
  GST_PAD_LINK_CHECK_NOTHING       = 0,
  /* 检查垫片具有相同的父/祖父关系。
   * 如果已知拥有垫片的两个元素位于相同的 bin 中,则可以省略此检查。 
   */
  GST_PAD_LINK_CHECK_HIERARCHY     = 1 << 0,
  /* 通过使用其 template caps 检查垫片是否兼容。
   * 这比 @GST_PAD_LINK_CHECK_CAPS 快得多,但是如果一个垫片具有 %GST_CAPS_ANY,则可能不安全。 
   */
  GST_PAD_LINK_CHECK_TEMPLATE_CAPS = 1 << 1,
  /* 通过比较由 gst_pad_query_caps() 返回的 caps 来检查pad是否兼容。 */
  GST_PAD_LINK_CHECK_CAPS          = 1 << 2,

  /**
   * 当pads已经被链接后,不能发送重新配置事件
   * 实际上不是检查,更像是在这里添加的标志,以避免创建新的gst_pad_link_variant 
  */
  GST_PAD_LINK_CHECK_NO_RECONFIGURE = 1 << 3,
  /* 链接pad时执行的默认检查(即 gst_pad_link() 使用的检查)。 */
  GST_PAD_LINK_CHECK_DEFAULT       = GST_PAD_LINK_CHECK_HIERARCHY | GST_PAD_LINK_CHECK_CAPS
} GstPadLinkCheck;

2.2.7 GstPadProbeType

/* filename: gstpad.h */

typedef enum
{
  GST_PAD_PROBE_TYPE_INVALID          = 0,  /* 无效监听类型 */

  /* flags to control blocking */
  GST_PAD_PROBE_TYPE_IDLE             = (1 << 0), /* 监听空闲的pads并在调用回调函数时阻塞 */
  GST_PAD_PROBE_TYPE_BLOCK            = (1 << 1), /* 监听然后阻塞pads */

  /* flags to select datatypes */
  GST_PAD_PROBE_TYPE_BUFFER           = (1 << 4), /* 监听buffers */
  GST_PAD_PROBE_TYPE_BUFFER_LIST      = (1 << 5), /* 监听buffers lists */

  GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM = (1 << 6), /* 监听下游事件 */
  GST_PAD_PROBE_TYPE_EVENT_UPSTREAM   = (1 << 7), /* 监听上游事件 */

 /* 监听flush事件,此探测必须显式启用,并且不包括在 @@GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM 或
  *  @@GST_PAD_PROBE_TYPE_EVENT_UPSTREAM 探测类型中。*/
  GST_PAD_PROBE_TYPE_EVENT_FLUSH      = (1 << 8),  


  GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM = (1 << 9), /* 监听下游查询 */
  GST_PAD_PROBE_TYPE_QUERY_UPSTREAM   = (1 << 10), /* 监听上游查询 */

  
  /* flags to select scheduling mode */
  GST_PAD_PROBE_TYPE_PUSH             = (1 << 12), /* 监听push */
  GST_PAD_PROBE_TYPE_PULL             = (1 << 13), /* 监听pull */

  /* flag combinations */
  GST_PAD_PROBE_TYPE_BLOCKING         = GST_PAD_PROBE_TYPE_IDLE | GST_PAD_PROBE_TYPE_BLOCK,
  GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM  = GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST | GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
  GST_PAD_PROBE_TYPE_DATA_UPSTREAM    = GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
  GST_PAD_PROBE_TYPE_DATA_BOTH        = GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM | GST_PAD_PROBE_TYPE_DATA_UPSTREAM,
  GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM = GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
  GST_PAD_PROBE_TYPE_BLOCK_UPSTREAM   = GST_PAD_PROBE_TYPE_BLOCK | GST_PAD_PROBE_TYPE_DATA_UPSTREAM,
  GST_PAD_PROBE_TYPE_EVENT_BOTH       = GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
  GST_PAD_PROBE_TYPE_QUERY_BOTH       = GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM | GST_PAD_PROBE_TYPE_QUERY_UPSTREAM,
  GST_PAD_PROBE_TYPE_ALL_BOTH         = GST_PAD_PROBE_TYPE_DATA_BOTH | GST_PAD_PROBE_TYPE_QUERY_BOTH,
  GST_PAD_PROBE_TYPE_SCHEDULING       = GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_PULL
} GstPadProbeType;

2.2.8 GstPadProbeReturn

/* filename: gstpad.h */

/* 监听函数的返回值 */
typedef enum
{
  /**
   * 丢弃数据。对于push模式,这意味着data不会推送到下游。对于pull模式,这意味着数据不是被传递到上游。
   * 在这两种情况下,不会为此项调用其他监听器,并将%GST_FLOW_OK或%TRUE返回给调用者。
  */
  GST_PAD_PROBE_DROP,
  /**
   * 正常监听的返回值。保留这个监听函数继续监听,如果该位置还有其他监听函数,
   * 会把数据的决定权(抛弃或者传递)给其他监听函数。
  */
  GST_PAD_PROBE_OK,
  /* 移除此监听器,传递函数。对于阻塞监听,这将会到导致数据流解除阻塞,除非还安装了其他监听器 */
  GST_PAD_PROBE_REMOVE,
  /* 在阻塞探测器中传递数据项并阻塞在下一监听器上。请注意,如果安装了多个pad监听器并且任何一个监听器返回PASS,则数据将被传递。 */
  GST_PAD_PROBE_PASS,
  /**
   * 数据已在探测器中处理,将不会进一步转发。对于事件和缓冲区,这与%GST_PAD_PROBE_DROP的行为相同(除了在这种情况下,您需要自行取消引用缓冲区或事件)。
   * 对于查询,它还将向调用者返回%TRUE。探测器还可以通过使用#GST_PAD_PROBE_INFO_FLOW_RETURN()访问器修改#GstFlowReturn值。请注意,结果查询必须包含有效的条目。
  */
  GST_PAD_PROBE_HANDLED
} GstPadProbeReturn;

2.2.9 GstPadFlags

/* filename: gstpad.h */

typedef enum {
  /* pad上的数据流会被阻塞 */
  GST_PAD_FLAG_BLOCKED          = (GST_OBJECT_FLAG_LAST << 0),
  /* pad正在处于刷新状态 */
  GST_PAD_FLAG_FLUSHING         = (GST_OBJECT_FLAG_LAST << 1),
  /* pad处于EOS状态 */
  GST_PAD_FLAG_EOS              = (GST_OBJECT_FLAG_LAST << 2),
  /* pad当前正处于buffer或者event阻塞状态 */
  GST_PAD_FLAG_BLOCKING         = (GST_OBJECT_FLAG_LAST << 3),
  /* 在调用回调函数之前确保pad有parent */
  GST_PAD_FLAG_NEED_PARENT      = (GST_OBJECT_FLAG_LAST << 4),
  /* pad应该重新配置/重新协商。在重新协商发生后,必须手动取消设置此标志。 */
  GST_PAD_FLAG_NEED_RECONFIGURE = (GST_OBJECT_FLAG_LAST << 5),
  /* pad上有待处理的事件  */
  GST_PAD_FLAG_PENDING_EVENTS   = (GST_OBJECT_FLAG_LAST << 6),
  /* pad正在使用固定的caps。这意味着一旦在pad上设置了caps, 默认的caps查询函数将只返回这些caps。 */
  GST_PAD_FLAG_FIXED_CAPS       = (GST_OBJECT_FLAG_LAST << 7),
  /* 默认的事件和查询处理程序将所有事件和查询转发到内部链接的pads,而不是将它们丢弃 */
  GST_PAD_FLAG_PROXY_CAPS       = (GST_OBJECT_FLAG_LAST << 8),
  /* 默认的查询处理程序将分配查询转发到内部链接的pads,而不是将它们丢弃。 */
  GST_PAD_FLAG_PROXY_ALLOCATION = (GST_OBJECT_FLAG_LAST << 9),
  /* 默认的查询处理程序将调度查询转发到内部链接的pads,而不是将它们丢弃 */
  GST_PAD_FLAG_PROXY_SCHEDULING = (GST_OBJECT_FLAG_LAST << 10),
  /* 默认的accept-caps处理程序将检查caps是否与查询caps结果相交,而不是检查是否为子集。这对于可以接受不完全指定的caps的解析器很有用。 */
  GST_PAD_FLAG_ACCEPT_INTERSECT = (GST_OBJECT_FLAG_LAST << 11),
  /* 默认的accept-caps处理程序将使用template pad caps而不是查询caps来与accept caps进行比较。与%GST_PAD_FLAG_ACCEPT_INTERSECT结合使用。(自1.6版起) */
  GST_PAD_FLAG_ACCEPT_TEMPLATE  = (GST_OBJECT_FLAG_LAST << 12),
  /* padding */
  GST_PAD_FLAG_LAST        = (GST_OBJECT_FLAG_LAST << 16)
} GstPadFlags;

2.3 GstPad相关结构体

2.3.1 GstPadProbeInfo

/* filename: gstpad.h */

/* 传递给 #GstPadProbeCallback. */
struct _GstPadProbeInfo
{
  GstPadProbeType type; /* 监听类型 */
  gulong id; /* 监听ID */
  gpointer data; /* 输入特定的数据,检查@type字段以了解数据类型。这个字段可以是%NULL */
  guint64 offset; /* pull模式偏移,当@type包含#GST_PAD_PROBE_TYPE_PULL这个字段是有效的 */
  guint size; /* pull模式size, 当@type包含#GST_PAD_PROBE_TYPE_PULL这个字段是有效的 */

  /*< private >*/
  union {
    gpointer _gst_reserved[GST_PADDING];
    struct {
      GstFlowReturn flow_ret;
    } abi;
  } ABI;
};

2.3.2 GstPadClass

/* filename: gstpad.h */
struct _GstPadClass {
  GstObjectClass	parent_class;

  /* signal callbacks */
  void		(*linked)		(GstPad *pad, GstPad *peer);
  void		(*unlinked)		(GstPad *pad, GstPad *peer);

  /*< private >*/
  gpointer _gst_reserved[GST_PADDING];
};

2.3.3 GstPad

/* filename: gstpad.h */
struct _GstPad {
  GstObject                      object;

  /*< public >*/
  gpointer                       element_private;

  GstPadTemplate                *padtemplate;

  GstPadDirection                direction;

  /*< private >*/
  /* streaming rec_lock */
  GRecMutex		         stream_rec_lock;
  GstTask			*task;


  /* block cond, mutex is from the object */
  GCond				 block_cond;
  GHookList                      probes;

  
  GstPadMode		         mode;   /* Pad处于什么调度模式下 */
  GstPadActivateFunction	       activatefunc;
  gpointer                       activatedata;
  GDestroyNotify                 activatenotify;


  GstPadActivateModeFunction	   activatemodefunc;
  gpointer                       activatemodedata;
  GDestroyNotify                 activatemodenotify;


  /* pad link */
  GstPad			*peer;  /* 对端Pad */

  GstPadLinkFunction		 linkfunc;
  gpointer                       linkdata;
  GDestroyNotify                 linknotify;

  GstPadUnlinkFunction		 unlinkfunc;
  gpointer                       unlinkdata;
  GDestroyNotify                 unlinknotify;

  /* data transport functions */
  GstPadChainFunction		 chainfunc; /* Push模式下,处理 GstBuffer的链函数 */
  gpointer                       chaindata;  /* 传入链函数的 user_data */
  GDestroyNotify                 chainnotify; /* 链函数被销毁通知函数调用 */

  GstPadChainListFunction        chainlistfunc;  /* Push模式下,处理 GstBufferList的链函数 */
  gpointer                       chainlistdata; /* 传入链函数的 user_data */
  GDestroyNotify                 chainlistnotify; /* 链函数被销毁通知函数调用 */

  GstPadGetRangeFunction	 getrangefunc; /* Pull模式下,拉取数据函数 */
  gpointer                       getrangedata; /* 传入 getrangefunc 函数的用户数据  */
  GDestroyNotify                 getrangenotify; /* 拉取数据函数被销毁通知函数调用 */

  GstPadEventFunction		 eventfunc; /* 事件函数 */
  gpointer                       eventdata;
  GDestroyNotify                 eventnotify;

  /* pad offset */
  gint64                         offset;

  /* generic query method */
  GstPadQueryFunction		 queryfunc; /* 查询函数 */
  gpointer                       querydata;
  GDestroyNotify                 querynotify;

  /* internal links */
  GstPadIterIntLinkFunction      iterintlinkfunc;
  gpointer                       iterintlinkdata;
  GDestroyNotify                 iterintlinknotify;

  /* counts number of probes attached. */
  gint				 num_probes;
  gint				 num_blocked;

  GstPadPrivate                 *priv;

  union {
    gpointer _gst_reserved[GST_PADDING];
    struct {
      GstFlowReturn last_flowret;
      GstPadEventFullFunction eventfullfunc;
    } abi;
  } ABI;
};

2.3.4 PadEvent

/* filename: gstpad.c */
/**
 * 我们Pad上有一个待处理pending和一个活动active事件。在Source pads上,仅使用活动事件。
 * 在Sink Pads上,事件被复制到待处理entry,然后在时间函数返回%TRUE时移动到active事件。
*/
typedef struct
{
  gboolean received; /* 是否接收事件 */
  guint sticky_order; /* 粘性事件顺序 */
  GstEvent *event; /* 事件 */
} PadEvent;

2.3.5 ProbeMarshall

/* filename: gstpad.c */
typedef struct
{
  GstPad *pad;
  GstPadProbeInfo *info;
  gboolean dropped;
  gboolean pass;
  gboolean handled;
  gboolean marshalled;

  gulong *called_probes;
  guint n_called_probes;
  guint called_probes_size;
  gboolean retry;
} ProbeMarshall;

2.3.6 GstPadPrivate

struct _GstPadPrivate
{
  guint events_cookie;
  GArray *events;
  guint last_cookie;

  gint using;
  guint probe_list_cookie;

  /* 计数器,记录直接从add_probe调用运行的空闲探针数量。用于在空闲回调没有完成工作时阻止Pad中流动的任何数据 */
  gint idle_running;

  /* 条件和变量用于确保Pads在任一时刻仅由单个线程进行激活或停用。受对象锁保护 */
  GCond activation_cond;
  gboolean in_activation;
};

3 GstPad激活调度模式

3.1 激活和激活模式

首先,要明白GStPad关于激活有两个虚函数,一个是 GstPadActivateFunction,另一个是 GstPadActivateModeFunction

3.1.1 GstPadActivateFunction

GstPadActivateFunction:功能是根据一些查询信息,确定Pad是Push还是Pull模式下激活,然后这个函数会去调用能具体的激活GstPadActivateModeFunction。如果sinkpad以Pull-mode激活,也会调用激活Pad函数去激活上游对等srcpad以Pull-mode激活。

示例1:GstPad默认的激活虚函数重写

GstPad默认的激活函数是 gst_pad_activate_default,默认在Push模式下,激活Pad。

/* filename: gstpad.c */
static gboolean
gst_pad_activate_default (GstPad * pad, GstObject * parent)
{
  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
  /* 这个函数会调用 GstPadActivateModeFunction 相应的实现函数(如果该虚函数被实现) */
  return activate_mode_internal (pad, parent, GST_PAD_MODE_PUSH, TRUE);
}

示例2:插件编写指南中的Pull-mode激活虚函数重写

该部分示例来源是GStreamer插件编写指南:高级概念,不同的调度模式

static void
gst_my_filter_init (GstMyFilter * filter)
{
...
/* 设定sinkpad默认激活函数 */
gst_pad_set_activate_function (filter->sinkpad, gst_my_filter_activate);
...
}


static gboolean
gst_my_filter_activate (GstPad * pad, GstObject * parent)
{
  GstQuery *query;
  gboolean pull_mode;

  /* 首先检查支持哪些上游调度 */
  query = gst_query_new_scheduling ();

  if (!gst_pad_peer_query (pad, query)) {
    gst_query_unref (query);
    goto activate_push;
  }

  /* 查看是否支持pull-mode */
  pull_mode = gst_query_has_scheduling_mode_with_flags (query,
      GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
  gst_query_unref (query);

  if (!pull_mode)
    goto activate_push;

  /* 现在我们可以在拉模式pull-mode下激活。GStreamer也将以pull-mode激活上游对等Pad */
  return gst_pad_activate_mode (pad, GST_PAD_MODE_PULL, TRUE);

activate_push:
  {
    /* 不支持pull-mode激活 */
    return gst_pad_activate_mode (pad, GST_PAD_MODE_PUSH, TRUE);
  }
}

3.1.2 GstPadActivateModeFunction

GstPadActivateModeFunction 会根据具体的调度模式,执行不同的任务。比如:在Pull模式下,sinkpad会开始一个task。

示例2:插件编写指南中的Pull-mode激活调度模式虚函数重写

static void
gst_my_filter_init (GstMyFilter * filter)
{
...
/* 设定sinkpad默认激活调度模式函数 */
gst_pad_set_activatemode_function (filter->sinkpad,
      gst_my_filter_activate_mode);
...
}

/*注意:参考指南这个函数的名称是 gst_my_filter_activate_pull
 *应该是 gst_my_filter_activate_mode 对 gst_my_filter_activate_pull包装
 *实际调用的就是 gst_my_filter_activate_pull () 函数
 */
static gboolean
gst_my_filter_activate_mode (GstPad    * pad,
                 GstObject * parent,
                 GstPadMode  mode,
                 gboolean    active)
{
  gboolean res;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (mode) {
    case GST_PAD_MODE_PUSH:
      res = TRUE;
      break;
    case GST_PAD_MODE_PULL:
      if (active) {
        filter->offset = 0;
        res = gst_pad_start_task (pad,
            (GstTaskFunction) gst_my_filter_loop, filter, NULL);
      } else {
        res = gst_pad_stop_task (pad);
      }
      break;
    default:
      /* unknown scheduling mode */
      res = FALSE;
      break;
  }
  return res;
}

4 不同调度模式下的数据处理

管道必须要有驱动源,这个驱动源其实就是一个启动线程处理数据的loop函数,主要就是推送数据给下游(链式处理下游数据)或者从上游拉取数据。

这节先介绍push-mode和pull-mode调度模式,下一节再介绍任务驱动函数。

4.1.1 push-mode

push-mode模式下:

  1. 需要给元素的sinkpad设定一个chain函数

  2. chain函数最后需要调用 gst_pad_push (filter->srcpad, buf) 函数。该函数调用下一个元素的sinkpad的链函数。

/*filename: gstpad.c*/
GstFlowReturn
gst_pad_push (GstPad * pad, GstBuffer * buffer)
{
  ...
  res = gst_pad_push_data (pad,
      GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH, buffer);
  ...
}

/* @pad一定是src pad */
static GstFlowReturn
gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
{
  ...
  /* peer就是对端的sink pad,调用对端Pad的链函数 */
  ret = gst_pad_chain_data_unchecked (peer, type, data);
  ...
}

/* @pad一定是sink pad */
static inline GstFlowReturn
gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data)
{
  ...
  /* 执行链函数 */
  ret = chainfunc (pad, parent, GST_BUFFER_CAST (data));
  ...
}

编写插件指南中的示例程序:

static void
gst_my_filter_init (GstMyFilter * filter) {
...
  gst_pad_set_chain_function (filter->sinkpad,
      gst_my_filter_chain);
...
}

static GstFlowReturn
gst_my_filter_chain (GstPad    *pad,
                     GstObject *parent,
             GstBuffer *buf)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);

  if (!filter->silent)
    g_print ("Have data of size %" G_GSIZE_FORMAT" bytes!\n",
        gst_buffer_get_size (buf));

  return gst_pad_push (filter->srcpad, buf);
}

4.1.2 pull-mode

pull-mode模式下: 需要给元素的srcpad设定一个getrange函数,一般来说都是源元素,pull模式元素也不是很多,所以遇见其他pull元素再作总结。

编写插件指南中的示例程序:

/* source element */
static void
gst_my_filter_init (GstMyFilter * filter) {
  ...
  gst_pad_set_getrange_function (filter->srcpad,
      gst_my_filter_get_range);
  ...
}

static GstFlowReturn
gst_my_filter_get_range (GstPad     * pad,
             GstObject  * parent,
             guint64      offset,
             guint        length,
             GstBuffer ** buf) {

  GstMyFilter *filter = GST_MY_FILTER (parent);

  /*.. here, you would fill *buf ..*/

  return GST_FLOW_OK;
}

5 调度模式下的驱动函数

在线程任务中,运行推送Push或者拉取Pull数据函数。

示例1:源元素推送数据给下游

/* filename: gstbasesrc. */

static GstStateChangeReturn
gst_base_src_change_state (GstElement * element, GstStateChange transition) {
  ...
  switch (transition) {
    ...
    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
      ...
      gst_base_src_set_playing (basesrc, TRUE);
      ...
  }
  ...
}

static gboolean
gst_base_src_set_playing (GstBaseSrc * basesrc, gboolean live_play) {
  ...
  GST_OBJECT_LOCK (basesrc->srcpad);
  start = (GST_PAD_MODE (basesrc->srcpad) == GST_PAD_MODE_PUSH);
  GST_OBJECT_UNLOCK (basesrc->srcpad);
  if (start)
    gst_pad_start_task (basesrc->srcpad, (GstTaskFunction) gst_base_src_loop,
        basesrc->srcpad, NULL); /* 如果是Push模式,启动任务函数 */
  ...
}

static void
gst_base_src_loop (GstPad * pad) {
  ...
  /* push buffer or buffer list */
  if (src->priv->pending_bufferlist != NULL) {
    ret = gst_pad_push_list (pad, src->priv->pending_bufferlist);
    src->priv->pending_bufferlist = NULL;
  } else {
    ret = gst_pad_push (pad, buf);
  }
  ...
}

示例2:rademux从上游拉数据,然后推送数据给下游

这种情况就是元素sink pad是pull-mode,元素src pad是push模式。后面小节会有相关播放示例分析。

/* rademux.c */

static gboolean
gst_real_audio_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
    GstPadMode mode, gboolean active)
{
  gboolean res;
  GstRealAudioDemux *demux;

  demux = GST_REAL_AUDIO_DEMUX (parent);

  switch (mode) {
    case GST_PAD_MODE_PUSH:
      demux->seekable = FALSE;
      res = TRUE;
      break;
    case GST_PAD_MODE_PULL:
      if (active) {
        demux->seekable = TRUE;
        /* 会在激活模式里面启动一个loop任务,成为管道的驱动力 */
        res = gst_pad_start_task (sinkpad,
            (GstTaskFunction) gst_real_audio_demux_loop, demux, NULL);
      } else {
        demux->seekable = FALSE;
        res = gst_pad_stop_task (sinkpad);
      }
      break;
    default:
      res = FALSE;
      break;
  }
  return res;
}

static void
gst_real_audio_demux_loop (GstRealAudioDemux * demux) {
  ...
  /* 从上游拉取数据 */
  ret = gst_pad_pull_range (demux->sinkpad, demux->offset, bytes_needed, &buf);
  ...
  ret = gst_real_audio_demux_handle_buffer (demux, buf); /* 这个函数的里面会有函数调用 ret = gst_pad_push (demux->srcpad, buf); */
  ...
}

6 设定管道状态开始启动任务

Alt text

  • Pad激活是一个关键部分。改变状态时,一个 bin 会按照从sink到source的顺序为其所有子元素设置状态。当元素经历 READY→PAUSED 过渡时,它们的Pad会被激活以准备数据流。一些Pad会启动任务以驱动数据流。具体可以查看 gst_element_set_state () 函数。

  • 一个元素会从sourcepads到sinkpads依次激活其Pad。这是为了确保当sinkpads被激活并准备接收数据时,sourcepads已经激活以向下游传递数据。具体可以查看 gst_element_pads_activate () 函数。

    • ready->paused调用gst_pad_set_active(pad, TRUE)激活Pad

    • pasued->ready调用gst_pad_set_active(pad, FALSE)停用pad

从设定元素开始,每个函数依次去调用1->2->3->4:

  1. gst_element_set_state ()
  2. gst_pad_set_active
  3. 对应Pad的虚函数GstPadActivateFunction(默认实现是gst_pad_activate_default)
  4. 对应Pad的虚函数GstPadActivateModeFunction(该虚函数没有默认实现)

6.1 示例程序分析

pad内部的括号中说明了该pad支持的功能:

  • l:暴露(expose)一个loop函数,所以该pad可以作为驱动源(调用者)。
  • g:暴露一个getrange函数
  • c:暴露一个chain函数

根据Pad暴露的调度方法,做出以下调度决策:

  • (g)-(l):srcpad将从srcpad拉取数据。
  • (l)-(c):srcpad主动向sinkpad推送数据。
  • ()-(c):-将向sinkpad推送数据。
  • ()-():不可调度。
  • ()-(l):不可调度。
  • (g)-():不可调度。
  • (g)-(c):不可调度。
  • (l)-():不可调度。
  • (l)-(l):不可调度。
  • ()-(g):不可能。
  • (g)-(g):不可能。
  • (l)-(g):不可能。
  • (c)-():不可能。
  • (c)-(g):不可能。
  • (c)-(l):不可能。
  • (c)-(c):不可能。
####################pull + push模式########################### 

gst-launch-1.0 filesrc location=./sample-480p.webm ! matroskademux ! vp8dec ! videoconvert ! ximagesink

+---------+    +---------------+    +--------+    +--------------+     +------------+
| filesrc |    | matroskademux |    | vp8dec |    | videoconvert |     | ximagesink |
|        src--sink            src--sink     src--sink           src--sink           |
+---------+    +---------------+    +--------+    +--------------+     +------------+
         (g)  (l)             ()   (c)      ()   (c)            ()    (c)
         pull pull            push push     push push           push  push 


# 打印元素调用状态变化函数相关变量如下:
elment = ximagesink0
pad name = sink, pad mode = push
elment = videoconvert0
pad name = src, pad mode = push
pad name = sink, pad mode = push
elment = vp8dec0
pad name = src, pad mode = push
pad name = sink, pad mode = push
elment = matroskademux0
pad name = sink, pad mode = pull
elment = filesrc0
pad name = src, pad mode = pull

####################push模式########################### 

gst-launch-1.0 fakesrc ! fakesink


+---------+    +-----------+
| fakesrc |    |  fakesrc  |
|        src--sink         |
+---------+    +-----------+  
         (l)  (c)           
         push push           


# 打印元素调用状态变化函数相关变量如下:
elment = fakesink0
pad name = sink, pad mode = push
elment = fakesrc0
pad name = src, pad mode = push

关于offset

/* Set an offset of 5s, meaning:
  * segment position 0 gives running time 5s, stream time 0s
  * segment start of 0 should stay 0
  */
gst_pad_set_offset (offset_pad, 5 * GST_SECOND);