gst_element_query_duration (playbin, GST_FORMAT_TIME, &mDuration);
细节分析
gboolean
gst_element_query_duration (GstElement * element, GstFormat format,
gint64 * duration)
{
GstQuery *query;
gboolean ret;
if (duration != NULL)
*duration = GST_CLOCK_TIME_NONE;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE);
query = gst_query_new_duration (format); //查询
ret = gst_element_query (element, query);
if (ret)
gst_query_parse_duration (query, NULL, duration); //解析查询结果
gst_query_unref (query);
return ret;
}
gboolean
gst_element_query (GstElement * element, GstQuery * query)
{
GstElementClass *klass;
gboolean res = FALSE;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (query != NULL, FALSE);
GST_TRACER_ELEMENT_QUERY_PRE (element, query);
klass = GST_ELEMENT_GET_CLASS (element);
if (klass->query) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send query on element %s",
GST_ELEMENT_NAME (element));
res = klass->query (element, query); //调用ele的query
}
GST_TRACER_ELEMENT_QUERY_POST (element, query, res);
return res;
}
我们继续看下GstPlaybin2.c的实现:
void
gst_play_bin_class_init (GstPlayBinClass * klass)
{
......
gstelement_klass->change_state =
GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query); //实现函数注册
gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin_set_context);
gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin_send_event);
gstbin_klass->handle_message =
GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
gstbin_klass->deep_element_added =
GST_DEBUG_FUNCPTR (gst_play_bin_deep_element_added);
gst_type_mark_as_plugin_api (GST_TYPE_PLAY_FLAGS, 0);
}
static gboolean
gst_play_bin_query (GstElement * element, GstQuery * query)
{
GstPlayBin *playbin = GST_PLAY_BIN (element);
gboolean ret;
/* During a group switch we shouldn't allow duration queries
* because it's not clear if the old or new group's duration
* is returned and if the sinks are already playing new data
* or old data. See bug #585969
*
* While we're at it, also don't do any other queries during
* a group switch or any other event that causes topology changes
* by taking the playbin lock in any case.
*/
GST_PLAY_BIN_LOCK (playbin);
if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
GstSourceGroup *group = playbin->curr_group;
gboolean pending;
GST_SOURCE_GROUP_LOCK (group);
pending = group->pending || group->stream_changed_pending;
if (pending) {
GstFormat fmt;
gint i;
ret = FALSE;
gst_query_parse_duration (query, &fmt, NULL);
for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
if (fmt == playbin->duration[i].format) {
ret = playbin->duration[i].valid;
gst_query_set_duration (query, fmt,
(ret ? playbin->duration[i].duration : -1));
break;
}
}
/* if nothing cached yet, we might as well request duration,
* such as during initial startup */
if (ret) {
GST_DEBUG_OBJECT (playbin,
"Taking cached duration because of pending group switch: %d", ret);
GST_SOURCE_GROUP_UNLOCK (group);
GST_PLAY_BIN_UNLOCK (playbin);
return ret;
}
}
GST_SOURCE_GROUP_UNLOCK (group);
}
ret = GST_ELEMENT_CLASS (parent_class)->query (element, query); //这里调用父类Gstbin的query
if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
gst_play_bin_update_cached_duration_from_query (playbin, ret, query); //然后将duration保存到playbin->duration
GST_PLAY_BIN_UNLOCK (playbin);
return ret;
}
继续看下gstbin的实现:
static gboolean
gst_bin_query (GstElement * element, GstQuery * query)
{
GstBin *bin = GST_BIN_CAST (element);
GstIterator *iter;
gboolean default_return = FALSE;
gboolean res = FALSE;
gboolean src_pads_query_result = FALSE;
GstIteratorFoldFunction fold_func;
QueryInitFunction fold_init = NULL;
QueryDoneFunction fold_done = NULL;
QueryFold fold_data;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_DURATION:
{
/* iterate and collect durations */
fold_func = (GstIteratorFoldFunction) bin_query_duration_fold; //函数指针赋值
fold_init = bin_query_min_max_init;
fold_done = bin_query_duration_done;
break;
}
case GST_QUERY_POSITION:
{
fold_func = (GstIteratorFoldFunction) bin_query_position_fold;
fold_init = bin_query_min_max_init;
fold_done = bin_query_position_done;
break;
}
case GST_QUERY_LATENCY:
{
fold_func = (GstIteratorFoldFunction) bin_query_latency_fold;
fold_init = bin_query_min_max_init;
fold_done = bin_query_latency_done;
default_return = TRUE;
break;
}
default:
fold_func = (GstIteratorFoldFunction) bin_query_generic_fold;
break;
}
fold_data.query = query;
iter = gst_bin_iterate_sinks (bin);c
GST_DEBUG_OBJECT (bin, "Sending query %p (type %s) to sink children",
query, GST_QUERY_TYPE_NAME (query));
if (fold_init)
fold_init (bin, &fold_data);
res =
bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func, &fold_data,
default_return); //迭代器遍历bin中所有sink,可以看到参数是函数指针
gst_iterator_free (iter);
if (!res) {
/* Query the source pads of the element */
iter = gst_element_iterate_src_pads (element);
src_pads_query_result = //迭代器遍历bin中所有srcpad,可以看到参数是函数指针
bin_iterate_fold (bin, iter, fold_init, fold_done, fold_func,
&fold_data, default_return);
gst_iterator_free (iter);
if (src_pads_query_result)
res = TRUE;
}
/*
1) 首先遍历bin中所有sink,查询duration
2) 如果上述失败在遍历bin中所有srcpad, 查询duration
*/
GST_DEBUG_OBJECT (bin, "query %p result %d", query, res);
return res;
}
这里简单看下迭代器的实现:
/* Perform a query iteration for the given bin. The query is stored in
* QueryFold and iter should be either a GstPad iterator or a
* GstElement iterator. */
static gboolean
bin_iterate_fold (GstBin * bin, GstIterator * iter, QueryInitFunction fold_init,
QueryDoneFunction fold_done, GstIteratorFoldFunction fold_func,
QueryFold * fold_data, gboolean default_return)
{
gboolean res = default_return;
GValue ret = { 0 };
/* set the result of the query to FALSE initially */
g_value_init (&ret, G_TYPE_BOOLEAN);
g_value_set_boolean (&ret, res);
while (TRUE) {
GstIteratorResult ires;
ires = gst_iterator_fold (iter, fold_func, &ret, fold_data);
switch (ires) {
case GST_ITERATOR_RESYNC:
gst_iterator_resync (iter);
if (fold_init)
fold_init (bin, fold_data);
g_value_set_boolean (&ret, res);
break;
case GST_ITERATOR_OK:
case GST_ITERATOR_DONE:
res = g_value_get_boolean (&ret);
if (fold_done != NULL && res)
fold_done (bin, fold_data);
goto done;
default:
res = FALSE;
goto done;
}
}
done:
return res;
}
/**
* gst_iterator_fold:
* @it: The #GstIterator to fold over
* @func: (scope call): the fold function
* @ret: the seed value passed to the fold function
* @user_data: (closure): user data passed to the fold function
*
* Folds @func over the elements of @iter. That is to say, @func will be called
* as @func (object, @ret, @user_data) for each object in @it. The normal use
* of this procedure is to accumulate the results of operating on the objects in
* @ret.
*
* This procedure can be used (and is used internally) to implement the
* gst_iterator_foreach() and gst_iterator_find_custom() operations.
*
* The fold will proceed as long as @func returns %TRUE. When the iterator has no
* more arguments, %GST_ITERATOR_DONE will be returned. If @func returns %FALSE,
* the fold will stop, and %GST_ITERATOR_OK will be returned. Errors or resyncs
* will cause fold to return %GST_ITERATOR_ERROR or %GST_ITERATOR_RESYNC as
* appropriate.
*
* The iterator will not be freed.
*
* Returns: A #GstIteratorResult, as described above.
*
* MT safe.
*/
GstIteratorResult
gst_iterator_fold (GstIterator * it, GstIteratorFoldFunction func,
GValue * ret, gpointer user_data)
{
GValue item = { 0, };
GstIteratorResult result;
g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR);
while (1) {
result = gst_iterator_next (it, &item);
switch (result) {
case GST_ITERATOR_OK:
if (!func (&item, ret, user_data))
goto fold_done;
g_value_reset (&item);
break;
case GST_ITERATOR_RESYNC:
case GST_ITERATOR_ERROR:
goto fold_done;
case GST_ITERATOR_DONE:
goto fold_done;
}
}
fold_done:
g_value_unset (&item);
return result;
}
继续分析下上面的细节
a)迭代器获取sink
/**
* gst_bin_iterate_sinks:
* @bin: a #GstBin
*
* Gets an iterator for all elements in the bin that have the
* #GST_ELEMENT_FLAG_SINK flag set.
*
* Returns: (transfer full) (nullable): a #GstIterator of #GstElement
*/
GstIterator *
gst_bin_iterate_sinks (GstBin * bin)
{
GstIterator *children;
GstIterator *result;
GValue vbin = { 0, };
g_return_val_if_fail (GST_IS_BIN (bin), NULL);
g_value_init (&vbin, GST_TYPE_BIN);
g_value_set_object (&vbin, bin);
children = gst_bin_iterate_elements (bin);
result = gst_iterator_filter (children,
(GCompareFunc) sink_iterator_filter, &vbin);
g_value_unset (&vbin);
return result;
}
b)迭代器获取srcpad
/**
* gst_element_iterate_src_pads:
* @element: a #GstElement.
*
* Retrieves an iterator of @element's source pads.
*
* The order of pads returned by the iterator will be the order in which
* the pads were added to the element.
*
* Returns: (transfer full): the #GstIterator of #GstPad.
*
* MT safe.
*/
GstIterator *
gst_element_iterate_src_pads (GstElement * element)
{
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
return gst_element_iterate_pad_list (element, &element->srcpads);
}
迭代器的使用,就是分别调用前面的函数指针:
重点fold_func:
static gboolean
bin_query_duration_fold (const GValue * vitem, GValue * ret, QueryFold * fold)
{
gboolean res = FALSE;
GstObject *item = g_value_get_object (vitem);
if (GST_IS_PAD (item))
res = gst_pad_query (GST_PAD (item), fold->query);
else
res = gst_element_query (GST_ELEMENT (item), fold->query);
if (res) {
gint64 duration;
g_value_set_boolean (ret, TRUE);
gst_query_parse_duration (fold->query, NULL, &duration);
GST_DEBUG_OBJECT (item, "got duration %" G_GINT64_FORMAT, duration);
if (duration == -1) {
/* duration query succeeded, but duration is unknown */
fold->max = -1;
return FALSE;
}
if (duration > fold->max)
fold->max = duration;
}
return TRUE;
}
static void
bin_query_duration_done (GstBin * bin, QueryFold * fold)
{
GstFormat format;
gst_query_parse_duration (fold->query, &format, NULL);
/* store max in query result */
gst_query_set_duration (fold->query, format, fold->max);
GST_DEBUG_OBJECT (bin, "max duration %" G_GINT64_FORMAT, fold->max);
}
在举一个例子mkvdemux:
static gboolean
gst_matroska_demux_query (GstMatroskaDemux * demux, GstPad * pad,
GstQuery * query)
{
gboolean res = FALSE;
GstMatroskaTrackContext *context = NULL;
if (pad) {
context = gst_pad_get_element_private (pad);
}
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
GstFormat format;
gst_query_parse_position (query, &format, NULL);
res = TRUE;
if (format == GST_FORMAT_TIME) {
GST_OBJECT_LOCK (demux);
if (context)
gst_query_set_position (query, GST_FORMAT_TIME,
MAX (context->pos, demux->stream_start_time) -
demux->stream_start_time);
else
gst_query_set_position (query, GST_FORMAT_TIME,
MAX (demux->common.segment.position, demux->stream_start_time) -
demux->stream_start_time);
GST_OBJECT_UNLOCK (demux);
} else if (format == GST_FORMAT_DEFAULT && context
&& context->default_duration) {
GST_OBJECT_LOCK (demux);
gst_query_set_position (query, GST_FORMAT_DEFAULT,
context->pos / context->default_duration);
GST_OBJECT_UNLOCK (demux);
} else {
GST_DEBUG_OBJECT (demux,
"only position query in TIME and DEFAULT format is supported");
res = FALSE;
}
break;
}
case GST_QUERY_DURATION:
{
GstFormat format;
gst_query_parse_duration (query, &format, NULL);
res = TRUE;
if (format == GST_FORMAT_TIME) {
GST_OBJECT_LOCK (demux);
gst_query_set_duration (query, GST_FORMAT_TIME,
demux->common.segment.duration);
GST_OBJECT_UNLOCK (demux);
} else if (format == GST_FORMAT_DEFAULT && context
&& context->default_duration) {
GST_OBJECT_LOCK (demux);
gst_query_set_duration (query, GST_FORMAT_DEFAULT,
demux->common.segment.duration / context->default_duration);
GST_OBJECT_UNLOCK (demux);
} else {
GST_DEBUG_OBJECT (demux,
"only duration query in TIME and DEFAULT format is supported");
res = FALSE;
}
break;
}
case GST_QUERY_SEEKING:
{
GstFormat fmt;
gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
GST_OBJECT_LOCK (demux);
if (fmt == GST_FORMAT_TIME) {
gboolean seekable;
if (demux->streaming) {
/* assuming we'll be able to get an index ... */
seekable = demux->seekable;
} else {
seekable = TRUE;
}
gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
0, demux->common.segment.duration);
res = TRUE;
}
GST_OBJECT_UNLOCK (demux);
break;
}
case GST_QUERY_SEGMENT:
{
GstFormat format;
gint64 start, stop;
format = demux->common.segment.format;
start =
gst_segment_to_stream_time (&demux->common.segment, format,
demux->common.segment.start);
if ((stop = demux->common.segment.stop) == -1)
stop = demux->common.segment.duration;
else
stop =
gst_segment_to_stream_time (&demux->common.segment, format, stop);
gst_query_set_segment (query, demux->common.segment.rate, format, start,
stop);
res = TRUE;
break;
}
default:
if (pad)
res = gst_pad_query_default (pad, (GstObject *) demux, query);
else
res =
GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (demux),
query);
break;
}
return res;
}
再举一个例子2
playsink没有实现duration的查询,但是其内部的sink,如audiosink继承了gstbasesink
static gboolean
default_element_query (GstElement * element, GstQuery * query)
{
gboolean res = FALSE;
GstBaseSink *basesink = GST_BASE_SINK (element);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
gint64 cur = 0;
GstFormat format;
gboolean upstream = FALSE;
gst_query_parse_position (query, &format, NULL);
GST_DEBUG_OBJECT (basesink, "position query in format %s",
gst_format_get_name (format));
/* first try to get the position based on the clock */
if ((res =
gst_base_sink_get_position (basesink, format, &cur, &upstream))) {
gst_query_set_position (query, format, cur);
} else if (upstream) {
/* fallback to peer query */
res = gst_pad_peer_query (basesink->sinkpad, query);
}
if (!res) {
/* we can handle a few things if upstream failed */
if (format == GST_FORMAT_PERCENT) {
gint64 dur = 0;
res = gst_base_sink_get_position (basesink, GST_FORMAT_TIME, &cur,
&upstream);
if (!res && upstream) {
res =
gst_pad_peer_query_position (basesink->sinkpad, GST_FORMAT_TIME,
&cur);
}
if (res) {
res = gst_base_sink_get_duration (basesink, GST_FORMAT_TIME, &dur,
&upstream);
if (!res && upstream) {
res =
gst_pad_peer_query_duration (basesink->sinkpad,
GST_FORMAT_TIME, &dur);
}
}
if (res) {
gint64 pos;
pos = gst_util_uint64_scale (100 * GST_FORMAT_PERCENT_SCALE, cur,
dur);
gst_query_set_position (query, GST_FORMAT_PERCENT, pos);
}
}
}
break;
}
case GST_QUERY_DURATION:
{
gint64 dur = 0;
GstFormat format;
gboolean upstream = FALSE;
gst_query_parse_duration (query, &format, NULL);
GST_DEBUG_OBJECT (basesink, "duration query in format %s",
gst_format_get_name (format));
if ((res =
gst_base_sink_get_duration (basesink, format, &dur, &upstream))) {
gst_query_set_duration (query, format, dur);
} else if (upstream) {
/* fallback to peer query */
res = gst_pad_peer_query (basesink->sinkpad, query);
}
if (!res) {
/* we can handle a few things if upstream failed */
if (format == GST_FORMAT_PERCENT) {
gst_query_set_duration (query, GST_FORMAT_PERCENT,
GST_FORMAT_PERCENT_MAX);
res = TRUE;
}
}
break;
}
case GST_QUERY_LATENCY:
{
gboolean live, us_live;
GstClockTime min, max;
if ((res = gst_base_sink_query_latency (basesink, &live, &us_live, &min,
&max))) {
gst_query_set_latency (query, live, min, max);
}
break;
}
case GST_QUERY_JITTER:
break;
case GST_QUERY_RATE:
/* gst_query_set_rate (query, basesink->segment_rate); */
res = TRUE;
break;
case GST_QUERY_SEGMENT:
{
if (basesink->pad_mode == GST_PAD_MODE_PULL) {
GstFormat format;
gint64 start, stop;
format = basesink->segment.format;
start =
gst_segment_to_stream_time (&basesink->segment, format,
basesink->segment.start);
if ((stop = basesink->segment.stop) == -1)
stop = basesink->segment.duration;
else
stop = gst_segment_to_stream_time (&basesink->segment, format, stop);
gst_query_set_segment (query, basesink->segment.rate, format, start,
stop);
res = TRUE;
} else {
res = gst_pad_peer_query (basesink->sinkpad, query);
}
break;
}
case GST_QUERY_SEEKING:
case GST_QUERY_CONVERT:
case GST_QUERY_FORMATS:
default:
res = gst_pad_peer_query (basesink->sinkpad, query);
break;
}
GST_DEBUG_OBJECT (basesink, "query %s returns %d",
GST_QUERY_TYPE_NAME (query), res);
return res;
}
第一种适用于pull模式; 第二种直接使用gst_pad_peer_query查询.
最终通过不断的gst_pad_peer_query还是走到demux或者parse中获取的duration