ubuntn GStreamer + QT多媒体播放器开发(一)

  • A+

之前用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

3 参考文章

Playback tutorial 1: Playbin usage

w3cjava