提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、安装环境
- 1.QT5
- 2.gstreamer
- 二、代码
- 1.Windows实现
- 三、测试效果
- 总结
前言
最近在研究mpp,通过gstreamer实现了硬解码,但是我在想我可能需要一个播放器,我之前学过qt5所以就选择了qt5来结合gstreamer开发一个简单的播放器。今天的环境基于Windows,后续会把Linux的也补上的。
注意:基于QT5,我觉得因为全平台特性,可能QT5比MFC还是有优势,代码只需要小改就可以轻松移植。
一、安装环境
本身不需要特殊配置就支持N卡硬解码,A卡和I卡可能要借助VAAP,我手上没有设备,暂未研究。所以显卡驱动和芯片组驱动就不说了肯定都是要装的。
1.QT5
这个不介绍了,网上找找就好了。
2.gstreamer
Windows11安装并使用Gstreamer-1.0
二、代码
代码很简单,没有特别做UI,只实现了视频硬解码、播放和音频播放功能
。
1.Windows实现
代码只能跑在Windows上因为播放组件是需要区分WIndows和Linux甚至MacOS的。
Gstreamer_Player.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++17
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# 添加GStreamer和Glib库
unix {
PKGCONFIG += gstreamer-1.0 gstreamer-video-1.0 glib-2.0 gstvideo-1.0
}
# 如果你使用的是Windows,请添加以下内容,并调整路径
win32 {
INCLUDEPATH += E:/gstreamer/1.0/msvc_x86_64/include/gstreamer-1.0
INCLUDEPATH += E:/gstreamer/1.0/msvc_x86_64/include/glib-2.0
INCLUDEPATH += E:/gstreamer/1.0/msvc_x86_64/lib/glib-2.0/include
LIBS += -LE:/gstreamer/1.0/msvc_x86_64/lib -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -lgstvideo-1.0
}
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <gst/gst.h>
#include <gst/video/videooverlay.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_playButton_clicked();
private:
Ui::MainWindow *ui;
GstElement *pipeline;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QFileDialog>
//代码只有Windows系统才执行
#ifdef Q_OS_WIN
#include <windows.h>
#include <dwmapi.h>
#endif
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
gst_init(nullptr, nullptr);
pipeline = gst_pipeline_new("pipeline");
GstElement *source = gst_element_factory_make("filesrc", "source");
GstElement *demuxer = gst_element_factory_make("decodebin", "demuxer");
GstElement *videoconvert = gst_element_factory_make("videoconvert", "videoconvert");
GstElement *videosink = gst_element_factory_make("d3dvideosink", "videosink");
GstElement *audioconvert = gst_element_factory_make("audioconvert", "audioconvert");
GstElement *audioresample = gst_element_factory_make("audioresample", "audioresample");
GstElement *audiosink = gst_element_factory_make("autoaudiosink", "audiosink");
//这一句可以合并,因为都是必须创建成功的
if (!pipeline || !source || !demuxer || !videoconvert || !videosink || !audioconvert || !audioresample || !audiosink) {
QMessageBox::critical(this, "Error", "Failed to create GStreamer elements.");
return;
}
gst_bin_add_many(GST_BIN(pipeline), source, demuxer, videoconvert, videosink, audioconvert, audioresample, audiosink, nullptr);
gst_element_link(source, demuxer);
g_signal_connect(demuxer, "pad-added", G_CALLBACK(+[](GstElement *demuxer, GstPad *new_pad, gpointer user_data) {
GstElement *pipeline = GST_ELEMENT(user_data);
GstPad *videoconvert_sink_pad = gst_element_get_static_pad(gst_bin_get_by_name(GST_BIN(pipeline), "videoconvert"), "sink");
GstPad *audioconvert_sink_pad = gst_element_get_static_pad(gst_bin_get_by_name(GST_BIN(pipeline), "audioconvert"), "sink");
GstPadLinkReturn ret;
GstCaps *new_pad_caps = gst_pad_get_current_caps(new_pad);
GstStructure *new_pad_struct = gst_caps_get_structure(new_pad_caps, 0);
const gchar *new_pad_type = gst_structure_get_name(new_pad_struct);
if (g_str_has_prefix(new_pad_type, "video/x-raw")) {
ret = gst_pad_link(new_pad, videoconvert_sink_pad);
} else if (g_str_has_prefix(new_pad_type, "audio/x-raw")) {
ret = gst_pad_link(new_pad, audioconvert_sink_pad);
} else {
ret = GST_PAD_LINK_OK;
}
if (GST_PAD_LINK_FAILED(ret)) {
g_printerr("Type is '%s' but link failed.\n", new_pad_type);
} else {
g_print("Link succeeded (type '%s').\n", new_pad_type);
}
gst_object_unref(videoconvert_sink_pad);
gst_object_unref(audioconvert_sink_pad);
gst_caps_unref(new_pad_caps);
}), pipeline);
gst_element_link_many(videoconvert, videosink, nullptr);
gst_element_link_many(audioconvert, audioresample, audiosink, nullptr);
GstBus *bus = gst_element_get_bus(pipeline);
gst_bus_add_signal_watch(bus);
g_signal_connect(bus, "message::error", G_CALLBACK(+[](GstBus *bus, GstMessage *msg, gpointer user_data) {
GError *err;
gchar *debug_info;
gst_message_parse_error(msg, &err, &debug_info);
g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
g_clear_error(&err);
g_free(debug_info);
}), nullptr);
gst_object_unref(bus);
WId winId = ui->videoWidget->winId();
gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(videosink), winId);
}
MainWindow::~MainWindow()
{
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
delete ui;
}
void MainWindow::on_playButton_clicked()
{
QString filename = QFileDialog::getOpenFileName(this, "Open Video File", "", "Video Files (*.mp4 *.avi *.mkv)");
if (filename.isEmpty())
return;
gst_element_set_state(pipeline, GST_STATE_READY);
g_object_set(G_OBJECT(gst_bin_get_by_name(GST_BIN(pipeline), "source")), "location", filename.toStdString().c_str(), nullptr);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.ui
一个Widget命名为videoWidget
,一个Pushbutton命名为playButton
。布局参考下面的:
注意:选择MSVC 2015编译链,要不然会找不到库!
三、测试效果
除非花里胡哨的特效,一般UI在Windows上和Linux上可以复用。
实测可以正常调用N卡硬解码(需要显卡支持)。
总结
1、不算太难,复杂的应用还需要继续琢磨。
2、Windows上还是简单,我最终的目标是在开发板上实现gstreamer+qt5+硬解码播放,开发板上没有统一的接口。