二十二、GstPad
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 上的任务。
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模式下:
-
需要给元素的sinkpad设定一个chain函数
-
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 设定管道状态开始启动任务
-
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:
- gst_element_set_state ()
- gst_pad_set_active
- 对应Pad的虚函数GstPadActivateFunction(默认实现是gst_pad_activate_default)
- 对应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);