Gstreamer插件教程3.2—高级概念(Advanced Concepts):Cap协商(Caps negotiation)

cap协商是找到一种两个element都能够处理的媒体格式(即GstCaps)的一种行为。Gstreamer中的这种处理,对于整个pipeline,大多数情况下都可以找到最优的解决方案。在这个章节,我们解释了它是怎样工作的。

Caps negotiation basics

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

  • 一个下游的element在它的sinkpad上建议一种格式,并将此建议格式放至sinkpad CAPS查询的结果中。具体请见下面的Implementing a CAPS query function.
  • 一个上游的element决定一种格式。它在sourcepad上,通过CAPS event发送所选定的媒体格式。下游element在sinkpad上,根据CAPS event重新配置以使其能够处理此媒体格式。
  • 一个下游element能够通过发送RECONFIGURE event通知上游element,它将建议一个新格式。RECONFIGURE event只是简单的命令上游element重新开始协商步骤。因为此element发送出一个RECONFIGURE event是建议另一种格式,所以pipeline的格式可能改变。
除了CAPS event和RECONFIGURE event,以及CAPS查询,还有一个ACCEPT_CAPS查询能够快速地检测是否有某一个caps能够被element接受。

所有的协商都遵循以上这些简单规则。让我们来看一些典型的使用案例及协商是怎样发生的。

Caps negotiation use cases

我们将在下面看一些push调度模式的用例。pull调度模式阶段会在此章节的后面部分讨论,并且它和我所见的很相似。

由于sinkpad只是建议用哪种格式,真正的决定权在sourcepad上,所以最复杂的工作将会在sourcepad上完成。我们可以确定3种sourcepad的cap协商用例

  • 固定的协商(Fixed negotiation)。一个element只能输出一种格式。具体请见下文。
  • 转换协商(Transform negotiation)。在element的输入和输出之间有一个(固定的)转换,通常是基于一些element的属性。element所产生的caps将依赖于上游的caps,而element所能接受的caps依赖于下游的caps。具体见下文关于转换协商的介绍。
  • 动态协商(Dynamic negotiation)。一个element能够输出很多格式。具体见下文关于动态协商的介绍。

Fixed negotiation

在这种情况中,sourcepad只能够产生一个固定格式。通常这种格式被编码到媒体里面。没有下游的element能够要求另一种不同的模式,只有当element决定改变其自身的caps时,source才会重新协商。

能够(在它们的sourcepad上)实现固定caps的elements,通常都 不会重新协商。例子包括:

  • 一个类型探测器(typefinder),因为探测到的类型是真实数据流的一部分,所以其不能够重新协商。类型探测器将查看流字节,分辨出类型,发送一个携带caps的CPAS event,并push指明类型的buffers。
  • 相当多的分流器,因为所包含基本数据流是在文件头中被定义,因此不会重新协商。
  • 一些解码器,格式被嵌入到数据流中,而不是同级cpas的一部分,所以此时的解码器也不会重新协商。
  • 一些能够产生固定模式的源。

gst_pad_use_fixed_caps()函数是作用在sourcepad的固定caps上的。只要pad没有协商,默认的CAPS查询将返回padtemplate所提供的caps。当pad已经协商,CAPS查询将返回协商的caps(或什么都不返回)。这里有一些相关的,sourcepad的固定caps的代码片断。

[..]
  pad = gst_pad_new_from_static_template (..);
  gst_pad_use_fixed_caps (pad);
[..]
接着,固定的caps可通过调用 gst_pad_set_caps()函数,在pad上设置。
[..]
    caps = gst_caps_new_simple ("audio/x-raw",
        "format", G_TYPE_STRING, GST_AUDIO_NE(F32),
        "rate", G_TYPE_INT, <samplerate>,
        "channels", G_TYPE_INT, <num-channels>, NULL);
    if (!gst_pad_set_caps (pad, caps)) {
      GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
          ("Some debug information here"));
      return GST_FLOW_ERROR;
    }
[..]
element的这些类型对于输入模式和输出格式之间并不联系,输入的cpas并不简单的包含产生输出caps所需的信息。

所有其它配置格式的elements应该实现完整的caps协商,这将在下文中阐述。

Transform negotiation

在这个协商技术中,element的输入caps和输出caps之间有一个固定的转换。这个转换可通过element的属性,而不是通过流的内容,被参数化。

element能够接受的caps依赖于下游caps(的固定转换)。element能够产生的caps依赖于上游caps(的固定转换)。

element的这种类型,通常可以在sourcepad上设置。sourcepad是在其接收到CAPS event时,设置从sinkpad上的_event()函数传来的caps。这意味着,caps转换函数是转换一个固定caps为另一个固定caps。此element的例子包括:

  • Videobox。它根据对象属性在视频帧周围添加可配置边框
  • Identity elements。所有element不改变数据的格式,只改变内容。视频和音频就是一个例子。其它例子有检查流的element。
  • 一些解码器和编码器,输出的格式是由输入格式定义的,如mulawdec和mulawenc。这些解码器通常没有定义流内容的头。它们更像是转换elements。
下面是一个协商的例子,演示了一个典型转换element的步骤。在sinkpad上对CAPS event处理中,我们计算sourcepad的caps,并设置它们。

[...]

static gboolean
gst_my_filter_setcaps (GstMyFilter *filter,
               GstCaps *caps)
{
  GstStructure *structure;
  int rate, channels;
  gboolean ret;
  GstCaps *outcaps;

  structure = gst_caps_get_structure (caps, 0);
  ret = gst_structure_get_int (structure, "rate", &rate);
  ret = ret && gst_structure_get_int (structure, "channels", &channels);
  if (!ret)
    return FALSE;

  outcaps = gst_caps_new_simple ("audio/x-raw",
      "format", G_TYPE_STRING, GST_AUDIO_NE(S16),
      "rate", G_TYPE_INT, rate,
      "channels", G_TYPE_INT, channels, NULL);
  ret = gst_pad_set_caps (filter->srcpad, outcaps);
  gst_caps_unref (outcaps);

  return ret;
}

static gboolean
gst_my_filter_sink_event (GstPad    *pad,
                  GstObject *parent,
                  GstEvent  *event)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      ret = gst_my_filter_setcaps (filter, caps);
      break;
    }
    default:
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

  [...]

Dynamic negotiation

最后一个是最复杂且最有强大的动态协商。

和转换协商相同的是,动态协商将在下游/上游caps上进行一个转换。和转换协商不同的是,这种转换是将一个固定caps转换成一个非固定caps。这意味着,sinkpad输入的caps能够被转换成非固定的(多种的)格式。sourcepad将必须从这多种可能的格式中选择一种。它通常就像选择一个格式,能够产生的前提下的最少要求,但也不是必须的。格式的选择也应该依赖下游能够接受的caps。

一个典型的流类似于:

  • element的sinkpad上接收到的caps。
  • 如果element更倾向于passthrough模式,则通过ACCEPT_CAPS查询检查下游是否接受caps。如果接受,我们可以进行协商并以passthrough模式操作。
  • 计算sourcepad可能的的caps。
  • 查询下游对等pad中可能的caps的列表
  • 从下游列表中选择第一个你能转换成的caps,并设置此caps为输出caps。对于一些合理的默认值,你可能需要固定下caps以构成固定的caps。

这类型的element的例子包括:

  • converter elements,如videoconvert, audioresample, videoscale, ...
  • source elemnts, 如audiotestsrc, videotestsrc, v4l2src, pulsesrc, ...
让我们来看一个能够转变采样率的element的例子,即输入和输出的采样率不需要是一样的:

static gboolean
gst_my_filter_setcaps (GstMyFilter *filter,
               GstCaps *caps)
{
  if (gst_pad_set_caps (filter->srcpad, caps)) {
    filter->passthrough = TRUE;
  } else {
    GstCaps *othercaps, *newcaps;
    GstStructure *s = gst_caps_get_structure (caps, 0), *others;

    /* no passthrough, setup internal conversion */
    gst_structure_get_int (s, "channels", &filter->channels);
    othercaps = gst_pad_get_allowed_caps (filter->srcpad);
    others = gst_caps_get_structure (othercaps, 0);
    gst_structure_set (others,
      "channels", G_TYPE_INT, filter->channels, NULL);

    /* now, the samplerate value can optionally have multiple values, so
     * we "fixate" it, which means that one fixed value is chosen */
    newcaps = gst_caps_copy_nth (othercaps, 0);
    gst_caps_unref (othercaps);
    gst_pad_fixate_caps (filter->srcpad, newcaps);
    if (!gst_pad_set_caps (filter->srcpad, newcaps))
      return FALSE;

    /* we are now set up, configure internally */
    filter->passthrough = FALSE;
    gst_structure_get_int (s, "rate", &filter->from_samplerate);
    others = gst_caps_get_structure (newcaps, 0);
    gst_structure_get_int (others, "rate", &filter->to_samplerate);
  }

  return TRUE;
}

static gboolean
gst_my_filter_sink_event (GstPad    *pad,
                  GstObject *parent,
                  GstEvent  *event)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);

  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;

      gst_event_parse_caps (event, &caps);
      ret = gst_my_filter_setcaps (filter, caps);
      break;
    }
    default:
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

static GstFlowReturn
gst_my_filter_chain (GstPad    *pad,
             GstObject *parent,
             GstBuffer *buf)
{
  GstMyFilter *filter = GST_MY_FILTER (parent);
  GstBuffer *out;

  /* push on if in passthrough mode */
  if (filter->passthrough)
    return gst_pad_push (filter->srcpad, buf);

  /* convert, push */
  out = gst_my_filter_convert (filter, buf);
  gst_buffer_unref (buf);

  return gst_pad_push (filter->srcpad, out);
}
Upstream caps (re)negotiation
上游协商主要是用于重新协商一个已经协商过的pipeline成另一种新格式。一些实际例子包括,由于视频窗口的改变及输出自身不能够重新调节比例,所以需选择一个不同的视频尺寸,或者由于音频通道的配置改变了。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值