目录
模块的简单分析
【GStreamer】GstAllocator的简单分析
【GStreamer】GstMemory的简单分析
【GStreamer】GstBuffer的简单分析
【GStreamer】GstPad的简单分析
【GStreamer】GstElement的简单分析
【GStreamer】GstBus和GstMessage的简单分析
【GStreamer】GstDevice和GstPlugin的简单分析
GstPad用于在pipeline中连接不同的元素(GstElement),可以将pad理解为一个元素的连接点或者端口,数据通过这些连接点在元素之间流动。pad的核心作用是,数据流动的接口,可以是某一个元素的数据接收端口,也可以是某一个元素的数据发送端口。使用capabilities(Caps)描述pad可以处理的数据类型(如视频、音频、具体的编码格式等),必须确保两个pad的caps可以兼容。
有些pad是静态的(在元素创建时就存在),有些pad是动态的(在运行时根据需要创建),例如decodebin元素会根据输入的媒体类型动态的创建相应的pad。pad除了负责数据传输之外,还可以处理各种时间,例如流开始、流结束、刷新、查询和控制流速
1.pad结构体定义
(1)pad方向的定义
typedef enum {
GST_PAD_UNKNOWN,
GST_PAD_SRC, // 发送数据的pad
GST_PAD_SINK // 接收数据的pad
} GstPadDirection;
(2)pad模式的定义,描述pad的工作模式,决定了pad如何处理数据流
typedef enum {
GST_PAD_MODE_NONE, // 非活跃状态
GST_PAD_MODE_PUSH, // 推流模式
GST_PAD_MODE_PULL // 拉流模式
} GstPadMode;
(3)GstPadLinkReturn表示尝试链接两个pad的结果类型
typedef enum {
// 链接成功
GST_PAD_LINK_OK = 0,
// 两个pad没有共同的祖父元素(必须具有共同的祖父元素才能链接成功)
GST_PAD_LINK_WRONG_HIERARCHY = -1,
// pad已经处于链接状态
GST_PAD_LINK_WAS_LINKED = -2,
// 两个pad的方向不匹配
GST_PAD_LINK_WRONG_DIRECTION = -3,
// 两个pad格式(caps)不兼容
GST_PAD_LINK_NOFORMAT = -4,
// 两个pad无法在调度(scheduling)上协作
GST_PAD_LINK_NOSCHED = -5,
// 拒绝链接,但是需要排查错误
GST_PAD_LINK_REFUSED = -6
} GstPadLinkReturn;
(4)GstFlowReturn描述了数据传递的结果
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 */
// pad未连接到其他pad
// 如果尝试向未连接的pad传递数据,会返回此值
GST_FLOW_NOT_LINKED = -1,
// pad处于刷新状态
// 刷新状态下,pad不会处理数据,而是丢弃传入的数据
GST_FLOW_FLUSHING = -2,
/* error cases */
// 数据流结束
GST_FLOW_EOS = -3,
// pad格式(caps)未协商成功
GST_FLOW_NOT_NEGOTIATED = -4,
// 发生错误
// 生成错误的元素可以使用GST_ELEMENT_ERROR()发布错误信息
GST_FLOW_ERROR = -5,
// 当前操作不支持
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;
(5)GstPadLinkCheck控制在连接两个pad时,需要执行哪些检查的枚举类型,定义了在pad连接过程中可以启用的各种检查选项,以确保连接的合法性和安全性
typedef enum {
// 不进行任何检查
GST_PAD_LINK_CHECK_NOTHING = 0,
// 检查两个pad是否具有相同的父元素或者祖父元素
GST_PAD_LINK_CHECK_HIERARCHY = 1 << 0,
// 使用pad的模板caps检查它们的兼容性
GST_PAD_LINK_CHECK_TEMPLATE_CAPS = 1 << 1,
// 使用gst_pad_query_caps()返回的caps检查pad的兼容性
// 这种检查更安全,但性能开销较大
GST_PAD_LINK_CHECK_CAPS = 1 << 2,
/* Not really checks, more like flags
- Added here to avoid creating a new gst_pad_link_variant */
// 不允许在连接pad时,重新配置事件
GST_PAD_LINK_CHECK_NO_RECONFIGURE = 1 << 3,
// 默认检查选项
GST_PAD_LINK_CHECK_DEFAULT = GST_PAD_LINK_CHECK_HIERARCHY | GST_PAD_LINK_CHECK_CAPS
} GstPadLinkCheck;
(6)GstPadProbeType描述了pad探针(probe)类型的枚举类型,探针允许开发者在数据流经pad时插入回调函数,以监控或者修改数据
typedef enum
{
// 无效的探针类型
GST_PAD_PROBE_TYPE_INVALID = 0,
/* flags to control blocking */
// 监控空闲pad,并在回调函数调用时阻塞pad
GST_PAD_PROBE_TYPE_IDLE = (1 << 0),
// 监控并阻塞pad
GST_PAD_PROBE_TYPE_BLOCK = (1 << 1),
/* flags to select datatypes */
// 监控缓冲区buffer
GST_PAD_PROBE_TYPE_BUFFER = (1 << 4),
// 监控缓冲区列表buffer list
GST_PAD_PROBE_TYPE_BUFFER_LIST = (1 << 5),
// 监控下游事件(如EOS、Segment)
GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM = (1 << 6),
// 监控上游事件(如FLUSH_START、FLUSH_STOP等)
GST_PAD_PROBE_TYPE_EVENT_UPSTREAM = (1 << 7),
// 监控刷新事件(FLUSH)
// 需要显式调用,不包含在EVENT_DOWNSTREAM和EVENT_UPSTREAM中
GST_PAD_PROBE_TYPE_EVENT_FLUSH = (1 << 8),
// 监控下游查询(如DURATION、POSITION等)
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),
// 监控拉流模式的数据流
GST_PAD_PROBE_TYPE_PULL = (1 << 13),
/* 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;
(7)GstPadProbeReturn描述了pad探针回调函数返回值的枚举类型,决定了探针如何处理流经pad的数据(如缓冲区、事件、查询等),并控制数据是否继续传递、是否丢弃、是否移除探针等行为
typedef enum
{
// 丢弃数据
GST_PAD_PROBE_DROP,
// 正常返回值,探针保持激活状态
GST_PAD_PROBE_OK,
// 移除当前探针,并允许数据继续传递
GST_PAD_PROBE_REMOVE,
// 允许当前数据项通过,并在下一个数据项上继续阻塞
GST_PAD_PROBE_PASS,
// 数据已在探针中被处理,不会继续传递
GST_PAD_PROBE_HANDLED
} GstPadProbeReturn;
(8)GstPadFlags描述pad状态和行为的标志枚举类型,用于指示pad的当前状态、配置需求以及如何处理事件和查询。每个标志位都代表pad的某种特定状态或行为
typedef enum {
// 数据流被阻塞
GST_PAD_FLAG_BLOCKED = (GST_OBJECT_FLAG_LAST << 0),
// 正在刷新
GST_PAD_FLAG_FLUSHING = (GST_OBJECT_FLAG_LAST << 1),
// 到达流末尾
GST_PAD_FLAG_EOS = (GST_OBJECT_FLAG_LAST << 2),
// 正在阻塞某个缓冲区或者事件
GST_PAD_FLAG_BLOCKING = (GST_OBJECT_FLAG_LAST << 3),
// 需要确保存在父对象才能调用pad的回调函数
GST_PAD_FLAG_NEED_PARENT = (GST_OBJECT_FLAG_LAST << 4),
// 需要重新配置或重新协商(需要手动清除)
GST_PAD_FLAG_NEED_RECONFIGURE = (GST_OBJECT_FLAG_LAST << 5),
// 有待处理的事件
GST_PAD_FLAG_PENDING_EVENTS = (GST_OBJECT_FLAG_LAST << 6),
// 使用固定的caps
GST_PAD_FLAG_FIXED_CAPS = (GST_OBJECT_FLAG_LAST << 7),
// 默认事件和查询处理程序会将所有事件和查询转发到内部链接的pad
GST_PAD_FLAG_PROXY_CAPS = (GST_OBJECT_FLAG_LAST << 8),
// 默认处理程序会将分配查询(Allocation Query)转发到内部pad
GST_PAD_FLAG_PROXY_ALLOCATION = (GST_OBJECT_FLAG_LAST << 9),
// 默认处理程序会将调度查询(Scheduling Query)转发到内部pad
GST_PAD_FLAG_PROXY_SCHEDULING = (GST_OBJECT_FLAG_LAST << 10),
// 默认accept-caps处理程序会检查caps是否与查询结果相交
GST_PAD_FLAG_ACCEPT_INTERSECT = (GST_OBJECT_FLAG_LAST << 11),
// 默认accept-caps处理程序会使用模板pad的caps而不是查询caps来进行比较
GST_PAD_FLAG_ACCEPT_TEMPLATE = (GST_OBJECT_FLAG_LAST << 12),
/* padding */
GST_PAD_FLAG_LAST = (GST_OBJECT_FLAG_LAST << 16)
} GstPadFlags;
(9)GstPad中包含了pad的各种属性和功能函数,用于管理数据流、事件处理、查询处理等
struct _GstPad {
GstObject object;
/*< public >*/
// 父元素的私有数据
gpointer element_private;
// padtemplate中定义了pad的静态属性,例如方向、支持的Caps等
GstPadTemplate *padtemplate;
// pad的方向(SRC或SINK),创建之后不可修改
GstPadDirection direction;
/*< private >*/
/* streaming rec_lock */
// 保护pad数据流的递归互斥锁,确保在多线程环境下对pad数据流的访问是线程安全地
GRecMutex stream_rec_lock;
// task管理pad的数据流处理任务
GstTask *task;
/* block cond, mutex is from the object */
// 条件变量,用于在pad阻塞时等待条件满足
GCond block_cond;
// 探针列表,用于监控和拦截数据流
GHookList probes;
// pad的当前模式(PUSH、PULL)
GstPadMode mode;
// 激活函数
GstPadActivateFunction activatefunc;
gpointer activatedata;
GDestroyNotify activatenotify;
// 激活模式函数
GstPadActivateModeFunction activatemodefunc;
gpointer activatemodedata;
GDestroyNotify activatemodenotify;
/* pad link */
// 当前pad的对端pad
GstPad *peer;
// 链接函数
GstPadLinkFunction linkfunc;
gpointer linkdata;
GDestroyNotify linknotify;
// 断开链接函数
GstPadUnlinkFunction unlinkfunc;
gpointer unlinkdata;
GDestroyNotify unlinknotify;
/* data transport functions */
// 数据链函数,处理数据时调用
GstPadChainFunction chainfunc;
// 用户数据
gpointer chaindata;
GDestroyNotify chainnotify;
// 数据链列表函数,用于处理多个数据缓冲区
GstPadChainListFunction chainlistfunc;
gpointer chainlistdata;
GDestroyNotify chainlistnotify;
// 在PULL模式下,获取数据范围
GstPadGetRangeFunction getrangefunc;
gpointer getrangedata;
GDestroyNotify getrangenotify;
// 处理流事件(如EOS、SEGMENT等)
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. */
// 附加到pad的探针数量
gint num_probes;
// 当前被阻塞的探针数量
gint num_blocked;
// pad的私有数据
GstPadPrivate *priv;
union {
gpointer _gst_reserved[GST_PADDING];
struct {
GstFlowReturn last_flowret;
GstPadEventFullFunction eventfullfunc;
} abi;
} ABI;
};
_GstPadClass是GstPad的父类,其中包含了链接函数和断开链接函数
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.主要函数
2.1 初始化
(1)gst_pad_class_init
_GstPadClass的初始化,设置pad的虚函数(dispose、finalize),注册pad的信号(linked,unlinked),定义pad的属性(caps、direction和template)
static void
gst_pad_class_init (GstPadClass * klass)
{
GObjectClass *gobject_class;
GstObjectClass *gstobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gstobject_class = GST_OBJECT_CLASS (klass);
// dispose在对象销毁时被调用,用于释放资源
gobject_class->dispose = gst_pad_dispose;
// finalize在对象被完全释放时调用,用于清理内存
gobject_class->finalize = gst_pad_finalize;
// 设置对象属性
gobject_class->set_property = gst_pad_set_property;
// 获取对象属性
gobject_class->get_property = gst_pad_get_property;
/**
- GstPad::linked:
- @pad: the pad that emitted the signal
- @peer: the peer pad that has been connected
- 6. Signals that a pad has been linked to the peer pad.
*/
// 定义PAD_LINKED信号,当pad与另一个pad链接时触发
// "linked":信号名称
// G_TYPE_FROM_CLASS(kclass):信号所属类型,这里是GstPadClass
// G_SIGNAL_RUN_LAST:信号的运行方式,这里表示信号的默认处理函数将在所有连接的信号处理器之后运行
// G_STRUCT_OFFSET(GstPadClass, linked):指定信号的默认处理函数在类结构体中的偏移量
// 前两个NULL:信号的累加器函数和累加器数据,这里表示不使用累加器
// 第三个NULL:回调函数
// G_TYPE_NONE:信号的返回类型,这里表示没有返回值
// 1: 信号的参数数量,这里只有1个
// GST_TYPE_PAD: 信号的参数类型,表示信号的第一个参数是GstPad类型
// 这里最关键的地方,是当发生连接时,调用的是linked()这个函数,这是一个GstPadClass中的成员
gst_pad_signals[PAD_LINKED] =
g_signal_new ("linked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPadClass, linked), NULL, NULL,
NULL, G_TYPE_NONE, 1, GST_TYPE_PAD);
/**
- GstPad::unlinked:
- @pad: the pad that emitted the signal
- @peer: the peer pad that has been disconnected
- 11. Signals that a pad has been unlinked from the peer pad.
*/
// 定义PAD_UNLINKED信号,当pad与另一个pad断开连接时触发
gst_pad_signals[PAD_UNLINKED] =
g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GstPadClass, unlinked), NULL, NULL,
NULL, G_TYPE_NONE, 1, GST_TYPE_PAD);
// 定义属性caps
pspec_caps = g_param_spec_boxed ("caps", "Caps",
"The capabilities of the pad", GST_TYPE_CAPS,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
// 将caps属性install到pad类中
g_object_class_install_property (gobject_class, PAD_PROP_CAPS, pspec_caps);
// 将direction属性install到pad类中
g_object_class_install_property (gobject_class, PAD_PROP_DIRECTION,
g_param_spec_enum ("direction", "Direction", "The direction of the pad",
GST_TYPE_PAD_DIRECTION, GST_PAD_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
/* FIXME, Make G_PARAM_CONSTRUCT_ONLY when we fix ghostpads. */
// 将template属性install到pad类中
g_object_class_install_property (gobject_class, PAD_PROP_TEMPLATE,
g_param_spec_object ("template", "Template",
"The GstPadTemplate of this pad", GST_TYPE_PAD_TEMPLATE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
- GstPad:offset:
- 14. The offset that will be applied to the running time of the pad.
- 16. Since: 1.6
*/
// 将offset属性install到pad类中
g_object_class_install_property (gobject_class, PAD_PROP_OFFSET,
g_param_spec_int64 ("offset", "Offset",
"The running time offset of the pad", 0, G_MAXINT64, 0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
// 设置pad的路径分隔符为.
gstobject_class->path_string_separator = ".";
/* Register common function pointer descriptions */
// 注册一些常规的函数,激活函数,事件函数,查询函数等
// 默认的激活函数,在没有定义激活函数时才使用,一般不用
GST_DEBUG_REGISTER_FUNCPTR (gst_pad_activate_default);
GST_DEBUG_REGISTER_FUNCPTR (gst_pad_event_default);
GST_DEBUG_REGISTER_FUNCPTR (gst_pad_query_default);
GST_DEBUG_REGISTER_FUNCPTR (gst_pad_iterate_internal_links_default);
GST_DEBUG_REGISTER_FUNCPTR (gst_pad_chain_list_default);
}
(2)gst_pad_init
static void
gst_pad_init (GstPad * pad)
{
pad->priv = gst_pad_get_instance_private (pad);
GST_PAD_DIRECTION (pad) = GST_PAD_UNKNOWN;
GST_PAD_ACTIVATEFUNC (pad) = gst_pad_activate_default;
GST_PAD_EVENTFUNC (pad) = gst_pad_event_default;
GST_PAD_QUERYFUNC (pad) = gst_pad_query_default;
GST_PAD_ITERINTLINKFUNC (pad) = gst_pad_iterate_internal_links_default;
GST_PAD_CHAINLISTFUNC (pad) = gst_pad_chain_list_default;
GST_PAD_SET_FLUSHING (pad);
g_rec_mutex_init (&pad->stream_rec_lock);
g_cond_init (&pad->block_cond);
g_hook_list_init (&pad->probes, sizeof (GstProbe));
pad->priv->events = g_array_sized_new (FALSE, TRUE, sizeof (PadEvent), 16);
pad->priv->events_cookie = 0;
pad->priv->last_cookie = -1;
g_cond_init (&pad->priv->activation_cond);
pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
}
2.2 pad链接(gst_pad_link)
GstPadLinkReturn
gst_pad_link (GstPad * srcpad, GstPad * sinkpad)
{
return gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT);
}
gst_pad_link_full()执行链接的具体操作,主要过程为:
1.链接准备(gst_pad_link_prepare)
2.设置对端pad(GST_PAD_PEER)
3.调度事件(schedule_events),为自定义链接做准备,确保不会出现访问冲突
4.自定义链接(srcfunc或sinkfunc),通常不会执行自定义链接,不会进行这个步骤
5.链接完成(g_signal_emit ),发送信号表示两个pad链接成功
其中,pad链接最核心的地方在于设置peer,而不是自定义链接
GstPadLinkReturn
gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
{
GstPadLinkReturn result;
GstElement *parent;
GstPadLinkFunction srcfunc, sinkfunc;
g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_PAD_IS_SRC (srcpad), GST_PAD_LINK_WRONG_DIRECTION);
g_return_val_if_fail (GST_IS_PAD (sinkpad), GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_PAD_IS_SINK (sinkpad),
GST_PAD_LINK_WRONG_DIRECTION);
GST_TRACER_PAD_LINK_PRE (srcpad, sinkpad);
/* Notify the parent early. See gst_pad_unlink for details. */
if (G_LIKELY ((parent = GST_ELEMENT_CAST (gst_pad_get_parent (srcpad))))) {
if (G_LIKELY (GST_IS_ELEMENT (parent))) {
gst_element_post_message (parent,
gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, TRUE));
} else {
gst_object_unref (parent);
parent = NULL;
}
}
/* prepare will also lock the two pads */
// 1.为链接进行准备
result = gst_pad_link_prepare (srcpad, sinkpad, flags);
if (G_UNLIKELY (result != GST_PAD_LINK_OK)) {
GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad),
gst_pad_link_get_name (result));
goto done;
}
/* must set peers before calling the link function */
// 2.设置两个pad的对端pad,实际上这就是链接的核心,后续的链接函数是用户自定义的,
// 可以执行一些用户期望的操作,但不影响链接
GST_PAD_PEER (srcpad) = sinkpad;
GST_PAD_PEER (sinkpad) = srcpad;
/* check events, when something is different, mark pending */
// 3.调度事件,
schedule_events (srcpad, sinkpad);
/* get the link functions */
srcfunc = GST_PAD_LINKFUNC (srcpad);
sinkfunc = GST_PAD_LINKFUNC (sinkpad);
// 4.G_UNLIKELY表示srcfunc或sinkfunc通常不太会被定义
// 通常情况下,不会自定义链接函数
if (G_UNLIKELY (srcfunc || sinkfunc)) {
/* custom link functions, execute them */
GST_OBJECT_UNLOCK (sinkpad);
GST_OBJECT_UNLOCK (srcpad);
if (srcfunc) {
GstObject *tmpparent;
ACQUIRE_PARENT (srcpad, tmpparent, no_parent);
/* this one will call the peer link function */
result = srcfunc (srcpad, tmpparent, sinkpad);
RELEASE_PARENT (tmpparent);
} else if (sinkfunc) {
GstObject *tmpparent;
ACQUIRE_PARENT (sinkpad, tmpparent, no_parent);
/* if no source link function, we need to call the sink link
* function ourselves. */
result = sinkfunc (sinkpad, tmpparent, srcpad);
RELEASE_PARENT (tmpparent);
}
no_parent:
GST_OBJECT_LOCK (srcpad);
GST_OBJECT_LOCK (sinkpad);
/* we released the lock, check if the same pads are linked still */
if (GST_PAD_PEER (srcpad) != sinkpad || GST_PAD_PEER (sinkpad) != srcpad)
goto concurrent_link;
if (G_UNLIKELY (result != GST_PAD_LINK_OK))
goto link_failed;
}
GST_OBJECT_UNLOCK (sinkpad);
GST_OBJECT_UNLOCK (srcpad);
/* fire off a signal to each of the pads telling them
* that they've been linked */
// 5.发送信号,表明两个pad已经链接成功
g_signal_emit (srcpad, gst_pad_signals[PAD_LINKED], 0, sinkpad);
g_signal_emit (sinkpad, gst_pad_signals[PAD_LINKED], 0, srcpad);
GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
if (!(flags & GST_PAD_LINK_CHECK_NO_RECONFIGURE))
gst_pad_send_event (srcpad, gst_event_new_reconfigure ());
done:
if (G_LIKELY (parent)) {
gst_element_post_message (parent,
gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, FALSE));
gst_object_unref (parent);
}
GST_TRACER_PAD_LINK_POST (srcpad, sinkpad, result);
return result;
/* ERRORS */
concurrent_link:
{
GST_CAT_INFO (GST_CAT_PADS, "concurrent link between %s:%s and %s:%s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
GST_OBJECT_UNLOCK (sinkpad);
GST_OBJECT_UNLOCK (srcpad);
/* The other link operation succeeded first */
result = GST_PAD_LINK_WAS_LINKED;
goto done;
}
link_failed:
{
GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed: %s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad),
gst_pad_link_get_name (result));
GST_PAD_PEER (srcpad) = NULL;
GST_PAD_PEER (sinkpad) = NULL;
GST_OBJECT_UNLOCK (sinkpad);
GST_OBJECT_UNLOCK (srcpad);
goto done;
}
}
(1)gst_pad_link_prepare
为链接过程做准备,检查两个pad的状态、媒体类型和调度模式是否兼容,确保可以安全地链接
static GstPadLinkReturn
gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags)
{
GstPadLinkReturn result;
GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
GST_OBJECT_LOCK (srcpad);
GST_OBJECT_LOCK (sinkpad);
/* Check pads state, not already linked and correct hierachy. */
// 检查srcpad和sinkpad是否已链接,并检查是否具有相同的父辈
// 只有具有相同的父辈,才可以执行链接过程
result = gst_pad_link_check_relations (srcpad, sinkpad, flags);
if (result != GST_PAD_LINK_OK)
goto unlock_and_return;
/* check pad caps for non-empty intersection */
// 检查srcpad和sinkpad的媒体类型(caps)是否兼容
if (!gst_pad_link_check_compatible_unlocked (srcpad, sinkpad, flags)) {
GST_CAT_INFO (GST_CAT_PADS, "caps are incompatible");
result = GST_PAD_LINK_NOFORMAT;
goto unlock_and_return;
}
/* Need to recheck our pads since gst_pad_link_check_compatible_unlocked might have temporarily unlocked them.
Keeping the first check, because gst_pad_link_check_compatible_unlocked potentially is an expensive operation
which gst_pad_link_check_relations is not. */
/* 重新检查relation的原因是gst_pad_link_check_compatible_unlocked可能会临时解锁pad,
* 导致其他线程可能修改pad状态,重新检查以确保仍符合链接要求
*/
result = gst_pad_link_check_relations (srcpad, sinkpad, flags);
if (result != GST_PAD_LINK_OK)
goto unlock_and_return;
/* FIXME check pad scheduling for non-empty intersection */
return GST_PAD_LINK_OK;
unlock_and_return:
{
GST_OBJECT_UNLOCK (sinkpad);
GST_OBJECT_UNLOCK (srcpad);
return result;
}
}
(2)调度事件(schedule_events)
schedule_events()函数的简单理解是,如果srcpad上还有未发送给sinkpad的事件,先pending,并进行标记,等待pad链接成功之后,再发送给sinkpad
static void
schedule_events (GstPad * srcpad, GstPad * sinkpad)
{
gint i, len;
GArray *events;
PadEvent *ev;
gboolean pending = FALSE;
events = srcpad->priv->events;
len = events->len;
for (i = 0; i < len; i++) {
// 遍历srcpad上的事件
ev = &g_array_index (events, PadEvent, i);
if (ev->event == NULL)
continue;
// 如果sinkpad为空,或者sinkpad上面没有找到相同的事件
if (sinkpad == NULL || !find_event (sinkpad, ev->event)) {
ev->received = FALSE; // 设置为FALSE,表示该事件尚未被sinkpad接收
pending = TRUE; // 该事件待处理
}
}
// 存在待处理的事件,将srcpad的flag设置为PENDING_EVENTS
if (pending)
GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
}
2.3 pad激活(gst_pad_set_activate)
pad的激活函数为gst_pad_set_activate(),这是gstreamer中pad激活或停用的核心函数
gboolean
gst_pad_set_active (GstPad * pad, gboolean active)
{
GstObject *parent;
GstPadMode old;
gboolean ret = FALSE;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
GST_OBJECT_LOCK (pad);
old = GST_PAD_MODE (pad);
// 获取pad的父对象
ACQUIRE_PARENT (pad, parent, no_parent);
GST_OBJECT_UNLOCK (pad);
if (active) { // 激活pad
if (old == GST_PAD_MODE_NONE) {
// 从none状态激活pad
GST_DEBUG_OBJECT (pad, "activating pad from none");
ret = (GST_PAD_ACTIVATEFUNC (pad)) (pad, parent);
if (ret)
pad->ABI.abi.last_flowret = GST_FLOW_OK;
} else { // pad已激活,无需再次激活
GST_DEBUG_OBJECT (pad, "pad was active in %s mode",
gst_pad_mode_get_name (old));
ret = TRUE;
}
} else { // 停用pad
if (old == GST_PAD_MODE_NONE) {
GST_DEBUG_OBJECT (pad, "pad was inactive");
ret = TRUE;
} else {
GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode",
gst_pad_mode_get_name (old));
// 停用pad
ret = activate_mode_internal (pad, parent, old, FALSE);
if (ret)
pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
}
}
RELEASE_PARENT (parent);
if (G_UNLIKELY (!ret))
goto failed;
return ret;
/* ERRORS */
no_parent:
{
GST_DEBUG_OBJECT (pad, "no parent");
GST_OBJECT_UNLOCK (pad);
return FALSE;
}
failed:
{
GST_OBJECT_LOCK (pad);
if (!active) {
g_critical ("Failed to deactivate pad %s:%s, very bad",
GST_DEBUG_PAD_NAME (pad));
} else {
GST_WARNING_OBJECT (pad, "Failed to activate pad");
}
GST_OBJECT_UNLOCK (pad);
return FALSE;
}
}
(1)激活pad(GST_PAD_ACTIVATEFUNC)
GST_PAD_ACTIVATEFUNC指向的是activatefunc()
#define GST_PAD_ACTIVATEFUNC(pad) (GST_PAD_CAST(pad)->activatefunc)
不同的pad会使用不同的激活类型,例如flv当中的pad,使用的激活函数为gst_flv_demux_sink_activate(),在gst_flv_demux_init()中进行激活函数的配置
static void
gst_flv_demux_init (GstFlvDemux * demux)
{
// ...
gst_pad_set_activate_function (demux->sinkpad,
GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate));
gst_pad_set_activatemode_function (demux->sinkpad,
GST_DEBUG_FUNCPTR (gst_flv_demux_sink_activate_mode));
// ...
}
gst_flv_demux_sink_activate()的作用是激活demuxer的sink pad
/* If we can pull that's preferred */
static gboolean
gst_flv_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
{
GstQuery *query;
gboolean pull_mode;
// 创建新的查询对象
query = gst_query_new_scheduling ();
// 查询peer,就是srcpad的调度能力
if (!gst_pad_peer_query (sinkpad, query)) {
gst_query_unref (query);
goto activate_push;
}
// 查询srcpad是否支持pull模式
pull_mode = gst_query_has_scheduling_mode_with_flags (query,
GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
gst_query_unref (query);
// 如果srcpad不支持pull模式,则激活sinkpad push模式
if (!pull_mode)
goto activate_push;
GST_DEBUG_OBJECT (sinkpad, "activating pull");
// srcpad支持pull模式,则激活sinkpad的pull模式,保持srcpad和sinkpad具有相同模式
return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
activate_push:
{
GST_DEBUG_OBJECT (sinkpad, "activating push");
return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
}
}
上面代码中有一点需要注意,和常规理解不同,即srcpad处于push模式时,sinkpad也同样处理push模式,而不是pull模式,即srcpad和sinkpad在进行数据传输时,所处的模式是相同的。这样做最大的好处,应该是避免处理复杂的模式转换逻辑,因为当pipeline中链接的元素非常多时,会有大量的srcpad和sinkpad,如果每一次都需要进行模式转换,即srcpad push -> sinkpad pull -> srcpad push -> …,容易出现逻辑错误。
激活模式的函数使用的是gst_pad_activate_mode()
gboolean
gst_pad_activate_mode (GstPad * pad, GstPadMode mode, gboolean active)
{
GstObject *parent;
gboolean res;
GstPadMode old, new;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
GST_OBJECT_LOCK (pad);
// 检查old模式是否和要激活的模式一致
old = GST_PAD_MODE (pad);
new = active ? mode : GST_PAD_MODE_NONE;
if (old == new)
goto was_ok;
// 检查pad的父对象,如果父对象不存在,打印警告并返回FALSE
ACQUIRE_PARENT (pad, parent, no_parent);
GST_OBJECT_UNLOCK (pad);
// 内部激活函数
res = activate_mode_internal (pad, parent, mode, active);
RELEASE_PARENT (parent);
return res;
was_ok:
{
GST_OBJECT_UNLOCK (pad);
GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "already %s in %s mode",
active ? "activated" : "deactivated", gst_pad_mode_get_name (mode));
return TRUE;
}
no_parent:
{
GST_WARNING_OBJECT (pad, "no parent");
GST_OBJECT_UNLOCK (pad);
return FALSE;
}
}
activate_mode_internal()进行激活的主要步骤是:
1.检查激活信息
2.预激活(pre_activate)
3.自定义激活(GST_PAD_ACTIVATEMODEFUNC (pad))
4.后激活(post_activate)
// gst_add_activate_default()给的参数, mode = push, activate = TRUE
static gboolean
activate_mode_internal (GstPad * pad, GstObject * parent, GstPadMode mode,
gboolean active)
{
gboolean res = FALSE;
GstPadMode old, new;
GstPadDirection dir;
GstPad *peer;
GST_OBJECT_LOCK (pad);
old = GST_PAD_MODE (pad);
dir = GST_PAD_DIRECTION (pad);
GST_OBJECT_UNLOCK (pad);
new = active ? mode : GST_PAD_MODE_NONE;
// 原始状态和当前状态相同
if (old == new)
goto was_ok;
// pad模式分为PUSH和PULL,检查是否是激活错了方向
if (active && old != mode && old != GST_PAD_MODE_NONE) {
/* pad was activate in the wrong direction, deactivate it
* and reactivate it in the requested mode */
GST_DEBUG_OBJECT (pad, "deactivating pad from %s mode",
gst_pad_mode_get_name (old));
if (G_UNLIKELY (!activate_mode_internal (pad, parent, old, FALSE)))
goto deactivate_failed;
old = GST_PAD_MODE_NONE;
}
switch (mode) {
case GST_PAD_MODE_PULL:
{
if (dir == GST_PAD_SINK) { // 模式PULL,方向是SINK
if ((peer = gst_pad_get_peer (pad))) { // 有对端pad,即存在数据发送端
GST_DEBUG_OBJECT (pad, "calling peer");
// 激活mode
if (G_UNLIKELY (!gst_pad_activate_mode (peer, mode, active)))
goto peer_failed;
gst_object_unref (peer);
} else {
/* there is no peer, this is only fatal when we activate. When we
* deactivate, we must assume the application has unlinked the peer and
* will deactivate it eventually. */
if (active)
goto not_linked;
else
GST_DEBUG_OBJECT (pad, "deactivating unlinked pad");
}
} else { // 模式pull,但方向是src
if (G_UNLIKELY (GST_PAD_GETRANGEFUNC (pad) == NULL))
goto failure; /* Can't activate pull on a src without a
getrange function */
}
break;
}
default:
break;
}
/* Mark pad as needing reconfiguration */
if (active)
GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_RECONFIGURE);
/* pre_activate returns TRUE if we weren't already in the process of
* switching to the 'new' mode */
if (pre_activate (pad, new)) { // 1.预激活,检查是否需要切换到新的模式
if (GST_PAD_ACTIVATEMODEFUNC (pad)) { // 2.激活mode
if (G_UNLIKELY (!GST_PAD_ACTIVATEMODEFUNC (pad) (pad, parent, mode,
active)))
goto failure;
} else {
/* can happen for sinks of passthrough elements */
}
post_activate (pad, new); // 3.后激活,确保模式切换完成之后,pad的状态正确更新
}
GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "%s in %s mode",
active ? "activated" : "deactivated", gst_pad_mode_get_name (mode));
exit_success:
res = TRUE;
// ...
}
(1)预激活(pre_activate)
static gboolean
pre_activate (GstPad * pad, GstPadMode new_mode)
{
switch (new_mode) {
case GST_PAD_MODE_NONE:
GST_OBJECT_LOCK (pad);
// 如果pad已经在激活过程中,等待激活完成
while (G_UNLIKELY (pad->priv->in_activation))
g_cond_wait (&pad->priv->activation_cond, GST_OBJECT_GET_LOCK (pad));
if (new_mode == GST_PAD_MODE (pad)) {
GST_WARNING_OBJECT (pad,
"Pad is already in the process of being deactivated");
GST_OBJECT_UNLOCK (pad);
return FALSE;
}
pad->priv->in_activation = TRUE;
GST_DEBUG_OBJECT (pad, "setting PAD_MODE NONE, set flushing");
GST_PAD_SET_FLUSHING (pad);
pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
// 设置新的mode
GST_PAD_MODE (pad) = new_mode;
/* unlock blocked pads so element can resume and stop */
GST_PAD_BLOCK_BROADCAST (pad);
GST_OBJECT_UNLOCK (pad);
break;
case GST_PAD_MODE_PUSH: // push或pull函数
case GST_PAD_MODE_PULL:
GST_OBJECT_LOCK (pad);
while (G_UNLIKELY (pad->priv->in_activation))
g_cond_wait (&pad->priv->activation_cond, GST_OBJECT_GET_LOCK (pad));
if (new_mode == GST_PAD_MODE (pad)) {
GST_WARNING_OBJECT (pad,
"Pad is already in the process of being activated");
GST_OBJECT_UNLOCK (pad);
return FALSE;
}
pad->priv->in_activation = TRUE;
GST_DEBUG_OBJECT (pad, "setting pad into %s mode, unset flushing",
gst_pad_mode_get_name (new_mode));
GST_PAD_UNSET_FLUSHING (pad);
pad->ABI.abi.last_flowret = GST_FLOW_OK;
GST_PAD_MODE (pad) = new_mode;
// 如果pad是sinkpad,并且有peer,需要确保peer上的事件重新调度
if (GST_PAD_IS_SINK (pad)) {
GstPad *peer;
/* make sure the peer src pad sends us all events */
if ((peer = GST_PAD_PEER (pad))) {
gst_object_ref (peer);
GST_OBJECT_UNLOCK (pad);
GST_DEBUG_OBJECT (pad, "reschedule events on peer %s:%s",
GST_DEBUG_PAD_NAME (peer));
GST_OBJECT_LOCK (peer);
// 调度事件
schedule_events (peer, NULL);
GST_OBJECT_UNLOCK (peer);
gst_object_unref (peer);
} else {
GST_OBJECT_UNLOCK (pad);
}
} else {
GST_OBJECT_UNLOCK (pad);
}
break;
}
return TRUE;
}
(2)激活函数(GST_PAD_ACTIVATEMODEFUNC)
激活函数可以由用户自定义,通过gst_pad_set_activatemode_function_full()进行设置。对于flv demuxer而言,使用的是gst_flv_demux_sink_activate_mode()
static gboolean
gst_flv_demux_sink_activate_mode (GstPad * sinkpad, GstObject * parent,
GstPadMode mode, gboolean active)
{
gboolean res;
GstFlvDemux *demux;
demux = GST_FLV_DEMUX (parent);
switch (mode) {
case GST_PAD_MODE_PUSH: // sink的push模式,实际上是接收srcpad过来的数据流
// push模式下,sinkpad是被动接受数据的,会将数据传递给内部的解析逻辑
demux->random_access = FALSE; // 禁用随机访问
res = TRUE;
break;
case GST_PAD_MODE_PULL: // sink的pull模式,实际上是向srcpad发送信息,主动请求数据
if (active) {
demux->random_access = TRUE;
demux->segment_seqnum = gst_util_seqnum_next ();
// 启动任务gst_flv_demux_loop(),进行数据读取和解析
res = gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flv_demux_loop,
sinkpad, NULL);
} else {
demux->random_access = FALSE;
res = gst_pad_stop_task (sinkpad);
}
break;
default:
res = FALSE;
break;
}
return res;
}
(3)后激活(post_activate)
后激活函数用于在激活或停用pad之后执行一些清理和后续操作
static void
post_activate (GstPad * pad, GstPadMode new_mode)
{
switch (new_mode) {
case GST_PAD_MODE_NONE:
GST_OBJECT_LOCK (pad);
pad->priv->in_activation = FALSE;
g_cond_broadcast (&pad->priv->activation_cond);
GST_OBJECT_UNLOCK (pad);
/* ensures that streaming stops */
GST_PAD_STREAM_LOCK (pad);
GST_DEBUG_OBJECT (pad, "stopped streaming");
GST_OBJECT_LOCK (pad);
remove_events (pad);
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
break;
case GST_PAD_MODE_PUSH:
case GST_PAD_MODE_PULL:
GST_OBJECT_LOCK (pad);
// pad不再处于激活过程中
pad->priv->in_activation = FALSE;
// 广播其他线程,激活已经完成
g_cond_broadcast (&pad->priv->activation_cond);
GST_OBJECT_UNLOCK (pad);
/* NOP */
break;
}
}
2.4 数据buffer传递(gst_pad_push)
gst_pad_push()和gst_pad_chain()都用于数据传递,但是它们的调用对象和触发机制不同。对于gst_pad_chain()而言,调用流程是
gst_pad_chain() -> gst_pad_chain_data_unchecked() -> chainfunc()
gst_pad_push()的调用流程是
gst_pad_push() -> gst_pad_push_data() -> gst_pad_chain_data_unchecked() -> chainfunc()
通常来说,gst_pad_push()是上层API,直接使用就行。但如果想要自定义链接函数,可以使用gst_pad_chain(),这是一个底层API。另外,gst_pad_push()用于将数据从srcpad推送到下游元素,而gst_pad_chain()用于在sinkpad上处理接收到的数据。
// 入参是srcpad,buffer是srcpad的buffer
GstFlowReturn
gst_pad_push (GstPad * pad, GstBuffer * buffer)
{
GstFlowReturn res;
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (GST_PAD_IS_SRC (pad), GST_FLOW_ERROR);
g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
GST_TRACER_PAD_PUSH_PRE (pad, buffer);
res = gst_pad_push_data (pad,
GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH, buffer);
GST_TRACER_PAD_PUSH_POST (pad, res);
return res;
}
gst_pad_push_data()执行数据推送操作
static GstFlowReturn
gst_pad_push_data (GstPad * pad, GstPadProbeType type, void *data)
{
GstPad *peer;
GstFlowReturn ret;
gboolean handled = FALSE;
GST_OBJECT_LOCK (pad);
/* 状态检查 */
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
goto eos;
if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
goto wrong_mode;
#ifdef GST_ENABLE_EXTRA_CHECKS
if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) {
if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) {
g_warning (G_STRLOC
":%s:<%s:%s> Got data flow before stream-start event",
G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
}
if (!find_event_by_type (pad, GST_EVENT_SEGMENT, 0)) {
g_warning (G_STRLOC
":%s:<%s:%s> Got data flow before segment event",
G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
}
pad->priv->last_cookie = pad->priv->events_cookie;
}
#endif
// 检查pad上是否有未处理的粘性事件(如stream-start、segment等)
if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
goto events_error;
/* do block probes */
// 处理pad上的探针,允许在数据推送前后进行拦截和处理
PROBE_HANDLE (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped,
probe_handled);
/* recheck sticky events because the probe might have cause a relink */
if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
goto events_error;
/* do post-blocking probes */
// 进行probe的回调函数,实际上执行的是do_probe_callbacks()
PROBE_HANDLE (pad, type, data, probe_stopped, probe_handled);
/* recheck sticky events because the probe might have cause a relink */
if (G_UNLIKELY ((ret = check_sticky (pad, NULL))) != GST_FLOW_OK)
goto events_error;
if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL))
goto not_linked;
/* take ref to peer pad before releasing the lock */
gst_object_ref (peer);
pad->priv->using++;
GST_OBJECT_UNLOCK (pad);
// 执行数据传输
ret = gst_pad_chain_data_unchecked (peer, type, data);
data = NULL;
gst_object_unref (peer);
GST_OBJECT_LOCK (pad);
pad->ABI.abi.last_flowret = ret;
pad->priv->using--;
if (pad->priv->using == 0) {
/* pad is not active anymore, trigger idle callbacks */
PROBE_NO_DATA (pad, GST_PAD_PROBE_TYPE_PUSH | GST_PAD_PROBE_TYPE_IDLE,
probe_stopped, ret);
}
GST_OBJECT_UNLOCK (pad);
return ret;
/* ERROR recovery here */
/* ERRORS */
// 错误反馈...
}
gst_pad_chain_data_unchecked()将数据data传递到sinkpad
static inline GstFlowReturn
gst_pad_chain_data_unchecked (GstPad * pad, GstPadProbeType type, void *data)
{
GstFlowReturn ret;
GstObject *parent;
gboolean handled = FALSE;
if (type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
// 数据类型为buffer list,调用跟踪链表函数
GST_TRACER_PAD_CHAIN_LIST_PRE (pad, data);
} else {
// 数据类型为buffer,调用跟踪链函数
GST_TRACER_PAD_CHAIN_PRE (pad, data);
}
GST_PAD_STREAM_LOCK (pad);
GST_OBJECT_LOCK (pad);
// pad正在刷新
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
// pad达到EOS
if (G_UNLIKELY (GST_PAD_IS_EOS (pad)))
goto eos;
// pad不在push模式下
if (G_UNLIKELY (GST_PAD_MODE (pad) != GST_PAD_MODE_PUSH))
goto wrong_mode;
#ifdef GST_ENABLE_EXTRA_CHECKS
if (G_UNLIKELY (pad->priv->last_cookie != pad->priv->events_cookie)) {
if (!find_event_by_type (pad, GST_EVENT_STREAM_START, 0)) {
g_warning (G_STRLOC
":%s:<%s:%s> Got data flow before stream-start event",
G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
}
if (!find_event_by_type (pad, GST_EVENT_SEGMENT, 0)) {
g_warning (G_STRLOC
":%s:<%s:%s> Got data flow before segment event",
G_STRFUNC, GST_DEBUG_PAD_NAME (pad));
}
pad->priv->last_cookie = pad->priv->events_cookie;
}
#endif
// 处理pad上设置的探针(探针可以在数据传递前后进行拦截和处理)
PROBE_HANDLE (pad, type | GST_PAD_PROBE_TYPE_BLOCK, data, probe_stopped,
probe_handled);
PROBE_HANDLE (pad, type, data, probe_stopped, probe_handled);
ACQUIRE_PARENT (pad, parent, no_parent);
GST_OBJECT_UNLOCK (pad);
/* NOTE: we read the chainfunc unlocked.
* we cannot hold the lock for the pad so we might send
* the data to the wrong function. This is not really a
* problem since functions are assigned at creation time
* and don't change that often... */
// 数据类型为buffer
if (G_LIKELY (type & GST_PAD_PROBE_TYPE_BUFFER)) {
GstPadChainFunction chainfunc;
if (G_UNLIKELY ((chainfunc = GST_PAD_CHAINFUNC (pad)) == NULL))
goto no_function;
GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
"calling chainfunction &%s with buffer %" GST_PTR_FORMAT,
GST_DEBUG_FUNCPTR_NAME (chainfunc), GST_BUFFER (data));
// 调用链函数
ret = chainfunc (pad, parent, GST_BUFFER_CAST (data));
GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
"called chainfunction &%s with buffer %p, returned %s",
GST_DEBUG_FUNCPTR_NAME (chainfunc), data, gst_flow_get_name (ret));
} else { // 数据类型为bufferlist
GstPadChainListFunction chainlistfunc;
if (G_UNLIKELY ((chainlistfunc = GST_PAD_CHAINLISTFUNC (pad)) == NULL))
goto no_function;
GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
"calling chainlistfunction &%s",
GST_DEBUG_FUNCPTR_NAME (chainlistfunc));
// 调用链表函数
ret = chainlistfunc (pad, parent, GST_BUFFER_LIST_CAST (data));
GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, pad,
"called chainlistfunction &%s, returned %s",
GST_DEBUG_FUNCPTR_NAME (chainlistfunc), gst_flow_get_name (ret));
}
pad->ABI.abi.last_flowret = ret;
RELEASE_PARENT (parent);
GST_PAD_STREAM_UNLOCK (pad);
out:
if (type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
GST_TRACER_PAD_CHAIN_LIST_POST (pad, ret);
} else {
GST_TRACER_PAD_CHAIN_POST (pad, ret);
}
return ret;
/* ERRORS */
// 下面是一些错误处理
flushing: // pad正在刷新,丢弃数据
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"chaining, but pad was flushing");
pad->ABI.abi.last_flowret = GST_FLOW_FLUSHING;
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
ret = GST_FLOW_FLUSHING;
goto out;
}
eos: // pad达到EOS,数据被丢弃
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "chaining, but pad was EOS");
pad->ABI.abi.last_flowret = GST_FLOW_EOS;
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
ret = GST_FLOW_EOS;
goto out;
}
wrong_mode: // pad不在push模式下
{
g_critical ("chain on pad %s:%s but it was not in push mode",
GST_DEBUG_PAD_NAME (pad));
pad->ABI.abi.last_flowret = GST_FLOW_ERROR;
GST_OBJECT_UNLOCK (pad);
GST_PAD_STREAM_UNLOCK (pad);
gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
ret = GST_FLOW_ERROR;
goto out;
}
// ...
}
对于flv demux,chain function被设置为gst_flv_demux_chain(),这是flv解复用器的核心函数,用于处理从上游元素推送过来的数据。通过状态机解析FLV数据流,并将解析后的数据分发到对应的srcpad。此外,还处理了数据流中的不连续性、刷新、EOS等情况,确保数据流的正确处理。代码太长,就不展开了。
2.5 探针回调(do_probe_callbacks)
探针允许数据流经pad时进行拦截和处理,do_probe_callbacks()的作用是遍历pad上的所有探针,并根据探针的类型和返回值决定数据的处理方式
static GstFlowReturn
do_probe_callbacks (GstPad * pad, GstPadProbeInfo * info,
GstFlowReturn defaultval)
{
ProbeMarshall data;
guint cookie;
gboolean is_block;
gulong called_probes[N_STACK_ALLOCATE_PROBES];
data.pad = pad;
data.info = info;
data.pass = FALSE;
data.handled = FALSE;
data.dropped = FALSE;
/* We stack-allocate for N_STACK_ALLOCATE_PROBES hooks as a first step. If more are needed,
* we will re-allocate with g_malloc(). This should usually never be needed
*/
data.called_probes = called_probes;
data.n_called_probes = 0;
data.called_probes_size = N_STACK_ALLOCATE_PROBES;
data.retry = FALSE;
// 检查是否是阻塞探针
is_block =
(info->type & GST_PAD_PROBE_TYPE_BLOCK) == GST_PAD_PROBE_TYPE_BLOCK;
// 如果是阻塞探针,调用do_pad_idle_probe_wait()等待pad空闲
if (is_block && PROBE_TYPE_IS_SERIALIZED (info)) {
if (do_pad_idle_probe_wait (pad) == GST_FLOW_FLUSHING)
goto flushing;
}
again:
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "do probes");
cookie = pad->priv->probe_list_cookie;
/* Clear the marshalled flag before doing callbacks. Only if
* there are matching callbacks still will it get set */
data.marshalled = FALSE;
// 遍历pad上的所有探针,并调用每个探针的回调函数
g_hook_list_marshal (&pad->probes, TRUE,
(GHookMarshaller) probe_hook_marshal, &data);
/* if the list changed, call the new callbacks (they will not be in
* called_probes yet) */
if (cookie != pad->priv->probe_list_cookie) {
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"probe list changed, restarting");
data.retry = TRUE;
goto again;
}
/* 处理探针返回值 */
/* the first item that dropped will stop the hooks and then we drop here */
if (data.dropped) // 丢弃数据
goto dropped;
/* If one handler took care of it, let the the item pass */
if (data.handled) { // 数据已经被处理并继续传递
goto handled;
}
/* if no handler matched and we are blocking, let the item pass */
if (!data.marshalled && is_block)
goto passed;
/* At this point, all handlers returned either OK or PASS. If one handler
* returned PASS, let the item pass */
if (data.pass) // 数据继续传递
goto passed;
// 阻塞处理
if (is_block) {
while (GST_PAD_IS_BLOCKED (pad)) { // 进入阻塞等待状态
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"we are blocked %d times", pad->num_blocked);
/* we might have released the lock */
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad))) // pad被刷新
goto flushing;
/* now we block the streaming thread. It can be unlocked when we
* deactivate the pad (which will also set the FLUSHING flag) or
* when the pad is unblocked. A flushing event will also unblock
* the pad after setting the FLUSHING flag. */
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"Waiting to be unblocked or set flushing");
GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_BLOCKING);
// 阻塞pad的数据流
GST_PAD_BLOCK_WAIT (pad);
GST_OBJECT_FLAG_UNSET (pad, GST_PAD_FLAG_BLOCKING);
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "We got unblocked");
/* if the list changed, call the new callbacks (they will not be in
* called_probes yet) */
if (cookie != pad->priv->probe_list_cookie) {
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"probe list changed, restarting");
data.retry = TRUE;
goto again;
}
if (G_UNLIKELY (GST_PAD_IS_FLUSHING (pad)))
goto flushing;
}
}
if (data.called_probes_size > N_STACK_ALLOCATE_PROBES)
g_free (data.called_probes);
return defaultval;
/* ERRORS */
// 错误的情况
flushing:
{
GST_DEBUG_OBJECT (pad, "pad is flushing");
if (data.called_probes_size > N_STACK_ALLOCATE_PROBES)
g_free (data.called_probes);
return GST_FLOW_FLUSHING;
}
dropped:
{
GST_DEBUG_OBJECT (pad, "data is dropped");
if (data.called_probes_size > N_STACK_ALLOCATE_PROBES)
g_free (data.called_probes);
return GST_FLOW_CUSTOM_SUCCESS;
}
passed:
{
/* FIXME : Should we return FLOW_OK or the defaultval ?? */
GST_DEBUG_OBJECT (pad, "data is passed");
if (data.called_probes_size > N_STACK_ALLOCATE_PROBES)
g_free (data.called_probes);
return GST_FLOW_OK;
}
handled:
{
GST_DEBUG_OBJECT (pad, "data was handled");
if (data.called_probes_size > N_STACK_ALLOCATE_PROBES)
g_free (data.called_probes);
return GST_FLOW_CUSTOM_SUCCESS_1;
}
}
probe_hook_marshal()的作用是调用注册的pad probe回调函数,并根据回调的返回值执行相应的操作
static void
probe_hook_marshal (GHook * hook, ProbeMarshall * data)
{
GstPad *pad = data->pad;
GstPadProbeInfo *info = data->info;
GstPadProbeType type, flags;
GstPadProbeCallback callback;
GstPadProbeReturn ret;
gpointer original_data;
flags = hook->flags >> G_HOOK_FLAG_USER_SHIFT;
type = info->type;
original_data = info->data;
/* one of the scheduling types */
// 检查探针类型与当前数据流是否匹配
if ((flags & GST_PAD_PROBE_TYPE_SCHEDULING & type) == 0)
goto no_match;
// 检查探针是否已经被处理
if (G_UNLIKELY (data->handled)) {
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"probe previously returned HANDLED, not calling again");
goto no_match;
} else if (G_UNLIKELY (data->dropped)) {
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"probe previously returned DROPPED, not calling again");
goto no_match;
}
// 根据数据流类型(push或pull)检查探针是否匹配
if (type & GST_PAD_PROBE_TYPE_PUSH) {
/* one of the data types for non-idle probes */
if ((type & GST_PAD_PROBE_TYPE_IDLE) == 0
&& (flags & _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH & type) == 0)
goto no_match;
} else if (type & GST_PAD_PROBE_TYPE_PULL) {
/* one of the data types for non-idle probes */
if ((type & GST_PAD_PROBE_TYPE_BLOCKING) == 0
&& (flags & _PAD_PROBE_TYPE_ALL_BOTH_AND_FLUSH & type) == 0)
goto no_match;
} else {
/* Type must have PULL or PUSH probe types */
g_assert_not_reached ();
}
/* one of the blocking types must match */
// 检查阻塞类型
if ((type & GST_PAD_PROBE_TYPE_BLOCKING) &&
(flags & GST_PAD_PROBE_TYPE_BLOCKING & type) == 0)
goto no_match;
if ((type & GST_PAD_PROBE_TYPE_BLOCKING) == 0 &&
(flags & GST_PAD_PROBE_TYPE_BLOCKING))
goto no_match;
/* only probes that have GST_PAD_PROBE_TYPE_EVENT_FLUSH set */
if ((type & GST_PAD_PROBE_TYPE_EVENT_FLUSH) &&
(flags & GST_PAD_PROBE_TYPE_EVENT_FLUSH & type) == 0)
goto no_match;
// 检查探针是否已经调用,如果探针已经被调用过,设置marshalled标志
// 并跳转到alread_called标签
if (check_probe_already_called (hook, data)) {
/* Reset marshalled = TRUE here, because the probe
* was already called and set it the first time around,
* and we may want to keep blocking on it.
*
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/658
*/
data->marshalled = TRUE;
goto already_called;
}
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"hook %lu with flags 0x%08x matches", hook->hook_id, flags);
callback = (GstPadProbeCallback) hook->func;
if (callback == NULL) {
/* No callback is equivalent to just returning GST_PAD_PROBE_OK */
data->marshalled = TRUE;
return;
}
info->id = hook->hook_id;
if ((flags & GST_PAD_PROBE_TYPE_IDLE))
pad->priv->idle_running++;
GST_OBJECT_UNLOCK (pad);
// 调用探针回调函数
ret = callback (pad, info, hook->data);
GST_OBJECT_LOCK (pad);
/* If the probe callback asked for the
* probe to be removed, don't set the marshalled flag
* otherwise, you can get a case where you return
* GST_PAD_PROBE_REMOVE from a buffer probe and
* then the pad blocks anyway if there's any other
* blocking probes installed.
*
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/658
*/
if (ret != GST_PAD_PROBE_REMOVE)
data->marshalled = TRUE;
if ((flags & GST_PAD_PROBE_TYPE_IDLE))
pad->priv->idle_running--;
if (ret != GST_PAD_PROBE_HANDLED && original_data != NULL
&& info->data == NULL) {
GST_DEBUG_OBJECT (pad, "data item in pad probe info was dropped");
info->type = GST_PAD_PROBE_TYPE_INVALID;
data->dropped = TRUE;
}
// 处理回调返回值
switch (ret) {
case GST_PAD_PROBE_REMOVE:
/* remove the probe */
GST_DEBUG_OBJECT (pad, "asked to remove hook");
cleanup_hook (pad, hook);
break;
case GST_PAD_PROBE_DROP:
/* need to drop the data, make sure other probes don't get called
* anymore */
GST_DEBUG_OBJECT (pad, "asked to drop item");
info->type = GST_PAD_PROBE_TYPE_INVALID;
data->dropped = TRUE;
break;
case GST_PAD_PROBE_HANDLED:
GST_DEBUG_OBJECT (pad, "probe handled data");
data->handled = TRUE;
break;
case GST_PAD_PROBE_PASS:
/* inform the pad block to let things pass */
GST_DEBUG_OBJECT (pad, "asked to pass item");
data->pass = TRUE;
break;
case GST_PAD_PROBE_OK:
GST_DEBUG_OBJECT (pad, "probe returned OK");
break;
default:
GST_DEBUG_OBJECT (pad, "probe returned %d", ret);
break;
}
return;
no_match:
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"hook %lu with flags 0x%08x does not match %08x",
hook->hook_id, flags, info->type);
return;
}
already_called:
{
GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
"hook %lu already called", hook->hook_id);
return;
}
}
2.6 事件转发(gst_pad_forward)
gst_pad_forward()的作用是将事件从一个pad转发到另一个pad中,例如从上游元素的srcpad转发事件到下游元素的sinkpad。
gboolean
gst_pad_forward (GstPad * pad, GstPadForwardFunction forward,
gpointer user_data)
{
gboolean result = FALSE;
GstIterator *iter;
gboolean done = FALSE;
GValue item = { 0, };
GList *pushed_pads = NULL;
// pad的迭代器
iter = gst_pad_iterate_internal_links (pad);
if (!iter)
goto no_iter;
// 遍历内部链接的pads
while (!done) {
switch (gst_iterator_next (iter, &item)) {
case GST_ITERATOR_OK:
{
GstPad *intpad;
intpad = g_value_get_object (&item);
/* if already pushed, skip. FIXME, find something faster to tag pads */
// 如果当前pad已经处理过,跳过
if (intpad == NULL || g_list_find (pushed_pads, intpad)) {
g_value_reset (&item);
break;
}
GST_LOG_OBJECT (pad, "calling forward function on pad %s:%s",
GST_DEBUG_PAD_NAME (intpad));
// 回调forward,处理当前pad
done = result = forward (intpad, user_data);
// 将当前pad添加到已处理的pads列表中
pushed_pads = g_list_prepend (pushed_pads, intpad);
g_value_reset (&item);
break;
}
case GST_ITERATOR_RESYNC:
/* We don't reset the result here because we don't push the event
* again on pads that got the event already and because we need
* to consider the result of the previous pushes */
gst_iterator_resync (iter);
break;
case GST_ITERATOR_ERROR:
GST_ERROR_OBJECT (pad, "Could not iterate over internally linked pads");
done = TRUE;
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
g_value_unset (&item);
gst_iterator_free (iter);
g_list_free (pushed_pads);
no_iter:
return result;
}
上面的forward()函数,在gstpad.c中可以使用event_forward_func(),用于将事件传递给目标pad
// pad是目标pad,需要将data传递给这个pad
// data包含事件和处理结果
static gboolean
event_forward_func (GstPad * pad, EventData * data)
{
/* for each pad we send to, we should ref the event; it's up
* to downstream to unref again when handled. */
GST_LOG_OBJECT (pad, "Reffing and pushing event %p (%s) to %s:%s",
data->event, GST_EVENT_TYPE_NAME (data->event), GST_DEBUG_PAD_NAME (pad));
// 将event发送给pad
data->result |= gst_pad_push_event (pad, gst_event_ref (data->event));
data->dispatched = TRUE;
/* don't stop */
return FALSE;
}
gst_pad_push_event()通常用于元素之间事件的传递,下层会继续调用核心传递函数gst_pad_push_event_unchecked()、gst_pad_send_event_unchecked()等,最终使用pre_eventfunc_check()、eventfullfunc()和实现事件对应的操作,后续涉及到具体的事件,因为太多这里就不展开