gstreamer协商negoation
首先说明,这是自己的一些笔记,比较凌乱,还没有好好整理,不要留言骂人啊,我会慢慢整理出来,我本人是做gsrremer插件工作,所以明白gstreamer真的很难,不是看一些博客就能了解的,看了了解到的大部分也是错的,所以你真的想要学习gstreamer的话,一定要把gstreamer代码结构先看明白,那些是基础代码,那些是插件代码,当然还要多多少少了解一些meson的构建方式。
达到这个程度后,最好在linux下编译一个最简单的gstreamer库,然后准备一个非常简单的案例,打开gstreamer的log,当你跑通一个案例的时候,跟着日志看代码的流程,不要怕麻烦,一点一点来,先从最简单的插件加载方式看,然后看add,然后link,然后看state的改变,中途如果对某些代码调用步确定,可以用gdb跟踪下代码,这一套流程下来,你就会对gstreamer的整个框架有个非常宏观的了解了。
所以与其翻各种错误百出的帖子还不如自己静下心来好好看看代码,看看官方文档
gstreamer关键步骤有下面三个:
gst_bin_add_many(GST_BIN(data.pipeline), data.source, data.h264parse, data.omx264dec, data.convert, data.sink, nullptr);gst_element_link_many(data.source, data.h264parse, data.omx264dec, data.convert, data.sink, nullptr)gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
add最简单,主要将element添加到pipeline中,但是这里还有一个操作,就是将pipeline的child bus设置为所有element中的bus,各个element的parent设置为pipeline.
link阶段核心就是将上下element的sinkpad和srcpad通过peerpad连接在一起,但是连接之前会查询双方pad的caps,并且进行交集计算,如果有交集才进行link.查询的时候发送的是caps query
gst_element_set_state(data.pipeline, GST_STATE_PLAYING)
这是pipeline状态改变的重点函数,这里,状态首先从NULl改变到READY,然后再把状态从READY改变为PAUSED,其中到PAUSED的过程中会触发激活函数,比如src element的loop线程开始启动,解码器开始打开等动作。注意的是这个过程中的element是link状态下element倒着来的,比如先是sink,接着covert,然后omx264dec,然后。。。,当pipeline的t状态改变为AREADY的时候,会从source发送一个STREAM_START的event,这个event一直传递到最后的sink element,每个element接收到STREAM_START后会做一些事情。
下图就是event从上床底到下的一个流程图,不一定每个event都从上传递到下,个别event可能从中途就返回了。
当sink element收到一个stream-start event后会发送给一个msg到bus总线。协商就发生在stream start之后,发起者是source element就是第一个element,所以当你还没有了解到整个gstreamer运转机制的时候,先写一个简单的pipeline,然后从第一个element开始看协商函数。
下面函数是整个pipeline的动力起源:
/* Called with STREAM_LOCK */
static void
gst_base_src_loop (GstPad * pad)
{GstBaseSrc *src;GstBuffer *buf = NULL;GstFlowReturn ret;gint64 position;gboolean eos;guint blocksize;GList *pending_events = NULL, *tmp;eos = FALSE;src = GST_BASE_SRC (GST_OBJECT_PARENT (pad));/* Just leave immediately if we're flushing */GST_LIVE_LOCK (src);if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad)))goto flushing;GST_LIVE_UNLOCK (src);/* Just return if EOS is pushed again, as the app might be unaware that an* EOS have been sent already */if (GST_PAD_IS_EOS (pad)) {GST_DEBUG_OBJECT (src, "Pad is marked as EOS, pause the task");gst_pad_pause_task (pad);goto done;}//首先发送一个stream start的eventgst_base_src_send_stream_start (src);/* The stream-start event could've caused something to flush us */GST_LIVE_LOCK (src);if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad)))goto flushing;GST_LIVE_UNLOCK (src);/* check if we need to renegotiate */if (gst_pad_check_reconfigure (pad)) {//这里进行第一次协商if (!gst_base_src_negotiate_unlocked (src)) {gst_pad_mark_reconfigure (pad);if (GST_PAD_IS_FLUSHING (pad)) {GST_LIVE_LOCK (src);goto flushing;} else {goto negotiate_failed;}}}GST_LIVE_LOCK (src);if (G_UNLIKELY (src->priv->flushing || GST_PAD_IS_FLUSHING (pad)))goto flushing;blocksize = src->blocksize;/* if we operate in bytes, we can calculate an offset */if (src->segment.format == GST_FORMAT_BYTES) {position = src->segment.position;/* for negative rates, start with subtracting the blocksize */if (src->segment.rate < 0.0) {/* we cannot go below segment.start */if (position > src->segment.start + blocksize)position -= blocksize;else {/* last block, remainder up to segment.start */blocksize = position - src->segment.start;position = src->segment.start;}}} elseposition = -1;GST_LOG_OBJECT (src, "next_ts %" GST_TIME_FORMAT " size %u",GST_TIME_ARGS (position), blocksize);/* clean up just in case we got interrupted or so last time round */if (src->priv->pending_bufferlist != NULL) {gst_buffer_list_unref (src->priv->pending_bufferlist);src->priv->pending_bufferlist = NULL;}ret = gst_base_src_get_range (src, position, blocksize, &buf);if (G_UNLIKELY (ret != GST_FLOW_OK)) {GST_INFO_OBJECT (src, "pausing after gst_base_src_get_range() = %s",gst_flow_get_name (ret));GST_LIVE_UNLOCK (src);goto pause;}}
下面先看:gst_base_src_negotiate_unlocked()
static gboolean
gst_base_src_negotiate_unlocked (GstBaseSrc * basesrc)
{GstBaseSrcClass *bclass;gboolean result;bclass = GST_BASE_SRC_GET_CLASS (basesrc);GST_DEBUG_OBJECT (basesrc, "starting negotiation");if (G_LIKELY (bclass->negotiate))result = bclass->negotiate (basesrc);elseresult = TRUE;if (G_LIKELY (result)) {GstCaps *caps;
//首先查询capscaps = gst_pad_get_current_caps (basesrc->srcpad);
//开始分配bufresult = gst_base_src_prepare_allocation (basesrc, caps);if (caps)gst_caps_unref (caps);}return result;
}
首先看第一个:
caps = gst_pad_get_current_caps (basesrc->srcpad);
下面是日志:
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_negotiate_unlocked:3439 starting negotiation
[Level:5] ../subprojects/gstreamer/gst/gstutils.c:gst_pad_query_caps:3102 get pad caps with filter (NULL)
[Level:5] ../subprojects/gstreamer/gst/gstquery.c:gst_query_new_custom:679 creating new query 0x5630fc4bf6d0 caps
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4170 doing query 0x5630fc4bf6d0 (caps)
[Level:5] ../subprojects/gst-plugins-base/gst-libs/gst/app/gstappsrc.c:gst_app_src_internal_get_caps:860 caps: (NULL)
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_default_query:1381 query caps returns 0
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4193 sent query 0x5630fc4bf6d0 (caps), result 0
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4239 query failed
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_default_negotiate:3369 caps of src: ANY
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_default_negotiate:3415 no negotiation needed
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_get_current_caps:2734 get current pad caps (NULL)
从日志可以看到,source app返回的是ANY。
接着看如何allocation:
result = gst_base_src_prepare_allocation (basesrc, caps);//这个函数总体就发送了一个caps的event,然后根据decoder返回的allocation来创建了一个bufpool,然后启动部分pool;
static gboolean
gst_base_src_prepare_allocation (GstBaseSrc * basesrc, GstCaps * caps)
{GstBaseSrcClass *bclass;gboolean result = TRUE;GstQuery *query;GstBufferPool *pool = NULL;GstAllocator *allocator = NULL;GstAllocationParams params;bclass = GST_BASE_SRC_GET_CLASS (basesrc);/* make query and let peer pad answer, we don't really care if it worked or* not, if it failed, the allocation query would contain defaults and the* subclass would then set better values if needed *///发送了一个allocation event ,下一个element是h264parse,所以接下来看h264中关于allocation的query处理,query = gst_query_new_allocation (caps, TRUE);if (!gst_pad_peer_query (basesrc->srcpad, query)) {/* not a problem, just debug a little */GST_DEBUG_OBJECT (basesrc, "peer ALLOCATION query failed");}g_assert (bclass->decide_allocation != NULL);result = bclass->decide_allocation (basesrc, query);GST_DEBUG_OBJECT (basesrc, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,query);if (!result)goto no_decide_allocation;/* we got configuration from our peer or the decide_allocation method,* parse them */if (gst_query_get_n_allocation_params (query) > 0) {gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);} else {allocator = NULL;gst_allocation_params_init (¶ms);}if (gst_query_get_n_allocation_pools (query) > 0)gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
//这里把pool激活result = gst_base_src_set_allocation (basesrc, pool, allocator, ¶ms);if (allocator)gst_object_unref (allocator);if (pool)gst_object_unref (pool);gst_query_unref (query);return result;/* Errors */
no_decide_allocation:{GST_WARNING_OBJECT (basesrc, "Subclass failed to decide allocation");gst_query_unref (query);return result;}
}
下面看如何发送allocation的event:
Level:5] ../subprojects/gstreamer/gst/gstquery.c:gst_query_new_custom:679 creating new query 0x5630fc4bf770 allocation
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_peer_query:4297 peer query 0x5630fc4bf770 (allocation)
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4170 doing query 0x5630fc4bf770 (allocation)
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbaseparse.c:gst_base_parse_sink_query:1637 allocation query
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_default:3511 forwarding 0x5630fc4bf770 (allocation) query
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_iterate_internal_links_default:2965 Making iterator//h264parse不处理,直接向下转发
[Level:6] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_forward:3074 calling forward function on pad h264-parse:src
[Level:6] ../subprojects/gstreamer/gst/gstpad.c:query_forward_func:3447 query peer 0x5630fc4bf770 (allocation) of h264-parse:src
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_peer_query:4297 peer query 0x5630fc4bf770 (allocation)
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4170 doing query 0x5630fc4bf770 (allocation)
//videodecoder处理这个query,然后直接返回
[Level:5] ../subprojects/gst-plugins-base/gst-libs/gst/video/gstvideodecoder.c:gst_video_decoder_sink_query:2178 received query 35846, allocation
[Level:6] ../subprojects/gst-plugins-base/gst-libs/gst/video/gstvideodecoder.c:gst_video_decoder_sink_query_default:2087 handling query: allocation query: 0x5630fc4bf770, GstQueryAllocation, caps=(GstCaps)"NULL", need-pool=(boolean)true;
//这里返回了allocation
[Level:5] ../subprojects/gst-omx/omx/gstomxvideodec.c:gst_omx_video_dec_propose_allocation:3517 request at least 3 buffers of size 32768[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4193 sent query 0x5630fc4bf770 (allocation), result 1
[Level:2] ../subprojects/gstreamer/gst/gststructure.c:priv_gst_structure_append_to_gstring:2091 No value transform to serialize field 'pool' of type 'GArray'
[Level:6] ../subprojects/gstreamer/libs/gst/base/gstbaseparse.c:gst_base_parse_sink_query:1644 allocation query result: 1 allocation query: 0x5630fc4bf770, GstQueryAllocation, caps=(GstCaps)"NULL", need-pool=(boolean)true, pool=(GArray)NULL;
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4193 sent query 0x5630fc4bf770 (allocation), result 1
接着执行:
g_assert (bclass->decide_allocation != NULL);result = bclass->decide_allocation (basesrc, query);
下面是decide_allocation日志:
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_decide_allocation_default:3235 no pool, making new pool
[Level:5] ../subprojects/gstreamer/gst/gstpoll.c:gst_poll_new:681 0x7f3478003000: new controllable : 1
[Level:5] ../subprojects/gstreamer/gst/gstpoll.c:gst_poll_add_fd_unlocked:848 0x7f3478003000: fd (fd:9, idx:0)
[Level:5] ../subprojects/gstreamer/gst/gstpoll.c:gst_poll_fd_ctl_read_unlocked:1014 0x7f3478003000: fd (fd:9, idx:0), active : 1
[Level:6] ../subprojects/gstreamer/gst/gstpoll.c:raise_wakeup:290 0x7f3478003000: raise
[Level:5] ../subprojects/gstreamer/gst/gstbufferpool.c:gst_buffer_pool_init:179 created
[Level:5] ../subprojects/gstreamer/gst/gstbufferpool.c:gst_buffer_pool_new:231 created new buffer pool
[Level:2] ../subprojects/gstreamer/gst/gststructure.c:priv_gst_structure_append_to_gstring:2091 No value transform to serialize field 'params' of type 'GstAllocationParams'[Level:5] ../subprojects/gstreamer/gst/gstbufferpool.c:default_set_config:622 config GstBufferPoolConfig, caps=(GstCaps)"NULL", size=(uint)32768, min-buffers=(uint)3, max-buffers=(uint)0, allocator=(GstAllocator)"NULL", params=(GstAllocationParams)NULL;
[Level:2] ../subprojects/gstreamer/gst/gststructure.c:priv_gst_structure_append_to_gstring:2091 No value transform to serialize field 'pool' of type 'GArray'
[Level:2] ../subprojects/gstreamer/gst/gststructure.c:priv_gst_structure_append_to_gstring:2091 No value transform to serialize field 'allocator' of type 'GArray'
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_prepare_allocation:3315 ALLOCATION (1) params: allocation query: 0x5630fc4bf770, GstQueryAllocation, caps=(GstCaps)"NULL", need-pool=(boolean)true, pool=(GArray)NULL, allocator=(GArray)NULL;
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbasesrc.c:gst_base_src_set_allocation:3143 activate pool
[Level:6] ../subprojects/gstreamer/gst/gstbufferpool.c:gst_buffer_pool_set_active:514 active 1
gst_base_src_decide_allocation_default()
static gboolean
gst_base_src_decide_allocation_default (GstBaseSrc * basesrc, GstQuery * query)
{GstCaps *outcaps;GstBufferPool *pool;guint size, min, max;GstAllocator *allocator;GstAllocationParams params;GstStructure *config;gboolean update_allocator;
//首先解析querygst_query_parse_allocation (query, &outcaps, NULL);/* we got configuration from our peer or the decide_allocation method,* parse them */* //解析allocation paramsif (gst_query_get_n_allocation_params (query) > 0) {/* try the allocator */gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms);update_allocator = TRUE;} else {allocator = NULL;gst_allocation_params_init (¶ms);update_allocator = FALSE;}//解析allocation poolif (gst_query_get_n_allocation_pools (query) > 0) {gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);if (pool == NULL) {/* no pool, we can make our own */GST_DEBUG_OBJECT (basesrc, "no pool, making new pool");//创建poolpool = gst_buffer_pool_new ();}} else {pool = NULL;size = min = max = 0;}/* now configure */if (pool) {config = gst_buffer_pool_get_config (pool);gst_buffer_pool_config_set_params (config, outcaps, size, min, max);gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);/* buffer pool may have to do some changes */if (!gst_buffer_pool_set_config (pool, config)) {config = gst_buffer_pool_get_config (pool);/* If change are not acceptable, fallback to generic pool */if (!gst_buffer_pool_config_validate_params (config, outcaps, size, min,max)) {GST_DEBUG_OBJECT (basesrc, "unsupported pool, making new pool");gst_object_unref (pool);pool = gst_buffer_pool_new ();gst_buffer_pool_config_set_params (config, outcaps, size, min, max);gst_buffer_pool_config_set_allocator (config, allocator, ¶ms);}if (!gst_buffer_pool_set_config (pool, config))goto config_failed;}}if (update_allocator)gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms);elsegst_query_add_allocation_param (query, allocator, ¶ms);if (allocator)gst_object_unref (allocator);if (pool) {gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);gst_object_unref (pool);}return TRUE;config_failed:GST_ELEMENT_ERROR (basesrc, RESOURCE, SETTINGS,("Failed to configure the buffer pool"),("Configuration is most likely invalid, please report this issue."));gst_object_unref (pool);return FALSE;
}
接下来看协商,最难的协商也是发生在这里,下面是调用栈,可以看到发生在gst_app_src_create()这里
今天继续,我们从gst_app_src_create()开始吧,
gst_app_src_create ()gst_app_src_do_negotiate (bsrc)gst_base_src_set_caps (basesrc, caps);current_caps = gst_pad_get_current_caps (GST_BASE_SRC_PAD (src))gst_pad_push_event (src->srcpad, gst_event_new_caps (caps))
所以整个pipeline的协商起始位置在这里,发送了一个caps event,下面看日志,可以看出创建的event发送到了下方的peerpad h264-parse:sink,对于gst_pad_push_event()这个函数,这里不展开,请单独看,非常简单的,同时也能解决你的一个疑问,sticky event.
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:ginf:2734 get current pad caps (NULL)
[Level:4] ../subprojects/gstreamer/gst/gstevent.c:gst_event_new_caps:892 creating caps event video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)176, height=(int)144
[Level:5] ../subprojects/gstreamer/gst/gstevent.c:gst_event_new_custom:310 creating new event 0x5630fc4c3560 caps 12814
[Level:6] ../subprojects/gstreamer/gst/gstpad.c:store_sticky_event:5354 stored sticky event caps
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:store_sticky_event:5360 notify caps
[Level:6] ../subprojects/gstreamer/gst/gstobject.c:gst_object_dispatch_properties_changed:472 deep notification from src (caps)
[Level:6] ../subprojects/gstreamer/gst/gstobject.c:gst_object_dispatch_properties_changed:472 deep notification from src (caps)
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:check_sticky:4102 pushing all sticky events
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:push_sticky:4031 event stream-start was already received[Level:6] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_push_event_unchecked:5538 sending event 0x5630fc4c3560 (caps) to peerpad <h264-parse:sink>
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_send_event_unchecked:5827 have event type caps event: 0x5630fc4c3560, time 99:99:99.999999999, seq-num 31, GstEventCaps, caps=(GstCaps)"video/x-h264\,\ alignment\=\(string\)au\,\ stream-format\=\(string\)byte-stream\,\ width\=\(int\)176\,\ height\=\(int\)144";
大致调用流程是:
gst_pad_push_event_unchecked()gst_pad_send_event_unchecked (peerpad, event, type)pre_eventfunc_check (pad, event);eventfunc (pad, parent, event)
上面pre_eventfunc_check (pad, event)做了很多事情:
static GstFlowReturn
pre_eventfunc_check (GstPad * pad, GstEvent * event)
{GstCaps *caps;switch (GST_EVENT_TYPE (event)) {case GST_EVENT_CAPS:{/* backwards compatibility mode for caps */gst_event_parse_caps (event, &caps);//这里查询,也就是发送caps事件的时候先发送caps query//如果有accept caps,才可以发送caps eventif (!gst_pad_query_accept_caps (pad, caps))goto not_accepted;break;}default:break;}return GST_FLOW_OK;
}
下面日志可以看出先构建了一个accept-capsquery,然后查询
[Level:5] ../subprojects/gstreamer/gst/gstutils.c:gst_pad_query_accept_caps:3185 accept caps of video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)176, height=(int)144
[Level:5] ../subprojects/gstreamer/gst/gstquery.c:gst_query_new_custom:679 creating new query 0x7f3478003050 accept-caps
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4170 doing query 0x7f3478003050 (accept-caps)
source element发送了查询后,接下来肯定是h264parse接受到了啊,下面是日志,
//h264parese接收到这个查询
[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbaseparse.c:gst_base_parse_sink_query:1637 accept-caps query[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_accept_caps_default:3197 query accept-caps accept-caps query: 0x7f3478003050, GstQueryAcceptCaps, caps=(GstCaps)"video/x-h264\,\ alignment\=\(string\)au\,\ stream-format\=\(string\)byte-stream\,\ width\=\(int\)176\,\ height\=\(int\)144", result=(boolean)false;
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_accept_caps_default:3222 allowed caps intersect video/x-h264, caps video/x-h264, alignment=(string)au, stream-format=(string)byte-stream, width=(int)176, height=(int)144
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_default:3511 not forwarding 0x7f3478003050 (accept-caps) query[Level:6] ../subprojects/gstreamer/libs/gst/base/gstbaseparse.c:gst_base_parse_sink_query:1644 accept-caps query result: 1 accept-caps query: 0x7f3478003050, GstQueryAcceptCaps, caps=(GstCaps)"video/x-h264\,\ alignment\=\(string\)au\,\ stream-format\=\(string\)byte-stream\,\ width\=\(int\)176\,\ height\=\(int\)144", result=(boolean)true;
[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4193 sent query 0x7f3478003050 (accept-caps), result 1
[Level:5] ../subprojects/gstreamer/gst/gstutils.c:gst_pad_query_accept_caps:3191 query returned 1
根据日志提示,我们来看gst_base_parse_sink_query()函数:
gst_base_parse_sink_query()gst_base_parse_sink_query_default()gst_pad_query_default (pad, GST_OBJECT_CAST (parse), query)gst_pad_query_accept_caps_default (pad, query)st_query_parse_accept_caps (query, &caps)gst_caps_can_intersect (caps, allowed)gst_query_set_accept_caps_result (query, result)
gst_base_parse_sink_query()//最终返回到这里
接下来到了eventfunc (pad, parent, event)函数:
static GstFlowReturn
gst_pad_send_event_unchecked (GstPad * pad, GstEvent * event,GstPadProbeType type)
{//也就是pre函数成功了才开发送事件函数,根据上面我们日志和代码的跟踪,发现本次pre就是查看了
//source srcpad和h264parse sinkpad是否有交集,如果有交集才发送event caps。ret = pre_eventfunc_check (pad, event);if (G_UNLIKELY (ret != GST_FLOW_OK))goto precheck_failed;if (sticky)gst_event_ref (event);if (eventfullfunc) {ret = eventfullfunc (pad, parent, event);} else if (eventfunc (pad, parent, event)) {ret = GST_FLOW_OK;} else {/* something went wrong */switch (event_type) {case GST_EVENT_CAPS:ret = GST_FLOW_NOT_NEGOTIATED;break;default:ret = GST_FLOW_ERROR;break;}}
这里开始eventfunc()函数:
static gboolean
gst_base_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{GstBaseParse *parse = GST_BASE_PARSE (parent);GstBaseParseClass *bclass = GST_BASE_PARSE_GET_CLASS (parse);gboolean ret;ret = bclass->sink_event (parse, event);return ret;
}//起始内部执行的是这个函数
static gboolean
gst_base_parse_sink_event_default (GstBaseParse * parse, GstEvent * event)
{GstBaseParseClass *klass = GST_BASE_PARSE_GET_CLASS (parse);gboolean ret = FALSE;gboolean forward_immediate = FALSE;GST_DEBUG_OBJECT (parse, "handling event %d, %s", GST_EVENT_TYPE (event),GST_EVENT_TYPE_NAME (event));switch (GST_EVENT_TYPE (event)) {case GST_EVENT_CAPS:{GstCaps *caps;gst_event_parse_caps (event, &caps);GST_DEBUG_OBJECT (parse, "caps: %" GST_PTR_FORMAT, caps);if (klass->set_sink_caps)ret = klass->set_sink_caps (parse, caps);elseret = TRUE;/* will send our own caps downstream */gst_event_unref (event);event = NULL;break;}}
接着是set_sink_caps (parse, caps),实际执行的是gst_h264_parse_set_caps,这个函数太长了,大致做了一些解析query的工作,然后
static gboolean
gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps)
{GstH264Parse *h264parse;GstStructure *str;const GValue *codec_data_value;GstBuffer *codec_data = NULL;gsize size;guint format, align, off;GstH264NalUnit nalu;GstH264ParserResult parseres;GstCaps *old_caps;h264parse = GST_H264_PARSE (parse);/* reset */h264parse->push_codec = FALSE;old_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));if (old_caps) {if (!gst_caps_is_equal (old_caps, caps))gst_h264_parse_reset_stream_info (h264parse);gst_caps_unref (old_caps);}str = gst_caps_get_structure (caps, 0);/* accept upstream info if provided */gst_structure_get_int (str, "width", &h264parse->width);gst_structure_get_int (str, "height", &h264parse->height);gst_structure_get_fraction (str, "framerate", &h264parse->fps_num,&h264parse->fps_den);gst_structure_get_fraction (str, "pixel-aspect-ratio",&h264parse->upstream_par_n, &h264parse->upstream_par_d);/* get upstream format and align from caps */gst_h264_parse_format_from_caps (caps, &format, &align);codec_data_value = gst_structure_get_value (str, "codec_data");/* fix up caps without stream-format for max. backwards compatibility */if (format == GST_H264_PARSE_FORMAT_NONE) {/* codec_data implies avc */if (codec_data_value != NULL) {GST_ERROR ("video/x-h264 caps with codec_data but no stream-format=avc");format = GST_H264_PARSE_FORMAT_AVC;} else {/* otherwise assume bytestream input */GST_ERROR ("video/x-h264 caps without codec_data or stream-format");format = GST_H264_PARSE_FORMAT_BYTE;}}.../* bytestream caps sanity checks */if (format == GST_H264_PARSE_FORMAT_BYTE) {/* should have SPS/PSS in-band (and/or oob in streamheader field) */if (codec_data_value != NULL)goto bytestream_caps_with_codec_data;}if (codec_data_value != NULL) {...
}else if (format == GST_H264_PARSE_FORMAT_BYTE) {GST_DEBUG_OBJECT (h264parse, "have bytestream h264");/* nothing to pre-process */h264parse->packetized = FALSE;/* we have 4 sync bytes */h264parse->nal_length_size = 4;} {GstCaps *in_caps;/* prefer input type determined above */in_caps = gst_caps_new_simple ("video/x-h264","parsed", G_TYPE_BOOLEAN, TRUE,"stream-format", G_TYPE_STRING,gst_h264_parse_get_string (h264parse, TRUE, format),"alignment", G_TYPE_STRING,gst_h264_parse_get_string (h264parse, FALSE, align), NULL);/* negotiate with downstream, sets ->format and ->align *///继续向下协商,h264parse里面有前面解析到的width,hight,framerate等gst_h264_parse_negotiate (h264parse, format, in_caps);gst_caps_unref (in_caps);}
}
gst_h264_parse_negotiate (h264parse, format, in_caps);做了些什么呢:
tatic void
gst_h264_parse_negotiate (GstH264Parse * h264parse, gint in_format,GstCaps * in_caps)
{GstCaps *caps;guint format = h264parse->format;guint align = h264parse->align;g_return_if_fail ((in_caps == NULL) || gst_caps_is_fixed (in_caps));//这里是个重点GST_BASE_PARSE_SRC_PAD (h264parse)获取到了src padcaps = gst_pad_get_allowed_caps (GST_BASE_PARSE_SRC_PAD (h264parse));GST_DEBUG_OBJECT (h264parse, "allowed caps: %" GST_PTR_FORMAT, caps);/* concentrate on leading structure, since decodebin parser* capsfilter always includes parser template caps */if (caps) {caps = gst_caps_truncate (caps);GST_DEBUG_OBJECT (h264parse, "negotiating with caps: %" GST_PTR_FORMAT,caps);}h264parse->can_passthrough = FALSE;if (in_caps && caps) {if (gst_caps_can_intersect (in_caps, caps)) {GST_DEBUG_OBJECT (h264parse, "downstream accepts upstream caps");gst_h264_parse_format_from_caps (in_caps, &format, &align);gst_caps_unref (caps);caps = NULL;h264parse->can_passthrough = TRUE;}}/* FIXME We could fail the negotiation immediately if caps are empty */if (caps && !gst_caps_is_empty (caps)) {/* fixate to avoid ambiguity with lists when parsing */caps = gst_caps_fixate (caps);gst_h264_parse_format_from_caps (caps, &format, &align);}/* default */if (!format)format = GST_H264_PARSE_FORMAT_BYTE;if (!align)align = GST_H264_PARSE_ALIGN_AU;GST_DEBUG_OBJECT (h264parse, "selected format %s, alignment %s",gst_h264_parse_get_string (h264parse, TRUE, format),gst_h264_parse_get_string (h264parse, FALSE, align));h264parse->format = format;h264parse->align = align;h264parse->transform = in_format != h264parse->format ||align == GST_H264_PARSE_ALIGN_AU;if (caps)gst_caps_unref (caps);
}
接着继续看gst_pad_get_allowed_caps():
GstCaps *gst_pad_get_allowed_caps (GstPad * pad)
{GstCaps *mycaps;GstCaps *caps = NULL;GstQuery *query;g_return_val_if_fail (GST_IS_PAD (pad), NULL);GST_OBJECT_LOCK (pad);if (G_UNLIKELY (GST_PAD_PEER (pad) == NULL))goto no_peer;GST_OBJECT_UNLOCK (pad);GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "getting allowed caps");
//查询src pad的caps
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_get_allowed_caps:2819 getting allowed caps
//[Level:5] ../subprojects/gstreamer/gst/gstutils.c:gst_pad_query_caps:3102 get pad caps with filter (NULL)
//[Level:5] ../subprojects/gstreamer/gst/gstquery.c:gst_query_new_custom:679 creating new query 0x7f34780030f0 caps
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4170 doing query 0x7f34780030f0 (caps)
//[Level:5] ../subprojects/gstreamer/libs/gst/base/gstbaseparse.c:gst_base_parse_src_query:1660 caps query: caps query: 0x7f34780030f0, GstQueryCaps, filter=(GstCaps)"NULL", caps=(GstCaps)"NULL";
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_caps_default:3256 query caps caps query: 0x7f34780030f0, GstQueryCaps, filter=(GstCaps)"NULL", caps=(GstCaps)"NULL";//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_caps_default:3274 fixed pad caps: trying pad caps
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_caps_default:3280 trying pad template caps
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_caps_default:3309 using caps 0x5630fc4a2a30 video/x-h264, parsed=(boolean)true, stream-format=(string){ avc, avc3, byte-stream }, alignment=(string){ au, nal }
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query_default:3511 not forwarding 0x7f34780030f0 (caps) query
//[Level:6] ../subprojects/gstreamer/libs/gst/base/gstbaseparse.c:gst_base_parse_src_query:1668 caps query result: 1 caps query: 0x7f34780030f0, GstQueryCaps, filter=(GstCaps)"NULL", caps=(GstCaps)"video/x-h264\,\ parsed\=\(boolean\)true\,\ stream-format\=\(string\)\{\ avc\,\ avc3\,\ byte-stream\ \}\,\ alignment\=\(string\)\{\ au\,\ nal\ \}";
//[Level:5] ../subprojects/gstreamer/gst/gstpad.c:gst_pad_query:4193 sent query 0x7f34780030f0 (caps), result 1
//[Level:5] ../subprojects/gstreamer/gst/gstutils.c:gst_pad_query_caps:3109 query returned video/x-h264, parsed=(boolean)true, stream-format=(string){ avc, avc3, byte-stream }, alignment=(string){ au, nal }mycaps = gst_pad_query_caps (pad, NULL);/* Query peer caps *///继续向下查询query = gst_query_new_caps (mycaps);if (!gst_pad_peer_query (pad, query)) {GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "Caps query failed");goto end;}gst_query_parse_caps_result (query, &caps);if (caps == NULL) {g_warn_if_fail (caps != NULL);goto end;}gst_caps_ref (caps);GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, pad, "allowed caps %" GST_PTR_FORMAT,caps);end:gst_query_unref (query);gst_caps_unref (mycaps);return caps;no_peer:{GST_CAT_DEBUG_OBJECT (GST_CAT_PROPERTIES, pad, "no peer");GST_OBJECT_UNLOCK (pad);return NULL;}
}
gst_pad_peer_query()这个函数,会从h264parse开始查询,逐步进入:
gst_video_decoder_sink_getcaps()st_video_decoder_proxy_getcaps (decoder, NULL, filter)__gst_video_element_proxy_getcaps()gst_pad_peer_query_caps (srcpad, NULL);//这里查询covert的sink了
(未完待续)
h264parse srcpad查询,应该是decoder的sinkpad 收到这个pad
所以接下来我们直接看h264-parse:sink pad的event函数就可以了,
parse_class->sink_event = GST_DEBUG_FUNCPTR (gst_h264_parse_event);parse_class->src_event = GST_DEBUG_FUNCPTR (gst_h264_parse_src_event);
总结:gstbasesrc从gst_app_src_do_negotiate (bsrc)
,发送caps event结束后,再次进入到gst_app_src_create()
函数,
接着到:gst_app_src_emit_need_data()
告诉app用户需要数据,用户开始发送数据。
gstreamer协商negoation相关推荐
- Gstreamer 内存分配协商机制
在两个衬垫的caps协商完成之后,元件之间需要确认如何分配buffer.本文梳理Gstreamer 内存协商机制,比如当某元件不能自己分配内存时,如何使用其他元件的分配器. 场景和目的 一般而言,内存 ...
- 什么是GStreamer?
什么是GStreamer? GStreamer是用于创建流媒体应用程序的框架.基本设计来自俄勒冈大学研究生院的视频管道以及DirectShow的一些想法. GStreamer的开发框架使编写任何类型的 ...
- GStreamer跨平台多媒体框架
GStreamer跨平台多媒体框架 Gstreamer基本概念 GStreamer是用于构造媒体处理组件图的库.它支持的应用程序范围从简单的Ogg / Vorbis回放,音频/视频流到复杂的音频(混合 ...
- GStreamer 的调试工具
目标 有时一些事情没有按照预期的运行,但从总线(bus)获得的错误消息也没有提供足够的信息.幸运地是,GStreamer 带有大量的调试信息,它们通常可以对哪里出了问题给出一些提示.这里将介绍: 如何 ...
- [转]详细的GStreamer开发教程
详细的GStreamer开发教程 文章目录 详细的GStreamer开发教程 1. 什么是GStreamer? 2. GStreamer架构 2.1 Media Applications 2.2 Co ...
- GStreamer开发介绍
https://blog.csdn.net/liu_xiao_cheng/article/details/43017491#section-bin-ghost-img 发Chat 转载 2015年01 ...
- GStreamer教程-基础教程2:GStreamer概念
目标 上一篇教程展示了如何自动构建管道.现在,我们将通过实例化每个元素并将它们链接在一起来手动构建管道.在这个过程中,我们会学到: 什么是GStreamer元素以及如何创建它. 如何将元素相互连接. ...
- 二、什么是GStreamer
GStreamer是一个用于创建流媒体应用程序的框架.基本的设计来自俄勒冈研究生院的视频管道,还有一些来自DirectShow的想法. GStreamer的开发框架使编写任何类型的流媒体应用程序成为可 ...
- Gstreamer说明
http://general.blog.51cto.com/927298/294928 http://general.blog.51cto.com/927298/294931 ============ ...
- gstreamer插件开发指南(一)
翻译自:https://gstreamer.freedesktop.org/documentation/plugin-development/index.html 1 简介 GStreamer是一个非 ...
最新文章
- SpringBoot实现Redis分布式锁
- XStream – XStreamely使用Java中的XML数据的简便方法
- java类加载过程_java类的加载过程
- python爬虫框架Scrapy采集数据,并制作词云图分析!
- Collections带有的排序方法 传入的元素类型 需是子类或者这个类的实例
- JQuery Ajax 在asp.net中使用总结
- .class与.java_Java中Class类的作用与深入理解
- 数据库学习--MySQL锁
- paip.chrome使用ACTIVX 的总结
- C语言经典游戏开发,零基础也能打造微信飞机大战
- MemReduct内存自动清理工具
- hbase时间同步造成region severs的问题
- 计算机木材染色 配色技术的应用技术,木材怎么染色?木材染色剂电脑配色方法...
- linux设置法语键盘布局,法语键盘布局图;
- java线程游戏之随机小球游戏V1
- mysql操作基础知识
- 按关键词搜索商品 淘宝API
- linux awl***程序说明
- 设置切换输入法按键ctrl+shift
- python编程心得体会800字_Python学习心得体会
热门文章
- c语言初步经典题4----求圆柱体的表面积
- 算法题 高斯消元解线性方程组(Python)
- 史上最详细教你制作“U盘启动盘”重装Windows10系统
- 使用Diskgenius将U盘分区,分为启动盘和文件存储两大功能详解
- 如果让markdown的图片变清晰/改变大小
- mysql rownum写法_mysql类似oracle rownum写法
- 在method方法被调用之后,仅打印出a=100,b=200,请写出method方法的代码
- 计算机主板大小性能区别,电脑主板是大板好还是小板好 主板中大板和小板的区别介绍...
- 网易云升级服务(云函数)
- 特征选择:嵌入法---《菜菜机器学习笔记》