GStreamer---Basic tutorial 3: 动态管道

学习完这篇教程,你讲具备开始Playback tutorial的必要知识:

  1. 如何在连接elements的时候实现更好的控制。
  2. 如何获取感兴趣的事件通知以便及时做出处理。
#include <gst/gst.h>

/* Structure to contain all our information, so we can pass it to callbacks */
//涉及回调,把所有数据分组到一个结构便于处理。
typedef struct _CustomData {
    //指向GstElement的指针
    GstElement* pipeline;
    GstElement* source;
    GstElement* convert;
    GstElement* resample;
    GstElement* sink;
} CustomData;

/* Handler for the pad-added signal */
//只是一个声明,后面会用到
static void pad_added_handler(GstElement* src, GstPad* pad, CustomData* data);

int main(int argc, char* argv[]) {
    CustomData data;
    GstBus* bus;
    GstMessage* msg;
    GstStateChangeReturn ret;
    gboolean terminate = FALSE;

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

    /* Create the elements */
    data.source = gst_element_factory_make("uridecodebin", "source");
    data.convert = gst_element_factory_make("audioconvert", "convert");
    data.resample = gst_element_factory_make("audioresample", "resample");
    data.sink = gst_element_factory_make("autoaudiosink", "sink");

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

    if (!data.pipeline || !data.source || !data.convert || !data.resample || !data.sink) {
        g_printerr("Not all elements could be created.\n");
        return -1;
    }

    /* Build the pipeline. Note that we are NOT linking the source at this
     * point. We will do it later. */
    gst_bin_add_many(GST_BIN(data.pipeline), data.source, data.convert, data.resample, data.sink, NULL);

    //这里我们将audioconvert,audioresample和autoaudiosink连接起来,但是并没有将它们和source连接
    //因为这这个阶段,uridecodebin还没有生成source pad
    if (!gst_element_link_many(data.convert, data.resample, data.sink, NULL)) {
        g_printerr("Elements could not be linked.\n");
        gst_object_unref(data.pipeline);
        return -1;
    }

    /* Set the URI to play */
    //设置uridecodebin的uri属性。
    g_object_set(data.source, "uri", "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);

    /* Connect to the pad-added signal */
    /*
    gulong g_signal_connect( 
        gpointer instance,
        const gchar *detailed_signal,
        GCallback c_handler,
        gpointer data );

        instance:信号发出者,可以认为我们操作的控件,如按下按钮,这个就为按钮指针
        detiled_signal:信号标志,如"pressed"
        ndler:回调函数的名称,需要用G_CALLBACK()进行转换
        data:给回调函数传的参数,gpointer 相当于C语言的 void *
        返回值:注册函数的标志
    */
    g_signal_connect(data.source, "pad-added", G_CALLBACK(pad_added_handler), &data);

    /* Start playing */
    ret = gst_element_set_state(data.pipeline, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE) {
        g_printerr("Unable to set the pipeline to the playing state.\n");
        gst_object_unref(data.pipeline);
        return -1;
    }

    /* Listen to the bus */
    //监听pipeline的erroe和EOS信号,并在发生时进行了打印了相应的信息,便于debug
    bus = gst_element_get_bus(data.pipeline);
    do {
        msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
            GST_MESSAGE_STATE_CHANGED | 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);
                terminate = TRUE;
                break;
            case GST_MESSAGE_EOS:
                g_print("End-Of-Stream reached.\n");
                terminate = TRUE;
                break;
            case GST_MESSAGE_STATE_CHANGED:
                /* We are only interested in state-changed messages from the pipeline */
                if (GST_MESSAGE_SRC(msg) == GST_OBJECT(data.pipeline)) {
                    GstState old_state, new_state, pending_state;
                    gst_message_parse_state_changed(msg, &old_state, &new_state, &pending_state);
                    g_print("Pipeline state changed from %s to %s:\n",
                        gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));
                }
                break;
            default:
                /* We should not reach here */
                g_printerr("Unexpected message received.\n");
                break;
            }
            gst_message_unref(msg);
        }
    } while (!terminate);

    /* Free resources */
    gst_object_unref(bus);
    gst_element_set_state(data.pipeline, GST_STATE_NULL);
    gst_object_unref(data.pipeline);
    return 0;
}

/* This function will be called by the pad-added signal */
/*
      src 是触发信号的 GstElement。在此示例中,它只能是 uridecodebin,因为它是我们附加的唯一信号。信号处理程序的第一个参数始终是触发它的对象。
      new_pad是加到src上的pad。通常来说,是我们需要连接的pad。
      data指针是跟随信号一起过来的参数,在这个例子中,我们传递的时CustomData指针。
*/
static void pad_added_handler(GstElement* src, GstPad* new_pad, CustomData* data) {
    //我们从CustomData中获取autoaudioconvertelement,并使用gst_element_get_static_pad()获取它的sink pad,要与new_pad连接的pad
    GstPad* sink_pad = gst_element_get_static_pad(data->convert, "sink");
    GstPadLinkReturn ret;
    GstCaps* new_pad_caps = NULL;
    GstStructure* new_pad_struct = NULL;
    const gchar* new_pad_type = NULL;

    g_print("Received new pad '%s' from '%s':\n", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(src));

    /* If our converter is already linked, we have nothing to do here */
    //uridecodebin将生成尽可能多的pads,这取决于它能够出处理的数据类型,并且没生成一个pad,回调都会被调用一次,
    //以下代码将避免我们连接后还尝试将new_pad与一个已经连接了的element连接。
    if (gst_pad_is_linked(sink_pad)) {
        g_print("We are already linked. Ignoring.\n");
        goto exit;
    }

    /* Check the new pad's type */
    //gst_pad_get_current_caps()检查当前pad的capabilities(当前输出的数据类型)
    new_pad_caps = gst_pad_get_current_caps(new_pad);
    //一个pad当前的caps上只有一个GstStructure代表了单一的媒体格式。在这个pad中我们想要的只有音频capability,所以我们使用gst_caps_get_structure()检索pad的第一个GstStructure。
    new_pad_struct = gst_caps_get_structure(new_pad_caps, 0);
    //使用gst_structure_get_name()来恢复结构体的name成员,它包含媒体数据格式的主要描述。
    new_pad_type = gst_structure_get_name(new_pad_struct);
    if (!g_str_has_prefix(new_pad_type, "audio/x-raw")) {
        g_print("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
        goto exit;
    }

    /* Attempt the link */
    ret = gst_pad_link(new_pad, sink_pad);
    if (GST_PAD_LINK_FAILED(ret)) {
        g_print("Type is '%s' but link failed.\n", new_pad_type);
    }
    else {
        g_print("Link succeeded (type '%s').\n", new_pad_type);
    }

exit:
    /* Unreference the new pad's caps, if we got them */
    if (new_pad_caps != NULL)
        gst_caps_unref(new_pad_caps);

    /* Unreference the sink pad */
    gst_object_unref(sink_pad);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ap21ril

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值