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