ubuntu GStreamer + QT多媒体播放器开发(二)

本篇博客在上一篇ubuntu GStreamer + QT多媒体播放器开发(一)的基础上主要修改新增以下几点:
(1)使用gstreamer播放控制的部分封装成mmpalyer lib,对外的接口以及一些结构体和枚举定义单独抽离到mediaplayer.h头文件中;
(2)使用QT调用mmpalyer lib提供的接口实现播放,视频在QT窗口中进行渲染;
(3)增加playbin3 的element-added signal 监听,方便查看playbin3在自动创建pipeline时增加了那些element;
(4)mmpalyer lib初始化player完成后通过callback 返回给UI(QT)需要的参数,如pipeline地址,player id等。
以下对新增的部分简单进行说明。

1 封装mmpalyer lib

mmpalyer lib预期对外开放以下6个接口,供UI调用,实现player init、play、resume、pause等基本操作,目前只实现了init 、play、stop三个接口。

int MMPlayerInit(ST_INIT_PARAM *pstInitParam);
int MMPlayerPlay(HANDLE_ID hanldeId);
int MMPlayerStop(HANDLE_ID hanldeId);
int MMPlayerPause(HANDLE_ID hanldeId);
int MMPlayerResume(HANDLE_ID hanldeId);
int MMPlayerGetPostion(HANDLE_ID hanldeId);

play初始化时传入的是ST_INIT_PARAM结构体指针,主要向mmpalyer传递播放文件路径和初始化完成后的CALL_BACK_FUNCTION。

typedef struct _ST_INIT_PARAM {
    
    
    gint handleId;
    char *path;
    CALL_BACK_FUNCTION callBackFunction;
} ST_INIT_PARAM;

call back function传递的参数一个是CALL_BACK_EVENT_TYPE,另一个是void的类型指针,可以传递一些需要数据给UI。

/*call back function*/ 
typedef void (*CALL_BACK_FUNCTION)(CALL_BACK_EVENT_TYPE eventType, void *param);

CALL_BACK_EVENT_TYPE主要定义了一些关键的event,如init ok、play ok等,后期会扩充。

typedef enum {
    
    
    PLAYER_INIT_OK = 0,         
    CHANGE_TO_PALYING_OK,
    CHANGE_TO_PUASED_OK, 
    PALY_ERROR = 20,      
} CALL_BACK_EVENT_TYPE;

2 视频在QT窗口中渲染

实现的QT窗口中播放视频的关键代码如下:

    //play mm in QT window
    PlayerWindow *window = new PlayerWindow(userHandle.pipeline);
    window->setWindowTitle("Qt&&GStreamer Player demo");
    window->resize(900, 600);
    xwinid = window->getVideoWId();

    //wait playsink added
    while (NULL == userHandle.videoSink)
    {
    
    
        userHandle.videoSink = gst_bin_get_by_name (GST_BIN (userHandle.pipeline), "playsink");
        maxWaitCount++;
        if (maxWaitCount > 20)
        {
    
    
            g_printerr ("can not find video sink.\n", initParam.path);
            goto stop;
        }
        sleep(100);
    }

    gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY(userHandle.videoSink), xwinid);

创建一个基类为QWidget的PlayerWindow类,main函数中实例化window,并获window id。

WId PlayerWindow::getVideoWId(){
    
    
    return videoWindow->winId();
}

获取pipeline中的videosink:

userHandle.videoSink = gst_bin_get_by_name (GST_BIN (userHandle.pipeline), "playsink");

调用gst_video_overlay_set_window_handle将视频祯在QT窗口中渲染,传入的两个参数一个是
videosink,另一个时窗口Id。

 gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY(userHandle.videoSink), xwinid);

GstVideoOverlay的详细用法可以参考:GstVideoOverlay

3 playbin3 的element-added signal 监听

上篇博客里说过,playbin3是是gstreamer的一个高级plugin,可以根据播放的文件自动在pipeline创建时添加需要的elemet和plugin,新element添加进来时会发出element-added signal,监听这个signal就可以知道有哪些element被添加进来了。

g_signal_connect(G_OBJECT(mediaHandle->pipeline), "element-added",  G_CALLBACK(handle_element_added), mediaHandle);

监听的时候需要定义一个callback function,element-added signal发出后会回调这个函数,回调函数定义如下。

//Will be emitted after the element was added to the bin.
void user_function (GstBin     *bin,
               GstElement *element,
               gpointer    user_data)

Parameters :
bin:the GstBin
element:the GstElement that was added to the bin
user_data:user data set when the signal handler was connected.

前两个参数是固定的,最后一个参数是指针类型,可以传递自定义数据,这里传递了一个mediaHandle结构体指针,handle_element_added函数实现如下,目前只打印了一下element name,有需要可以在这里给element 设置属性。


void handle_element_added(GstBin *bin, GstElement *element, ST_MEDIA_HANDLE *mediaHandle)
{
    
    
    //Q_UNUSED(bin);

    gchar *elementName = gst_element_get_name(element);
    g_print("elementName %s. \n", elementName);

    if (g_str_has_prefix(elementName, "video-sink")) {
    
    
        g_print("find video-sink in element_add function. \n");
    } 
    else if (g_str_has_prefix(elementName, "ximagesink")) {
    
    
        g_print("find ximagesink in element_add function. \n");
    } 
    else if (g_str_has_prefix(elementName, "uridecodebin3") 
    || g_str_has_prefix(elementName, "decodebin2") 
    || g_str_has_prefix(elementName, "urisourcebin0")
    ) {
    
    
        g_signal_connect(element, "element-added",
                         G_CALLBACK(handle_element_added), mediaHandle);
    }

    g_free(elementName);
}

4 初始化player完成后通过callback 返回给UI需要的参数

callback function在player init时通过init param传递给mmplayer lib:

mediaHandle->filePath = pstInitParam->path;
mediaHandle->hanlecallBackFn = pstInitParam->callBackFunction;

init 完成后调用callback function把player 信息结构体返回给UI(QT)。

    mediaHandle->handleInfo.handleId = (int)mediaHandle;
    mediaHandle->handleInfo.handleStatus = READY_STATUS;
    mediaHandle->handleInfo.pipeline = mediaHandle->pipeline;
    if (mediaHandle->hanlecallBackFn)
    {
    
    
       (mediaHandle->hanlecallBackFn)(PLAYER_INIT_OK ,(void *)&mediaHandle->handleInfo);
    }

相关结构体定义:

/* handle status */
typedef enum {
    
    
    INIT_STATUS = 0,         
    READY_STATUS,
    PLAYING_STATUS,
    PAUSE_STATUS,
    ERROR_STATUS,        
} HANDLE_STATUS;

typedef struct _ST_HANDLE_INFO {
    
    
    gint handleId;
    HANDLE_STATUS handleStatus;
    GstElement *pipeline;
} ST_HANDLE_INFO;

callback function需要在QT中实现,为了方便后期的功能增加,callback function 中通过CALL_BACK_EVENT_TYPE来决定该如何处理返回的数据,目前只在PLAYER_INIT_OK后传递hanle id、pipeline 地址和player status给QT。

void hanleCallBackEvent(CALL_BACK_EVENT_TYPE eventType, void *param)
{
    
    
    g_print("recive event type (%d)\n", eventType);
    switch (eventType)
    {
    
    
        case PLAYER_INIT_OK:
        {
    
    
            g_print("player init OK! \n");
            ST_HANDLE_INFO *retHanleInfo = (ST_HANDLE_INFO *)param;
            userHandle.handleId = retHanleInfo->handleId;
            userHandle.pipeline = retHanleInfo->pipeline;
        }
            break;

        default:
            break;
    }
}

5 最终效果以及github地址

最终播放效果如下,QT上的按钮功能暂未实现。
在这里插入图片描述

最后附上github地址:https://github.com/zhenghaiyang123/gst_player.git,该篇博客对应tag v0.2。

猜你喜欢

转载自blog.csdn.net/qq_38694388/article/details/125073656