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

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

文章目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CVPR 2024第三弹:李飞飞教授惊喜亮相,CVPR之家乐队火爆演奏惊艳全场

CVPR 2024第三弹&#xff1a;小编与李飞飞教授惊喜同框&#xff0c;"CVPR之家"乐队火爆演奏惊艳全场&#xff01; 会议之眼 快讯 2024 年 CVPR &#xff08;Computer Vision and Pattern Recogntion Conference) 即国际计算机视觉与模式识别会议&#xff0c;于6月1…

CAC 2.0融合智谱AI大模型,邮件安全新升级

在数字化时代&#xff0c;电子邮件的安全问题日益成为关注的焦点。Coremail CACTER邮件安全人工智能实验室&#xff08;以下简称“CACTER AI实验室”&#xff09;凭借其在邮件安全领域的深入研究与创新实践&#xff0c;不断推动技术进步。 此前&#xff0c;CACTER AI实验室已获…

任务4.8.3 利用SparkSQL统计每日新增用户

实战概述&#xff1a;利用SparkSQL统计每日新增用户 任务背景 在大数据时代&#xff0c;快速准确地统计每日新增用户是数据分析和业务决策的重要部分。本任务旨在使用Apache SparkSQL处理用户访问历史数据&#xff0c;以统计每日新增用户数量。 任务目标 处理用户访问历史数…

Apifox 中如何处理加密或编码过的响应数据?

接口返回的响应数据有时是经过编码或加密处理的&#xff0c;要转换成可读的明文&#xff0c;可以使用 Apifox 内置的 JS 类库、或者通过调用外部编程语言 &#xff08;如 Python、JavaScript 等&#xff09; 来进行处理。 例如&#xff0c;一个经过 Base64 编码的数据可以通过…

vue2与vue3数据响应式对比之检测变化

vue2 由于javascript限制&#xff0c;vue不能检测数组和对象的变化 什么意思呢&#xff0c;举例子来说吧 深入响应式原理 对象 比如说我们在data里面定义了一个info的对象 <template><div id"app"><div>姓名: {{ info.name }}</div><…

本地部署Ollama+qwen本地大语言模型Web交互界面

什么是 Ollama WebUI&#xff1f; Ollama WebUI 已经更名为 Open WebUI. Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 WebUI&#xff0c;旨在完全离线操作。它支持各种 LLM 运行程序&#xff0c;包括 Ollama 和 OpenAI 兼容的 API。 Ollama WebUI 是一个革命性的 L…

复盘最近的面试

这个礼拜一直在面试&#xff0c;想着看看能否拿到不错的offer前去实习&#xff0c;从周一到周四&#xff0c;面了将近10家&#xff0c;特整理此份面经&#xff0c;希望对秋招的各位有所帮助 A公司 一面 面试官人很好&#xff0c;我回答的时候不会他会笑笑然后提醒我 自我介绍~…

2024软考系规考前复习20问!看看你能答上来多少

今天给大家整理了——2024系统规划与管理师考前20问&#xff0c;这是一份很重要的软考备考必看干货&#xff0c;包含很多核心知识点。有PDF版&#xff0c;可打印下来&#xff0c;过完一遍教材后&#xff0c;来刷一刷、背一背&#xff0c;说不定可以帮你拿下不少分。 第1问- 信息…

怎么用二维码在线下载视频?视频用二维码下载的制作方法

怎么把视频转换成二维码之后还可以下载视频呢&#xff1f;现在使用二维码的方式来分享视频内容在很多行业和场景中都有应用&#xff0c;这种方式能够更加简单快捷的完成视频的传播分享&#xff0c;那么怎么让扫码者可以自由选择下载视频呢&#xff1f;下面来给大家分享扫码下载…

STM32小项目———感应垃圾桶

文章目录 前言一、超声波测距1.超声波简介2.超声波测距原理2.超声波测距步骤 二、舵机的控制三、硬件搭建及功能展示总结 前言 一个学习STM32的小白~ 有问题请评论区或私信指出 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、超声波测距 1.超声波…

项目训练营第三天

项目训练营第三天 注册登录测试 前面我们编写了用户注册、登录的逻辑代码&#xff0c;每编写完一个功能模块之后&#xff0c;我们都要对该模块进行单元测试&#xff0c;来确保该功能模块的正确性。一般情况下使用快捷键Ctrl Shift Insert&#xff0c;鼠标左击类名可以自动生…

代码随想录算法训练营第30天| 122.买卖股票的最佳时机II、55. 跳跃游戏、 45.跳跃游戏II、 1005.K次取反后最大化的数组和

122.买卖股票的最佳时机II 题目链接&#xff1a;122.买卖股票的最佳时机II 文档讲解&#xff1a;代码随想录 状态&#xff1a;so easy 思路&#xff1a;不用考虑操作的次数&#xff0c;其实只要有赚就拿下就行了。 题解&#xff1a; class Solution {public int maxProfit(in…

数据通信与网络(三)

物理层概述&#xff1a; 物理层是网络体系结构中的最低层 它既不是指连接计算机的具体物理设备&#xff0c;也不是指负责信号传输的具体物理介质&#xff0c; 而是指在连接开放系统的物理媒体上为上一层(指数据链路层)提供传送比特流的一个物理连接。 物理层的主要功能——为…

期末考后怎样发成绩?

老师们&#xff0c;下周可就是期末考啦&#xff0c;又到了头疼发成绩的时候了。每当这个时候&#xff0c;家长们总是急切地咨询孩子的考试表现&#xff0c;向老师们询问成绩。这种场景几乎成了每学期结束时的常态。 别担心&#xff0c;我来安利一个超棒的工具——“易查分小程序…

前端菜鸡流水账日记 -- git管理工具(多版本)

哈喽哇&#xff0c;我又又又来了&#xff0c;其实之前就挺想进行一篇关于git管理工具的分享的&#xff0c;但是一直都没有来的及&#xff0c;直到今天&#xff0c;在学习的时候&#xff0c;&#xff0c;一个朋友新发现了一个vscode中的小插件&#xff0c;所以我就决定一起来分享…

市值3万亿英伟达的崛起:技术、坚持与市场的力量,厚积薄发的经典案例

在科技领域&#xff0c;英伟达&#xff08;NVIDIA&#xff09;的故事无疑是一个厚积薄发的经典案例。作为一家专注于图形处理单元&#xff08;GPU&#xff09;的公司&#xff0c;英伟达用31年的时间证明了技术的价值、计算的价值和坚持的价值。本文将详细探讨英伟达如何从一家市…

Leaflet【四】轨迹回放效果控制台控制轨迹运动效果

安装依赖&准备静态资源 轨迹回放使用leaflet-trackplayer插件 npm i leaflet-trackplayer --force初始化地图&#xff0c;在这里初始化地图可以参考&#xff0c;然后导入了轨迹回放插件和一个图片&#xff08;用于标记当前轨迹运动点&#xff09;图片资源 import L from…

【对抗去偏】BiasAdv: Bias-Adversarial Augmentation for Model Debiasing

原文标题&#xff1a; BiasAdv: Bias-Adversarial Augmentation for Model Debiasing 原文代码&#xff1a; 暂无 发布年度&#xff1a; 2023 发布期刊&#xff1a; CVPR 摘要 Neural networks are often prone to bias toward spurious correlations inherent in a dataset, …

【代码仓库提交大文件,用Git LFS!】

开始前 Git LFS&#xff1a;请注意&#xff0c;你的远程仓库需要支持Git LFS。GitHub、GitLab和Bitbucket都支持Git LFS&#xff0c;但可能需要额外的配置或开启特定的支持选项。 介绍 Git LFS (Large File Storage) 是一个 Git 扩展&#xff0c;用于处理和存储大文件。通常…

vivado WIRE

WIRE是用于在Xilinx部件上路由连接或网络的设备对象。一根电线 是单个瓦片内部的一条互连金属。PIP、系紧装置和 SITE_PINs。 提示&#xff1a;WIRE对象不应与设计的Verilog文件中的WIRE实体混淆。那些 电线在设计中与网络有关&#xff0c;而不是与定义的设备的路由资源有关 WI…