文章

gstreamer插件编写指南:预制基类

到目前为止,我们已经了解了创建任何类型 GStreamer 元素的底层概念。现在,让我们假设你只想创建一个与“esdsink”工作原理完全相同的简单音频链路,或者一个简单地将音频音量正常化的过滤器。这类元素在概念上非常通用,而且由于它们没有什么特殊功能,因此无需提供自己的调度器激活函数和进行复杂的能力集协商,它们更容易编写代码。为此,GStreamer 提供了简化某些类型元素的基类。本章将讨论这些基类。

编写汇

汇是 GStreamer 中的特殊元素。这是因为汇元素必须进行预滚动(preroll),即在进入GST_STATE_PAUSED状态后,为进入状态的元素准备好缓冲区。这样做的结果是,这些元素在进入GST_STATE_PLAYING状态后可以立即开始处理数据,而不需要花时间初始化输出或设置解码器;所有这些都会在成功完成状态转换到GST_STATE_PAUSED之前完成。

然而,预滚动是一个复杂的过程,需要在许多元素中使用相同的代码。因此,汇元素可以派生自GstBaseSink基类,它可以自动完成预滚动和一些其他实用功能。派生类只需实现一些虚函数即可自动运行。

基类实现了汇必须执行的大部分同步逻辑。

不过,GstBaseSink基类对元素规定了一些限制:

  • 它要求汇只有一个衬底。如果汇元素需要多个衬底,则必须制作一个管理器元素,并在其中包含多个 GstBaseSink 元素。

汇元素可以使用通常的GObjectG_DEFINE_TYPE () ,方便从GstBaseSink派生而来:

1
2
3
4
5
6
7
8
9
10
11
G_DEFINE_TYPE (GstMySink, gst_my_sink, GST_TYPE_BASE_SINK);

[..]

static void
gst_my_sink_class_init (GstMySinkClass * klass)
{
  klass->set_caps = [..];
  klass->render = [..];
[..]
}

GstBaseSink派生的优点有很多:

  • 派生实现几乎不需要意识到预滚动,也不需要了解预滚动的技术实现要求。基类会完成所有艰巨的工作。

  • 减少派生类中的代码编写,共享代码(从而共享错误修正)。

音频和视频也有专门的基类,让我们来了解一下。

编写音频汇

从本质上讲,音频汇的实现只是一般汇的一个特例。音频汇的复杂性还在于它需要安排采样回放。它必须将流水线中选择的时钟与音频设备的时钟相匹配,并计算和补偿漂移和抖动。

根据需要,您可以选择从两个音频基类派生:GstAudioBasesinkGstAudioSink。audiobasesink 通过使用由派生类控制和提供的环形缓冲器,可完全控制同步和调度的处理方式。audiosink 基类是 audiobasesink 的派生类,它实现了一个标准的环形缓冲区,实现了默认同步,并提供了一个标准的音频采样时钟。 该基类的派生类只需提供_open()_close()_write()函数实现,以及一些可选函数。要求更高的音频系统(如 Jack)需要实现GstAudioBaseSink基类。

GstAudioBaseSink几乎没有任何限制,几乎适用于所有系统,但很难实现。另一方面,GstAudioSink 只适用于具有简单_open()_close()_write()API 的系统(实际上是指几乎所有系统),但它的优点是更容易实现。第二种基类的优点很多:

  • 自动同步,无需在派生类中编写任何代码。

  • 还可自动提供时钟,以便同步其他汇(如音频/视频播放)。

  • 只需更改基类,就能为所有音频汇衬底添加功能,从而使维护工作变得简单。

  • 派生类只需要三个小函数,外加一些GObject模板代码。

除了实现音频基类虚函数外,派生类还可以(应该)实现GstBaseSinkset_caps ()get_caps ()虚函数,以进行协商。

编写视频汇

编写视频汇可以使用GstVideoSink基类,该基类内部派生自GstBaseSink。目前,它除了增加另一个编译依赖性外,什么也没做,因此派生类需要实现所有GstVideoSink的虚拟函数。如果能正确实现这些功能,将对最终用户使用视频汇的体验产生积极影响:

  • 由于使用了preroll(和preroll ()虚拟函数),因此在进入GST_STATE_PAUSED状态时已经可以显示视频帧。

  • 通过为GstVideoSink 添加新功能,就可以为视频汇添加影响所有视频汇的扩展功能,但只需编码一次,这对维护工作大有裨益。

编写源

在上一部分,特别是“提供随机存取”部分,我们了解到某些类型的元素可以提供随机存取。这无疑适用于可随机搜索位置并读取的源元素,如文件源。不过,其他源元素可能更适合描述为实时源元素,如摄像头源、音频卡源等;这些元素不可寻址,也不提供精确的字节访问。针对所有这些使用情况,GStreamer 提供了两个基类:GstBaseSrc用于基本源功能,而GstPushSrc 则是非字节精确源基类。pushsource 基类本身也派生自 basesource,因此关于 basesource 的所有声明也适用于 pushsource。

basesrc 类会自动为派生类做几件事,因此派生类不必再为此操心:

  • GstBaseSrc的修正会自动应用于所有派生类。

  • 自动处理衬底激活,并在我们被指派启动任务时进行任务包装。

不过,GstBaseSrc可能并不适合所有情况,它有其局限性:

  • 源衬底只有一个。需要多个源衬底的源元素必须实现管理器 bin 并在内部使用多个源元素,或者制作一个管理器元素,在内部使用一个源元素件和一个解复用器。

在从create() 虚拟函数返回的缓冲区中,可以使用特殊内存(如 X 服务器内存指针或mmap () 编辑的内存区域)作为数据指针。

编写音频源

音源只是推源的一种特例,音源可以是任何读取音频的东西,例如从音效服务器、内核接口(如 ALSA)或测试声音/信号发生器读取的音源。GStreamer 提供了两个基类,类似于 “编写音频汇”中描述的两个音频汇;其中一个是基于 环形缓冲区的GstAudioBaseSrc,需要派生类自行处理调度、同步等问题。另一个是基于GstAudioBaseSrc名为GstAudioSrc的汇,提供了一个简单的open (), close ()read ()接口,实现起来相当简单,足以满足大多数音源和音频接口(如 ALSA 或 OSS)的需要。

除了基于GstPushSrc基类的优点外,GstAudioSrc基类对派生类也有一些好处:

  • 同步并提供时钟。

  • 新功能可以添加到其中,并自动应用于所有派生类。

编写转换元素

GStreamer 提供的第三个基类是GstBaseTransform(GstBaseTransform),它是一个基类,用于具有一个 sourcepad 和一个 sinkpad 的元素,这些元素充当某种滤波器,如音量改变、音频重采样、音频格式转换等等。为了使缓冲区分配转发、穿透、就地处理等功能正常工作,此类元素需要进行大量的簿记工作。该基类会为您完成所有这些工作,因此您只需进行实际处理即可。

由于GstBaseTransform基于 1 对 1 的滤波器模型,它可能无法很好地应用于解码器等元素,因为解码器可能需要解析流中的属性。此外,它也不适用于需要一个以上 sourcepad 或 sinkpad 的元素。

本文由作者按照 CC BY 4.0 进行授权