1、Pad是什么

  • 首先pad是GStreamer Element必不可少的组成部分,你可以把它看作是element和外界(其它element)交换数据的端口。数据从element的一端流入,另一端流出(一般情形,但是,source element只有出没有进, sink只有进没有出),那么一个element就该有两个pad分别位于element的两端。从pad的角度来讲,根据数据的流入和流出方向,就该有source pad和sink pad 的区分了:

pad 属性之一: direction(方向)
数据流入端叫sink pad, 数据流出端叫source 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创建的方式可以分为:
静态pad(always pad )
动态pad (sometimes pad)
手动pad (on-request pad)


2、Capabilities of a pad

pad的能力, 这里的能力就是可以流经这个pad的数据流格式。
我们知道GStreamer pipeline是由一系列互相连接的element组成的数据流通道, 当数据从一个element流向另外一个element时,数据类型必须是双方都能够识别的,从这个角度来讲,数据类型当然是越简单越好。 但显然element的实现者们希望它们的 element 能够尽可能的处理更多的数据类型。 鉴于多媒体数据类型的庞杂, 因此需要一套管理element之间数据类型协商的机制。由于GStreamer element之间的连接是基于pad的, 因此处理数据的能力就定义在pad上,而不是element上。

GstPadTemplate

一个element的实现, 需要注册一个或多个GstPadTemplate,然后才能够通过这些template创建pad。 下面的代码是从GStreamer插件指南里面截取的一段代码,用来实现GstMyFilter element:

//GstPadTemplate for sink pad
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之间的传输率, [ ]: 范围

另外创建方式是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), 非常直观的表述,目的就是要确定最终的格式和取值。


3、Caps Negotiation

以上我们通过_init构造函数创建了GstMyFilter的两个pad,当然这只是创建pad的一种方式。给element 创建新的pad不一定要在element实现内部完成,在外面也同样可以为一个已经存在的element实例创建pad, 这里暂不关心pad 是怎么被创建出来的, 我们只要知道我们GStreamer Pipeline中的所有elements都链接好了,接下来该是Caps Negotiation粉墨登场了。

Caps协商是在它们可以处理的element之间寻找媒体格式(GstCaps)的行为。GStreamer中的这个过程在大多数情况下可以为整个管道找到一个最佳解决方案。在本节中,我们将解释其工作原理。

在GStreamer中,媒体格式的协商总是遵循以下简单规则:

  • downstream的element可以通过其sinkpad给出建议的格式, 在其sinkpad收到CAPS query后,将其建议的格式作为结果返回。参见CAPS查询函数的实现。

  • 一个upstream element可以决定某种格式,该element可以将其选定的格式以CAPS event的方式通过其source pad发送向downstream。Downstream的element在其sinkpad收到CAPS event所发送的选定格式后,对自己做重新配置。

  • 一个downstream的element可以通过发送RECONFIGURE event给upstream,通过这种方式提出自己的建议格式。RECONFIGURE event命令upstream element重启negotiation。因为发送RECONFIGURE事件的元素现在建议使用另一种格式,所以管道中的格式可能会改变。

Caps Negotiation定义了一套element之间互相询问,回答与事件处理机制。 这里有两个概念:

  • 查询 Query
  • 事件 Event

3.1 查询

GStreamer内部的Query可以有很多种, 这里主要涉及到

  • GST_QUERY_CAPS
  • GST_QUERY_ACCEPT_CAPS

GST_QUERY_CAPS

GST_QUERY_CAPS 用来查询相邻pad所支持的caps:

GstCaps * gst_pad_peer_query_caps (GstPad *pad,GstCaps *filter);

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;}...
}

处理的时候要注意以下几点:

  1. element2在创建返回caps列表的时候应该考虑相邻元素,最理想的状态是返回的caps能够被pipeline上所有的element支持。
  2. 假设输入的filter不为NULL,则只能返回与filter匹配的caps
  3. 假设输入的filter包含多个caps, 则 caps 的先后次序反应查询者预期的caps 优先级, 在创建返回列表之时, 应将优先级高的caps放在列表前端。

GST_QUERY_ACCEPT_CAPS

看完了GST_QUERY_CAPS 查询, 再来看看GST_QUERY_ACCEPT_CAPS又是怎么回事呢?ACCEPT_CAPS查询,用于快速检查某个element是否可以接受特定的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_CAPSif( gst_pad_peer_query_accept_caps(srcpad, fixed_caps) ){...}}

element2 对于 GST_QUERY_ACCEPT_CAPS查询的处理可以不必转发给下游,而是根据自身情况决定返回值。因为此时上游发过来的fixed caps已经是从element2返回的caps内部筛选出的结果。

3.2 事件

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

  • 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;}...}

貌似此时我们的能力协商工作已经完成了, 下图总结了此过程:

GST_EVENT_RECONFIGURE

在以上的能力协商中,发起查询,发送事件的都是上游element, 下游element则一直回复和处理这些请求或事件。 最终fixed caps 也是由上游确定的,那么下游element 有没有办法发起一次能力协商或者有自己的意志去选择合适的数据类型呢? 这就是GST_EVENT_RECONFIGURE事件的作用, 它是由下游发送给上游。

一个GST_EVENT_RECONFIGURE事件的发起首先由下游确定一个fixed caps, 然后通过GST_QUERY_ACCEPT_CAPS查询上游是否支持,如果支持的话再发送GST_EVENT_RECONFIGURE事件,上游的处理这是发起一次新的能力协商,在上游查询GstCaps列表时,这次下游这只返回选定的fixed caps.

Gstreamer学习笔记(5):GStreamer Pad and Capabilities Negotiation相关推荐

  1. gstreamer学习笔记---编码videoencoder

      既上一节的<gstreamer学习笔记-v4l2src>之后,我们这一次,学习gstreamer的编码流程.稍微了解gstreamer的小伙伴都知道,gstreamer具备强大的音视频 ...

  2. gstreamer学习笔记---v4l2src

      v4l2src element源码位于gst-plugins-good-xxx/sys/v4l2/gstv4l2src.c,v4l2src主要是从v4l2设备获取视频数据的element,基于v4 ...

  3. 【GStreamer学习】之GStreamer基础教程

    目标 没有什么比在屏幕上打印出"Hello World"更能获得对软件库的第一印象了! 但是由于我们正在学习多媒体框架,所以我们将输出"Hello World!" ...

  4. Gstreamer学习笔记(4):pad定义、连接、流动

    pad相当于element的接口,各个element就是通过pad连接进行传输数据,同时pad会通过caps限制特定的数据类型通过,只有当两个pad的caps数据类型一致时才可以建立连接.那么pad在 ...

  5. gstreamer学习笔记---pad定义、连接、流动

      pad相当于element的接口,各个element就是通过pad连接进行传输数据,同时pad会通过caps限制特定的数据类型通过,只有当两个pad的caps数据类型一致时才可以建立连接.那么pa ...

  6. gstreamer学习笔记:将音视频合成MPEG2-TS流并打包通过rtp传输

    一.通过rtpbin插件发送 (1)发送端 gst-launch -v gstrtpbin name=rtpbin latency=100 mpegtsmux name="mux" ...

  7. GStreamer学习笔记

    目录 前言 一.GStreamer编程接口介绍 二.gst-launch-1.0工具使用 总结 前言 GStreamer 是用来构建流媒体应用的开源多媒体框架(framework),其目标是要简化音/ ...

  8. gstreamer学习笔记---demux使用

      在使用gstreamer的过程中,相信很多是都通过gst-launch-1.0直接使用playbin或者自建pipeline完成相应的播放测试,反而很少的会自己编写代码,其实,gst-launch ...

  9. gstreamer应用笔记

    gstreamer官网 https://gstreamer.freedesktop.org/ 应用手册 https://gstreamer.freedesktop.org/documentation/ ...

  10. gstreamer-1.0学习笔记

    gstreamer-1.0安装 apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-pl ...

最新文章

  1. [笔记].关于在Quartus II 11.0无法正常使用SignalTap的解决方法
  2. C9---include,编译
  3. 从pfx里面 解析出公私钥
  4. 24点——判断4个数能否经过运算使得结果为24
  5. linux播放视频的最简单方法
  6. 可以无限增加iPhone 的图标吗?
  7. python生成多个列表_python生成多个只含0,1元素的随机数组或列表(代码)
  8. Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate
  9. python完成‘21点游戏’
  10. 使用jquery.cookie操作Cookie实例
  11. 微软推出 Power Platform 漏洞奖励计划
  12. 张季跃 201771010139《面向对象程序设计(java)》第十三周学习总结
  13. 教育学外文文献是哪里找的?
  14. PdShell16扫描内容不符合数据库规范整合
  15. 吐槽 intent:#Intent;S.K_1171477665=;end
  16. 嵌入式Linux应用学习(一)------QT控制LED设备硬件
  17. Chaos Emulator v0.2.3 自建引力模拟器分享
  18. 在win8.1系统下运行VC++6.0
  19. 定向光流直方图是什么_OpenCV计算机视觉编程攻略(第3版)
  20. 专为mac用户设计的苹果手机助手。爱思助手 Mac版结构更清晰,功能更好用,界面更清晰,让你能够轻松查看设备信息,下载最新最火的应用游戏。

热门文章

  1. html中好看的英文字体,一组漂亮的英文字体在线演示
  2. R语言聚类分析可视化
  3. linux kylin 终端字体,安装Nerd Fonts字体
  4. MeteoInfoLab脚本示例:数据投影-FLEXPART
  5. 数据仓库--事实表和维度表
  6. matlab排序函数 下标,Matlab自带排序函数sort用法
  7. blender基本翻译+快捷键
  8. Android 应用是如何启动 binder 机制的?
  9. 上海域格ASR平台4g模块低功耗应用指导
  10. 网络拓扑图:网络拓扑图介绍及在线制作