本篇博客在上一篇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。