之前用gstreamer和QT开发了一个粗糙的MP4播放器(GStreamer开发简单MP4播放器(二)),只能实现mp4格式的文件播放,而且bug比较多,代码结构比较乱,没有继续开发下去。最近事不是很多,因此打算抽空重新用gstreamer写一个player,记录一下折腾过程。
1 实现思路
本次主要实现两个功能:
(1)封装player start 和stop接口,main函数中调用这两个接口实现播放和停止。
(2)播放任务放在一个单独的线程中执行。
首先定义一个结构体,存放hanle(也称player)的一些重要字段:
typedef struct _ST_MEDIA_HANDLE {
char *filePath;
GThread *playThread;
GstElement *pipeline; /* Our one and only element */
GMainLoop *main_loop; /* GLib's Main Loop */
GstBus *bus;
gint flags;
} ST_MEDIA_HANDLE;
再定义两个对外的接口:
int Start_Play(char *path);
int Stop_Play(void);
调用Start_Play并传入需要播放的文件地址或者streame 地址,初始化资源并开始播放;调用Stop_Play来停止播放,并释放资源。
Start_Play接口如下,通过 g_thread_new创建paly_thread:
int Start_Play(char *path)
{
int ret = 0;
g_print("start play IN. \n");
if (path == NULL)
{
g_printerr ("file path is NULL.\n");
ret = -1;
goto end;
}
mediaHandle = (ST_MEDIA_HANDLE *)malloc(sizeof(ST_MEDIA_HANDLE));
if (!mediaHandle)
{
g_printerr ("malloc mediaHandle fail.\n");
ret = -1;
goto end;
}
mediaHandle->filePath = path;
gst_init(NULL, NULL);
mediaHandle->playThread = g_thread_new("paly_thread", _paly_thread, NULL);
if (!mediaHandle->playThread)
{
g_printerr ("create paly thread fail.\n");
ret = -1;
}
g_print("start play OUT. \n");
end:
return ret;
}
paly_thread执行_paly_thread函数:
void _paly_thread(void)
{
GstStateChangeReturn ret;
mediaHandle->pipeline = gst_element_factory_make ("playbin", "media_playbin");
if (!mediaHandle->pipeline)
{
g_printerr ("Not all elements could be created.\n");
goto exit;
}
/* Set the URI to play */
g_object_set (mediaHandle->pipeline, "uri", mediaHandle->filePath, NULL);
/* Set flags to show Audio and Video but ignore Subtitles */
g_object_get (mediaHandle->pipeline, "flags", &mediaHandle->flags, NULL);
mediaHandle->flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO;
mediaHandle->flags &= ~GST_PLAY_FLAG_TEXT;
g_object_set (mediaHandle->pipeline, "flags", mediaHandle->flags, NULL);
/* Add a bus watch, so we get notified when a message arrives */
mediaHandle->bus = gst_element_get_bus (mediaHandle->pipeline);
gst_bus_add_watch (mediaHandle->bus, (GstBusFunc)handle_message, mediaHandle);
gst_object_unref (mediaHandle->bus);
mediaHandle->bus = NULL;
/* Start playing */
ret = gst_element_set_state (mediaHandle->pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE)
{
g_printerr ("Unable to set the pipeline to the playing state.\n");
goto exit;
}
/* Create a GLib Main Loop and set it to run */
mediaHandle->main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (mediaHandle->main_loop);
g_print("main loop run stop. \n");
exit:
if (mediaHandle->pipeline)
{
gst_object_unref (mediaHandle->pipeline);
}
g_thread_unref(mediaHandle->playThread);
mediaHandle->playThread = NULL;
if (mediaHandle->main_loop)
{
g_main_loop_unref (mediaHandle->main_loop);
}
if (mediaHandle)
{
free(mediaHandle);
}
return NULL;
}
之前的博客里创建pipeline时先把需要的每个element创建好再add到pipeline,这种方法只能播放一种特定类型的文件,现在使用gst_element_factory_make创建playbin,playbin也是gstreamer的一个plugin,可以根据播放的文件自动添加需要的elemet和plugin,这样就可以播放各种类型的多媒体文件。
mediaHandle->pipeline = gst_element_factory_make ("playbin", "media_playbin");
_paly_thread其余的bus监听和msg处理不再细说,有兴趣的可以对着代码看看,都是一些固定的套路。
Stop_Play主要将pipeline 状态设置为GST_STATE_NULL,并退出main loop。
stateRet = gst_element_set_state (mediaHandle->pipeline, GST_STATE_NULL);
...
g_main_loop_quit (mediaHandle->main_loop);
这样通过调用Start_Play、Stop_Play可实现简单的播放控制,后期会编译生成一个so,把两个接口export出去,目前先编写一个简单的main函数测试一下这两个接口:
int main()
{
int ret = 0;
char *filePath = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm";
ret = Start_Play(filePath);
if (ret != 0)
{
g_printerr ("play file %s error.\n", filePath);
return - 1;
}
//play 50s
sleep(50);
ret = Stop_Play();
if (ret != 0)
{
g_printerr ("stop play file %s error.\n", filePath);
return - 1;
}
return 0;
}
makefile比较简单,编译的时候链接到gstreamer-1.0 glib-2.0就可以了:
CFLAGS=`pkg-config --cflags gstreamer-1.0 glib-2.0`
LIBS=`pkg-config --libs gstreamer-1.0 glib-2.0`
TARGET=MediaPlayer
all: gst_player
gst_player:
gcc -o $(TARGET) mediaPlayer.c $(LIBS) $(CFLAGS)
clean:
rm -rf $(TARGET)
2 项目git地址
项目已上传到github,本博客对应的tag为v0.1,git地址:https://github.com/zhenghaiyang123/gst_player.git