ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Gstreamer- 协商(Negotiation)

2021-07-03 12:02:50  阅读:493  来源: 互联网

标签:Negotiation 协商 Gstreamer element pad sink caps CAPS


协商

Capabilities 协商是为 GStreamer pipeline内的数据流决定适当格式的过程。理想情况下,协商(也称为“capsnego”)将信息从pipeline中具有信息的那些部分传输到pipeline的那些易扩展的部分,受pipeline中不易扩展部分的约束。

基本规则

必须遵循这些简单的规则:

  1. 下游建议格式
  2. 上游决定格式

caps协商中使用了 4 种 查询/事件(queries/events ):

  1. GST_QUERY_CAPS:获取可能的格式
  2. GST_QUERY_ACCEPT_CAPS:检查格式是否可行
  3. GST_EVENT_CAPS:配置格式(下游)
  4. GST_EVENT_RECONFIGURE:通知上游可能的新caps

查询

pad可以向peer pad询问其支持的 GstCaps。它通过 CAPS 查询完成此操作。支持的caps列表可用于为数据传输选择合适的 GstCap。 CAPS 查询以递归方式工作,element在构建可能的caps时应考虑其 peer element。由于结果caps可能非常大,因此可以使用过滤器来限制caps。只有与过滤器匹配的caps才会作为结果caps返回。过滤器caps的顺序给出了调用者的优先顺序,并且应该考虑到返回的caps。

  • filter (in) GST_TYPE_CAPS (default NULL): - 一个用于过滤结果的 GstCaps
  • caps (out) GST_TYPE_CAPS (default NULL): - 结果caps

pad可以询问peer pad 是否支持给定的caps。它通过 ACCEPT_CAPS 查询完成此操作。caps必须固定。 ACCEPT_CAPS 查询不需要递归工作,如果带有这些caps的后续 CAPS 事件返回成功,它可以简单地返回 TRUE。

  • caps (in) GST_TYPE_CAPS: - 要检查的 GstCaps,必须固定
  • result (输出)G_TYPE_BOOLEAN(默认为 FALSE): - 如果接受caps则为 TRUE

事件

当协商媒体格式时,通过 CAPS 事件将 GstCaps 通知给peer element。caps必须固定。

  • caps GST_TYPE_CAPS: - 协商的 GstCaps,必须是固定的

操作

GStreamer 的两种调度模式,push 模式和 pull 模式,适用于不同的机制来实现这个目标。由于更常见,我们首先描述推模式协商。

推模式协商

当element想要推送缓冲区并需要决定格式时,会发生推模式协商。这称为下游协商,因为上游element决定下游element的格式。这是最常见的情况。

当下游element想要从上游element接收另一种数据格式时,也会发生协商。这称为上游协商。

协商的基本原则如下:

  • GstCaps(参见 caps)在它们作为事件推送之前被引用以描述随后的缓冲区的内容。

  • 在处理随后的缓冲区之前,element应将自身重新配置为作为 CAPS 事件接收的新格式。如果 caps 事件中的数据类型不可接受,则element应拒绝该事件。该element还应通过从链函数返回适当的 GST_FLOW_NOT_NEGOTIATED 返回值来拒绝下一个缓冲区。

  • 下游element可以通过向上游发送 RECONFIGURE 事件来请求流的格式更改。上游element在收到 RECONFIGURE 事件时将重新协商新格式。

source pad开始协商的一般流程。

  src              sink
             |                 |
             |  querycaps?     |
             |---------------->|
             |     caps        |
select caps  |< - - - - - - - -|
from the     |                 |
candidates   |                 |
             |                 |-.
             |  accepts?       | |
 type A      |---------------->| | optional
             |      yes        | |
             |< - - - - - - - -| |
             |                 |-'
             |  send_event()   |
send CAPS    |---------------->| Receive type A, reconfigure to
event A      |                 | process type A.
             |                 |
             |  push           |
push buffer  |---------------->| Process buffer of type A
             |                 |

一种可能的实现方式的伪代码:

[element wants to create a buffer]
    if not format
      # see what we can do
      ourcaps = gst_pad_query_caps (srcpad)
      # see what the peer can do filtered against our caps
      candidates = gst_pad_peer_query_caps (srcpad, ourcaps)

    foreach candidate in candidates
      # make sure the caps is fixed
      fixedcaps = gst_pad_fixate_caps (srcpad, candidate)

    # see if the peer accepts it
    if gst_pad_peer_accept_caps (srcpad, fixedcaps)
      # store the caps as the negotiated caps, this will
      # call the setcaps function on the pad
      gst_pad_push_event (srcpad, gst_event_new_caps (fixedcaps))
      break
    endif
  done
endif

使用ALLOCATION 查询 协商 allocator/bufferpool

   buffer = gst_buffer_new_allocate (NULL, size, 0);
    # fill buffer and push

sink pad 开始重新协商的通用流程.

          src              sink
             |                 |
             |  accepts?       |
             |<----------------| type B
             |      yes        |
             |- - - - - - - - >|-.
             |                 | | suggest B caps next
             |                 |<'
             |                 |
             |   push_event()  |
 mark      .-|<----------------| send RECONFIGURE event
renegotiate| |                 |
           '>|                 |
             |  querycaps()    |
renegotiate  |---------------->|
             |  suggest B      |
             |< - - - - - - - -|
             |                 |
             |  send_event()   |
send CAPS    |---------------->| Receive type B, reconfigure to
event B      |                 | process type B.
             |                 |
             |  push           |
push buffer  |---------------->| Process buffer of type B
             |                 |

用例:

videotestsrc  ! xvimagesink
  • 谁决定使用什么格式?

    • 按照惯例, 总是由 src pad 决定。 sinkpad 可以通过将其放在 caps 查询结果 GstCaps 的高位来建议格式。
    • 由于由src 决定,它总是可以选择它可以做到的事情,所以只有当 sinkpad 声明它可以接受某些东西而稍后它又不能时,这一步才会失败。
  • 什么时候进行协商?

    • 在 srcpad 执行推送之前,它会计算出 1) 中所述的类型,然后推送具有该类型的 caps 事件。sink 检查媒体类型并为此类型配置自己。
    • 然后source通常会执行 ALLOCATION 查询以与sink协商缓冲池。然后它从池中分配一个缓冲区并将其推送到sink。由于sink接受了caps,因此它可以为对应的格式创建一个池。
    • 由于 1) 中所述的sink可以接受该类型,因此它将能够处理它。
  • sink如何请求另一种格式?

    • sink 询问source是否可以使用新格式。
    • sink 向上游推送 RECONFIGURE 事件
    • src 接收 RECONFIGURE 事件并标记重新协商
    • 在下一次缓冲区推送时,source重新协商caps和缓冲池。接收器会将新的首选格式放在它从其 caps 查询返回的 caps 列表中的高位。
videotestsrc ! queue ! xvimagesink
  • queue 代理所有的accept和caps 查询到另一个peer pad。
  • queue 代理缓冲池
  • queue 代理 RECONFIGURE 事件
  • queue 将 CAPS 事件存储在queue中。这意味着queue可以包含不同类型的缓冲区。

拉模式协商

拉模式下的pipeline与推模式下激活的pipeline具有不同的协商需求。推模式针对两个用例进行了优化:

  • 媒体文件的播放,其中分流器和解码器是应该将格式信息应传播到pipeline其余部分的点;和

  • 从实时源录制,其中用户习惯于在source element之后直接放置一个 capsfilter;因此,caps信息流从用户开始,通过source的潜在caps,到达pipeline的sink。

相比之下,拉模式还有其他典型用例:

  • 从有损源(例如 RTP)播放,在这种源中,更多地了解pipeline的延迟可以提高质量;或者

  • 音频合成,其中音频 API 被调整为仅产生必要数量的采样,通常由硬件中断驱动以填充 DMA 缓冲区或 Jack[0] 端口缓冲区。

  • 低延迟效果处理,当数据从环形缓冲区传输到sink应该应用过滤器,而不是传输之前。例如, wavsrc ! volume ! alsasink 不应在推模式 中使用内部 alsasink ringbuffer 线程,而是通过 wavsrc ! audioringbuffer ! volume ! alsasink 将volume 放在声卡写入器线程中 。

[0] http://jackit.sf.net

拉模式的问题是sink必须知道格式才能知道通过 gst_pad_pull_range() 拉多少字节。这意味着在拉取之前,sink必须发起协商以决定格式。

回顾 capsnego 的原则,即信息必须从拥有它的部分流向没有它的部分,我们看到三个具名的用例具有不同的协商要求:

  • RTP 和低延迟播放都类似于正常播放情况,其中信息流向下游。

  • 在音频合成中,拥有最多信息的pipeline部分是sink,受提供给它的能力图的限制(constrained by the capabilities of the graph that feeds it)。然而,没有完全指定caps;在某些时候,用户必须至少进行干预以选择采样率。这可以在 gstreamer 外部完成,就像在 jack element中一样,也可以通过 capsfilter 在内部完成,就像实时源一样。

鉴于sink可能需要source的输入,就像在RTP的情况下一样,至少在合成的情况下作为过滤器,在拉线程被激活之前必须有一个协商阶段。此外,考虑到拉模式提供的低延迟,我们希望避免从拉线程内进行 capsnego,以防它导致我们错过我们的调度的最后期限。

拉线程一般是在PAUSED→PLAYING状态变化下开始的。我们必须能够在这种状态改变发生之前完成协商。

那么,执行 capsnego 的时间是在 SCHEDULING 查询成功之后,但在 sink 产生拉线程之前。

机制

sink通过执行 SCHEDULING 查询来确定上游element支持基于拉的调度。

sink通过将gst_pad_query_caps()从sink pad和 peer src pad获得的结果进行相交来启动协商过程。这个操作是由gst_pad_get_allowed_caps()执行。在简单的传递情况下,peer pad的caps查询应该返回在其所有sink pad上调用get_allowed_caps()的交集。通过这种方式,sink element知道整个pipeline的能力。

如有必要,sink element然后将结果caps固定,从而产生流的caps。从现在开始,sinkpad 的 caps 查询将只返回这些固定的 caps,这意味着上游element只能生成这种格式的数据。

如果sink element 无法在其sink pad上设置caps,则它应该在总线上发布一条错误消息,指示无法进行协商。

当协商成功时,sinkpad 和所有上游内部链接的pad 在pull 模式下被激活。通常,此操作将触发下游element的协商,现在将被迫协商到sink pad的最终固定所需caps。

在这些步骤之后,sink element从状态改变函数返回 ASYNC。当sink中接收到第一个缓冲区时,状态将提交到 PAUSED。这需要为期望从sink返回ASYNC值的应用程序提供一致的API ,但它也允许我们在拉动线程的上下文之外执行其余的协商。

模式

我们可以在协商中确定 3 种模式:

  • Fixed :无法选择输出格式

    • caps在流中已编码
    • 视频/音频解码器
    • 通常使用 gst_pad_use_fixed_caps()
  • Transform

    • 未修改的caps(直通(passthrough))
    • 可以根据element属性做caps变换
    • 固定的caps转变为固定的caps
    • videobox
  • Dynamic :可以选择输出格式

    • 一个转换器element
    • 取决于下游caps,需要做一个caps查询来找到转换。
    • 通常更喜欢使用恒等变换
    • 固定的caps可以转换为非固定的caps。

标签:Negotiation,协商,Gstreamer,element,pad,sink,caps,CAPS
来源: https://blog.csdn.net/zk5950886/article/details/118421374

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有