项目相关:网络视频播放器
系统: 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;
}
}
这里就将两个element链接起来了,post message等操作,就可以发送消息,数据了
(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));
}
}