媒体能力协商

Posted on Sun 10 December 2023 in Journal

Abstract GStreamer 媒体能力协商
Authors Walter Fan
 Category    learning note  
Status v1.0
Updated 2023-12-10
License CC-BY-NC-ND 4.0

概述

在 GStreamer 中, 媒体能力协商 Capabilities negotiation (缩写为 Caps negotiation) 是一个决策的过程,用来决定在 GStreamer 管道中数据流采用何种合适的媒体格式。

GStreamer 的文档 Negotiation 中对这一块有详细的解释, 在此我做些翻译和记录

有些元件有相对固定的媒体能力,有些元件则比较灵活,有些则不那么灵活。理想情况下,媒体能力协商(Capabilities negotiation) 信息将从管道中具有媒体能力的那些元件,传送到管道中相对灵活的那部分元件中,并受到管道中并不灵活的那部分元件的约束。

基本规则

以下的基本规则需要遵守

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

在 caps negotiation 中有四种 queries/events

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

查询 Queries

一个 pad 可以询问其所连接的 pad 它是不是支持某种格式 GstCaps, 这就是 CAPS query。 支持的 caps 列表可用于为数据传输选择合适的 GstCaps

CAPS 查询以递归方式工作,元件在构建可能的 caps 时应考虑其对等元件。 由于结果 caps 可能非常大,因此可以使用过滤器 capsfilter 来限制 caps。 只有与过滤器匹配的 caps 才会作为结果 caps 返回。 过滤器caps 的顺序的按照调用者的优先顺序给出,并考虑下游元件所返回的 caps。

  • filter(in)GST_TYPE_CAPS(默认为 NULL):- 用于过滤结果的GstCaps
  • caps (out) GST_TYPE_CAPS(默认 NULL): - 查询到的 Caps 结果

pad 可以询问对等 pad 是否支持给定的 caps 。 它通过 ACCEPT_CAPS 查询来执行此操作, 这个返回的Caps 是固定。 “ACCEPT_CAPS”查询不需要递归地工作,如果具有这些 caps 的后续 CAPS 事件返回成功,它可以简单地返回 TRUE。

  • caps(in)GST_TYPE_CAPS:- 要检查的GstCaps,它是固定的值
  • result (out)G_TYPE_BOOLEAN(默认 FALSE): - 如果 Caps 被接受 则为 TRUE

事件 Events

当一个媒体格式协商成功,对端的元件就会以 CAPS 事件发送通知, 此 Caps 是固定的

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

操作 Operation

GStreamer 的两种调度模式(推模式和拉模式)适用于不同的机制来实现此目标。 由于推模式更常见,我们先描述推模式协商 Push-mode negotiation。

Push-mode negotiation

当一个元件想要推送缓冲区并且需要决定媒体格式时,就会发生推送模式协商。 这称为下游协商 (downstream negotiation),因为上游元件决定下游元件的格式。 这是最常见的情况。

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

协商的基本过程如下:

  • GstCaps (see caps) are refcounted before they are pushed as an event to describe the contents of the following buffer. GstCaps 用来描述媒体数据的内容 ,它会作为一个事件重新进行引用计数,然后发送给相连的元件

  • 一个元件在处理数据缓冲之前收到 CAPS 事件,它可以重新更改 (reconfigure) 自己的配置为新的媒体格式。如果这个 caps 事件的数据类型不可接受,此元件可以拒绝这个 caps 事件,它同时也会拒绝接下来的数据缓冲,方法是在 chain 函数中返回 GST_FLOW_NOT_NEGOTIATED

  • 下游的元件可通过发送 GST_FLOW_NOT_NEGOTIATED 事件给上游的元件要求媒体流的格式更改

一个 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

一个 sink pad 开始重新协商的一般流程如下,还是讨价还价, 只不过这回发起的是买方(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
             |                 |

参考资料


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。