项目相关:网络视频播放器
系统: meego-1.2
应用: Qt + Gstreamer
应用使用的Gstreamer playbin2 控件。
(1) 概述
(1.1) Element, Pad, Caps 之间的关系。
+-------------------------------------+ | ELEMENT | |--------------+ +-------| |sinkpad | | | | +---------+ | | | | | caps | | |srcpad | | | caps | | | | | +---------+ | | | |--------------+ +-------| +-------------------------------------+这里列举实例如:qtdemux (quicktime demux)
这里简单列出,详细部分参考源码:gst-plugins-good-0.10.27/gst/qtdemux/qtdemux.c
static GstStaticPadTemplate gst_qtdemux_sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; " "application/x-3gp") ); static void gst_qtdemux_init (GstQTDemux * qtdemux, GstQTDemuxClass * klass) { qtdemux->sinkpad = gst_pad_new_from_static_template (&gst_qtdemux_sink_template, "sink"); gst_element_add_pad (GST_ELEMENT_CAST (qtdemux), qtdemux->sinkpad); } static gboolean qtdemux_parse_trak (GstQTDemux * qtdemux, GNode * trak) { stream->caps = qtdemux_video_caps (qtdemux, stream, fourcc, stsd_data, &codec); qtdemux->streams[qtdemux->n_streams] = stream; }(1.2) playbin内部
+------------------------------------------------------------+ | playbin2 | | +---------------------------------------------------+ | | |uridecodebin | | | | +----------+ +----------+ +-----------+ | | | | | typefind | | queue2 | | decodebin | | | | |->| |>--->| |>--->| |> | | | | | | | | | | | | | | +----------+ +----------+ +-----------+ | | | +---------------------------------------------------+ | +------------------------------------------------------------+
(2) 链接(link)
gstreamer中,element, pad 都是通过gst_element_link_pads, 或gst_pad_link链接起来的。并且显然,pad嵌在element元件内部,当将两个element link起来时,真正发生的改变是什么呢?
(2.1) gst_element_lin_pads 与 gst_pad_link 对比
gboolean gst_element_link_pads (GstElement * src, const gchar * srcpadname, GstElement * dest, const gchar * destpadname) { return gst_element_link_pads_full (src, srcpadname, dest, destpadname, GST_PAD_LINK_CHECK_DEFAULT); } gboolean gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, GstElement * dest, const gchar * destpadname, GstPadLinkCheck flags) { result = pad_link_maybe_ghosting (srcpad, destpad, flags); } static gboolean pad_link_maybe_ghosting (GstPad * src, GstPad * sink, GstPadLinkCheck flags) { ret = (gst_pad_link_full (src, sink, flags) == GST_PAD_LINK_OK); } /* 和 gst_pad_link 一样 */ GstPadLinkReturn gst_pad_link (GstPad * srcpad, GstPad * sinkpad) { return gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_DEFAULT); }(2.2) pad link 到底做什么了?
GstPadLinkReturn gst_pad_link_full (GstPad * srcpad, GstPad * sinkpad, GstPadLinkCheck flags) { /* 这个参考 GstPad 的 structure, 这里就是互相将peer设置为对端 */ GST_PAD_PEER (srcpad) = sinkpad; GST_PAD_PEER (sinkpad) = srcpad; /* 如果有link func那么执行函数,大部分element中没有设置link func函数 */ if (GST_PAD_LINKFUNC (srcpad)) { result = GST_PAD_LINKFUNC (srcpad) (srcpad, sinkpad); } else if (GST_PAD_LINKFUNC (sinkpad)) { result = GST_PAD_LINKFUNC (sinkpad) (sinkpad, srcpad); } else { result = GST_PAD_LINK_OK; } }
(3) 在element内/间,消息/数据是如何路径呢,下面我们分析下数据,事件原理与数据类似。
以queue2为例子,看看如何从sinkpad收到消息,以及从srcpad发送到对端的sinkpad中。
/* gstreamer-0.10.32/plugins/elements/gstqueue2.c */ static void gst_queue2_init (GstQueue2 * queue, GstQueue2Class * g_class) { /*创建sinkpad*/ queue->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); /* 设置sinkpad的chain function, 这个函数后面会用到, 也是sinkpad数据的入口*/ gst_pad_set_chain_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue2_chain)); queue->srcpad = gst_pad_new_from_static_template (&srctemplate, "src"); /* 这里是用srcpad开始向外推送数据 */ gst_pad_set_activatepush_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue2_src_activate_push)); } /*我们从chain function开始看,为什么chain function是入口函数? 往下看*/ static GstFlowReturn gst_queue2_chain (GstPad * pad, GstBuffer * buffer) { gst_queue2_locked_enqueue (queue, buffer, TRUE); } static void gst_queue2_locked_enqueue (GstQueue2 * queue, gpointer item, gboolean isbuffer) { if (isbuffer) { if (QUEUE_IS_USING_QUEUE (queue)) { queue->cur_level.buffers++; } } else if (GST_IS_EVENT (item)) { } if (item) { if (QUEUE_IS_USING_QUEUE (queue)) { /* 这里把buffer放在queue里 */ g_queue_push_tail (queue->queue, item); } } } /* 接收数据完成了,下面看srcpad向外推送数据 */ static gboolean gst_queue2_src_activate_push (GstPad * pad, gboolean active) { if (active) { result = gst_pad_start_task (pad, (GstTaskFunction) gst_queue2_loop, pad); } } static void gst_queue2_loop (GstPad * pad) { ret = gst_queue2_push_one (queue); } static GstFlowReturn gst_queue2_push_one (GstQueue2 * queue) { data = gst_queue2_locked_dequeue (queue, &is_buffer); if (is_buffer) { /* 发送数据 */ result = gst_pad_push (queue->srcpad, buffer); } else if (GST_IS_EVENT (data)) { /* 这里是发送事件,会获取srcpad的peerpad,然后调用eventfunc,原理与发送数据类似 */ gst_pad_push_event (queue->srcpad, event); } } /* gstreamer/gst/gstpad.c */ GstFlowReturn gst_pad_push (GstPad * pad, GstBuffer * buffer) { /* 这个看起来很复杂,... 初一见,都觉得头疼,这么多层。 * 实际上并不复杂,他的作用就是,此前如果该gstpad,有过push操作,并建立好了路径,那么直接使用, * 否则跳到slow_path执行。 */ cache_ptr = (gpointer *) & pad->abidata.ABI.priv->cache_ptr; cache = pad_take_cache (pad, cache_ptr); if (G_UNLIKELY (cache == NULL)) goto slow_path; peer = cache->peer; ret = GST_PAD_CHAINFUNC (peer) (peer, buffer); return ret; slow_path: GstPadPushCache scache = { NULL, }; /* 实际上这里面一样是调用了srcpad的peer端的chain_func [注:与我们开始从chain_func分析吻合] */ ret = gst_pad_push_data (pad, TRUE, buffer, &scache); if (scache.peer) { /* 这里就将cache_ptr 赋值到pad->abidata.ABI.priv->cache_ptr 这么长的里面了 */ pad_put_cache (pad, ncache, cache_ptr); } } /* 贯彻到底,我们将srcpad最后的调用对端的chainfunc挖出来 */ static GstFlowReturn gst_pad_push_data (GstPad * pad, gboolean is_buffer, void *data, GstPadPushCache * cache) { if (G_UNLIKELY ((peer = GST_PAD_PEER (pad)) == NULL)) goto not_linked; ret = gst_pad_chain_data_unchecked (peer, is_buffer, data, cache); } static inline GstFlowReturn gst_pad_chain_data_unchecked (GstPad * pad, gboolean is_buffer, void *data, GstPadPushCache * cache) { if (G_LIKELY (is_buffer)) { if (G_UNLIKELY ((chainfunc = GST_PAD_CHAINFUNC (pad)) == NULL)) goto no_function; /* 这里调用了srcpad的peer端的 chainfunc 开始执行。 queue2的chainfunc一样的开始道理 */ ret = chainfunc (pad, GST_BUFFER_CAST (data)); } }