GStreamer 02 Manual Hello World

发布于 2023-02-15  108 次阅读


一、目标

  在上一篇文章中,我们介绍了如何使用gst_parse_launch快速创建一个pipeline。本章中我们将学习播放一个test video的流程:

  1. 什么是GStreamer Element,以及如何创建。
  2. 如何连接这些元素。
  3. 如何自定义元素的属性。
  4. 如何监视总线的错误情况,以及如何从GStreamer message中提取出其信息。

二、代码

int BT02_ManualHelloWorld(int argc, char** argv)
{
    GstElement *pipeline, *source, *sink;
    GstBus *bus;
    GstMessage *msg;
    GstStateChangeReturn ret;
    int retval = 0;

    /* Initialize GStreamer */
    gst_init (&argc, &argv);

    /* Create the elements */
    source = gst_element_factory_make("videotestsrc", "source");
    sink = gst_element_factory_make("autovideosink", "sink");

    /* Create the empty pipeline */
    pipeline = gst_pipeline_new("test-pipeline");

    if (!pipeline || !source || !sink)
    {
        g_printerr("No Element Created.\n");
        retval = -1;
        goto out_return;
    }

    /* Build the pipeline */
    gst_bin_add_many(GST_BIN(pipeline), source, sink, NULL);
    if (gst_element_link(source, sink) != TRUE)
    {
        g_printerr("No Element Link.\n");
        retval = -2;
        goto out_unref;
    }

    /* Modify the source's properties */
    g_object_set(source, "pattern", 0 , NULL);

    /* Start playing */
    ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) 
    {
        g_printerr("Unable to set the pipeline to the playing state.\n");
        retval = -3;
        goto out_unref;
    }

    /* Wait until error or EOS */
    bus = gst_element_get_bus(pipeline);
    msg = gst_bus_timed_pop_filtered(
        bus, GST_CLOCK_TIME_NONE,
        GST_MESSAGE_ERROR | GST_MESSAGE_EOS
    );

    /* Parse message */
    if (msg != NULL)
    {
        GError *err;
        gchar *debug_info;

        switch (GST_MESSAGE_TYPE(msg))
        {
            case GST_MESSAGE_ERROR:
                gst_message_parse_error(msg, &err, &debug_info);

                g_printerr("Error received from element %s: %s\n",
                    GST_OBJECT_NAME (msg->src), err->message
                );
                g_printerr("Debugging information: %s\n",
                    debug_info ? debug_info : "none"
                );

                g_clear_error(&err);
                g_free(debug_info);
                break;
            case GST_MESSAGE_EOS:
                g_print("End-Of-Stream reached.\n");
                break;
            default:
                g_printerr("Unexpected message received.\n");
                break;
        }

        gst_object_unref(msg);
    }

    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
out_unref:
    gst_object_unref(pipeline);
out_return:
    return retval;
}

三、解释

source -> filter -> sink

  Elements是GStreamer最基本的构造块。它们处理从source(data producers)藉由filter到sink(data consumers)的数据流。

3.1 创建元素

source = gst_element_factory_make("videotestsrc", "source");
sink = gst_element_factory_make("autovideosink", "sink");

  通过上述代码我们已知,元素可被gst_element_factory_make()创建。其第一个参数,指创建的元素的类型(参考BT14,和BT10)。第二个参数是该Element实例化的名字。如果没有保留对应元素的指针,那么使用命名将有助于debug一类的管理。如果传递NULL作为参数,则GStreamer将自动为其命名。

  在本例中,我们创建了两个元素,videotestsrcautovideosink。这里没有filter元素,因此整个管线看起来是这样的:

source(videotestsrc) -> sink(autovideosink)

  • videotestsrc创建一个测试视频。该视频只在测试中使用,实际项目中并不会用到。
  • autovideosink在窗口上显示它所接收到的图像。该命令自动选择适合的sink实例。

3.2 创建管线

  所有的元素需要在管线中才能被使用,管线负责传递元素直接的信息和时钟。我们使用pipeline = gst_pipeline_new()来创建管线。

  管线是一种特殊类型的元素,它是一个存放其他元素的容器。在我们创建一条完整的管线时,我们需要先向其中添加元素(gst_bin_add),然后再连接它们(gst_element_link),但是需要注意,在连接时有些元素的pad并不会自动实现。在上面的例子中:

gst_bin_add_many(GST_BIN(pipeline), source, sink, NULL);

gst_element_link(source, sink) != TRUE

同时,连接需要注意其顺序。

3.3 特性

  GStreamer元素都是特殊的GObject,它是提供property facilities的实体。大多数的元素都有可自定义的属性:可以修改以更改元素的行为(可写属性)或查询以了解元素的内部状态(可读属性)的命名属性。

  属性可以通过g_object_get()获取;通过g_object_set()设置。g_object_set()接受以NULL结尾的属性名、属性值对列表,因此可以一次更改多个属性。

g_object_set(source, "pattern", 0 , NULL);

这里通过设置videotestsrcpattern属性,来改变测试视频的输出。

3.4 错误检查

  此时我们已经完成了整个管线的建构,下面是一些错误检查。

ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) 
{
    g_printerr("Unable to set the pipeline to the playing state.\n");
    retval = -3;
    goto out_unref;
}

  在上一节教程中,我们同样试了了gst_element_set_state。但是这次我们需要检查其返回值,以确定语句是否正常运行。关于“更改状态”的更多内容将在下一篇文章中讨论。

/* Wait until error or EOS */
bus = gst_element_get_bus(pipeline);
msg = gst_bus_timed_pop_filtered(
    bus, GST_CLOCK_TIME_NONE,
    GST_MESSAGE_ERROR | GST_MESSAGE_EOS
);

/* Parse message */
if (msg != NULL)
{
    GError *err;
    gchar *debug_info;

    switch (GST_MESSAGE_TYPE(msg))
    {
        case GST_MESSAGE_ERROR:
            gst_message_parse_error(msg, &err, &debug_info);

            g_printerr("Error received from element %s: %s\n",
                GST_OBJECT_NAME (msg->src), err->message
            );
            g_printerr("Debugging information: %s\n",
                debug_info ? debug_info : "none"
            );

            g_clear_error(&err);
            g_free(debug_info);
            break;
        case GST_MESSAGE_EOS:
            g_print("End-Of-Stream reached.\n");
            break;
        default:
            g_printerr("Unexpected message received.\n");
            break;
    }

    gst_object_unref(msg);
}

  gst_bus_timed_pop_filtered等待执行结束并返回我们之前忽略的GstMessage。我们要求gst_bus_timed_pop_filtered在遇到错误或者到达EOS(End of Stream)时返回一条GstMessage,因此我们检查这个返回值是错误还是EOS

  GstMessage是一种非常通用的结构,它几乎可以传递所有类型的消息。并且GStreamer为其提供了一系列解析函数。

  1. 我们首先可以通过GST_MESSAGE_TYPE()来解析消息的类型。
  2. 如果遇到错误,则可以使用gst_message_parse_error提取消息到GError中。

3.5 Gst总线

  在这一点上,值得更正式地介绍一下GStreamer总线。它是负责将元素生成的GstMessages按顺序交付给应用程序并交付给应用程序线程的对象。最后一点很重要,因为实际的媒体流是在应用程序之外的另一个线程中完成的。

  可以使用gst_bus_timed_pop_filtered()及其同级从总线同步提取消息,或者使用信号异步提取消息(在下一教程中显示)。 您的应用程序应始终关注总线,以便收到有关错误和其他播放相关问题的通知。

    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
out_unref:
    gst_object_unref(pipeline);
out_return:
    return retval;

余下的这部分代码则是清理。

四、总结

  本篇文章描述了:

  1. 如何使用gst_element_factory_make来创建元素。
  2. 如何使用gst_pipeline_new来创建管线。
  3. 如何使用gst_bin_add_many来为管线添加元素。
  4. 如何使用gst_element_link来连接管线中元素。