Gstreamer初见

    项目相关:网络视频播放器

    系统: 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));
  }
}



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值