《二十三》Qt 简单小项目---视频播放器

QT 使用QMediaPlayer实现的简易视频播放器

效果如下:

功能点
  1. 播放指定视频
  2. 点击屏幕暂停/播放
  3. 开始/暂停/重置视频
  4. 拖拽到指定位置播放

类介绍

  •     需要在配置文件中加入Multimedia, MultimediaWidgets这俩个库。
  •     Multimedia:提供了一套用于处理音频、视频、摄像头和广播数据的API。
  •     MultimediaWidgets:提供了一些与多媒体相关的图形界面组件。
  •     QVideoProbe是Qt多媒体模块中的一个类,它用于监控视频流的输出。这个类允许你接收视频帧的数据,而不需要直接与视频输出设备交互。
  •     QMediaPlayer 使用生产者-消费者模型来处理媒体内容。它从媒体源(如文件或网络流)获取数据,然后通过播放控制接口(如播放、暂停、停止)和播放状态接口(如当前播放位置、总时长)来控制媒体内容的播放。
  •     使用 QMediaPlayer时,通常需要将其与一个或多个媒体输出组件结合使用,例如QVideoWidget用于视频播放,QAudioOutput用于音频播放。

注意:在Qt6中使用QMediaPlayer时,使用的是setSource函数设置视频资源,而Qt5中并没有这个函数,使用的是setMedia函数。而且有个非常坑的地方,Qt6设置完QVideoWidget直接使用没有问题,而Qt5就会存在问题。 

 信息栏会报错:

DirectShowPlayerService::doRender: Unresolved error code 0x80040266 

在window上需安装LAV解码器的,并放在Qt的安装目录下
在Linux上个人验证并不需要安装

解决办法:QT无法播放视频:报错:DirectShowPlayerService::doRender: Unresolved error code 0x80040266-CSDN博客

代码介绍

我们需要在pro文件中先添加:

 随后我们添加一个C++类,基类选择QVideoWidget,来定义一些鼠标和键盘事件:

qmyselfvideo.h
#include<QVideoWidget>
#include<QObject>
#include<QWidget>
#include<QMediaPlayer>

#include<QKeyEvent>
#include<QMouseEvent>

class QMySelfVideo : public QVideoWidget
{
public:
    QMySelfVideo(QWidget *parent=nullptr);

    void SetMediaPlayer(QMediaPlayer *player);

private:
    QMediaPlayer  *theplayer;

protected:
    void keyPressEvent(QKeyEvent *event);//键盘按键事件,当按键按下esc退出全屏
    void mousePressEvent(QMouseEvent *event);//鼠标按键时间,点击暂停和播放
};

qmyselfvideo.cpp

#include "qmyselfvideo.h"

QMySelfVideo::QMySelfVideo(QWidget *parent):QVideoWidget(parent)
{

}

void QMySelfVideo::SetMediaPlayer(QMediaPlayer *player)
{
    //设置播放器操作
    theplayer=player;
}

void QMySelfVideo::keyPressEvent(QKeyEvent *event)//当播放器为全屏的时候,我们按下esc就可以退出全屏
{
    if((event->key()==Qt::Key_Escape)&&(isFullScreen())){
        setFullScreen(false);
        event->accept();
        QVideoWidget::keyPressEvent(event);
    }
}

void QMySelfVideo::mousePressEvent(QMouseEvent *event)//按下左键设置播放和暂停
{
    if(event->button()==Qt::LeftButton){
        if(theplayer->state()==QMediaPlayer::PlayingState){
            theplayer->pause();
        }else{
            theplayer->play();
        }
    }
    QVideoWidget::mousePressEvent(event);
}

 注意:我们要对graphicsView进行提升,提升的类名就是我们刚刚创建的C++类名,因为它的基类就是QVideoWidget。

 代码初始化定义:

#include <QMainWindow>
#include"qmyselfvideo.h"
#include<QMediaPlayer>
#include<QMediaPlaylist>
#include<QVideoWidget>
#include<QFileDialog>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    QMediaPlayer *player;//播放器
    QString drtTime;//视频文件长度
    QString posTime;//播放视频的当前位置

private slots:
    void OnStateButtonChanged(QMediaPlayer::State state);//控制按钮状态
    void OndrtTimeChanged(qint64 drt);//视频文件时间长度,更新变化
    void OnposTimeChanged(qint64 pos);//播放视频当前位置时间更新变化
}

 定义我们所需要的量,以及一些我们需要的槽函数来处理一些变化。

代码功能分析:

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    player=new QMediaPlayer(this);
    player->setNotifyInterval(1000);
    player->setVideoOutput(ui->graphicsView);//视频显示组件

    ui->graphicsView->SetMediaPlayer(player);//设置显示组件的关联播放器
}

初始化,没什么好说的。

根据state的状态,来更新播放,暂停,停止按钮的状态:

void MainWindow::OnStateButtonChanged(QMediaPlayer::State state)
{
    ui->pushButton_play->setEnabled(!(state==QMediaPlayer::PlayingState));
    ui->pushButton_pause->setEnabled(state==QMediaPlayer::PlayingState);
    ui->pushButton_stop->setEnabled(state==QMediaPlayer::PlayingState);
}

根据视频的长度来显示时长,格式:已经播放的时长/总时长。时间是:分:秒。将设置好的时长显示在我们添加的label中显示

void MainWindow::OndrtTimeChanged(qint64 drt)
{
    ui->horizontalSlide_speedr->setMaximum(drt);

    int sec=drt/1000;//总时长
    int min=sec/60;//分
    sec=sec%60;//秒

    drtTime=QString::asprintf("%d:%d",min,sec);
    ui->label_speed->setText(posTime+"|"+drtTime);
}

void MainWindow::OnposTimeChanged(qint64 pos)
{
    if(ui->horizontalSlide_speedr->isSliderDown()){
        return;//如果正在拖动滑动条,则直接退出
    }
    ui->horizontalSlide_speedr->setSliderPosition(pos);

    int sec=pos/1000;
    int min=sec/60;
    sec=sec%60;

    posTime=QString::asprintf("%d:%d",min,sec);
    ui->label_speed->setText(posTime+"|"+drtTime);
}

槽函数链接:

connect(player,SIGNAL(stateChanged(QMediaPlayer::State)),this,SLOT(OnStateButtonChanged(QMediaPlayer::State)));
    connect(player,SIGNAL(positionChanged(qint64)),this,SLOT(OnposTimeChanged(qint64)));
    connect(player,SIGNAL(durationChanged(qint64)),this,SLOT(OndrtTimeChanged(qint64)));

根据状态改变来连接相应的槽函数 。

按钮滑动条槽函数 :

    void on_pushButton_open_clicked();//打开文件
    void on_pushButton_play_clicked();//播放
    void on_pushButton_pause_clicked();//暂停
    void on_pushButton_stop_clicked();//停止
    void on_pushButton_Volumn_clicked();//声音
    void on_horizontalSlider_Volumn_valueChanged(int value);//声音滑动条
    void on_horizontalSlide_speedr_valueChanged(int value);//进度条

对于open:

void MainWindow::on_pushButton_open_clicked()
{
    QString currentpath=QDir::homePath();//获得系统当前目录
    QString dlgtitle="请选择视频文件";
    QString filter="所有文件(*.*);;mp4文件(*.mp4)";//文件过滤器
    QString strfile=QFileDialog::getOpenFileName(this,dlgtitle,currentpath,filter);

    if(strfile.isEmpty()){
        return;
    }
    QFileInfo fileinfo(strfile);
    ui->label_name->setText(fileinfo.fileName());
    player->setMedia(QUrl::fromLocalFile(strfile));//设置播放文件
    player->play();
}

我们首先获取文件的位置路径,在设置文件过滤器来获取我们所需要的MP4文件格式,最后设置播放文件进行播放。

其他开始,暂停等一些按钮槽函数:

void MainWindow::on_pushButton_play_clicked()
{
    player->play();
}

void MainWindow::on_pushButton_pause_clicked()
{
    player->pause();
}

void MainWindow::on_pushButton_stop_clicked()
{
    player->stop();
}

void MainWindow::on_pushButton_Volumn_clicked()
{
    bool bmute=player->isMuted();
    player->setMuted(!bmute);
    //根据是否静音来显示音量图标
    if(bmute){
        ui->pushButton_Volumn->setIcon(QIcon(":/icon/sound.png"));
    }else{
        ui->pushButton_Volumn->setIcon(QIcon(":/icon/mute.png"));
    }
}

void MainWindow::on_horizontalSlider_Volumn_valueChanged(int value)
{
    player->setVolume(value);
}

void MainWindow::on_horizontalSlide_speedr_valueChanged(int value)
{
    player->setPosition(value);
}

总结:

只是一个简单视频播放器,博主能力有限还在学习当中。大家可自行尝试修改改进。

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

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

相关文章

如何开启深色模式【攻略】

如何开启深色模式【攻略】 前言版权推荐如何开启深色模式介绍手机系统手机微信手机QQ手机快手手机抖音 电脑系统电脑微信电脑QQ电脑WPS电脑浏览器 最后 前言 2024-5-9 20:48:21 深色模式给人以一种高级感。 本文介绍一些常用软件深色模式的开启 以下内容源自《【攻略】》 仅…

基于Spring Boot的酒店管理系统设计与实现

基于Spring Boot的酒店管理系统设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 系统首页界面图&#xff0c;在系统首页可以查看首页…

【数据结构-二叉搜索树的增删查改】

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a;基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 …

python-类和对象

1、设计一个 Circle类来表示圆,这个类包含圆的半径以及求面积和周长的函数。再使用这个类创建半径为1~10的圆,并计算出相应的面积和周长。 &#xff08;1&#xff09;源代码&#xff1a; import math class Circle: def __init__(self, r): self.r r #面积 def area(self): r…

最佳实践 | 八爪鱼采集器如何用PartnerShare做全民分销?

在数字化时代&#xff0c;数据采集和分析已经成为企业运营和决策的重要一环。八爪鱼采集器作为一款领先的SaaS产品&#xff0c;凭借其强大的数据采集和处理能力&#xff0c;成为了众多企业和个人用户的心头好。为了进一步拓展市场份额&#xff0c;提升品牌影响力&#xff0c;八…

TCP通信并发:

上次的程序只能保持&#xff0c;单线程或者进程 多进程并发服务器 进程的特点&#xff08;有血缘关系&#xff09; 创建子进程&#xff1a;fork&#xff08;&#xff09;&#xff1b; 虚拟地址空间被复制 &#xff0c;从一份变成两份&#xff08;用户区和内核区&#xff09…

国内如何访问 OpenAI 的 api

这个问题甚至我的一些大厂的朋友也不太清楚&#xff0c;所以我觉得有必备写一篇文章来简单盘盘它&#xff0c;希望能帮助到有需要的人 众所周知&#xff0c;由于大陆与 OpenAI 双方互相封锁&#xff0c;大陆是无法直接访问 OpenAI api 的 不过由于 GPT 4 的统治地位&#xff0c…

下一代自动化,国外厂商如何通过生成性AI重塑RPA?

企业自动化的未来趋势是什么&#xff1f;科技巨头们普遍认为&#xff0c;由生成性AI驱动的AI Agent将成为下一个重大发展方向。尽管“AI Agent”这一术语尚无统一定义&#xff0c;但它通常指的是那些能够根据指令通过模拟人类互动&#xff0c;在软件和网络平台上执行复杂任务的…

[C++核心编程-05]----C++类和对象之对象的初始化和清理

目录 引言 正文 01-构造函数和析构函数 ​02-构造函数的分类及调用 03-拷贝构造函数调用时机 04-构造函数调用规则 05-深拷贝与浅拷贝 06-初始化列表 07-静态成员变量 08-静态成员函数 …

Eigen求解线性方程组

1、线性方程组的应用 线性方程组可以用来解决各种涉及线性关系的问题。以下是一些通常可以用线性方程组来解决的问题&#xff1a; 在实际工程和科学计算中&#xff0c;求解多项式方程的根有着广泛的应用。 在控制系统的设计中&#xff0c;我们经常需要求解特征方程的根来分析…

【训练与预测】02 - 完整的模型验证套路

02 - 完整的模型验证套路 模型图 验证一个模型就是指使用已经训练好的模型&#xff0c;然后给它提供输入。 test.py import torch import torchvision from PIL import Imagedevice torch.device("cuda" if torch.cuda.is_available() else "cpu") ima…

C++学习笔记——仿函数

文章目录 仿函数——思维导图仿函数是什么仿函数的优势理解仿函数仿函数的原理举例 仿函数——思维导图 仿函数是什么 使用对象名调用operator&#xff08;&#xff09;函数看起来像是在使用函数一样&#xff0c;因此便有了仿函数的称呼&#xff1b;仿函数存在的意义是&#x…

Burp Suite 抓包,浏览器提示有软件正在阻止Firefox安全地连接到此网站

问题现象 有软件正在阻止Firefox安全地连接到此网站 解决办法 没有安装证书&#xff0c;在浏览器里面安装bp的证书就可以了 参考&#xff1a;教程合集 《H01-启动和激活Burp.docx》——第5步

如何防止源代码泄露?彻底解决源代码防泄密的方法

SDC沙盒系统&#xff1a;数据安全的守护者 SDC沙盒系统&#xff0c;为研发型企业设计&#xff0c;实现了对数据的代码级保护&#xff0c;同时不影响工作效率和正常使用。系统通过自动加密敏感数据&#xff0c;并配合多种管控机制&#xff0c;有效防止了数据的泄露。 涉密可信…

代码随想录算法训练营第四十二天| 01背包问题(二维、一维)、416.分割等和子集

系列文章目录 目录 系列文章目录动态规划&#xff1a;01背包理论基础①二维数组②一维数组&#xff08;滚动数组&#xff09; 416. 分割等和子集①回溯法&#xff08;超时&#xff09;②动态规划&#xff08;01背包&#xff09;未剪枝版剪枝版 动态规划&#xff1a;01背包理论基…

渗透之sql注入----二次注入

目录 二次注入的原理&#xff1a; 实战&#xff1a; 第一步&#xff1a;找注入点 找漏洞&#xff1a; 注入大概过程&#xff1a; 第二步&#xff1a;开始注入 二次注入的原理&#xff1a; 二次注入是由于对用户输入的数据过滤不严谨&#xff0c;导致存在异常的数据被出入…

FreeRTOS的任务详解、创建与删除

目录 1、任务详解 1.1 什么是任务&#xff1f; 1.2 任务的特点 1.3 任务的状态 1.4 任务的优先级 1.5 任务的堆和栈 2、任务的创建与删除 2.1 相关API 2.2 函数解析 2.2.1 xTaxkCreate() 2.2.2 xTaskCreateStatic() 2.2.3 vTaskDelete() 3、实战案例 3.1 创建两个…

达梦数据刷盘测试

达梦数据库为了保证数据故障恢复的一致性&#xff0c;REDO 日志的刷盘必须在数据页刷盘之前进行。 下面我们通过测试来验证是不是这样 执行我们事先准备的SHELL脚本 可以看到第一次strings文件没有输出&#xff0c;说明刚写的数据在数据库的BUFFER缓冲区内&#xff0c;还没有刷…

RN阴影组件使用

yarn add react-native-shadow yarn add react-native-svg // 这个是必须的,阴影依赖这个包四周都有阴影,如下设置 import React from react; import {StyleSheet, View, Text} from react-native; import {BoxShadow} from react-native-shadow;const App () > {const …

3GBJ5016A-ASEMI电焊机专用3GBJ5016A

编辑&#xff1a;ll 3GBJ5016A-ASEMI电焊机专用3GBJ5016A 型号&#xff1a;3GBJ5016A 品牌&#xff1a;ASEMI 封装&#xff1a;3GBJ-5 正向电流&#xff08;Id&#xff09;&#xff1a;50A 反向耐压&#xff08;VRRM&#xff09;&#xff1a;1600V 正向浪涌电流&#xf…