英文原文:https://gstreamer.freedesktop.org/documentation/plugin-development/basics/testapp.html
你将经常想用尽可能小的设置来测试你新写的插件。通常,gst-launch-1.0是用于测试插件的第一步。如果你还未在Gstreamer的搜索目录上安装你的插件,你将需要设置插件路径。或者设置GST_PLUGIN_PATH变量为包含你插件的目录路径,或者用命令行选项--gst-plugin-path。如果你的插件基于gst-plugin模板,它将类似于gst-launch-1.0 --gst-plugin-path=$HOME/gst-template/gst-plugin/src/.libs TESTPIPELINE一样查找。然而,你将经常需要更多的测试特性,而不仅仅是gst-launch-1,0提供的,如seeking, events, interactivity等更多。为了实现这些,写一个你自己的小型的测试程序是最简单的方式。这部分会以非常少的内容来描述如何实现。对于一个完整的应用开发教程,请见应用开发手册。
首先,你需要通过调用gst_init()函数来初始化Gstreamer的核心库。你也可以通过调用gst_init_get_option_group()函数来实现,这个函数将返回一个指向GOptionGroup的指针,然后,你可以用GOption来处理初始化工作,这将完成Gstreamer的初始化。
你可以使用gst_element_factory_make()函数来创建elemens,其中第一个参数是你想创建的element类型,第二个参数是一个任取的名称。例子使用了一个简单的filesource - decoder - soundcard output pipeline,但是,如果需要的话,你可以使用特殊的需调试的element。例如,一个identity element可以作用在pipeline的中间,作为应用中的一个数据传送者。在你的应用中,它可用于检查数据的不正当性和正确性。同样的,你可在pipeline的最后面用一个fakesink element来下载你的数据到标准输出(为了实现这个,请设置dump的属性为TRUE)。最后,你可以用valgrind来检查内存错误。
在链接期间,你的测试应用可以用过滤caps来驱动一个特殊类型的数据从你的element输出或输入到你的element中。在你的element中,这是一个非常简单且有效的检测多种输入输出类型的方式。
注意到在运行期间,为了正确的处理,你应该在总线和/或你的插件/element上监听至少"error"和"eos"信息。同样的,你应该添加event到pipeline中,并确保你的插件会正确地处理这些event(关于clocking, internal caching等)。
永远别忘记在你的插件和应用中清理内存。当进入NULL状态时,你的element应该清理已分配的内存和caches。同样的,应该关闭任何引用到可能支持的库。你的应用程序应该unref() pipeline并确保它不会崩溃。
#include <gst/gst.h>
static gboolean
bus_call (GstBus *bus,
GstMessage *msg,
gpointer data)
{
GMainLoop *loop = 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 = NULL;
GError *err = NULL;
gst_message_parse_error (msg, &err, &debug);
g_print ("Error: %s\n", err->message);
g_error_free (err);
if (debug) {
g_print ("Debug details: %s\n", debug);
g_free (debug);
}
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
gint
main (gint argc,
gchar *argv[])
{
GstStateChangeReturn ret;
GstElement *pipeline, *filesrc, *decoder, *filter, *sink;
GstElement *convert1, *convert2, *resample;
GMainLoop *loop;
GstBus *bus;
guint watch_id;
/* initialization */
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
if (argc != 2) {
g_print ("Usage: %s <mp3 filename>\n", argv[0]);
return 01;
}
/* create elements */
pipeline = gst_pipeline_new ("my_pipeline");
/* watch for messages on the pipeline's bus (note that this will only
* work like this when a GLib main loop is running) */
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
watch_id = gst_bus_add_watch (bus, bus_call, loop);
gst_object_unref (bus);
filesrc = gst_element_factory_make ("filesrc", "my_filesource");
decoder = gst_element_factory_make ("mad", "my_decoder");
/* putting an audioconvert element here to convert the output of the
* decoder into a format that my_filter can handle (we are assuming it
* will handle any sample rate here though) */
convert1 = gst_element_factory_make ("audioconvert", "audioconvert1");
/* use "identity" here for a filter that does nothing */
filter = gst_element_factory_make ("my_filter", "my_filter");
/* there should always be audioconvert and audioresample elements before
* the audio sink, since the capabilities of the audio sink usually vary
* depending on the environment (output used, sound card, driver etc.) */
convert2 = gst_element_factory_make ("audioconvert", "audioconvert2");
resample = gst_element_factory_make ("audioresample", "audioresample");
sink = gst_element_factory_make ("pulsesink", "audiosink");
if (!sink || !decoder) {
g_print ("Decoder or output could not be found - check your install\n");
return -1;
} else if (!convert1 || !convert2 || !resample) {
g_print ("Could not create audioconvert or audioresample element, "
"check your installation\n");
return -1;
} else if (!filter) {
g_print ("Your self-written filter could not be found. Make sure it "
"is installed correctly in $(libdir)/gstreamer-1.0/ or "
"~/.gstreamer-1.0/plugins/ and that gst-inspect-1.0 lists it. "
"If it doesn't, check with 'GST_DEBUG=*:2 gst-inspect-1.0' for "
"the reason why it is not being loaded.");
return -1;
}
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, convert1, filter,
convert2, resample, sink, NULL);
/* link everything together */
if (!gst_element_link_many (filesrc, decoder, convert1, filter, convert2,
resample, sink, NULL)) {
g_print ("Failed to link one or more elements!\n");
return -1;
}
/* run */
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
GstMessage *msg;
g_print ("Failed to start up pipeline!\n");
/* check if there is an error message with details on the bus */
msg = gst_bus_poll (bus, GST_MESSAGE_ERROR, 0);
if (msg) {
GError *err = NULL;
gst_message_parse_error (msg, &err, NULL);
g_print ("ERROR: %s\n", err->message);
g_error_free (err);
gst_message_unref (msg);
}
return -1;
}
g_main_loop_run (loop);
/* clean up */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
g_source_remove (watch_id);
g_main_loop_unref (loop);
return 0;
}