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时进行两部分的最后的链接。