文章

gstreamer插件编写指南:简介

导言

GStreamer 是一个功能强大、用途广泛的框架,可用于创建流媒体应用程序。GStreamer 框架的许多优点都来自于它的模块化:GStreamer 可以无缝集成新的插件模块。但是,由于模块化和强大的功能往往是以更高的复杂性为代价的(例如CORBA),因此编写新的插件并非易事。

本指南旨在帮助你了解 GStreamer 框架,以便开发新插件来扩展现有功能。本指南通过开发一个用 C 语言编写的示例插件(音频过滤器插件)来解决大部分问题。不过,本指南的后半部分也介绍了编写其他类型插件所涉及的一些问题,指南的最后还介绍了 GStreamer 的一些 Python 绑定。

本篇分为序言和基础两个章节进行介绍。

序言

什么是 GStreamer?

GStreamer 是一个用于创建流媒体应用程序的框架。其基本设计来自俄勒冈研究生院的视频流水线处理,以及 DirectShow 的一些理念。

GStreamer 开发框架可以编写任何类型的流媒体应用程序。GStreamer 框架的设计目的是让用户能轻松编写处理音频、视频或两者的应用程序,它并不局限于音频和视频,还能处理任何类型的数据流。流水线设计的目的是在应用过滤器的基础上减少开销。这使得 GStreamer 设计为对延迟或性能有较高要求的高端音频应用程序的良好框架。

GStreamer 最明显的用途之一就是用来制作媒体播放器。GStreamer 已包含用于构建媒体播放器的组件,可支持多种格式,包括 MP3、Ogg/Vorbis、MPEG-1/2、AVI、Quicktime、mod 等。然而,GStreamer 不仅仅是一款媒体播放器。它的主要优势在于,可插拔组件可以混合搭配成任意的流水线,这样就可编写出完整的视频或音频编辑应用程序。

该框架以提供各种编解码器和其他功能的插件为基础。这些插件可以链接起来,并排列在一个流水线中。该流水线定义了数据流。

GStreamer 的核心功能是为插件、数据流、同步和媒体类型处理/协商提供一个框架。它还提供了一个应用程序接口,以便使用各种插件编写应用程序。

谁应该阅读本指南?

本指南介绍如何为 GStreamer 编写新模块。本指南适用于几类人群:

  • 任何希望为 GStreamer 添加新方法处理数据的人。例如,这些人可能想创建新的数据格式转换器、新的可视化工具或新的解码器或编码器。

  • 任何希望增加对新输入和输出设备提供支持的人。例如,该部分人可能希望增加对新视频输出系统提供写入能力,或从数码相机或特殊麦克风读取数据的能力。

  • 任何想以任何方式扩展 GStreamer 的人。在了解插件系统对其他代码的限制之前,你需要先了解插件系统是如何工作的。此外,读完这篇文章后,你可能会对插件的功能之多感到惊讶。

如果你只想使用 GStreamer 的现有功能,或者只想使用一个使用 GStreamer 的应用程序,那么本指南与你无关。如果你只对使用现有插件编写新程序感兴趣(现在已经有很多插件了)你可能需要查阅《GStreamer 应用程序开发手册》。如果你只是想获得 GStreamer 应用程序的帮助,那么你应该查看该应用程序的用户手册。

初步阅读

本指南假定你对 GStreamer 的基本工作原理有所了解。如果想了解 GStreamer 的编程概念,不妨先阅读《GStreamer 应用程序开发手册》。还可以查看GStreamer 网站上的其他文档。

为了理解本手册,您需要对 C 语言有基本的了解。由于 GStreamer 遵循 GObject 编程模型,本指南也假定您了解GObject编程的基础知识。您还可以参考 Eric Harlow 的《使用 GTK+ 和 GDK 开发 Linux 应用程序》一书。

本指南的结构

为帮助您浏览本指南,本指南分为几大部分。每一部分都涉及与 GStreamer 插件开发有关的特定广泛主题。本指南各部分按以下顺序编排:

  • 构建插件——介绍插件的结构,以音频过滤器为例进行说明。

    本部分涵盖了构建插件通常需要执行的所有基本步骤,例如向 GStreamer 注册元素和设置基本结构,以便从邻近元素接收数据并向其发送数据。讨论将从举例说明生成基本结构和在构建模板中注册元素开始。然后,您将学习如何编写代码完成一个基本的过滤器插件,并了解什么是衬底、链函数和状态?

    之后,我们将在添加属性和信号(Adding PropertiesandSignals)中展示一些 GObject 概念,包括如何为应用程序配置元素,以及如何实现应用程序与元素之间的交互。接下来,您将在“构建测试应用程序”中学习构建一个快速测试应用程序,以测试您刚刚学到的所有知识。在这里,我们将只介绍基础知识。如需全面了解应用程序的开发,请参阅《应用程序开发手册》。

  • 高级过滤器概念——有关 GStreamer 插件开发高级功能的知识。

    在学习了基本步骤之后,你应该能够创建一个具有一些不错功能的音频或视频滤镜插件。 不过,GStreamer 为插件编写者提供了更多功能。本指南的这一部分包含了更多高级主题的章节,如 GStreamer 中的调度、媒体类型定义、时钟、接口和标记。 由于这些功能都是特定用途的,因此你可以按照任意顺序阅读,其中大部分都不需要其他章节的知识。

    第一章名为 “不同的调度模式”,将解释元素调度的一些基础知识。这一章并不深入,主要是介绍工作原理。如果你对 GStreamer 内部结构感兴趣,请阅读本章。接下来,我们将应用这些知识,讨论比在链函数中学到的更多的数据传输类型:不同的调度模式。基于线程循环的元素可以让你对输入速率有更多控制。例如,这在编写多路复用器或解多路复用器时非常有用。

    接下来,我们将在《媒体类型和属性》中讨论 GStreamer 中的媒体类型识别。你将学习如何定义新的媒体类型,并了解 GStreamer 中定义的标准媒体类型列表。

    在下一章中,你将学习请求衬底和暂态衬底的概念,它们是动态创建的衬底,或者是因为应用程序的要求(请求),或者是因为媒体流的需要(暂态)。这将在请求衬底和暂态衬底中介绍。

    下一章“时钟”将解释 GStreamer 中时钟的概念。当你想知道元素应如何实现音频和视频同步时,就需要这些信息。

    接下来的几章将讨论应用程序与元素交互的高级方法。在此之前,我们已在《添加属性和信号》中学习了 GObject 的交互方式。我们将在《支持动态参数》中讨论动态参数,这是一种提前定义元素行为的方法。 接下来,您将在《接口》中了解接口。 接口是基于 GObjectGInterface 的应用程序与元素交互的非常具体的方法。最后,您将在《标签(元数据和流信息)》中了解 GStreamer 如何处理元数据。

    最后一章是《事件:搜索、导航和其他》将讨论 GStreamer 中的事件概念。事件是应用程序与元素交互的另一种方式。例如,它们可以处理寻路问题。它们也是元素之间进行交互的另一种方式,例如让彼此知道媒体流的中断、在流水线内转发标签等。

  • 创建特殊元素类型——其他插件类型的编写说明。

    本指南的前两部分以音频滤波器为例,介绍了适用于滤波器插件的概念。但许多概念同样适用于其他插件类型,包括源、汇和自动插件。本指南的这一部分介绍了在处理这些更专业的插件类型时出现的问题。本章首先特别关注可以使用基类(预定义基类)编写的元素,随后还将在编写解复用器或解析器、编写N对1元素或复用器以及编写管理器中介绍编写特殊类型元素的方法。

  • 附录——为插件开发人员提供的更多信息。

    附录中包含的一些信息在指南的其他章节中很难找到合适的位置。本部分的大部分内容尚未完成。

本指南介绍章节的其余部分简要概述了 GStreamer 插件开发所涉及的基本概念。涉及的主题包括元素和插件、衬底、GstMiniObject、缓冲区和事件以及媒体类型和属性。如果您已经熟悉这些信息,可以使用本简短概述来温故知新,也可以跳到“构建插件”部分。

正如您所看到的,要学的东西很多,让我们开始吧!

  • 通过从 GstBin 扩展来创建复合元素,这样就可以创建嵌入了其他插件的插件。

  • 在注册表中添加新的媒体类型以及类型检测功能。这样,您的插件就可以在全新的媒体类型上运行。

基础

本章将介绍 GStreamer 的基本概念,了解这些概念将有助于你理解扩展 GStreamer 所涉及的问题。其中许多概念在《GStreamer 应用程序开发手册》中有更详细的解释;这里介绍的基本概念主要是为了加深你的记忆。

元素和插件

元素是 GStreamer 的核心。在插件开发中,元素是从GstElement类派生出来的对象。当与其他元素链接时,元素会提供某种功能:例如,源元素为数据流提供数据,过滤器元素对数据流中的数据进行过滤。如果没有元素,GStreamer 只是一个概念上的流水线,没有任何联系。GStreamer 内置了大量的元素,但也可以编写额外的元素。

不过,仅仅编写一个新元素还不够:你需要将元素封装在一个插件中,以便 GStreamer 使用它。插件本质上是可加载的代码块,通常称为共享对象文件或动态链接库。一个插件可能包含多个元素的实现,也可能只包含一个元素。为简单起见,本指南主要介绍包含一个元素的插件。

过滤器是处理数据流的一种重要元素。数据的生产者和消费者分别称为源元素和汇元素。Bin元素包含其他元素。其中一种 Bin 元素负责同步它们所包含的元素,以便数据顺利流动。另一种 bin 称为自动插件元素,可自动将其他元素添加到 bin 中,并将它们连接在一起,从而在任意两种流类型之间起到过滤器的作用。

插件机制在 GStreamer 中随处可见,即使只使用标准软件包也不例外。一些非常基本的功能存在于核心库中,所有其他功能都在插件中实现。插件注册表用于在二进制注册表文件中存储插件的详细信息。这样,使用 GStreamer 的程序就不必加载所有插件来确定需要哪些插件。插件只在其需要提供元素时才会被加载。

有关GstElementGstPlugin的当前实现细节,请参阅《GStreamer 库参考手册》。

衬底

衬底(pads)用于协商 GStreamer 中元素之间的链接和数据流。衬底可以看作是元素上的一个“位置”或“端口”,在这里可以与其他元素建立链接,数据也可以通过它流进或流出这些元素。衬底具有特定的数据处理功能:只有当两个衬底允许的数据类型兼容时,才允许在两个衬底之间建立链接。

打个比方可能会有帮助。衬底类似于物理设备上的插头或插孔。例如,一个家庭影院系统包括一个放大器、一个 DVD 播放器和一个(不能播放声音的)视频投影仪。允许将 DVD 播放器链接到功放,因为这两个设备都有音频插孔;允许将投影仪链接到 DVD 播放器,因为这两个设备都有兼容的视频插孔。投影仪和功放之间不能链接,因为投影仪和功放的插孔类型不同。GStreamer 中的 PAD 与家庭影院系统中的插孔具有相同的作用。

在大多数情况下,GStreamer 中的所有数据都是通过元素之间的链接单向流动的。数据通过一个或多个源衬底从一个元素流出,而元素则通过一个或多个汇衬底接受输入数据。源元素和汇元素分别只有源衬底和汇衬底。

有关GstPad的当前实现细节,请参阅《GStreamer 库参考手册》。

GstMiniObject、缓冲区和事件

GStreamer 中的所有数据流都被分割成数据块,这些数据块从一个元素上的源衬底传递到另一个元素上的汇衬底。GstMiniObject就是用来保存这些数据块的结构。

GstMiniObject 包含以下重要类型:

  • 精确类型,表示此 GstMiniObject 是哪种类型的数据(事件、缓冲区……)。

  • 引用计数,表示当前持有miniobject引用的元素数量。当引用计数为零时,miniobject将被清理,其内存也将在某种程度上被释放(详见下文)。

在数据传输方面,定义了两种类型的 GstMiniObject:事件(控制)和缓冲区(内容)。

缓冲区可包含互相链接的两个衬底能够处理的任何类型的数据。通常,缓冲区包含从一个元素流向另一个元素的音频或视频数据块。

缓冲区还包含描述缓冲区内容的元数据。其中一些重要的元数据类型包括:

  • 指向一个或多个 GstMemory 对象的指针。GstMemory 对象是封装内存区域的计数对象。

  • 时间戳,表示缓冲区中内容的首选显示时间戳。

事件包含两个互相链接衬底之间的流状态信息。只有当元素明确支持事件时,才会发送事件,否则内核将(尝试)自动处理事件。例如,事件用于指示媒体类型、媒体流结束或缓存应被刷新。

事件可能包含以下几个项目:

  • 子类型,表示所含事件的类型。

  • 事件的其他内容,取决于具体的事件类型。

我们将在《事件:搜索、导航及其他》一章对事件进行详细讨论。在此之前,将使用的唯一事件是EOS事件,它用于指示流的结束(通常是文件的结束)。

有关GstMiniObjectGstBufferGstEvent的当前实现细节,请参阅《GStreamer 库参考手册》。

缓冲区分配

缓冲区可以存储几种不同类型的内存块。 最普通的缓冲区类型包含由 malloc() 分配的内存。 这种缓冲区虽然方便,但速度并不总是很快,因为数据往往需要专门复制到缓冲区中。

许多专用元素会创建指向特殊内存的缓冲区。 例如,filesrc 元素通常会将文件映射到应用程序的地址空间(使用 mmap()),并创建指向该地址范围的缓冲区。这些由 filesrc 创建的缓冲区与一般缓冲区的作用完全相同,只是它们是只读的。缓冲区释放代码会自动确定释放底层内存的正确方法。接收这类缓冲区的下游元素不需要做任何特殊处理或取消引用。

元素获取专用缓冲区的另一种方式是通过 GstBufferPoolGstAllocator 向下游对等元素请求。元素可以向下游对等元素申请 GstBufferPoolGstAllocator。如果下游能提供这些对象,上游就能使用它们分配缓冲区。请参阅内存分配中的更多内容。

许多汇元素都有将数据复制到硬件的加速方法,或可直接访问硬件。这些元素通常可以为其上游对等元素创建 GstBufferPoolGstAllocatorximagesink 就是这样一个例子。它能创建包含 XImages 的缓冲区。这样,当上游对等设备将数据复制到缓冲区时,就会直接复制到 XImage 中,这样 ximagesink 就能直接将图像绘制到屏幕上,而不必先将数据复制到 XImage 中。

过滤元件通常有机会在缓冲区上就地工作,或者在从源缓冲区复制到目标缓冲区时工作。实现这两种算法是最佳做法,因为 GStreamer 框架可以根据情况选择最快的算法。当然,这只适用于严格的过滤器——在源缓冲区和目标缓冲区中格式完全相同的元素。

媒体类型和属性

GStreamer 使用类型系统来确保元素之间传递的数据是公认的格式。类型系统非常重要,它能确保在元素间连接衬底时,他们支持的格式参数能正确匹配。元素之间的每个链接都有指定的类型和可选的属性集。有关衬底协商的更多信息,请参阅衬底协商章节。

基本类型

GStreamer 已经支持许多基本媒体类型。下面的表格列出了 GStreamer 缓冲区使用的几种基本类型。该表包含名称(“媒体类型”)、类型描述、与类型相关的属性以及每个属性的含义。支持类型的完整列表包含在已定义类型列表中。

示例类型表:

媒体类型描述属性属性类型属性值属性描述
audio/*所有支持的音频类型rateinteger大于 0数据采样率,单位为每秒采样(每个通道)。
  channelsintegergreater than 0音频数据的通道数。
audio/x-raw非结构化、未压缩的原始整数音频数据。formatstringS8 U8
S16LE S16BE
U16LE U16BE
S24_32LE S24_32BE
U24_32LE U24_32BE
S32LE S32BE
U32LE U32BE
S24LE S24BE
U24LE U24BE
S20LE S20BE
U20LE U20BE
S18LE S18BE
U18LE U18BE
F32LE F32BE
F64LE F64BE
样本数据的格式。
audio/mpeg使用 MPEG 音频编码方案压缩的音频数据。mpegversioninteger1, 2 或 4用于数据编码的 MPEG 版本。值 1 指的是 MPEG-1、-2 和-2.5 第 1、2 或 3 层。数值 2 和 4 指的是 MPEG-AAC 音频编码方案。
  framedboolean0 或 1真值表示每个缓冲区都包含一个帧。假值表示帧和缓冲区不一定匹配。
  layerinteger1, 2, 或 3用于压缩数据的压缩方案层(仅当 mpegversion=1 时)。
  bitrateinteger大于0比特率,单位为比特/秒。对于 VBR(可变比特率)MPEG 数据,这是平均比特率。
audio/x-vorbisVorbis 音频数据   目前还没有为该类型定义特定属性。
本文由作者按照 CC BY 4.0 进行授权