本章将总结您在前几章中学到的所有内容。它描述了一个简单的GStreamer应用程序的所有方面,包括初始化库、创建元素、将元素打包到管道中以及播放此管道。通过完成所有这些,您将能够构建一个简单的Ogg/Vorbis音频播放器。
Hello world
我们将创建一个简单的第一个应用程序,一个简单的Ogg/Vorbis命令行音频播放器。为此,我们将仅使用标准GStreamer组件。播放器将读取命令行上指定的文件。让我们开始吧!
我们在初始化GStreamer时了解到在您的应用程序中要做的第一件事是通过以下方式初始化GStreamer 调用gst_init ()
。此外,请确保应用程序包括 gst/gst.h
,以便正确定义所有函数名称和对象。使用 #include <gst/gst.h>
。
接下来,您将需要使用 gst_element_factory_make ()
。对于Ogg/Vorbis音频播放器,我们将需要一个从磁盘读取文件的源元素。GStreamer包括名称为“filesrc”的元素。接下来,我们需要一些东西来解析文件并将其解码为原始音频。GStreamer有两个元素:第一个将Ogg流解析为基本流(视频, 音频)并被称为“oggdemux”。第二个是Vorbis音频解码器, 它被方便地称为“Vorbisdec”。由于“oggdemux”对于每个基本流创建动态 pads,您需要设置一个“pad-add”事件 oggdemux元素上的处理程序,就像你在动态(或有时)pads中学到的那样,链接 Ogg解复用器和Vorbis解码器元素在一起。最后,我们将还需要一个音频输出元素,我们将使用“autoaudiosink”,它自动检测您的音频设备。
最后要做的是将所有元素添加到容器中元素,GstPipeline
,然后等到我们播放了整首歌。 我们之前已经学会了如何在Bins中向容器bin中添加元素,并且我们已经了解了Element States中的元素状态。我们还将附加一个消息处理程序到管道总线,以便我们可以检索错误并检测流结束。
现在让我们将所有代码添加在一起以获得我们的第一个音频播放器:
#include <gst/gst.h>
#include <glib.h>
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 <Ogg/Vorbis filename>\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. c
要编译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-env)的不太可能的情况下,您将需要使用libtools来构建hello world程序,如下所示:
libtool --mode=link gcc -Wall helloworld.c -o helloworld $(pkg-config --cflags --libs gstreamer-1.0)
您可以使用./helloworld file.ogg
运行此示例应用程序。将file.ogg
替换为您最喜欢的Ogg/Vorbis文件。
结论
我们的第一个例子到此结束。如您所见,设置管道是非常低级但功能强大。您将在本手册后面看到您如何可以使用更少的努力创建更强大的媒体播放器 高级接口。我们将在GStreamer应用程序的高级接口中讨论所有这些。但是,我们首先深入了解更高级的GStreamer内部结构。
从示例中应该很清楚,我们可以非常轻松地将“filesrc”元素替换为从网络读取数据的其他一些元素,或者与您的桌面环境更好地集成的其他一些数据源元素。此外,您可以使用其他解码器和解析器/解复用器来支持其他媒体类型。如果您运行的不是Linux,而是Mac OS X、Windows或FreeBSD,您可以使用另一个音频接收器,或者您可以改为使用文件链接将音频文件写入磁盘,而不是回放它们。通过使用音频卡源,您甚至可以进行音频捕获而不是播放。所有这些都显示了GStreamer元素的可重用性,这是它最大的优势。
helloworld. c 解析:
#include <gst/gst.h> // GStreamer库的头文件
#include <glib.h> // GLib库的头文件
// 处理GStreamer总线消息的回调函数
static gboolean
bus_call (GstBus *bus, // GStreamer总线
GstMessage *msg, // 接收到的消息
gpointer data) // 用户数据(这里是主循环)
{
GMainLoop *loop = (GMainLoop *) data; // 将用户数据转换为GMainLoop类型
// 根据消息类型进行处理
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; // 继续监听消息
}
// 动态链接pad的回调函数
static void
on_pad_added (GstElement *element, // 被添加pad的元素
GstPad *pad, // 新的pad
gpointer data) // 用户数据(这里是解码器)
{
GstPad *sinkpad;
GstElement *decoder = (GstElement *) data; // 将用户数据转换为GstElement类型(解码器)
// 打印调试信息
g_print ("Dynamic pad created, linking demuxer/decoder\n");
// 获取解码器的sink pad
sinkpad = gst_element_get_static_pad (decoder, "sink");
// 将新创建的pad链接到解码器的sink pad
gst_pad_link (pad, sinkpad);
// 解除引用sink pad
gst_object_unref (sinkpad);
}
// 主函数
int
main (int argc, // 参数数量
char *argv[]) // 参数值
{
GMainLoop *loop; // 主循环
GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink; // GStreamer元素
GstBus *bus; // 用于消息的总线
guint bus_watch_id; // 总线监听ID
// 初始化GStreamer
gst_init (&argc, &argv);
// 创建一个新的主循环
loop = g_main_loop_new (NULL, FALSE);
// 检查输入参数
if (argc != 2) {
g_printerr ("Usage: %s <Ogg/Vorbis filename>\n", argv[0]);
return -1;
}
// 创建GStreamer元素
// 创建一个名为"audio-player"的GStreamer管道
pipeline = gst_pipeline_new ("audio-player");
// 创建一个文件源元素,用于读取音频文件
source = gst_element_factory_make ("filesrc", "file-source");
// 创建一个Ogg格式的解复用器,用于将Ogg文件中的音频和视频分离
demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer");
// 创建一个Vorbis解码器,用于将Vorbis编码的音频数据解码为原始音频数据
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;
}
// 设置管道
// 设置输入文件名到source元素
g_object_set (G_OBJECT (source), "location", argv[1], NULL);
// 添加消息处理器到总线
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus); // 解除引用总线
// 将所有元素添加到管道中
gst_bin_add_many (GST_BIN (pipeline),
source, demuxer, decoder, conv, sink, NULL);
// 将source元素链接到demuxer
gst_element_link (source, demuxer);
// 将decoder、converter和sink元素链接在一起
gst_element_link_many (decoder, conv, sink, NULL);
// 连接demuxer的pad-added信号到回调函数
g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder);
// 打印正在播放的文件名
g_print ("Now playing: %s\n", argv[1]);
// 设置管道为“播放”状态
gst_element_set_state (pipeline, GST_STATE_PLAYING);
// 打印运行状态
g_print ("Running...\n");
// 运行主循环
g_main_loop_run (loop);
// 主循环退出后进行清理
g_print ("Returned, stopping playback\n");
gst_element_set_state (pipeline, GST_STATE_NULL); // 设置管道为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;
}