当前位置: 首页 > news >正文

Qt 开源音视频框架模块之QtAV播放器实践

Qt 开源音视频框架模块QtAV播放器实践

1 摘要

QtAV是一个基于Qt的多媒体框架,旨在简化音视频播放和处理。它是一个跨平台的库,支持多种音视频格式,并提供了一个简单易用的API来集成音视频功能。QtAV的设计目标是为Qt应用程序提供强大的音视频处理能力,同时保持灵活性和可扩展性。可支持音频、视频播放,并提供了丰富的 API 和功能,让开发者能够轻松地处理多媒体数据。

2 QtAV的特点

•跨平台:支持Windows、Linux、macOS等多个操作系统。
•多媒体格式支持:支持多种音视频格式,包括常见的MP4、AVI、MKV、FLV、MP3、AAC等。
•高性能:基于FFmpeg,提供了高效的音视频解码和播放能力。
•简单易用的API:提供了直观的接口,方便开发者快速上手。
•集成Qt的特性:与Qt的信号和槽机制兼容,支持在Qt应用程序中使用。

3 简单播放器实践

3.1 环境配置

QtAv是基于ffmpeg开发的,因此我们需要下载相关依赖库。QtAV-depends-windows-x86+x64,下载完成后库目录结构如下
在这里插入图片描述

新建Qt项目QAVPlayer,在项目中添加QtAVWidgetsd库。

在这里插入图片描述

3.2 播放功能实现

•Qt实现简单的播放器功能,主要用到的函数:
•播放控制:play(), pause(), stop()等方法控制播放。
•音量调节:使用setVolume(int volume)方法设置音量。
•进度控制:使用seek(int position)方法跳转到特定时间。
•信号与槽:可以连接信号,例如播放结束、加载完成等事件,以便进行相应的处理。

初始化代码

 	QtAV::VideoOutput  *m_vo;QtAV::AVPlayer     *m_player;
 	QtAV::Widgets::registerRenderers();m_player = new QtAV::AVPlayer(this);m_vo = new QtAV::VideoOutput(this);m_player->setRenderer(m_vo);ui->verticalLayout->addWidget(m_vo->widget());connect(ui->playSlider, SIGNAL(sliderMoved(int)), SLOT(onSliderMoved(int)));connect(ui->playSlider, SIGNAL(sliderReleased()), SLOT(onSliderReleased()));connect(m_player, SIGNAL(positionChanged(qint64)), this, SLOT(onPositionChange(qint64)));connect(m_player, SIGNAL(notifyIntervalChanged()), SLOT(updateSliderUnit()));CommonUtils::loadStyleSheet(ui->playSlider, ":/QSS/QSS/VolumnSlider.css");connect(ui->volumnBtn, &VolumnButton::valueChanged, this, &MainWindow::onValueChanged);

打开文件播放功能代码

fileName= QFileDialog::getOpenFileName(this, tr("Open File"), "", tr("*.avi *.wmv *.mpg *.mpeg *.mov *.mp4 *.flv"));if (fileName.isEmpty())return;
m_player->seekBackward();
m_player->play(fileName);

暂停功能代码

	if (m_player->isPaused()){m_player->pause(false);}else{m_player->pause(true);}this->style()->unpolish(ui->abortBtn);this->style()->polish(ui->abortBtn);ui->abortBtn->update();

音量设置功能代码

void MainWindow::onValueChanged(int value)
{if (value == 0 && ui->volumnBtn->property("silence").toBool() == false){ui->volumnBtn->setProperty("silence", true);ui->volumnBtn->setToolTip("静音");}else if (ui->volumnBtn->property("silence").toBool()){ui->volumnBtn->setProperty("silence", false);ui->volumnBtn->setToolTip("音量");}this->style()->unpolish(ui->volumnBtn);this->style()->polish(ui->volumnBtn);ui->volumnBtn->update();setVolume();
}
void MainWindow::setVolume()
{QtAV::AudioOutput *ao = m_player? m_player->audio() : 0;qreal v = qreal(ui->volumnBtn->volumnSlider()->value())*kVolumeInterval;if (ao) {if (qAbs(int(ao->volume() / kVolumeInterval) - ui->volumnBtn->volumnSlider()->value()) >= int(0.1 / kVolumeInterval)) {ao->setVolume(v);}}
}

音量滑竿功能代码

#include "volumnslider.h"
#include "commonutils.h"#include <QStyleOption>
#include <QPainter>
#include <QLabel>
#include <QDebug>VolumnSlider::VolumnSlider(QWidget *parent): QSlider(parent)
{CommonUtils::loadStyleSheet(this, ":/QSS/QSS/VolumnSlider.css");setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow);setAttribute(Qt::WA_TranslucentBackground);m_label = new QLabel(nullptr);m_label->setStyleSheet("QLabel{color:rgb(179, 179, 179);background:rgb(69,69,69);}");m_label->setMargin(6);m_label->setFixedHeight(24);m_label->setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow);m_label->hide();this->setRange(0, 100);this->setValue(100);
}VolumnSlider::~VolumnSlider()
{delete m_label;
}void VolumnSlider::paintEvent(QPaintEvent *event)
{QStyleOption opt;opt.init(this);QPainter p(this);style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);QSlider::paintEvent(event);
}void VolumnSlider::showEvent(QShowEvent *event)
{setFocus();QSlider::showEvent(event);
}void VolumnSlider::focusOutEvent(QFocusEvent *event)
{QPoint gloabPoint = this->mapToGlobal(QPoint(0, 0));QRect gloabRect(gloabPoint.x(), gloabPoint.y(), width(), height());if (!gloabRect.contains(QCursor::pos())){this->hide();m_label->hide();}else{this->setFocus();}QSlider::focusOutEvent(event);
}void VolumnSlider::mouseMoveEvent(QMouseEvent *event)
{setLabelText();QSlider::mouseMoveEvent(event);
}void VolumnSlider::mousePressEvent(QMouseEvent *event)
{setLabelText();m_label->show();QSlider::mousePressEvent(event);
}void VolumnSlider::mouseReleaseEvent(QMouseEvent *event)
{m_label->hide();QSlider::mouseReleaseEvent(event);
}void VolumnSlider::setLabelText()
{const QString&& value = QString::number(this->value());m_label->setFixedWidth(m_label->fontMetrics().width(value) + 12);m_label->setText(value);QPoint pos;;QPoint thePos = this->mapToGlobal(QPoint(0, 0));int posY = QCursor::pos().y();pos.setX(thePos.x() + 20);if (posY < thePos.y()){posY = thePos.y();}else if (posY > thePos.y() + this->height() - m_label->height()){posY = thePos.y() + this->height() - m_label->height();}pos.setY(posY);m_label->move(pos);
}

滑竿拖动快进功能核心代码

#include "playtimeslider.h"
//#include "head.h"#include <QPainter>
#include <QVariant>
#include <QTime>
#include <QApplication>const int pixwidth = 200;
const int pixheight = 116;using namespace QtAV;
ImageWidget::ImageWidget(QWidget *parent)
{m_timestr = "00:00:00";m_extractor = new VideoFrameExtractor(this);setWindowFlags(Qt::FramelessWindowHint | Qt::SubWindow);setAttribute(Qt::WA_TranslucentBackground);hide();connect(m_extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), SLOT(displayFrame(QtAV::VideoFrame)));connect(m_extractor, SIGNAL(error(const QString &)), SLOT(displayNoFrame()));connect(m_extractor, SIGNAL(aborted(const QString &)), SLOT(displayNoFrame()));
}void ImageWidget::displayFrame(const QtAV::VideoFrame &frame){if (!frame.isValid()) {displayNoFrame();return;}m_image = frame.toImage();update();
}void ImageWidget::displayNoFrame()
{m_image = QImage();update();
}void ImageWidget::seekVideoFrame(qint64 value)
{m_timestr = QTime(0, 0, 0).addMSecs(value).toString(QString::fromLatin1("HH:mm:ss"));m_extractor->setPosition(value);m_extractor->extract();update();
}void ImageWidget::setPlayFile(const QString& file)
{m_extractor->setSource(file);
}void ImageWidget::paintEvent(QPaintEvent *event)
{QWidget::paintEvent(event);QPainter painter(this);painter.setPen(Qt::NoPen);painter.setBrush(QColor("333333"));painter.drawRect(0, 0, this->width(), this->height() - 16);painter.setPen(Qt::white);painter.drawText(0, this->height() - 16, this->width(), 16, Qt::AlignCenter, m_timestr);painter.drawText(0, 0, this->width(), this->height() - 16, Qt::AlignCenter, "加载中...");if (!m_image.isNull()){painter.drawImage(QRect(0, 0, this->width(), this->height() - 16), m_image.scaled(this->width(), this->height() - 16, Qt::KeepAspectRatio, Qt::FastTransformation));}
}PlayTimeSlider::PlayTimeSlider(QWidget *parent): QSlider(parent)
{m_pixWidget = new ImageWidget(nullptr);m_pixWidget->setFixedSize(pixwidth, pixheight);
}PlayTimeSlider::~PlayTimeSlider()
{delete m_pixWidget;
}void PlayTimeSlider::stopPlayer()
{this->setValue(minimum());
}void PlayTimeSlider::setPlayervalue(int value)
{if (!isSliderDown())setValue(value);
}void PlayTimeSlider::showEvent(QShowEvent *event)
{setFocus();QSlider::showEvent(event);
}void PlayTimeSlider::focusOutEvent(QFocusEvent *event)
{QPoint gloabPoint = this->mapToGlobal(QPoint(0, 0));QRect gloabRect(gloabPoint.x(), gloabPoint.y(), width(), height());if (!gloabRect.contains(QCursor::pos())){m_pixWidget->hide();}else{this->setFocus();}QSlider::focusOutEvent(event);
}void PlayTimeSlider::mouseMoveEvent(QMouseEvent *event)
{updatePixWidgetPosition();QSlider::mouseMoveEvent(event);
}void PlayTimeSlider::mousePressEvent(QMouseEvent *event)
{QSlider::mousePressEvent(event);
}void PlayTimeSlider::mouseReleaseEvent(QMouseEvent *event)
{m_pixWidget->hide();QSlider::mouseReleaseEvent(event);
}void PlayTimeSlider::setImageWidgetVisibe(bool visible)
{m_pixWidget->setVisible(visible);
}void PlayTimeSlider::seekVideoFrame(qint64 value)
{m_pixWidget->seekVideoFrame(value);
}void PlayTimeSlider::updatePixWidgetPosition()
{QPoint pos;QPoint thePos = this->mapToGlobal(QPoint(0, 0));int mouseX = QCursor::pos().x();int posX = mouseX - pixwidth / 2;pos.setY(thePos.y() - pixheight - 4);if (mouseX < thePos.x()){posX = thePos.x() - pixwidth / 2;}else if (mouseX > thePos.x() + this->width()){posX = thePos.x() + this->width() - pixwidth / 2;}pos.setX(posX);m_pixWidget->move(pos);
}void PlayTimeSlider::setVideoFile(const QString& file)
{m_pixWidget->setPlayFile(file);
}

4 播放器效果

在这里插入图片描述

在这里插入图片描述

5 总结

本文介绍了Qt中开源音视频框架模块QtAV,通过使用该音视频播放库实现简单的播放器,了解该库的使用方法,可为后续音视频开发提供更丰富的功能,开发出更好用灵活音视频设计。后续则继续总结分享Qt应用开发中的其他应用,开发不易珍惜每一分原创和劳动成果,同时注意平时开发过程中的经验积累总结。

相关文章:

Qt 开源音视频框架模块之QtAV播放器实践

Qt 开源音视频框架模块QtAV播放器实践 1 摘要 QtAV是一个基于Qt的多媒体框架&#xff0c;旨在简化音视频播放和处理。它是一个跨平台的库&#xff0c;支持多种音视频格式&#xff0c;并提供了一个简单易用的API来集成音视频功能。QtAV的设计目标是为Qt应用程序提供强大的音视…...

【前端】XML,XPATH,与HTML的关系

XML与HTML关系 XML&#xff08;可扩展标记语言&#xff09;和 HTML&#xff08;超文本标记语言&#xff09;是两种常见的标记语言&#xff0c;但它们有不同的目的和用途。它们都使用类似的标记结构&#xff08;标签&#xff09;&#xff0c;但在设计上存在一些关键的差异。 XML…...

ubuntu服务器安装VASP.6.4.3

ubuntu服务器安装VASP.6.4.3 1 安装Intel OneAPI Base Toolkit和Intel OneAPI HPC Toolkit1.1 更新并安装环境变量1.2 下载Intel OneAPI Base Toolkit和Intel OneAPI HPC Toolkit安装包1.3 安装 Intel OneAPI Base Toolkit1.4 安装 Intel OneAPI HPC Toolkit1.5 添加并激活环境…...

市场加速下跌,但监管「坚冰」正在消融

作者&#xff1a;Techub 热点速递 撰文&#xff1a;Yangz&#xff0c;Techub News 与近日气温逐步回暖不同&#xff0c;自 2 月 25 日比特币跌破 9 万美元以来&#xff0c;加密货币市场行情一路下滑。今日 10 时 50 分左右&#xff0c;比特币更是跌破 8 万美元大关&#xff0c…...

7.2 - 定时器之计算脉冲宽度实验

文章目录 1 实验任务2 系统框图3 软件设计 1 实验任务 本实验任务是通过CPU私有定时器来计算按键按下的时间长短。 2 系统框图 参见7.1。 3 软件设计 注意事项&#xff1a; 定时器是递减计数的&#xff0c;需要考虑StartCount&#xff1c;EndCount的情况。 /***********…...

Imagination DXTP GPU IP:加速游戏AI应用,全天候畅玩无阻

日前&#xff0c;Imagination 推出了最新产品——Imagination DXTP GPU IP&#xff0c;在智能手机和其他功耗受限设备上加速图形和AI工作负载时&#xff0c;保证全天候的电池续航。它是我们最新D系列GPU的最终产品&#xff0c;集成了自2022年发布以来引入的一系列功能&#xff…...

关于流水线的理解

还是不太理解&#xff0c;我之前一直以为&#xff0c;对axis总线&#xff0c;每一级的寄存器就像fifo一样&#xff0c;一级一级的分级存储最后一级需要的数据。&#xff08;现在看来&#xff0c;我这个理解应该也是没有问题的&#xff09; 如下图&#xff0c;一开始是在解析axi…...

采样算法二:去噪扩散隐式模型(DDIM)采样算法详解教程

参考 https://arxiv.org/pdf/2010.02502 一、背景与动机 去噪扩散隐式模型&#xff08;DDIM&#xff09; 是对DDPM的改进&#xff0c;旨在加速采样过程同时保持生成质量。DDPM虽然生成效果优异&#xff0c;但其采样需迭代数百至数千次&#xff0c;效率较低。DDIM通过以下关键…...

北京大学DeepSeek提示词工程与落地场景(PDF无套路免费下载)

近年来&#xff0c;大模型技术飞速发展&#xff0c;但许多用户发现&#xff1a;即使使用同一款 AI 工具&#xff0c;效果也可能天差地别——有人能用 AI 快速生成精准方案&#xff0c;有人却只能得到笼统回答。这背后的关键差异&#xff0c;在于提示词工程的应用能力。 北京大…...

Hutool - POI:让 Excel 与 Word 操作变得轻而易举

各位开发者们&#xff0c;在日常的 Java 开发工作里&#xff0c;处理 Excel 和 Word 文件是相当常见的需求。无论是从 Excel 里读取数据进行分析&#xff0c;还是将数据写入 Excel 生成报表&#xff0c;亦或是对 Word 文档进行内容编辑&#xff0c;传统的 Apache POI 库虽然功能…...

IDEAPyCharm安装ProxyAI(CodeGPT)插件连接DeepSeek-R1教程

背景&#xff1a;最近DeepSeek比较火嘛&#xff0c;然后在githup上也看到了GitHub Copilot&#xff0c;就想着现在AI的准确率已经可以提高工作效率了。所以从网上找了一些编程插件&#xff0c;发现Proxy支持的模型比较多&#xff0c;通用性和适配性比较好。所以本文记录一下pro…...

Iceberg Catalog

使用限制​ 支持 Iceberg V1/V2 表格式。支持 Position Delete。2.1.3 版本开始支持 Equality Delete。支持 Parquet 文件格式2.1.3 版本开始支持 ORC 文件格式。 创建 Catalog​ 基于 Hive Metastore 创建 Catalog​ 和 Hive Catalog 基本一致&#xff0c;这里仅给出简单示…...

2025年2月个人工作生活总结

本文为 2025年2月工作生活总结。 工作记录 AI浪潮 AI非常火&#xff0c;春节至今&#xff0c;到处充斥着大量和AI、DeepSeek有关的新闻。领导也一再强调要用AI&#xff0c;甚至纳入到新一年的考核里。再往上&#xff0c;大领导开会的新闻稿里也作出要求&#xff0c;不能停下脚…...

vscode java环境中文乱码的问题

先说我的结论&#xff1a; 由于我的系统是windows的&#xff0c;所以vscode使用的是默认gbk的编码进行的。 但是我的目的是全部都使用utf-8&#xff0c;因为我的程序始终是要去linux上去运行的&#xff0c;总不能在本地是好的&#xff0c;然后到服务器上就不行了吧&#xff0c;…...

Java数据结构第十五期:走进二叉树的奇妙世界(四)

专栏&#xff1a;Java数据结构秘籍 个人主页&#xff1a;手握风云 目录 一、二叉树OJ练习题&#xff08;续&#xff09; 1.1. 二叉树的层序遍历 1.2. 二叉树的最近公共祖先 1.3. 从前序与中序遍历序列构造二叉树 1.4. 从中序与后序遍历序列构造二叉树 1.5. 根据二叉树创建…...

【MySQL】CAST()在MySQL中的用法以及其他常用的数据类型转换函数

1. cast() CAST() 在 MySQL 中用于将一个表达式的类型转换为另一个类型。这在处理不同类型的数据时非常有用&#xff0c;比如将字符串转换为数字&#xff0c;或者将浮点数转换为整数等。 1.1 CAST() 函数的基本语法 CAST() 函数的基本语法如下&#xff1a; CAST(expression…...

使用Truffle、Ganache、MetaMask、Vue+Web3完成的一个简单区块链项目

文章目录 概要初始化Truffle项目创建编写合约编译合约配置Ganache修改truffle-config.js文件编写迁移文件部署合约使用Truffle 控制台使用MetaMask和VueWeb3与链交互 概要 使用Truffle、Ganache、MetaMask、VueWeb3完成的一个简单区块链项目。 初始化Truffle项目 安装好truf…...

初出茅庐的小李博客之按键驱动库使用

驱动库介绍 源码地址&#xff1a;https://github.com/jiejieTop/ButtonDrive 使用只需3步&#xff0c;创建按键&#xff0c;按键事件与回调处理函数链接映射&#xff0c;周期检查按键&#xff0c;支持单双击、连按、长按&#xff1b;采用回调处理按键事件&#xff08;自定义消…...

如何调试Linux内核?

通过创建一个最小的根文件系统&#xff0c;并使用QEMU和GDB进行调试。 1.准备工作环境 确保系统上安装了所有必要的工具和依赖项。 sudo apt-get update //更新一下软件包 sudo apt-get install build-essential git libncurses-dev bison flex libssl-dev qemu-system-x…...

ECharts组件封装教程:Vue3中的实践与探索

在日常的前端开发中,ECharts 作为一款强大且易用的图表库,被广泛应用于数据可视化场景。为了更好地在 Vue3 项目中复用 ECharts 功能,我们可以将其封装成一个组件。本文将带大家一步步实现 ECharts 的 Vue3 组件封装,并演示如何在父组件中调用和使用。 一、封装 ECharts 组…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...