最近在看gstreamer中的link,下面是应用代码中的一段:
if (gst_element_link_many(data.source, data.h264parse, data.omx264dec, data.convert, data.sink, nullptr) != TRUE) {
g_printerr("Elements could not be linked.\n");
gst_object_unref(data.pipeline);
return -1;
}
我们来看看都做了些什么:
gboolean
gst_element_link_many (GstElement * element_1, GstElement * element_2, ...)
{
gboolean res = TRUE;
va_list args;
g_return_val_if_fail (GST_IS_ELEMENT (element_1), FALSE);
g_return_val_if_fail (GST_IS_ELEMENT (element_2), FALSE);
va_start (args, element_2);
//在这里循环link,按顺序进行的喔
while (element_2) {
if (!gst_element_link (element_1, element_2)) {
res = FALSE;
break;
}
element_1 = element_2;
element_2 = va_arg (args, GstElement *);
}
va_end (args);
return res;
}
gboolean
gst_element_link (GstElement * src, GstElement * dest)
{
//这里都是匿名pad,自动来连接
return gst_element_link_pads (src, NULL, dest, NULL);
}
gboolean
gst_element_link_pads (GstElement * src, const gchar * srcpadname,
GstElement * dest, const gchar * destpadname)
{
//按照默认的方式来link
return gst_element_link_pads_full (src, srcpadname, dest, destpadname,
GST_PAD_LINK_CHECK_DEFAULT);
}
层层调用这么多,终于到了link的地方:
gst_element_link_pads_full (src, srcpadname, dest, destpadname,GST_PAD_LINK_CHECK_DEFAULT);
1、获取src ement的srcpad的链表
srcpads = GST_ELEMENT_PADS (src);
//获取链表数据指针
srcpad = srcpads ? GST_PAD_CAST (srcpads->data) : NULL;
2、获取dst element的sinkpad的链表
destpads = GST_ELEMENT_PADS (dest);
destpad = destpads ? GST_PAD_CAST (destpads->data) : NULL;
3、首先尝试步骤1中获取到的srcpad链表,看看在其dst-element中有没有匹配的sinkpad
temp = gst_element_get_compatible_pad (dest, srcpad, NULL);
4、开始link
pad_link_maybe_ghosting (srcpad, temp, flags)
5、返回
return TRUE;
下面详细介绍步骤4中的函数步骤:pad_link_maybe_ghosting (srcpad, temp, flags)
1、首先检查是不是ghosting pad,如果两个pad在同一个bin,说明不用ghosting.
prepare_link_maybe_ghosting (&src, &sink, &pads_created)
2、开始full-link
gst_pad_link_full (src, sink, flags)
3、继续进入到link_full函数中,首先发送一个GST_STRUCTURE_CHANGE_TYPE_PAD_LINK消息到总线
gst_element_post_message (parent,
gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, TRUE));
4、link前的准备工作
result = gst_pad_link_prepare (srcpad, sinkpad, flags);
//准备工作主要做两件事情
// 1、 /* check hierarchy, pads can only be linked if the grandparents
* are the same. */
if ((flags & GST_PAD_LINK_CHECK_HIERARCHY)
&& !gst_pad_link_check_hierarchy (srcpad, sinkpad))
goto wrong_hierarchy;
// 2、 /* check pad caps for non-empty intersection */
//主要进行下面的工作
// srccaps = gst_pad_query_caps (src, NULL);
// sinkcaps = gst_pad_query_caps (sink, NULL);
// compatible = gst_caps_can_intersect (srccaps, sinkcaps);
if (!gst_pad_link_check_compatible_unlocked (srcpad, sinkpad, flags))
goto no_format;
5、开始link,起始非常简单,就是peerpad设置为对方
/* must set peers before calling the link function */
GST_PAD_PEER (srcpad) = sinkpad;
GST_PAD_PEER (sinkpad) = srcpad;
6、协商事件
/* check events, when something is different, mark pending */
schedule_events (srcpad, sinkpad);
7、调用linkfunc
/* get the link functions */
srcfunc = GST_PAD_LINKFUNC (srcpad);
sinkfunc = GST_PAD_LINKFUNC (sinkpad);
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);
}
8、发送信号
/* fire off a signal to each of the pads telling them
* that they've been linked */
g_signal_emit (srcpad, gst_pad_signals[PAD_LINKED], 0, sinkpad);
g_signal_emit (sinkpad, gst_pad_signals[PAD_LINKED], 0, srcpad);
9、发送reconfigure事件
if (!(flags & GST_PAD_LINK_CHECK_NO_RECONFIGURE))
gst_pad_send_event (srcpad, gst_event_new_reconfigure ());
10、再次发送msg
gst_element_post_message (parent,
gst_message_new_structure_change (GST_OBJECT_CAST (sinkpad),
GST_STRUCTURE_CHANGE_TYPE_PAD_LINK, parent, FALSE));
下面看看
schedule_events (GstPad * srcpad, GstPad * sinkpad)
``
主要看srcpad上的event和dstpad上的event,如果有不相同的,则把srcpad上的事件标记为received=%FALSE,下次就可以发送
```c
/* check all events on srcpad against those on sinkpad. All events that are not
* on sinkpad are marked as received=%FALSE and the PENDING_EVENTS is set on the
* srcpad so that the events will be sent next time */
/* should be called with srcpad and sinkpad LOCKS */
static void
schedule_events (GstPad * srcpad, GstPad * sinkpad)
{
gint i, len;
GArray *events;
PadEvent *ev;
gboolean pending = FALSE;
//获取到srcpad的events链表
events = srcpad->priv->events;
len = events->len;
for (i = 0; i < len; i++) {
ev = &g_array_index (events, PadEvent, i);
if (ev->event == NULL)
continue;
//其实就是两个for循环遍历对比srcpad和sinkpad中evens是否相等
if (sinkpad == NULL || !find_event (sinkpad, ev->event)) {
ev->received = FALSE;
pending = TRUE;
}
}
if (pending)
GST_OBJECT_FLAG_SET (srcpad, GST_PAD_FLAG_PENDING_EVENTS);
}
/* should be called with OBJECT lock */
static PadEvent *
find_event (GstPad * pad, GstEvent * event)
{
guint i, len;
GArray *events;
PadEvent *ev;
events = pad->priv->events;
len = events->len;
for (i = 0; i < len; i++) {
ev = &g_array_index (events, PadEvent, i);
if (event == ev->event)
goto found;
else if (GST_EVENT_TYPE (ev->event) > GST_EVENT_TYPE (event))
break;
}
ev = NULL;
found:
return ev;
}
纵观上面,
上面牵扯到两个非常重要的函数
gst_element_get_compatible_pad()函数定义如下:
//上面对caps传入了null,element出入了dstpad,这个好理解,为srcpad找一个dstpad
GstPad *
gst_element_get_compatible_pad (GstElement * element, GstPad * pad,
GstCaps * caps)
{
GstIterator *pads;
GstPadTemplate *templ;
GstCaps *templcaps;
GstPad *foundpad = NULL;
gboolean done;
GValue padptr = { 0, };
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
//日志:
//finding pad in h264-parse compatible with app-source:src
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"finding pad in %s compatible with %s:%s",
GST_ELEMENT_NAME (element), GST_DEBUG_PAD_NAME (pad));
g_return_val_if_fail (GST_PAD_PEER (pad) == NULL, NULL);
done = FALSE;
/* try to get an existing unlinked pad */
//从dst中的sinkpad中获取迭代器
if (GST_PAD_IS_SRC (pad)) {
pads = gst_element_iterate_sink_pads (element);
} else if (GST_PAD_IS_SINK (pad)) {
pads = gst_element_iterate_src_pads (element);
} else {
pads = gst_element_iterate_pads (element);
}
while (!done) {
switch (gst_iterator_next (pads, &padptr)) {
case GST_ITERATOR_OK:
{
GstPad *peer;
GstPad *current;
GstPad *srcpad;
GstPad *sinkpad;
current = g_value_get_object (&padptr);
//日志:
// examining pad h264-parse:sink
GST_CAT_LOG (GST_CAT_ELEMENT_PADS, "examining pad %s:%s",
GST_DEBUG_PAD_NAME (current));
if (GST_PAD_IS_SRC (current)) {
srcpad = current;
sinkpad = pad;
} else {
srcpad = pad;
sinkpad = current;
}
//获取peer pad保证为null,因为link的时候必须peer pad 是为NULL的
peer = gst_pad_get_peer (current);
//检查能不能link
if (peer == NULL && gst_pad_check_link (srcpad, sinkpad)) {
GstCaps *temp, *intersection;
gboolean compatible;
/* Now check if the two pads' caps are compatible */
temp = gst_pad_query_caps (pad, NULL);
if (caps) {
intersection = gst_caps_intersect (temp, caps);
gst_caps_unref (temp);
} else {
//因为caps为null
intersection = temp;
}
temp = gst_pad_query_caps (current, NULL);
//是怎么做交集的呢
compatible = gst_caps_can_intersect (temp, intersection);
gst_caps_unref (temp);
gst_caps_unref (intersection);
if (compatible) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"found existing unlinked compatible pad %s:%s",
GST_DEBUG_PAD_NAME (current));
gst_iterator_free (pads);
current = gst_object_ref (current);
g_value_unset (&padptr);
//说明有存在的pad 可以link
return current;
} else {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "incompatible pads");
}
} else {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"already linked or cannot be linked (peer = %p)", peer);
}
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unreffing pads");
g_value_reset (&padptr);
if (peer)
gst_object_unref (peer);
break;
}
...
}
}//while
g_value_unset (&padptr);
gst_iterator_free (pads);
...
上面用到了 compatible = gst_caps_can_intersect (temp, intersection);,那么具体操作是
boolean
gst_caps_can_intersect (const GstCaps * caps1, const GstCaps * caps2)
{
guint64 i; /* index can be up to 2 * G_MAX_UINT */
guint j, k, len1, len2;
GstStructure *struct1;
GstStructure *struct2;
GstCapsFeatures *features1;
GstCapsFeatures *features2;
g_return_val_if_fail (GST_IS_CAPS (caps1), FALSE);
g_return_val_if_fail (GST_IS_CAPS (caps2), FALSE);
/* caps are exactly the same pointers */
if (G_UNLIKELY (caps1 == caps2))
return TRUE;
/* empty caps on either side, return empty */
if (G_UNLIKELY (CAPS_IS_EMPTY (caps1) || CAPS_IS_EMPTY (caps2)))
return FALSE;
/* one of the caps is any */
if (G_UNLIKELY (CAPS_IS_ANY (caps1) || CAPS_IS_ANY (caps2)))
return TRUE;
/* run zigzag on top line then right line, this preserves the caps order
* much better than a simple loop.
*
* This algorithm zigzags over the caps structures as demonstrated in
* the following matrix:
*
* caps1 0 1 2 3
* +------------- total distance: +-------------
* | 1 2 4 7 0 | 0 1 2 3
* caps2 | 3 5 8 10 1 | 1 2 3 4
* | 6 9 11 12 2 | 2 3 4 5
*
* First we iterate over the caps1 structures (top line) intersecting
* the structures diagonally down, then we iterate over the caps2
* structures. The result is that the intersections are ordered based on the
* sum of the indexes in the list.
*/
len1 = GST_CAPS_LEN (caps1);
len2 = GST_CAPS_LEN (caps2);
for (i = 0; i < len1 + len2 - 1; i++) {
/* superset index goes from 0 to superset->structs->len-1 */
j = MIN (i, len1 - 1);
/* subset index stays 0 until i reaches superset->structs->len, then it
* counts up from 1 to subset->structs->len - 1 */
k = (i > j) ? (i - j) : 0; /* MAX (0, i - j) */
/* now run the diagonal line, end condition is the left or bottom
* border */
while (k < len2) {
struct1 = gst_caps_get_structure_unchecked (caps1, j);
features1 = gst_caps_get_features_unchecked (caps1, j);
if (!features1)
features1 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
struct2 = gst_caps_get_structure_unchecked (caps2, k);
features2 = gst_caps_get_features_unchecked (caps2, k);
if (!features2)
features2 = GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY;
if (gst_caps_features_is_equal (features1, features2) &&
gst_structure_can_intersect (struct1, struct2)) {
return TRUE;
}
/* move down left */
k++;
if (G_UNLIKELY (j == 0))
break; /* so we don't roll back to G_MAXUINT */
j--;
}
}
return FALSE;
}