gstreamer+qt5实现简易视频播放器

news2024/12/24 3:04:44

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、安装环境
    • 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+硬解码播放,开发板上没有统一的接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1846678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ip地址怎么写才是的对的?合法ip地址正确的格式

IP地址怎么写才是的对的&#xff1f;在互联网的世界里&#xff0c;IP地址就像是我们生活中的门牌号码&#xff0c;它是每个设备在网络中的唯一标识。正确的书写IP地址对于确保网络通信的顺畅至关重要。本文将带您了解合法IP地址的正确格式与书写规范&#xff0c;并深入探讨其在…

【MySQL】索引的原理及其使用

文章目录 什么叫索引减少磁盘IO次数缓存池(Buffer Pool&#xff09;MySQL的页页内目录页目录 正确理解索引结构为什么Innodb的索引是B树结构各种存储引擎支持的索引聚簇索引和非聚簇索引索引类型 关于索引的操作创建主键索引唯一索引的创建普通索引的创建查看索引删除索引 什么…

6月21日(周五)AH股总结:沪指失守3000点,恒生科技指数跌近2%,多只沪深300ETF午后量能显著放大

内容提要 沪指全天围绕3000点关口来回拉锯&#xff0c;收盘跌破3000点。白酒及光刻机概念集体走低&#xff0c;中芯国际港股跌超2%。CRO医药概念及水利股逆势走强。 A股低开低走 沪指全天围绕3000点关口来回拉锯&#xff0c;收盘跌破3000点&#xff0c;跌0.24%。深成指跌0.04…

如何在 MySQL 中创建和使用事务?

目录 1. 环境准备 2. 创建事务 3. 事务执行 4. 事务撤消 5. 总结 事务是数据库区别于文件系统的重要特征之一&#xff0c;当我们有了事务就会让数据库始终保持一致&#xff0c;同时我们还能通过事务机制恢复到某个时间点&#xff0c;这样可以保证已提交到数据库的修改不会…

【Linux】基础IO_2

文章目录 六、基础I/O2. 系统文件I/O磁盘的存储结构 未完待续 六、基础I/O 2. 系统文件I/O 磁盘的存储结构 系统中不是所有对文件都是打开的状态&#xff0c;大部分的文件都是没有被打开的。这些文件一般都被存储在磁盘当中。磁盘通过柱面&#xff0c;扇面&#xff0c;扇区确…

JS 【详解】树的遍历(含深度优先遍历和广度优先遍历的算法实现)

用 js 描述树 let tree [{label:a,children:[{label:b,children:[{label:d},{label:e}]},{label:c,children:[{label:f}]}]} ]使用数组是因为树的节点有顺序 深度优先遍历 从根节点出发&#xff0c;优先遍历最深的节点 遍历顺序为 abdecf function DFS(tree) {tree.forEach(…

c++编译器优化不显示拷贝构造函数

一.错误情景&#xff08;无法打印拷贝函数&#xff09; #include<iostream> using namespace std;class person { public:person(){cout << "person默认构造函数调用" << endl;}person(int age){cout << "有参构造函数调用" <…

IIS代理配置-反向代理

前后端分离项目&#xff0c;前端在开发中使用proxy代理解决跨域问题&#xff0c;打包之后无效。 未配置前无法访问 部署环境为windows IIS&#xff0c;要在iis设置反向代理 安装代理模块 需要在iis中实现代理&#xff0c;需要安装Application Request Routing Cache和URL重…

在Verilog HDL中使用任务(task)

代码&#xff1a; sort4.v module sort4(ra,rb,rc,rd,a,b,c,d);output[3:0] ra,rb,rc,rd;input[3:0] a,b,c,d;reg[3:0] ra,rb,rc,rd;reg[3:0] va,vb,vc,vd;always (a or b or c or d)begin{va,vb,vc,vd}{a,b,c,d};sort2(va,vc); //va 与vc互换。sort2(vb,vd);…

为什么在React中一定要使用setState来更新数据?

为什么在React中一定要使用setState来更新数据&#xff1f; 因为我们修改了State后&#xff0c;我们希望看到React更具最新的State来重新渲染界面&#xff0c;但是这种方式修改的React并不知道数据发送的改变&#xff1b; 在React中并没有使用像Vue3或者Vue2中的数据劫持来监…

时间?空间?复杂度??

1.什么是时间复杂度和空间复杂度&#xff1f; 1.1算法效率 算法效率分析分为两种&#xff1a;第一种是时间效率&#xff0c;第二种是空间效率。时间效率被称为时间复杂度&#xff0c;而空间效率被称为空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度&#xff0c;而空…

爱眼小妙招:台灯怎么选?学生如何正确使用台灯?

视力是心灵的窗户&#xff0c;尤其对于儿童来说更为重要。然而&#xff0c;随着现代生活方式的改变&#xff0c;孩子们面临越来越多的视力挑战。据统计&#xff0c;在近视学生中&#xff0c;近10%的人患有高度近视&#xff0c;而这一比例随年级的增加而逐渐上升。从幼儿园的小小…

Redis-数据类型-Bit的基本操作-getbit-setbit-Bitmap

文章目录 0、Bitmaps&#xff08;位图&#xff09;1、查看redis是否启动2、通过客户端连接redis3、切换到db7数据库4、设置&#xff08;或覆盖&#xff09;一个键&#xff08;key&#xff09;的值&#xff08;value&#xff09;5、获取存储在给定键&#xff08;key&#xff09;…

解锁空间数据奥秘:ArcGIS Pro与Python双剑合璧,处理表格数据、矢量数据、栅格数据、点云数据、GPS数据、多维数据以及遥感云平台数据等

ArcGISPro提供了用户友好的图形界面&#xff0c;适合初学者快速上手进行数据处理和分析。它拥有丰富的工具和功能&#xff0c;支持各种数据格式的处理和分析&#xff0c;适用于各种规模的数据处理任务。ArcGISPro在地理信息系统&#xff08;GIS&#xff09;领域拥有广泛的应用&…

鸿蒙Harmony角落里的知识:从ECMA规范到ArkTS接口(二)

上篇介绍了typedArray.slice方法&#xff0c;鸿蒙Harmony角落里的知识&#xff1a;从ECMA规范到ArkTS接口&#xff08;一&#xff09;本文介绍一个返回结果和参数和slice非常类似的函数&#xff1a;TypedArray.prototype.subarray。 ECMA对TypedArray.prototype.subarray接口的…

【启明智显产品介绍】Model3C工业级HMI芯片详解专题(三)通信接口

Model3C 是一款基于 RISC-V 的高性能、国产自主、工业级高清显示与智能控制 MCU, 集成了内置以太网控制器&#xff0c;配备2路CAN、4路UART、5组GPIO、2路SPI等多种通信接口&#xff0c;能够轻松与各种显示设备连接&#xff0c;实现快速数据传输和稳定通信&#xff0c;可以与各…

Matplotlib绘制一个X轴2个Y轴的图表

import matplotlib matplotlib.use(Agg) # 使用Agg后端&#xff0c;这个后端适用于生成图像文件但不显示它们 import matplotlib.pyplot as plt fig plt.figure(figsize(15, 8))# 字体使用楷体 matplotlib.rc("font", family"Microsoft YaHei") ax1 fig…

【大数据】—二手车用户数据可视化分析案例

项目背景 在当今的大数据时代&#xff0c;数据可视化扮演着至关重要的角色。随着信息的爆炸式增长&#xff0c;我们面临着前所未有的数据挑战。这些数据可能来自社交媒体、商业交易、科学研究、医疗记录等各个领域&#xff0c;它们庞大而复杂&#xff0c;难以通过传统的数据处…

ISCC2024 WriteUpReverse 迷失之门

Reverse 迷失之门 迷失之门 writeup解题思路 打开题目是一个压缩包解压后是一个.exe程序 按照做题顺序第一步查壳发现并没有壳将其拖入ida中进行查看 使用shiftF12进行字符串查看 发现flag字符了我们双击它 将光标移动到yes哪里右击空白地方打开交叉索引并按F5进行反汇编发现…

Ci2451和Ci2454:2.4GHz无线MCU的芯片对比数据资料分析

一、2.4GHz无线MCU芯片的背景介绍 1、开头我们先聊聊&#xff0c;关于南京中科微2.4GHz无线MCU芯片&#xff08;Ci2451、Ci2454、CSM2433)是建立在现有的2.4GHz射频芯片基础上面&#xff0c;它的内部是集成了8位RISC内核&#xff0c;且集成丰富的MCU资源、更小的尺寸可以来满足…