1. 前言
在工程应用中通常使用Gstreamer来作为播放器,同时又需要其他功能,比如:播放,暂停,停止等功能。因此需要一个GUI开发组件,而Qt作为工业控制领域常用的GUI开发软件,所以将Qt与Gstreamer结合开发是十分有必要的。本文将介绍将Gstreamer视频输出到指定窗口,以及实现对视频的播放、暂停、停止和快进等功能。
2. 核心代码
将Qt创建的视频窗口ID设置给Gstreamer,同时窗口新的ximagesink插件给Gstreamer管道使用,达到最终管道的播放sink窗口为Qt窗口的目的。
// seg window id to gstreamer
GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");
// 获取Qt窗口ID
WId xwinid = window->getVideoWId();
// 给ximagesink设置Qt窗口ID
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), xwinid);
// 设置管道最终sink为上面的ximagesink
g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);
3. 完整代码
完整代码如下,也可以直接下载整个工程https://download.csdn.net/download/qq_27897937/89892328
# pro文件
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
ONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += link_pkgconfig
PKGCONFIG += gstreamer-1.0 gstreamer-app-1.0 gstreamer-video-1.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
//main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
gst_init (&argc, &argv);
QApplication app(argc, argv);
app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit ()));
// prepare the pipeline
GstElement *pipeline = gst_parse_launch ("playbin uri=file:///home/work/80s.mp4", NULL);
// prepare the ui
MainWindow *window = new MainWindow(pipeline);
window->resize(900, 600);
window->show();
// seg window id to gstreamer
GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");
WId xwinid = window->getVideoWId();
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), xwinid);
g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);
// connect to interesting signals
GstBus *bus = gst_element_get_bus(pipeline);
gst_bus_add_watch(bus, &MainWindow::postGstMessage, window);
gst_object_unref(bus);
// run the pipeline
GstStateChangeReturn sret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (sret == GST_STATE_CHANGE_FAILURE) {
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
// Exit application
QTimer::singleShot(0, QApplication::activeWindow(), SLOT(quit()));
}
int ret = app.exec();
window->hide();
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (pipeline);
return ret;
}
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <gst/gst.h>
#include <gst/video/videooverlay.h>
#include <QWidget>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QSlider>
#include <QTimer>
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(GstElement *p);
WId getVideoWId() const ;
static gboolean postGstMessage(GstBus * bus, GstMessage * message, gpointer user_data);
private slots:
void onPlayClicked() ;
void onPauseClicked() ;
void onStopClicked() ;
void onAlbumAvaiable(const QString &album);
void onState(GstState st);
void refreshSlider();
void onSeek();
void onEos();
signals:
void sigAlbum(const QString &album);
void sigState(GstState st);
void sigEos();
private:
GstElement *pipeline;
QPushButton *playBt;
QPushButton *pauseBt;
QPushButton *stopBt;
QWidget *videoWindow;
QSlider *slider;
QHBoxLayout *buttonLayout;
QVBoxLayout *playerLayout;
QTimer *timer;
GstState state;
gint64 totalDuration;
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(GstElement *p)
:pipeline(p)
,state(GST_STATE_NULL)
,totalDuration(GST_CLOCK_TIME_NONE)
{
playBt = new QPushButton("Play");
pauseBt = new QPushButton("Pause");
stopBt = new QPushButton("Stop");
videoWindow = new QWidget();
slider = new QSlider(Qt::Horizontal);
timer = new QTimer();
connect(playBt, SIGNAL(clicked()), this, SLOT(onPlayClicked()));
connect(pauseBt, SIGNAL(clicked()), this, SLOT(onPauseClicked()));
connect(stopBt, SIGNAL(clicked()), this, SLOT(onStopClicked()));
connect(slider, SIGNAL(sliderReleased()), this, SLOT(onSeek()));
buttonLayout = new QHBoxLayout;
buttonLayout->addWidget(playBt);
buttonLayout->addWidget(pauseBt);
buttonLayout->addWidget(stopBt);
buttonLayout->addWidget(slider);
playerLayout = new QVBoxLayout;
playerLayout->addWidget(videoWindow);
playerLayout->addLayout(buttonLayout);
this->setLayout(playerLayout);
connect(timer, SIGNAL(timeout()), this, SLOT(refreshSlider()));
connect(this, SIGNAL(sigAlbum(QString)), this, SLOT(onAlbumAvaiable(QString)));
connect(this, SIGNAL(sigState(GstState)), this, SLOT(onState(GstState)));
connect(this, SIGNAL(sigEos()), this, SLOT(onEos()));
}
WId MainWindow::getVideoWId() const {
return videoWindow->winId();
}
void MainWindow::onPlayClicked() {
GstState st = GST_STATE_NULL;
gst_element_get_state (pipeline, &st, NULL, GST_CLOCK_TIME_NONE);
if (st < GST_STATE_PAUSED) {
// Pipeline stopped, we need set overlay again
GstElement *vsink = gst_element_factory_make ("ximagesink", "vsink");
g_object_set(GST_OBJECT(pipeline), "video-sink", vsink, NULL);
WId xwinid = getVideoWId();
gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (vsink), xwinid);
}
gst_element_set_state (pipeline, GST_STATE_PLAYING);
}
void MainWindow::onPauseClicked() {
gst_element_set_state (pipeline, GST_STATE_PAUSED);
}
void MainWindow::onStopClicked() {
gst_element_set_state (pipeline, GST_STATE_NULL);
}
void MainWindow::onAlbumAvaiable(const QString &album) {
setWindowTitle(album);
}
void MainWindow::onState(GstState st) {
if (state != st) {
state = st;
if (state == GST_STATE_PLAYING){
timer->start(1000);
}
if (state < GST_STATE_PAUSED){
timer->stop();
}
}
}
void MainWindow::refreshSlider() {
gint64 current = GST_CLOCK_TIME_NONE;
if (state == GST_STATE_PLAYING) {
if (!GST_CLOCK_TIME_IS_VALID(totalDuration)) {
if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &totalDuration)) {
slider->setRange(0, totalDuration/GST_SECOND);
}
}
if (gst_element_query_position (pipeline, GST_FORMAT_TIME, ¤t)) {
g_print("%ld / %ld\n", current/GST_SECOND, totalDuration/GST_SECOND);
slider->setValue(current/GST_SECOND);
}
}
}
void MainWindow::onSeek() {
gint64 pos = slider->sliderPosition();
g_print("seek: %ld\n", pos);
gst_element_seek_simple (pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH ,
pos * GST_SECOND);
}
void MainWindow::onEos() {
gst_element_set_state (pipeline, GST_STATE_NULL);
}
gboolean MainWindow::postGstMessage(GstBus * bus, GstMessage * message, gpointer user_data) {
MainWindow *pw = NULL;
if (user_data) {
pw = reinterpret_cast<MainWindow*>(user_data);
}
switch (GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_STATE_CHANGED: {
GstState old_state, new_state, pending_state;
gst_message_parse_state_changed (message, &old_state, &new_state, &pending_state);
pw->sigState(new_state);
break;
}
case GST_MESSAGE_TAG: {
GstTagList *tags = NULL;
gst_message_parse_tag(message, &tags);
gchar *album= NULL;
if (gst_tag_list_get_string(tags, GST_TAG_ALBUM, &album)) {
pw->sigAlbum(album);
g_free(album);
}
gst_tag_list_unref(tags);
break;
}
case GST_MESSAGE_EOS: {
pw->sigEos();
break;
}
default:
break;
}
return TRUE;
}