Gstreamer之Caps協商

GStreamer一個多媒體框架,用來做多媒體播放器以及一些與之相關的應用都是一個不錯的選擇,當然這裏指的多是Linux平臺。如果你的平臺是windows那不防可以考慮一下DirectShow等其它一些框架。
好吧,關於框架我先說到這裏,先點到爲止吧。如果後期時間允許我會單獨的說說這些東西的。

開始說說gstreamer的caps協商過程吧。
說到gstreamer其實主要有兩部分組成,element和pad。當然說到caps的協商就與它們兩有着不可迴避的聯繫了。
對於element:
在創建element時,你可以先定義一個模板,它會對你的element的caps有一個大致的約束,代碼如下:
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png")
    );


static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
    GST_PAD_SRC,
    GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-yuv; video/x-raw-rgb; image/jpeg; image/png")
    );


static void
gst_xxx_class_init (GstxxxClass * klass)
    gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&src_factory));
    gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory));
}


static void
gst_xxx_init (Gstxxx * filter,
    GstxxxClass * gclass)
{
    filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
    gst_pad_set_event_function(filter->sinkpad, gst_xxx_event);
//      gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
    gst_pad_set_setcaps_function (filter->sinkpad, gst_xxx_set_caps);
    gst_pad_set_getcaps_function (filter->sinkpad, gst_xxx_get_caps);
    gst_pad_set_chain_function   (filter->sinkpad, gst_xxx_chain);
    gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);


    filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
    gst_pad_set_getcaps_function (filter->srcpad, gst_xxx_query);
    gst_pad_set_setcaps_function (filter->srcpad, gst_xxx_set_caps);
    gst_pad_set_getcaps_function (filter->srcpad, gst_xxx_get_caps);
    gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);

    GST_DEBUG_FUNCPTR(gst_xxx_set_caps);
    GST_DEBUG_FUNCPTR(gst_xxx_get_caps);
    GST_DEBUG_FUNCPTR(gst_xxx_chain);
    GST_DEBUG_FUNCPTR(gst_xxx_event);
}

其中關於caps協商的主要函數有gst_xxx_set_caps()、gst_xxx_get_caps()和gst_xxx_chain()
先說這個_set_caps()和_get_caps():
這兩個函數屬於基礎層的函數,當你調用gst_pad_set_caps()和gst_pad_get_caps()這兩個函數時,最後都會在你設置的這兩個函數中使用。
所以注意初學者,在寫plugin的時候請謹慎的調用gst_pad_set_caps()和gst_pad_get_caps()。


當程序運行時(這裏只針對Downstream的模式)
你的sink_pad中的set_caps函數將會率先被上游模塊調用:

gst_xxx_set_caps()實現細節:獲取本element的src_pad並獲取其中的caps然後和sink_pad的caps做一次交叉,將交叉結果繼續往下游發送;當然其中你的element如果對其中的任何信息有興趣,便可以在這個地方獲取了;
主要用的函數有:
  in_templ = gst_pad_get_pad_template_caps (in_pad);
  gst_pad_get_caps (opeer);
  intersect = gst_caps_intersect (peercaps, transform);
  caps = gst_caps_copy_nth (intersect, 0);
  gst_caps_unref (intersect);
  structure = gst_caps_get_structure (caps, 0);
  gst_structure_fixate_field_nearest_fraction (structure, "framerate", rate_numerator, rate_denominator);
  gst_structure_get_fraction (structure, "framerate", &rate_numerator, &rate_denominator);

gst_xxx_get_caps()實現細節:這個函數主要是在兩個element link獲取其pad的caps時被調用,所以一般返回的屬性的範圍。
主要調用的函數有:
  gst_pad_peer_get_caps (otherpad);
  in_templ = gst_pad_get_pad_template_caps (in_pad);
  gst_pad_get_caps (opeer);
  intersect = gst_caps_intersect (peercaps, transform);
  gst_caps_unref (caps);
  caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
注:一般的caps可以先用capsfile先做約束


最後一步就是要設置在gst_xxx_chain()中的buffer的caps了,如果不去設定程序一般回出現一些不可預知的問題,如程序hung住不動。
gst_xxx_chain()的簡單實現:
static GstFlowReturn
gst_xxx_chain (GstPad * pad, GstBuffer * buf)
{
    GstFlowReturn ret;
    Gstxxx *filter;
    GstBuffer *outbuf;

    filter = GST_XXX (GST_OBJECT_PARENT (pad));
    if (filter->silent == TRUE)
        return gst_pad_push (filter->srcpad, buf);

    outbuf = gst_buffer_make_metadata_writable(buf);
    gst_buffer_set_caps(outbuf, filter->srcpad->caps);

    ret = gst_pad_push (filter->srcpad, outbuf);

    return ret;
}


其中注意函數gst_buffer_make_metadata_writable()它的實現是:
GstBuffer *
gst_buffer_make_metadata_writable (GstBuffer * buf)
{
  GstBuffer *ret;

  if (gst_buffer_is_metadata_writable (buf)) { //這個地方很有意思,它實際判斷buf->refcount == 1
    ret = buf;
  } else {
    ret = gst_buffer_create_sub (buf, 0, GST_BUFFER_SIZE (buf)); //這個函數的意思是根據buf的信息創建一個新的buf
    gst_buffer_unref (buf); //注意它已經把原始的buf釋放了,我們在外面就不需要再做什麼了
  }


  return ret;
}


-流媒體-
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章