翻译自:https://gstreamer.freedesktop.org/documentation/application-development/index.html
GStreamer是一个非常强大且通用的框架,用于创建流媒体应用程序。GStreamer框架的许多优点来自其模块化:GStreamer可以无缝地合并新的插件模块。但是,由于模块化和功耗通常需要更高的复杂性,因此编写新的应用程序并不总是那么容易。
本指南旨在帮助您了解GStreamer框架,以便您可以基于它开发应用程序。第一章将重点介绍简单音频播放器的开发,并努力帮助您了解GStreamer概念。后面的章节将讨论与媒体播放和其他形式的媒体处理(捕获,编辑等)相关的更高级主题。
本书是从应用程序开发人员的角度来看GStreamer的。它描述了如何使用GStreamer库和工具编写GStreamer应用程序。有关编写插件的说明,我们建议使用插件编写器指南。
另请查看GStreamer网站上提供的其他文档。
为了理解本手册,您需要对C语言有基本的了解。
由于GStreamer遵循GObject编程模型,本指南还假设您了解GObject和glib编程的基础知识。特别是:
为了帮助您浏览本指南,它分为几个大部分。每个部分都涉及有关GStreamer应用程序开发的特定主题。本指南的各部分按以下顺序排列:
About GStreamer:概述了GStreamer,其设计原则和基础。
Building an Application:涵盖了GStreamer应用程序编程的基础知识。在本部分的最后,您应该能够使用GStreamer构建自己的音频播放器;
在高级GStreamer概念中,我们将继续研究使GStreamer从竞争对手中脱颖而出的高级课程。我们将讨论使用动态参数和接口的应用程序 - 管道交互,我们将讨论线程和线程管道,调度和时钟(以及同步)。大多数这些主题不仅仅是向您介绍他们的API,而且主要是为了更深入地了解使用GStreamer解决应用程序编程问题并理解他们的概念。
接下来,在GStreamer应用程序的高级接口中,我们将进入GStreamer的高级编程API。您不需要了解前面部分的所有细节来理解这一点,但您仍需要了解基本的GStreamer概念。除其他外,我们将讨论playbin和autopluggers。
最后在附录中,您将找到有关与GNOME,KDE,OS X或Windows集成的一些随机信息,一些调试帮助以及改进和简化GStreamer编程的一般提示。
本部分概述了本书中介绍的技术。
GStreamer是一个用于创建流媒体应用程序的框架。基础设计来自俄勒冈研究生院的视频管道,以及DirectShow的一些想法。
GStreamer的开发框架可以编写任何类型的流媒体多媒体应用程序。GStreamer框架旨在简化编写处理音频或视频或两者的应用程序。它不仅限于音频和视频,还可以处理任何类型的数据流。管道设计的开销小于所应用的过滤器引起的开销。这使得GStreamer成为设计高端音频应用程序的良好框架,这些应用程序对延迟提出了很高的要求。
GStreamer最明显的用途之一是使用它来构建媒体播放器。 GStreamer已经包含用于构建媒体播放器的组件,可以支持各种格式,包括MP3,Ogg / Vorbis,MPEG-1/2,AVI,Quicktime,mod等。然而,GStreamer不仅仅是一个媒体播放器。它的主要优点是可插拔组件可以混合并匹配到任意管道中,因此可以编写成熟的视频或音频编辑应用程序。
该框架基于各种具备编解码器和其他功能的插件。插件可以链接并布置在管道中。此管道定义数据流。管道也可以使用GUI编辑器进行编辑并保存为XML,以便可以轻松地制作管道库。
GStreamer核心功能是为插件、数据流和媒体类型处理/协商提供框架。它还提供了使用各种插件编写应用程序的API。
特别地,GStreamer支持:
GStreamer的插件可以分为:
GStreamer提供了一个干净的界面,用于:
GStreamer坚持使用GLIB 2.0对象模型GObject。熟悉GLib 2.0或GTK +的程序员会对GStreamer感到满意。
GStreamer使用信号和对象属性的机制。
可以在运行时查询所有对象的各种属性和功能。
GStreamer打算在编程方法上与GTK +类似。这适用于对象模型、对象的所有权、引用计数等。
可以使用GObject继承方法扩展所有GStreamer对象。
所有插件都是动态加载的,可以单独扩展和升级。
插件是在运行时加载的共享库。由于可以使用GObject属性设置插件的所有属性,因此不需要(实际上没有办法)为插件安装任何头文件。
已经特别注意使插件完全独立。可以在运行时查询插件的所有相关方面。
高性能基于:
GStreamer的核心本质上是与媒体无关的。它只知道字节和块,只包含基本元素。 GStreamer的核心功能甚至可以实现低级系统工具,如cp。
所有媒体处理功能都由核心外部的插件提供。这些插件告诉核心如何处理特定类型的媒体。
GStreamer还希望成为一个简单的框架,编解码器开发人员可以在其中试验不同的算法,加速免费的多媒体编解码器的开发,如Xiph.Org基金会(如Theora和Vorbis)开发的那些。
本指南的这一章介绍了GStreamer的基本概念。在阅读本指南的任何其他内容时,理解这些概念非常重要,所有这些概念都假设您理解这些基本概念。
Elements是GStreamer中最重要的对象类。您通常会创建一个链接在一起的元素链,让数据流过这个元素链。元素具有一个特定功能,可以是从文件读取数据,解码此数据或将此数据输出到声卡(或其他任何内容)。通过将几个这样的元素链接在一起,您可以创建一个可以执行特定任务的管道,例如媒体播放或捕获。默认情况下,GStreamer附带了大量元素,使得各种媒体应用程序的开发成为可能。如果需要,您还可以编写新元素。 GStreamer Plugin Writer’s Guide中对该主题进行了大量解释。
Pads是元素的输入和输出,您可以在其中连接其他元素。它们用于协商GStreamer中元素之间的链接和数据流。可以将Pad视为元件上的“插头”或“端口”,其可以与其他元件建立链接,并且数据可以通过该元件流入或流出这些元件。Pads具有特定的数据处理功能:pad可以限制流经它的数据类型。只有当两个pad允许的数据类型(功能)兼容时,才允许在两个pad之间建立链接。使用称为caps negotiation的过程在pad之间协商数据类型。数据类型由GstCaps描述。
这里的比喻可能会有所帮助。pad类似于物理设备上的插头或插孔。例如,考虑一个由音频放大器、DVD播放器和(静音)视频投影仪组成的家庭影院系统。允许将DVD播放器连接到放大器,因为两个设备都有音频插孔,并且允许将投影仪连接到DVD播放器,因为两个设备都具有兼容的视频插孔。由于投影仪和放大器具有不同类型的插孔,因此可能无法建立投影仪和放大器之间的链接。 GStreamer中的Pad与家庭影院系统中的插孔具有相同的用途。
在大多数情况下,GStreamer中的所有数据都通过元素之间的链接流动。数据通过一个或多个源pad流出一个元件,并且元件通过一个或多个pad接收输入数据。source和sink元件分别仅具有source pad和slink pad。数据通常包括缓冲区(由GstBuffer对象描述)和事件(由GstEvent对象描述)。
bin是元素集合的容器。由于bin是元素本身的子类,因此您可以主要控制bin,就像它是一个元素一样,从而为应用程序抽象出很多复杂性。例如,您可以通过更改bin本身的状态来更改bin中所有元素的状态。bins还会转发来自其包含的children的总线消息(例如错误消息,标记消息或EOS消息)。
管道是顶级的bin。它为应用程序提供总线并管理其children的同步。当您将其设置为PAUSED或PLAYING状态时,将启动数据流并进行媒体处理。一旦启动,管道将在一个单独的线程中运行,直到您停止它们或达到数据流的末尾。
GStreamer为应用程序和管道之间的通信和数据交换提供了几种机制。
在这些章节中,我们将讨论GStreamer的基本概念和最常用的对象,例如elements,pad和buffers。我们将使用这些对象的可视化表示,以便我们可以可视化您将稍后构建的更复杂的管道。您将初步了解GStreamer API,它应该足以构建基本应用程序。稍后在本部分中,您还将学习构建基本的命令行应用程序。
请注意,本部分将介绍GStreamer的低级API和概念。一旦您要构建应用程序,您可能希望使用更高级别的API。这些将在本手册的后面部分讨论。
编写GStreamer应用程序时,只需包含gst / gst.h即可访问库函数。除此之外,您还需要初始化GStreamer库。
一个典型的程序会使用代码来初始化GStreamer,如下所示:
#include
#include
int
main (int argc,
char *argv[])
{
const gchar *nano_str;
guint major, minor, micro, nano;
gst_init (&argc, &argv);
gst_version (&major, &minor, µ, &nano);
if (nano == 1)
nano_str = "(CVS)";
else if (nano == 2)
nano_str = "(Prerelease)";
else
nano_str = "";
printf ("This program is linked against GStreamer %d.%d.%d %s\n",
major, minor, micro, nano_str);
return 0;
}
使用GST_VERSION_MAJOR,GST_VERSION_MINOR和GST_VERSION_MICRO宏来获取您正在构建的GStreamer版本,或使用函数gst_version获取应用程序链接的版本。 GStreamer目前使用的方案是具有相同主要版本和次要版本的版本兼容API和ABI。
也可以使用两个NULL参数调用gst_init函数,在这种情况下,GStreamer不会解析任何命令行选项。
您还可以使用GOption表初始化您自己的参数,如下一个示例所示:
#include
int
main (int argc,
char *argv[])
{
gboolean silent = FALSE;
gchar *savefile = NULL;
GOptionContext *ctx;
GError *err = NULL;
GOptionEntry entries[] = {
{ "silent", 's', 0, G_OPTION_ARG_NONE, &silent,
"do not output status information", NULL },
{ "output", 'o', 0, G_OPTION_ARG_STRING, &savefile,
"save xml representation of pipeline to FILE and exit", "FILE" },
{ NULL }
};
ctx = g_option_context_new ("- Your application");
g_option_context_add_main_entries (ctx, entries, NULL);
g_option_context_add_group (ctx, gst_init_get_option_group ());
if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
g_print ("Failed to initialize: %s\n", err->message);
g_clear_error (&err);
g_option_context_free (ctx);
return 1;
}
g_option_context_free (ctx);
printf ("Run me with --help to see the Application options appended.\n");
return 0;
}
如此片段所示,您可以使用GOption表定义特定于应用程序的命令行选项,并将此表连同从函数gst_init_get_option_group返回的选项组一起传递给GLib初始化函数。除标准GStreamer选项外,还将解析您的应用程序选项。
GStreamer中最重要的对象是GstElement对象。元素是媒体管道的基本构建块。您将使用的所有不同的高级组件都派生自GstElement。每个解码器、编码器、分路器、视频或音频输出实际上都是GstElement。
对于应用程序员来说,elements最好视为为黑盒子。在一端,你可能会放入一些东西,元素用它做一些事情,另一端出现其他东西。例如,对于解码器element,您将放入编码数据,该元素将输出解码数据。在下一章中,您将了解有关元素中数据输入和输出的更多信息,以及如何在应用程序中进行设置。
source elements生成供管道使用的数据,例如从磁盘或声卡读取。源元素的可视化显示了我们如何可视化源元素。我们总是在元素的右边画一个源 pad。
源元素不接受数据,它们只生成数据。您可以在图中看到这一点,因为它只有一个源pad(在右侧)。源 pad只能生成数据。
滤波器和类似滤波器的元件具有输入和输出 pad。它们对输入(接收器)pad 上接收的数据进行操作,并在其输出(源)pad上提供数据。这些元件的示例是体积元件(滤波器)、视频缩放器(转换器)、Ogg分路器或Vorbis解码器。
类似滤波器的元件可以具有任意数量的源或接收 pad。例如,视频分路器将具有一个接收 pad和若干(1-N)个源 pad,一个对应于容器格式中包含的每个基本流。另一方面,解码器只有一个源和接收 pad。
滤波元素的可视化显示了我们如何可视化类似滤波的元素。该特定元素具有一个源和一个sink pad。接收输入数据的sink pad位于元件的左侧;source pad仍在右侧。
具有多个输出pad的滤波器元件的可视化显示另一个类似滤波器的元件,该元件具有多个输出(源)pad。例如,一个这样的元素的示例可以是用于包含音频和视频的Ogg流的Ogg分路器。一个源pad将包含基本视频流,另一个将包含基本音频流。解复用器通常会在创建新pad时触发信号,程序员可以在信号处理程序中处理新的基本流。
sink element是media 管道的端点。他们接受数据但不生产任何东西。磁盘写入、声卡重放和视频输出都将由接收器元素实现。 sink元素的可视化显示了sink元素。
创建元素的最简单方法是使用gst_element_factory_make()。此函数输入新创建元素的工厂名称和元素名称。例如,元素的名称可以在稍后用于查找bin中的元素。该名称也将用于调试输出。您可以传递NULL作为name参数以获取唯一的默认名称。
当您不再需要该元素时,您需要使用gst_object_unref()取消它。这会将元素的引用计数减少1。元素在创建时的引用计数为1。当refcount减少到0时,元素会被完全析构。
以下示例[1]显示了如何从名为fakesrc的元素工厂创建名为source的元素。它检查创建是否成功。检查后,它会解析元素。
#include
int main (int argc,
char *argv[])
{
GstElement *element;
/* init GStreamer */
gst_init (&argc, &argv);
/* create element */
element = gst_element_factory_make ("fakesrc", "source");
if (!element) {
g_print ("Failed to create element of type 'fakesrc'\n");
return -1;
}
gst_object_unref (GST_OBJECT (element));
return 0;
}
gst_element_factory_make实际上是两个函数组合的简写。 GstElement对象是从工厂创建的。要创建元素,您必须使用唯一的工厂名称访问GstElementFactory对象。这是通过gst_element_factory_find()完成的。
以下代码片段用于获取可用于创建fakesrc元素(伪数据源)的工厂。函数gst_element_factory_create()将使用元素工厂来创建具有给定名称的元素。
#include
int
main (int argc,
char *argv[])
{
GstElementFactory *factory;
GstElement * element;
/* init GStreamer */
gst_init (&argc, &argv);
/* create element, method #2 */
factory = gst_element_factory_find ("fakesrc");
if (!factory) {
g_print ("Failed to find factory of type 'fakesrc'\n");
return -1;
}
element = gst_element_factory_create (factory, "source");
if (!element) {
g_print ("Failed to create element, even though its factory exists!\n");
return -1;
}
gst_object_unref (GST_OBJECT (element));
gst_object_unref (GST_OBJECT (factory));
return 0;
}
GstElement可以具有使用标准GObject属性实现的多个属性。因此,支持查询、设置和获取属性值以及GParamSpec的常用GObject方法。
每个GstElement都从其父GstObject继承至少一个属性:“name”属性。这是您为函数gst_element_factory_make()或gst_element_factory_create()提供的名称。您可以使用函数gst_object_set_name和gst_object_get_name来获取和设置此属性,或使用GObject属性机制,如下所示。
#include
int
main (int argc,
char *argv[])
{
GstElement *element;
gchar *name;
/* init GStreamer */
gst_init (&argc, &argv);
/* create element */
element = gst_element_factory_make ("fakesrc", "source");
/* get name */
g_object_get (G_OBJECT (element), "name", &name, NULL);
g_print ("The name of the element is '%s'.\n", name);
g_free (name);
gst_object_unref (GST_OBJECT (element));
return 0;
}
大多数插件都提供其他属性,以提供有关其配置或配置元素的更多信息。 gst-inspect是查询特定元素属性的有用工具,它还将使用属性自省来简要说明属性的功能以及它支持的参数类型和范围。有关gst-inspect的详细信息,请参阅附录中的gst-inspect。
有关GObject属性的更多信息,我们建议您阅读GObject手册和Glib对象系统简介。
GstElement还提供各种GObject信号,可用作灵活的回调机制。在这里,您也可以使用gst-inspect来查看特定元素支持的信号。信号和属性一起是元素和应用程序交互的最基本方式。
在上一节中,我们简要介绍了GstElementFactory对象作为创建元素实例的方法。然而,元素工厂的作用远不止于此。元素工厂是从GStreamer注册表中检索的基本类型,它们描述了GStreamer可以创建的所有插件和元素。这意味着元素工厂对自动化元素实例化非常有用,例如自动插件的功能,以及创建可用元素列表。
像gst-inspect这样的工具将提供有关元素的一些通用信息,例如编写插件的人、描述性名称(和短名称)、等级和类别。该类别可用于获取可使用此元素工厂创建的元素的类型。类别的示例包括编解码器/解码器/视频(视频解码器),编解码器/编码器/视频(视频编码器),源/视频(视频发生器),接收器/视频(视频输出),以及所有这些也可用于音频。然后,还有Codec / Demuxer和Codec / Muxer等等。 gst-inspect将给出所有工厂的列表,gst-inspect 将列出所有上述信息,还有更多。
#include
int
main (int argc,
char *argv[])
{
GstElementFactory *factory;
/* init GStreamer */
gst_init (&argc, &argv);
/* get factory */
factory = gst_element_factory_find ("fakesrc");
if (!factory) {
g_print ("You don't have the 'fakesrc' element installed!\n");
return -1;
}
/* display information */
g_print ("The '%s' element is a member of the category %s.\n"
"Description: %s\n",
gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)),
gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS),
gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_DESCRIPTION));
gst_object_unref (GST_OBJECT (factory));
return 0;
}
您可以使用gst_registry_pool_feature_list(GST_TYPE_ELEMENT_FACTORY)获取GStreamer知道的所有元素工厂的列表。
也许元素工厂最强大的特性是它们包含元素可以生成的pad的完整描述,以及这些pad的功能(用外行的话说:什么类型的介质可以在这些pad上流动),而不必实际需要将这些插件加载到内存中。这可以用于为编码器提供编解码器选择列表,或者它可以用于媒体播放器的自动插拔目的。目前所有基于GStreamer的媒体播放器和自动插件都以这种方式工作。我们将在下一章了解GstPad和GstCaps时仔细研究这些功能:Pads和capabilities。
通过将source元素与零个或多个类似过滤器的元素以及最后一个sink元素相链接,可以设置媒体管道。数据将流经元素。这是GStreamer中媒体处理的基本概念。
通过链接这三个元素,我们创建了一个非常简单的元素链。这样做的结果是源元素(“element1”)的输出将用作类似过滤器的元素(“element2”)的输入。类似过滤器的元素将对数据执行某些操作并将结果发送到最终接收器元素(“element3”)。
想象一下上面的图形是一个简单的Ogg / Vorbis音频解码器。源是从磁盘读取文件的磁盘源。第二个元素是Ogg / Vorbis音频解码器。接收器元素是您的声卡,播放解码的音频数据。我们将在本手册后面使用这个简单的图形构建一个Ogg / Vorbis播放器。
在代码中,上面的图形是这样写的:
#include
int
main (int argc,
char *argv[])
{
GstElement *pipeline;
GstElement *source, *filter, *sink;
/* init */
gst_init (&argc, &argv);
/* create pipeline */
pipeline = gst_pipeline_new ("my-pipeline");
/* create elements */
source = gst_element_factory_make ("fakesrc", "source");
filter = gst_element_factory_make ("identity", "filter");
sink = gst_element_factory_make ("fakesink", "sink");
/* must add elements to pipeline before linking them */
gst_bin_add_many (GST_BIN (pipeline), source, filter, sink, NULL);
/* link */
if (!gst_element_link_many (source, filter, sink, NULL)) {
g_warning ("Failed to link elements!");
}
[..]
}
对于更具体的行为,还有函数gst_element_link()和gst_element_link_pads()。您还可以获取对各个pad的引用,并使用各种gst_pad_link_ *()函数链接它们。有关更多详细信息,请参阅API参考。
重要提示:您必须在链接之前向bin或管道添加元素,因为向bin添加元素将断开任何已存在的链接。此外,您不能直接链接不在同一个bin或管道中的元素;如果你想链接不同层次的元素或pad,你需要使用ghost pads(稍后介绍)。
创建后,元素实际上不会执行任何操作。您需要更改元素状态以使其执行某些操作。 GStreamer知道四种元素状态,每种状态都具有非常特定的含义。这四个状态是:
您可以使用函数gst_element_set_state()更改元素的状态。如果将元素设置为另一个状态,GStreamer将在内部遍历所有中间状态。因此,如果您将元素从NULL设置为PLAYING,GStreamer将在内部将元素设置为READY和PAUSED。
当移动到GST_STATE_PLAYING时,管道将自动处理数据。它们不需要以任何形式进行迭代。在内部,GStreamer将启动承担各任务的线程。 GStreamer还将使用GstBus将消息从管道的线程传输到应用程序自己的线程中。详情请见Bus。
当您将bin或管道设置为某个目标状态时,它通常会自动将状态更改传播到bin或管道中的所有元素,因此通常只需要设置顶级管道的状态以启动管道或关闭它。但是,当动态地向已经运行的管道添加元素时,例如,在“pad-added”信号回调中,您需要使用gst_element_set_state()或gst_element_sync_state_with_parent()将其设置为所需的目标状态。
bin是容器元素。您可以向bin添加元素。由于bin本身就是一个元素,因此可以以与任何其他元素相同的方式处理bin。因此,整个前一章(元素)也适用于bin。
Bins允许您将一组链接元素组合成一个逻辑元素。您不再处理单个元素,而只使用一个元素bin。我们将看到,当您构建复杂的管道时,这非常强大,因为它允许您以较小的块分解管道。
bin还将管理其中包含的元素。它将对元素执行状态更改以及收集和转发总线消息。
GStreamer程序员可以使用一种特殊类型的bin:
管道:管理所包含元素的同步和总线消息的通用容器。顶层bin必须是管道,因此每个应用至少需要其中一个。
以与创建其他元素相同的方式创建bin,即使用元素工厂。还有便利功能(gst_bin_new()和gst_pipeline_new())。要向bin中添加元素或从bin中删除元素,可以使用gst_bin_add()和gst_bin_remove()。请注意,添加元素的bin将获取该元素的所有权。如果你析构了bin,那么该元素将被解除引用。如果从bin中删除元素,它将自动解除引用。
#include
int
main (int argc,
char *argv[])
{
GstElement *bin, *pipeline, *source, *sink;
/* init */
gst_init (&argc, &argv);
/* create */
pipeline = gst_pipeline_new ("my_pipeline");
bin = gst_bin_new ("my_bin");
source = gst_element_factory_make ("fakesrc", "source");
sink = gst_element_factory_make ("fakesink", "sink");
/* First add the elements to the bin */
gst_bin_add_many (GST_BIN (bin), source, sink, NULL);
/* add the bin to the pipeline */
gst_bin_add (GST_BIN (pipeline), bin);
/* link the elements */
gst_element_link (source, sink);
[..]
}
查找bin中的元素有各种函数。最常用的是gst_bin_get_by_name()和gst_bin_get_by_interface()。您还可以使用函数gst_bin_iterate_elements()迭代bin包含的所有元素。有关详细信息,请参阅GstBin的API参考。
程序员可以创建包含元素的自定义bin来执行特定任务。例如,这允许您使用以下代码行编写Ogg / Vorbis解码器:
int
main (int argc,
char *argv[])
{
GstElement *player;
/* init */
gst_init (&argc, &argv);
/* create player */
player = gst_element_factory_make ("oggvorbisplayer", "player");
/* set the source audio file */
g_object_set (player, "location", "helloworld.ogg", NULL);
/* start playback */
gst_element_set_state (GST_ELEMENT (player), GST_STATE_PLAYING);
[..]
}
(当然,这是一个愚蠢的例子,已经存在一个功能更强大,功能更多的自定义bin:playbin元素。)
可以使用插件或应用程序创建自定义bin。您可以在Plugin Writer指南中找到有关创建自定义bin的更多信息。
这种自定义bin的示例是来自gst-plugins-base的playbin和uridecodebin元素。
bins管理其包含的所有元素的状态。如果使用gst_element_set_state()将bin(或管道,它是特殊的顶级bin类型)设置为某个目标状态,它将确保其中包含的所有元素也将设置为此状态。这意味着通常只需要设置顶级管道的状态以启动管道或关闭它。
bin将对其所有子节点从sink元素到源元素执行状态更改(从后向前进行状态更改)。这确保了当上游元素进入PAUSED或PLAYING时下游元素已准备好接收数据。类似地,当关闭时,sink元素将首先设置为READY或NULL,这将导致上游元素接收FLUSHING错误并在元素设置为READY或NULL状态之前停止流线程。
但是,请注意,如果将元素添加到已经运行的bin或管道中,例如在“pad-added”信号回调中,其状态不会自动与其添加到的bin或管道的当前状态或目标状态一致。相反,在向已经运行的管道添加元素时,需要使用gst_element_set_state()或gst_element_sync_state_with_parent()将其设置为所需的目标状态。
总线是一个简单的系统,负责将消息从流线程转发到其自己的线程上下文中的应用程序。总线的优点是应用程序不需要是线程感知就可以使用GStreamer,即使GStreamer本身具有高度线程性。
默认情况下,每个管道都包含一个总线,因此应用程序不需要创建总线或任何东西。应用程序应该做的唯一事情是在总线上设置消息处理程序,类似于对象的信号处理程序。当主循环运行时,将定期检查总线是否有新消息,并且当有任何消息可用时将调用回调。
使用总线有两种不同的方法:
#include
static GMainLoop *loop;
static gboolean
my_bus_callback (GstBus *bus,
GstMessage *message,
gpointer data)
{
g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_ERROR: {
GError *err;
gchar *debug;
gst_message_parse_error (message, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
g_free (debug);
g_main_loop_quit (loop);
break;
}
case GST_MESSAGE_EOS:
/* end-of-stream */
g_main_loop_quit (loop);
break;
default:
/* unhandled message */
break;
}
/* we want to be notified again the next time there is a message
* on the bus, so returning TRUE (FALSE means we want to stop watching
* for messages on the bus and our callback should not be called again)
*/
return TRUE;
}
gint
main (gint argc,
gchar *argv[])
{
GstElement *pipeline;
GstBus *bus;
guint bus_watch_id;
/* init */
gst_init (&argc, &argv);
/* create pipeline, add handler */
pipeline = gst_pipeline_new ("my_pipeline");
/* adds a watch for new message on our pipeline's message bus to
* the default GLib main context, which is the main context that our
* GLib main loop is attached to below
*/
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL);
gst_object_unref (bus);
/* [...] */
/* create a mainloop that runs/iterates the default GLib main context
* (context NULL), in other words: makes the context check if anything
* it watches for has happened. When a message has been posted on the
* bus, the default main context will automatically call our
* my_bus_callback() function to notify us of that message.
* The main loop will be run until someone calls g_main_loop_quit()
*/
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
/* clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
g_source_remove (bus_watch_id);
g_main_loop_unref (loop);
return 0;
}
重要的是要知道将在mainloop的线程上下文中调用处理程序。这意味着管道和应用程序通过总线之间的交互是异步的,因此不适合某些实时目的,例如音轨之间的交叉淡入淡出,做(理论上)无间隙回放或视频效果。所有这些事情都应该在管道上下文中完成,这是编写GStreamer插件最简单的。但它的主要用途非常有用:将消息从管道传递到应用程序。这种方法的优点是GStreamer在内部执行的所有线程都是对应用程序隐藏的,应用程序开发人员根本不必担心线程问题。
请注意,如果您使用默认的GLib主循环集成,则可以连接到总线上的“消息”信号,而不是连接监视。这样您就不必在所有可能的消息类型上执行switch操作;只需以message :: 的形式连接到有趣的信号,其中是一种特定的消息类型(有关消息类型的说明,请参阅下一节)。
上面的代码段也可以写成:
GstBus *bus;
[..]
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline);
gst_bus_add_signal_watch (bus);
g_signal_connect (bus, "message::error", G_CALLBACK (cb_message_error), NULL);
g_signal_connect (bus, "message::eos", G_CALLBACK (cb_message_eos), NULL);
[..]
如果您不使用GLib主循环,则默认情况下不会提供异步消息信号。但是,您可以安装一个自定义同步处理程序,该处理程序唤醒自定义主循环并使用gst_bus_async_signal_func()发出信号。 (有关详细信息,请参阅文档)
GStreamer有一些可以通过总线传递的预定义消息类型。但是,这些消息是可扩展的。插件可以定义其他消息,应用程序可以决定为这些消息设置特定代码或忽略它们。强烈建议所有应用程序通过向用户提供可视反馈来至少处理错误消息。
所有消息都有消息源、类型和时间戳。消息源可用于查看哪个element发出了消息。例如,对于某些消息,只有顶级管道发出的消息对大多数应用程序都很有用(例如,对于状态变化通知)。下面列出了所有消息,并简要说明了它们的作用以及如何解析特定消息的内容。
正如我们在Elements中看到的那样,pad是元素与外部世界的接口。数据从一个元素的源pad流到另一个元素的sink pad。某element可以处理的特定媒体类型将由该pad的功能公开。我们将在本章后面讨论更多关于功能的内容(参见pad的功能)。
pad类型由两个属性定义:其方向和可用性。正如我们之前提到的,GStreamer定义了两个pad方向:source pads和sink pads。该术语是从元素内部定义的:元素在其sink pads上接收数据并在其source pads上生成数据。示意性地,在元件的左侧绘制sink pads,而在元件的右侧绘制source pads。在这些图中,数据从左向右流动。
与pads可用性相比,pads方向非常简单。pads可以具有三种可用性中的任何一种:always,sometimes和on request。这三种类型的含义完全如下所示:always表示pads始终存在,sometimes pad仅在某些情况下存在(并且可以随机消失),on-requests pads在应用程序明确请求时才会出现。
创建元素时,某些元素可能没有所有的pads。例如,这可能发生在Ogg demuxer元素上。当元素在Ogg流中检测到这样的流时,该元素将读取Ogg流并为每个包含的基本流(vorbis,theora)创建动态pads。同样,它将在流结束时删除pad。例如,该原理对于解复用器元件非常有用。
运行gst-inspect oggdemux将显示该元素只有一个pad:一个名为“sink”的sink pad。其他pad是“休眠”。您可以在pad模板中看到这一点,因为存在“Exists:Sometimes”属性。根据您播放的Ogg文件的类型,将创建pads。我们将看到,当您要创建动态管道时,这非常重要。您可以将信号处理程序附加到元素,以便在元素从其“sometimes”pad模板之一创建新pad时通知您。以下代码是如何执行此操作的示例:
#include
static void
cb_new_pad (GstElement *element,
GstPad *pad,
gpointer data)
{
gchar *name;
name = gst_pad_get_name (pad);
g_print ("A new pad %s was created\n", name);
g_free (name);
/* here, you would setup a new pad link for the newly created pad */
[..]
}
int
main (int argc,
char *argv[])
{
GstElement *pipeline, *source, *demux;
GMainLoop *loop;
/* init */
gst_init (&argc, &argv);
/* create elements */
pipeline = gst_pipeline_new ("my_pipeline");
source = gst_element_factory_make ("filesrc", "source");
g_object_set (source, "location", argv[1], NULL);
demux = gst_element_factory_make ("oggdemux", "demuxer");
/* you would normally check that the elements were created properly */
/* put together a pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, demux, NULL);
gst_element_link_pads (source, "src", demux, "sink");
/* listen for newly created pads */
g_signal_connect (demux, "pad-added", G_CALLBACK (cb_new_pad), NULL);
/* start the pipeline */
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
[..]
}
通常仅在“pad-added”回调中向管道添加元素并不罕见。如果这样做,请不要忘记使用gst_element_set_state()或gst_element_sync_state_with_parent()将新添加的元素的状态设置为管道的目标状态。
元素也可以有request pads。这些pads不是自动创建的,只是按需创建。这对于多路复用器、聚合器和T形元件非常有用。聚合器是将多个输入流的内容合并为一个输出流的元素。T形元件是相反的:它们是具有一个输入流的元素,并将此流复制到它们的每个输出pad,这些输出pad是根据请求创建的。每当应用程序需要另一个流副本时,它只需从T形元素请求一个新的输出pad。
以下代码显示了如何从“tee”元素请求新的输出pad:
#include
static void
link_to_multiplexer (GstPad *tolink_pad,
GstElement *mux)
{
GstPad *pad;
gchar *srcname, *sinkname;
srcname = gst_pad_get_name (tolink_pad);
pad = gst_element_get_compatible_pad (mux, tolink_pad, NULL);
gst_pad_link (tolink_pad, pad);
sinkname = gst_pad_get_name (pad);
gst_object_unref (GST_OBJECT (pad));
g_print ("A new pad %s was created and linked to %s\n", sinkname, srcname);
g_free (sinkname);
g_free (srcname);
}
static void
some_function (GstElement *tee)
{
GstPad * pad;
gchar *name;
pad = gst_element_get_request_pad (tee, "src%d");
name = gst_pad_get_name (pad);
g_print ("A new pad %s was created\n", name);
g_free (name);
/* here, you would link the pad */
/* [..] */
/* and, after doing that, free our reference */
gst_object_unref (GST_OBJECT (pad));
}
gst_element_get_request_pad()方法可用于根据pad模板的名称从元素中获取pad。还可以请求与另一个pad模板兼容的pad。如果要将元素链接到多路复用器元素并且需要请求兼容的pad,这非常有用。方法gst_element_get_compatible_pad()可用于请求兼容的pad,如下一个示例所示。它将从任何输入请求来自Ogg多路复用器的兼容pad。
#include
static void
link_to_multiplexer (GstPad *tolink_pad,
GstElement *mux)
{
GstPad *pad;
gchar *srcname, *sinkname;
srcname = gst_pad_get_name (tolink_pad);
pad = gst_element_get_compatible_pad (mux, tolink_pad, NULL);
gst_pad_link (tolink_pad, pad);
sinkname = gst_pad_get_name (pad);
gst_object_unref (GST_OBJECT (pad));
g_print ("A new pad %s was created and linked to %s\n", sinkname, srcname);
g_free (sinkname);
g_free (srcname);
}
static void
some_function (GstElement *tee)
{
GstPad * pad;
gchar *name;
pad = gst_element_get_request_pad (tee, "src%d");
name = gst_pad_get_name (pad);
g_print ("A new pad %s was created\n", name);
g_free (name);
/* here, you would link the pad */
/* [..] */
/* and, after doing that, free our reference */
gst_object_unref (GST_OBJECT (pad));
}
由于pad在外部世界如何观察元件中起着非常重要的作用,因此实现了一种机制通过使用capabilities描述可以流经或当前正在流过pad的数据。在这里,我们将简要描述哪些功能以及如何使用它们,足以理解这个概念。要深入了解功能以及GStreamer中定义的所有功能列表,请参阅插件编写指南。
capabilities附加到pad模板和pad本身。对于pad模板,它将描述可以通过此模板创建的pad上流式传输的媒体类型。对于pads,它可以是可能的caps列表(通常是pads模板功能的副本),在这种情况下,pads尚未协商,或者它是当前在此pads上流动的媒体类型,在这种情况下pads已经谈判了。
pad的功能使用GstCaps对象描述。在内部,GstCaps将包含一个或多个将描述一种媒体类型的GstStructure。协商的pad将具有仅包含一个结构的功能集。此外,此结构仅包含固定值。对于未协商的pad或pad模板,这些约束不适用。
例如,下面是“vorbisdec”元素功能的转储,您可以通过运行gst-inspect vorbisdec获得。你会看到两个pad:一个源和一个接收pad。这两种pad始终可用,并且都具有附加功能。接收pad将接受vorbis编码的音频数据,媒体类型为“audio / x-vorbis”。源pad用于将原始(解码)音频样本发送到下一个元素,具有原始音频媒体类型(在本例中为“audio / x-raw”)。源pad还将包含音频采样率和通道数量的属性,以及您现在不需要担心的一些属性。
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
audio/x-raw
format: F32LE
rate: [ 1, 2147483647 ]
channels: [ 1, 256 ]
SINK template: 'sink'
Availability: Always
Capabilities:
audio/x-vorbis
属性用于描述功能的额外信息。属性由键(字符串)和值组成。可以使用不同的可能值类型:
capabilities(简称:caps)描述在两个pads之间流动的数据类型,或者一个pad(模板)支持的数据类型。这使它们对各种用途非常有用:
pad可以具有附加到其上的一组(即,一个或多个)能力。能力(GstCaps)表示为一个或多个GstStructures的阵列,并且每个GstStructure是字段阵列,其中每个字段由字段名称字符串(例如“宽度”)和类型值(例如G_TYPE_INT或GST_TYPE_INT_RANGE)组成。
请注意,pad的可能功能之间存在明显的差异(即,通常你发现的pad模板的caps,因为它们在gst-inspect中显示),pads的允许caps(可以与pad模板的caps相同或是其子集,取决于对等垫的可能上限)和最后协商的caps(这些描述了流或缓冲区的确切格式,并且只包含一个结构,并且没有像范围或列表那样的可变位,即,他们是固定的caps)。
您可以通过查询一个结构的各个属性来获取一组功能中的属性值。您可以使用gst_caps_get_structure()从封装中获取结构,使用gst_caps_get_size()获取GstCaps中的结构数。
当Caps仅包含一个结构时,Caps称为简单caps;当它们只包含一个结构且没有可变字段类型(如范围或可能值列表)时,将称为固定caps。另外两种特殊类型的caps是ANY caps和empty caps。
以下是如何从一组固定视频caps中提取宽度和高度的示例:
static void
read_video_props (GstCaps *caps)
{
gint width, height;
const GstStructure *str;
g_return_if_fail (gst_caps_is_fixed (caps));
str = gst_caps_get_structure (caps, 0);
if (!gst_structure_get_int (str, "width", &width) ||
!gst_structure_get_int (str, "height", &height)) {
g_print ("No width/height available\n");
return;
}
g_print ("The video size of this set of capabilities is %dx%d\n",
width, height);
}
虽然capabilities主要在插件内部使用来描述pads的媒体类型,但应用程序员通常还必须对capabilities有基本的了解才能与插件对接,尤其是在使用过滤后的caps。当您使用过滤后的caps或固定caps时,您将限制在两个pads之间流动的媒体类型为其支持的媒体类型的子集。您可以在管道中使用capsfilter元素执行此操作。为此,您还需要创建自己的GstCaps。最简单的方法是使用函数gst_caps_new_simple():
static gboolean
link_elements_with_filter (GstElement *element1, GstElement *element2)
{
gboolean link_ok;
GstCaps *caps;
caps = gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, "I420",
"width", G_TYPE_INT, 384,
"height", G_TYPE_INT, 288,
"framerate", GST_TYPE_FRACTION, 25, 1,
NULL);
link_ok = gst_element_link_filtered (element1, element2, caps);
gst_caps_unref (caps);
if (!link_ok) {
g_warning ("Failed to link element1 and element2!");
}
return link_ok;
}
这将迫使这两个元素之间的数据流转换为某种视频格式,宽度,高度和帧速率(如果在所涉及的元素的上下文中无法实现链接,则链接将失败)。请记住,当您使用gst_element_link_filtered()时,它会自动为您创建一个capsfilter元素,并将其插入您要连接的两个元素之间的bin或管道中(如果您想要断开这些元素,这很重要因为那时你将不得不从capsfilter断开这两个元素)。
在某些情况下,您需要创建一组更精细的功能来过滤两个pads之间的链接。然后,这个函数太简单了,你会想要使用方法gst_caps_new_full():
static gboolean
link_elements_with_filter (GstElement *element1, GstElement *element2)
{
gboolean link_ok;
GstCaps *caps;
caps = gst_caps_new_full (
gst_structure_new ("video/x-raw",
"width", G_TYPE_INT, 384,
"height", G_TYPE_INT, 288,
"framerate", GST_TYPE_FRACTION, 25, 1,
NULL),
gst_structure_new ("video/x-bayer",
"width", G_TYPE_INT, 384,
"height", G_TYPE_INT, 288,
"framerate", GST_TYPE_FRACTION, 25, 1,
NULL),
NULL);
link_ok = gst_element_link_filtered (element1, element2, caps);
gst_caps_unref (caps);
if (!link_ok) {
g_warning ("Failed to link element1 and element2!");
}
return link_ok;
}
有关GstStructure和GstCaps的完整API,请参阅API参考。
你可以从没有Ghost pads的GstBin元素的可视化中看到bin如何没有自己的pads。这就是“ghost pads”发挥作用的地方。
ghost pads是来自bin中某些element的pad,也可以直接从bin中访问。将它与UNIX文件系统中的符号链接进行比较。在bin上使用ghost pad,bin也有一个pad,可以透明地用作代码其他部分的元素。
具有ghost pad的GstBin元素的可视化是ghost pad的表示。elemnet 1的sink pad现在也是bin的pad。因为ghost pad的外观和工作方式与任何其他pad一样,它们可以添加到任何类型的元素,而不仅仅是普通的pads添加到GstBin。
使用函数gst_ghost_pad_new()创建ghost pad:
#include
int
main (int argc,
char *argv[])
{
GstElement *bin, *sink;
GstPad *pad;
/* init */
gst_init (&argc, &argv);
/* create element, add to bin */
sink = gst_element_factory_make ("fakesink", "sink");
bin = gst_bin_new ("mybin");
gst_bin_add (GST_BIN (bin), sink);
/* add ghostpad */
pad = gst_element_get_static_pad (sink, "sink");
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (GST_OBJECT (pad));
[..]
}
在上面的例子中,bin现在还有一个pad:给定元素的称为“sink”的pad。从这里开始,bin可以用作sink element的替代物。例如,您可以将另一个element链接到bin。
流经管道的数据由缓冲区和事件组合而成。缓冲区包含实际的媒体数据。事件包含控制信息,例如搜索信息和流末尾通知。所有这些都会在运行时自动流过管道。本章主要是为了向您解释这个概念;你不需要为此做任何事情。
缓冲区包含将流经您创建的管道的数据。源元素通常会创建一个新缓冲区并将其通过pad传递给链中的下一个元素。使用GStreamer基础架构创建媒体管道时,您不必自己处理缓冲区;元素会为你做到这一点。
缓冲区包括:
简单的情况是创建缓冲区、分配内存、将数据放入其中,然后传递给下一个元素。该元素读取数据,执行某些操作(如创建新缓冲区并解码),并解除对缓冲区的引用。这导致数据被释放并且缓冲区被破坏。典型的视频或音频解码器就像这样工作。
但是,有更复杂的场景。元素可以就地修改缓冲区,即不分配新的缓冲区。元素也可以写入硬件存储器(例如来自视频捕获源)或从X服务器分配的内存(使用XShm)。缓冲区可以是只读的,依此类推。
Events表示控制信息,它们与缓冲区一起在管道中向上和向下发送。向下传递的事件通知流的其他元素。可能的事件包括搜索、刷新、流末尾通知等。向上传递的事件既用于应用程序元素交互,也用于元素 - 元素交互以请求流状态的更改,例如搜索。对于应用程序,只有向上传递的事件很重要。刚刚解释了向下传递的事件,以便更全面地了解数据概念。
由于大多数应用程序都在寻找时间单位,因此下面的示例也是如此:
static void
seek_to_time (GstElement *element,
guint64 time_ns)
{
GstEvent *event;
event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
GST_SEEK_FLAG_NONE,
GST_SEEK_METHOD_SET, time_ns,
GST_SEEK_TYPE_NONE, G_GUINT64_CONSTANT (0));
gst_element_send_event (element, event);
}
函数gst_element_seek()是此的快捷方式。这主要是为了展示它是如何工作的。
本章将总结您在前几章中学到的所有内容。它描述了简单GStreamer应用程序的所有方面,包括初始化库,创建元素,在管道中将元素打包在一起并播放此管道。通过这一切,您将能够构建一个简单的Ogg / Vorbis音频播放器。
我们将创建一个简单的第一个应用程序,一个简单的Ogg / Vorbis命令行音频播放器。为此,我们将仅使用标准GStreamer组件。播放器将读取命令行中指定的文件。让我们开始吧!
我们在初始化GStreamer中了解到,在您的应用程序中要做的第一件事是通过调用gst_init()来初始化GStreamer。此外,请确保应用程序包含gst / gst.h,以便正确定义所有函数名称和对象。使用#include
接下来,您将要使用gst_element_factory_make()创建不同的元素。对于Ogg / Vorbis音频播放器,我们需要一个从磁盘读取文件的源元素。GStreamer在名称“filesrc”下包含此元素。接下来,我们需要解析文件并将其解码为原始音频。GStreamer有两个元素:第一个将Ogg流解析为基本流(视频,音频),称为“oggdemux”。第二种是Vorbis音频解码器,它被称为“vorbisdec”。由于“oggdemux”为每个基本流创建动态pad,因此您需要在“oggdemux”元素上设置“pad-added”事件处理程序,就像您在动态(或sometimes)pads中学习的那样,链接Ogg demuxer和Vorbis解码器元素在一起。最后,我们还需要一个音频输出element,我们将使用“autoaudiosink”,它会自动检测您的音频设备。
剩下要做的就是将所有元素添加到容器元素GstPipeline中,并等到我们播放整首歌曲。我们之前已经学会了如何在Bins中向容器bin中添加元素,并且我们已经了解了Element States中的元素状态。我们还将一个消息处理程序附加到管道总线,以便我们可以检索错误并检测流末尾。
现在让我们将所有代码添加到一起,以获得我们的第一个音频播放器:
#include
#include
static gboolean
bus_call (GstBus *bus,
GstMessage *msg,
gpointer data)
{
GMainLoop *loop = (GMainLoop *) data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:
g_print ("End of stream\n");
g_main_loop_quit (loop);
break;
case GST_MESSAGE_ERROR: {
gchar *debug;
GError *error;
gst_message_parse_error (msg, &error, &debug);
g_free (debug);
g_printerr ("Error: %s\n", error->message);
g_error_free (error);
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static void
on_pad_added (GstElement *element,
GstPad *pad,
gpointer data)
{
GstPad *sinkpad;
GstElement *decoder = (GstElement *) data;
/* We can now link this pad with the vorbis-decoder sink pad */
g_print ("Dynamic pad created, linking demuxer/decoder\n");
sinkpad = gst_element_get_static_pad (decoder, "sink");
gst_pad_link (pad, sinkpad);
gst_object_unref (sinkpad);
}
int
main (int argc,
char *argv[])
{
GMainLoop *loop;
GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink;
GstBus *bus;
guint bus_watch_id;
/* Initialisation */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* Check input arguments */
if (argc != 2) {
g_printerr ("Usage: %s \n", argv[0]);
return -1;
}
/* Create gstreamer elements */
pipeline = gst_pipeline_new ("audio-player");
source = gst_element_factory_make ("filesrc", "file-source");
demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
decoder = gst_element_factory_make ("vorbisdec", "vorbis-decoder");
conv = gst_element_factory_make ("audioconvert", "converter");
sink = gst_element_factory_make ("autoaudiosink", "audio-output");
if (!pipeline || !source || !demuxer || !decoder || !conv || !sink) {
g_printerr ("One element could not be created. Exiting.\n");
return -1;
}
/* Set up the pipeline */
/* we set the input filename to the source element */
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
/* we add a message handler */
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus);
/* we add all elements into the pipeline */
/* file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output */
gst_bin_add_many (GST_BIN (pipeline),
source, demuxer, decoder, conv, sink, NULL);
/* we link the elements together */
/* file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output */
gst_element_link (source, demuxer);
gst_element_link_many (decoder, conv, sink, NULL);
g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder);
/* note that the demuxer will be linked to the decoder dynamically.
The reason is that Ogg may contain various streams (for example
audio and video). The source pad(s) will be created at run time,
by the demuxer when it detects the amount and nature of streams.
Therefore we connect a callback function which will be executed
when the "pad-added" is emitted.*/
/* Set the pipeline to "playing" state*/
g_print ("Now playing: %s\n", argv[1]);
gst_element_set_state (pipeline, GST_STATE_PLAYING);
/* Iterate */
g_print ("Running...\n");
g_main_loop_run (loop);
/* Out of the main loop, clean up nicely */
g_print ("Returned, stopping playback\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
g_print ("Deleting pipeline\n");
gst_object_unref (GST_OBJECT (pipeline));
g_source_remove (bus_watch_id);
g_main_loop_unref (loop);
return 0;
}
我们现在已经创建了一个完整的管道,我们可以将管道可视化如下:
要编译helloworld示例,请使用:
gcc -Wall helloworld.c -o helloworld $(pkg-config --cflags --libs gstreamer-1.0)
GStreamer使用pkg-config来获取编译此应用程序所需的编译器和链接器标志。
如果您正在运行非标准安装(即,您自己从源代码安装了GStreamer而不是使用预构建的软件包),请确保将PKG_CONFIG_PATH环境变量设置为正确的位置($ libdir / pkgconfig)。
在不太可能的情况下,您使用的是未安装的GStreamer设置(即gst-uninstalled),您将需要使用libtool来构建hello world程序,如下所示:
libtool --mode=link gcc -Wall helloworld.c -o helloworld $(pkg-config --cflags --libs gstreamer-1.0)
您可以使用./helloworld file.ogg运行此示例应用程序。用您喜欢的Ogg / Vorbis文件替换file.ogg。
这是我们的第一个例子。如您所见,设置管道非常低级但功能强大。您将在本手册的后面部分看到如何使用更高级别的接口创建功能更强大的媒体播放器。我们将在GStreamer应用程序的高级接口中讨论所有这些内容。但是,我们首先会更深入地了解更先进的GStreamer内部结构。
从示例中可以清楚地看出,我们可以非常轻松地将“filesrc”元素替换为从网络读取数据的其他元素,或者与桌面环境更好地集成的其他数据源元素。此外,您可以使用其他解码器和解析器/解复用器来支持其他媒体类型。如果您没有运行Linux,可以使用另一个音频接收器,但是Mac OS X,Windows或FreeBSD,或者您可以使用filesink将音频文件写入磁盘而不是播放它们。通过使用音频卡源,您甚至可以进行音频捕获而不是播放。所有这些都表明了GStreamer元素的可重用性,这是它最大的优势。