gs--插件高级

插件的注册

GST_PLUGIN_DEFINE

GST_PLUGIN_DEFINE_STATIC
gst_plugin_register_static

PAD

CAP协商

插件的协商在gst_pad_alloc_buffer里。

三大类:静态pad(always pad ),动态pad (sometimes pad),手动pad (on-request pad)
#每个element上有几个pad并不是固定的。
#有些pad在element的整个生命周期都存在, 这些pad 称为静态pad,也叫always pad, 顾名思义就是常在的意思。
#还有一些pad则是根据需要由element自行创建,比如demuxer element需要根据多媒体文件包含的数据源(Stream)动态的创建新的路径:
#还有一类pad是由程序员手动创建的,比如tee element可以根据需要拷贝多份数据到不同的分支, 比如, 在下图中ksvideosrc是一个window下的camera source 插件,假设我们需要一边在桌边上实时播放捕捉画面,另外又需要将捕捉到的视频编码保存到本地,这时候我们就需要用到tee element,在它的输出端手动创建两个source pad,分别用来预览和编码。 假如过一段时间,你又要添加捕捉帧到静态图像的功能, 那么你可能又要再手动创建第三个source pad了:
    pad的能力, 这里的能力就是可以流经这个pad的数据流格式。
我们知道GStreamer pipeline是由一系列互相连接的element组成的数据流通道, 当数据从一个element流向另外一个element时,数据类型必须是双方都能够识别的,从这个角度来讲,数据类型当然是越简单越好。 但显然element的实现者们希望它们的 element 能够尽可能的处理更多的数据类型。 鉴于多媒体数据类型的庞杂, 因此需要一套管理element之间数据类型协商的机制。由于GStreamer element之间的连接是基于pad的, 因此处理数据的能力就定义在pad上,而不是element上。
GstPadTemplate
一个element的实现, 需要注册一个或多个GstPadTemplate,然后才能够通过这些template创建pad。 下面的代码是从GStreamer插件指南里面截取的一段代码,用来实现GstMyFilter element:
static GstStaticPadTemplate sink_factory =
   GST_STATIC_PAD_TEMPLATE(
      "sink",        //GstPadTemplate的名字
      GST_PAD_SINK,  //可从该template创建的pad 方向属性 (sink, source)
      GST_PAD_AWAYS, //可从该template创建的pad 创建方式属性(always, sometimes, on-request)
      GST_STATIC_CAPS(
         "audio/x-raw, "
         "format = (string) " GST_AUDIO_NE(S16) ", "
         "channels = (int) { 1, 2}, "
         "rate = (int) [ 8000, 96000 ]"  ) //可从该template创建的pad支持数据类型列表
     );
 
//GstPadTemplate for src pad
static GstStaticPadTemplate src_factory =
   GST_STATIC_PAD_TEMPLATE(...);
 
//这里注册GstPadTemplate  
static void gst_my_filter_class_init( GstMyFilterClass* klass)
{
     GstElementClass* element_class = GST_ELEMENT_CLASS(klass);
     ...
    gst_element_class_add_pad_template( element_class, gst_static_pad_template_get(&sink_factory));
    gst_element_class_add_pad_template( element_class, gst_static_pad_template_get(&src_factory));
}
这里可以看出GstMyFilter element注册了两个GstPadTemplate.
每个GstPadTemplate有4项构成,其中我们最关注的当然是最后一项,包含了从该template创建的pad所能够支持的数据类型列表。我们抽取这部分进行分析
 "audio/x-raw, "
 "format = (string) " GST_AUDIO_NE(S16) ", "
 "channels = (int) { 1, 2}, "
 "rate = (int) [ 8000, 96000 ]"
这是列表里面的一项, 当然列表可能包含不仅仅一项,而有可能包含多项, 每项用一个字符串表示,每项之间用”;“隔开。 每项内部属性与属性之间则用”,“分隔。

    "audio/x-raw": 支持未经压缩的raw audio输入
    "format = (string) "GST_AUDIO_NE(S16) ", ": 整形16bit audio数据
    "channels = (int) { 1, 2}, ": 单声道或者立体声道, {}: 列表
    "rate = (int) [ 8000, 96000 ]": 支持范围在8000-96000bps之间的传输率, [ ]: 范围

扫描二维码关注公众号,回复: 4999327 查看本文章

另外创建方式是GST_PAD_AWAYS, 这就说明这两个pad是在GstMyFilter被创建的时候就已经存在并且在GstMyFilter实例的整个生命周期都将存在,因此在GstMyFilter的_init函数中:
    static void gst_my_filter_init( GstMyFilter* filter)
    {
        filter->sinkpad = gst_pad_new_from_static_template(&sink_factory , "sink");
        gst_pad_set_event_function(filter->sinkpad, gst_my_filter_sink_event);
        gst_pad_set_query_function(filter->sinkpad, gst_my_filter_sink_query);
        gst_element_add_pad( GST_ELEMENT(filter), filter->sinkpad);
     
        filter->srcpad = gst_pad_new_from_static_template(&src_factory, "src");
        gst_element_add_pad( GST_ELEMENT(filter), filter->srcpad);
    }
_init函数相当于GstMyFilter的构造函数,对每个实例,从注册的template创建一个sink pad, 一个src pad.

到此为止, 我们通过 gst_element_class_add_pad_template 注册了GstPadTemplate, 再在构造函数里面通过这些template创建pad的实例。但事情还没完, 我们看到我们注册的template,它们可能支持多种媒体格式,对于每个格式某些属性的取值范围也不是固定的,那当我们将两个pad连接起来的时候,到底怎么确定最终的媒体格式和确定属性的最终值呢? 这就涉及到了GStreamer中另外一个重要概念: 能力协商(Caps Negotiation), 非常直观的表述,目的就是要确定最终的格式和取值。

Caps Negotiation

Caps Negotiation定义了一套element之间互相询问,回答与事件处理机制。 这里有两个概念:
    查询 Query
    事件 Event
查询
GStreamer内部的Query可以有很多种, 这里主要涉及到
    GST_QUERY_CAPS
    GST_QUERY_ACCEPT_CAPS
gst_pad_peer_query_caps(srcpad, filter)是srcpad向sinkpad发出的一个GST_QUERY_CAPS查询,旨在获取element2 sinkpad端所支持的所有符合条件的caps集合。 这里符合条件是指能够通过filter 过滤的caps, 但是如果filter = NULL, 则会返回所有支持的caps.
那么element2收到GST_QUERY_CAPS查询是怎么返回caps的呢? 不知道大家有没有注意到上面我们在GstMyFilter _init函数里面有这么一行:
gst_pad_set_query_function(filter->sinkpad, gst_my_filter_sink_query);
这里注册一个query回调函数 gst_my_filter_sink_query, element2可以在这个函数里面处理所有对element2的查询:
static gboolean gst_my_filter_sink_query( GstPad *pad, GstObject *parent, GstQuery *query)
{   
    ...
    GstMyFilter* filter = GST_MY_FILTER(parent);
 
    switch( GST_QUERY_TYPE( query ) )
    {
        case GST_QUERY_CAPS:
         //这里是处理的地方 ...
         //并将查询转发给下游
         GstCaps* filter;
         gst_query_parse_caps( query, &filter);
         gst_pad_peer_query_caps(filter->srcpad, filter);
         break;
        case GST_QUERY_ACCEPT_CAPS:
         //处理GST_QUERY_ACCEPT_CAPS ...
         break;
        default:
         gst_pad_query_default( pad, parent, query );
         break;
    }
    ...
}

处理的时候要注意以下几点:
    element2在创建返回caps列表的时候应该考虑相邻元素,最理想的状态是返回的caps能够被pipeline上所有的element支持。
    假设输入的filter不为NULL,则只能返回与filter匹配的caps
    假设输入的filter包含多个caps, 则 caps 的先后次序反应查询者预期的caps 优先级, 在创建返回列表之时, 应将优先级高的caps放在列表前端。
GST_QUERY_ACCEPT_CAPS
看完了GST_QUERY_CAPS 查询, 再来看看GST_QUERY_ACCEPT_CAPS又是怎么回事呢?
当element2返回caps列表之后, element1 需要遍历列表并选出最优选线(a fixed caps), 并再次向element2发出查询,以下代码来自element1:
    GstCaps* elem1_src_caps = gst_pad_query_caps( srcpad ); //首先获取srcpad自身caps
    GstCaps* elem2_sink_candidates = gst_pad_peer_query_caps(srcpad, elem1_src_caps);
     
    foreach( GstCaps* candidate, elem2_sink_candidates )
    {
        //这里将candidate内部属性取值固定
        GstCaps* fixed_caps = gst_pad_fixate_caps( srcpad, candidate );
     
       //再次发送GST_QUERY_ACCEPT_CAPS
       if( gst_pad_peer_query_accept_caps(srcpad, fixed_caps) )
       {
           ...
       }
     
    }
element2 对于 GST_QUERY_ACCEPT_CAPS查询的处理可以不必转发给下游,而是根据自身情况决定返回值。因为此时上游发过来的fixed caps已经是从element2返回的caps内部筛选出的结果。
事件

这里也同样涉及两个事件:

    GST_EVENT_CAPS
    GST_EVENT_RECONFIGURE
GST_EVENT_CAPS
这是上游element通知下游element,一个fixed caps 已经被选定,各位应该做好相应的准备,等着接受由上游传递的buffer来处理吧 ,buffer的格式则由这个指定的fixed caps限定了。
从element1发送GST_EVENT_CAPS:
       if( gst_pad_peer_query_accept_caps(srcpad, fixed_caps) )
       {
          gst_pad_push_event( srcpad, gst_event_new_caps( fixed_caps ));
       }
element2的处理则放在_init 注册的事件处理函数中:
    gst_pad_set_event_function(filter->sinkpad, gst_my_filter_sink_event);
 
    static gboolean gst_my_filter_sink_event( GstPad *pad, GstObject *parent, GstEvent *event)
    {
        ...
        GstMyFilter* filter = GST_MY_FILTER(parent);
 
        switch( GST_EVENT_TYPE(event))
        {
           case GST_EVENT_CAPS:
             //这里做好处理
             //并将事件按转发给下游
             gst_pad_push_event( filter->srcpad, event );
             break;
           default:
             gst_pad_event_default( pad, parent, event);
             break;
        }
        ...
    }
貌似此时我们的能力协商工作已经完成了, 下图总结了此过程:

猜你喜欢

转载自blog.csdn.net/evsqiezi/article/details/82701614
gs
今日推荐