Gsteamer播放MP4文件

1.概述:

gstreamer提供了gst-launch工具,使用该工具我们可以很方便的搭建各种管道,比如gst-launc-1.0 videotestsrc ! autovideosink输入上述命令,我们就能测试视频通路是否OK,但有些场景需要我们提供代码形式。本篇文章将以mp4视频文件的播放为例子介绍基于gst-launc-1.0以及代码形式如何实现MP4视频文件播放.

2.使用gst-launch工具

终端输入如下命令即可播放MP4视频(封装格式是MP4,编码格式是H264的视频文件)

gst-launch-1.0 filesrc location = /path/to/your/file.mp4 ! qtdemux ! queue ! h264parse ! omxh264dec ! queue! v4l2sink device=/dev/video1

omxh264dec为h264硬解码器,不同平台所提供的h264硬解码器并不一样,需根据平台自行更换h264硬解码器,没有硬解码器可以使用avdec_h264替代进入软解码。

也可以使用playbin进行播放如下:

gst-launch-1.0 playbin uri=file:///path/to/your/file.mp4

3.代码形式实现MP4文件播放

在开发某个视频播放需求时,我们一般会使用gst-launch工具快速构建管道测试此管道是否满足这个需求,当验证通过后我们就会以此管道为参照以代码形式去实现此管道。

为什么一定要用代码去实现,不能直接调用系统命令执行管道这个问题?

1.命令行方式无法获取各元素的状态

2.无法精确控制pad的连接/断开,设置管道的各种状态以及错误信息判断等

3.1 快速构建管道代码

当我们实现的需求很简单,举个例子实现一个视频播放,如果不需要对每个元素精确设置属性,我们可以借助gst_parse_launch将gst-launch使用的管道快速转换成代码:

代码如下:

gboolean my_bus_callback (GstBus *bus, GstMessage *message, gpointer data)
{
    GstElement*    pipeline =(GstElement*    )data;

    switch (GST_MESSAGE_TYPE (message))
    {
        case GST_MESSAGE_ERROR:
        {
            GError *err;
            gchar *debug;
            gst_message_parse_error (message, &err, &debug);
            printf("####Error: %s\n", err->message);
            g_error_free (err);
            g_free (debug);
            break;
        }
        case GST_MESSAGE_ASYNC_DONE:
        {
            printf("######### logo play GST_MESSAGE_ASYNC_DONE #########");
            gst_element_set_state(pipeline, GST_STATE_PLAYING);
        }
            break;
        case GST_MESSAGE_EOS:
        {
            printf("######### logo play eos #########");

        }
            break;
        default:
            /* unhandled message */
            break;
    }
    return TRUE;
}
// play logo
void PlayLogo()
{
        char cmd[256];
        string file_path = "test.mp4"
        if (!gst_is_initialized())
        {
            if (!gst_init_check(nullptr, nullptr, nullptr))
            {
                return;
            }
        }
        sprintf(cmd,"filesrc location = %s ! qtdemux ! queue ! h264parse ! omxh264dec ! queue! v4l2sink \"device=/dev/video1\"",file_path.c_str())
        GstElement * logo_play_pipeline = gst_parse_launch(cmd,nullptr);

        bus = gst_pipeline_get_bus (GST_PIPELINE (logo_play_pipeline));
        gst_bus_add_watch (bus, my_bus_callback, logo_play_pipeline);

        gst_element_set_state(logo_play_pipeline, GST_STATE_PLAYING);

        g_main_loop_run(g_main_loop_new(NULL, FALSE));

        /* Free resources */
        gst_object_unref (bus);
        gst_element_set_state (logo_play_pipeline, GST_STATE_NULL);
        gst_object_unref (logo_play_pipeline);
}
3.2 构建管道代码

当我们需要对每个元素精确设置属性/操作时,或者在某些状态下控制pad的连接/断开等等,我们最好的方式是单独创建出每个element然后链接成管道,这样就能在有需要做逻辑的地方直接操作element.

代码举例如下:

/* This function will be called by the pad-added signal */
static void pad_added_handler (GstElement *src, GstPad *new_pad, GstElement *data){

    GstPad *sink_pad = gst_element_get_static_pad (data, "sink");
    GstPadTemplate* name_template = gst_pad_get_pad_template(new_pad);
    GstPadLinkReturn ret;

    /* If our converter is already linked, we have nothing to do here */
    if (gst_pad_is_linked (sink_pad)) {
        /* Unreference the sink pad */
        gst_object_unref (sink_pad);
        MWP_WARN("We are already linked. Ignoring:%s\n",name_template->name_template);
        return;
    }
    /* Check the new pad's type */
    if ((name_template != nullptr) && g_strcmp0(name_template->name_template, "video_%u") == 0)
    {
        /* Attempt the link */
        ret = gst_pad_link (new_pad, sink_pad);
        if (GST_PAD_LINK_FAILED (ret)) {
            MWP_ERROR ("Type is '%s' but link failed.\n",name_template->name_template);
        } else {
            MWP_DEBUG ("Link succeeded (type '%s').\n",name_template->name_template);
        }
    }

    /* Unreference the sink pad */
    gst_object_unref (sink_pad);
    return;
}

/* This function will be creat pipeline for mp4 video file to play.  */
GstElement * CreatLogoPlayPipeLine(std::string path)
{
    GstElement* logoPipeline = nullptr;
    GstElement* fileSrc = nullptr;
    GstElement* qtDemux = nullptr;
    GstElement* queue1 = nullptr;
    GstElement* h264parse = nullptr;
    GstElement* omxh264dec = nullptr;
    GstElement* queue2 = nullptr;
    GstElement* v4l2sink = nullptr;

    /* Create the empty pipeline */
    logoPipeline = gst_pipeline_new("logoPlayPipeLine");
    /* Create the elements */
    fileSrc = gst_element_factory_make ("filesrc", "filesrc");
    qtDemux = gst_element_factory_make ("qtdemux", "qtdemux");
    queue1 = gst_element_factory_make ("queue", "queue1");
    h264parse = gst_element_factory_make ("h264parse", "h264parse");
    omxh264dec = gst_element_factory_make ("omxh264dec", "omxh264dec");
    queue2 = gst_element_factory_make ("queue", "queue2");
    v4l2sink = gst_element_factory_make ("v4l2sink", "v4l2sink");

    if (!logoPipeline || !fileSrc || !qtDemux || !queue1 || !h264parse || !omxh264dec || !queue2 || !v4l2sink)
    {
        MWP_ERROR("element creat fail!");
        return nullptr;
    }

    g_object_set(G_OBJECT(fileSrc),"location", path.c_str(),nullptr);

    g_object_set(G_OBJECT(v4l2sink),
                 "sync", false,
                 "device","/dev/video1",
                 "io-mode",2,
                 "max-lateness",(gint64)10000000,
                  nullptr);
    /* Connect to the pad-added signal */
    g_signal_connect(qtDemux,"pad-added",G_CALLBACK(pad_added_handler),queue1);

    gst_bin_add_many(GST_BIN(logoPipeline),fileSrc,qtDemux,queue1,h264parse,omxh264dec,queue2,v4l2sink,nullptr);

    /* Build the pipeline. Note that we are NOT linking the source at this
     * point. We will do it later. */
    if (!gst_element_link_many(fileSrc,qtDemux, nullptr)){
        MWP_ERROR("[CreatLogoPlayPipeLine] pipeline elements could not be linked");
        if(logoPipeline != nullptr)
        {
            gst_object_unref(logoPipeline);
        }
        return nullptr;
    }

    if (!gst_element_link_many(queue1,h264parse,omxh264dec,queue2,v4l2sink, nullptr)){
        MWP_ERROR("[CreatLogoPlayPipeLine] pipeline elements could not be linked");
        if(logoPipeline != nullptr)
        {
            gst_object_unref(logoPipeline);
        }
        return nullptr;
    }
    return logoPipeline;
}

// play logo
void PlayLogo()
{
        char cmd[256];
        string file_path = "test.mp4"

        /* Initialize GStreamer */
        if (!gst_is_initialized())
        {
            if (!gst_init_check(nullptr, nullptr, nullptr))
            {
                return;
            }
        }
        GstElement * logo_play_pipeline = CreatLogoPlayPipeLine(file_path);

        bus = gst_pipeline_get_bus (GST_PIPELINE (logo_play_pipeline));

        gst_bus_add_watch (bus, my_bus_callback, logo_play_pipeline);

        gst_element_set_state(logo_play_pipeline, GST_STATE_PLAYING);

        g_main_loop_run(g_main_loop_new(NULL, FALSE));

        /* Free resources */
        gst_object_unref (bus);
        gst_element_set_state (logo_play_pipeline, GST_STATE_NULL);
        gst_object_unref (logo_play_pipeline);
}

Tips:由于它包含demuxer(qtdemux),它的源pad最初不可用,我们需要动态链接到它们。所以管道初始化时分为两部分,第一部分为fileSrc–>qtDemux,第二部分为queue1–>h264parse–>omxh264dec–>queue2–>v4l2sink,第一部分和第二部分管道的链接需要在运行时pad-added时进行两部分的最后的链接。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

威威攻城狮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值