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

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应用程序提供强大的音视…...

MS SQL 2008 技术内幕:T-SQL 语言基础

《MS SQL 2008 技术内幕&#xff1a;T-SQL 语言基础》是一部全面介绍 Microsoft SQL Server 2008 中 T-SQL&#xff08;Transact-SQL&#xff09;语言的书籍。T-SQL 是 SQL Server 的扩展版本&#xff0c;增加了编程功能和数据库管理功能&#xff0c;使得开发者和数据库管理员能…...

【Pandas】pandas Series filter

Pandas2.2 Series Computations descriptive stats 方法描述Series.align(other[, join, axis, level, …])用于将两个 Series 对齐&#xff0c;使其具有相同的索引Series.case_when(caselist)用于根据条件列表对 Series 中的元素进行条件判断并返回相应的值Series.drop([lab…...

uake 网络安全 reverse网络安全

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 本文首发于“合天网安实验室” 首先从PEID的算法分析插件来介绍&#xff0c;要知道不管是在CTF竞赛的REVERSE题目中&#xff0c;还是在实际的商业产品中&#xf…...

vue实现根据点击或滑动展示对应高亮

页面需求&#xff1a; 点击左侧版本号&#xff0c;右侧展示对应版本内容并置于顶部右侧某一内容滚动到顶部时&#xff0c;左侧需要展示高亮 实现效果&#xff1a; 实现代码&#xff1a; <template><div><div class"historyBox pd-20 bg-white">…...

Magma:多模态 AI 智体的基础模型

25年2月来自微软研究、马里兰大学、Wisconsin大学、韩国 KAIST 和西雅图华盛顿大学的论文“Magma: A Foundation Model for Multimodal AI Agents”。 Magma 是一个基础模型&#xff0c;可在数字和物理世界中服务于多模态 AI 智体任务。Magma 是视觉-语言 (VL) 模型的重要扩展…...

浅显易懂HashMap的数据结构

HashMap 就像一个大仓库&#xff0c;里面有很多小柜子&#xff08;数组&#xff09;&#xff0c;每个小柜子可以挂一串链条&#xff08;链表&#xff09;&#xff0c;链条太长的时候会变成更高级的架子&#xff08;红黑树&#xff09;。下面用超简单的例子解释&#xff1a; ​壹…...

怎么获取免费的 GPU 资源完成大语言模型(LLM)实验

怎么获取免费的 GPU 资源完成大语言模型(LLM)实验 目录 怎么获取免费的 GPU 资源完成大语言模型(LLM)实验在线平台类Google ColabKaggle NotebooksHugging Face Spaces百度飞桨 AI Studio在线平台类 Google Colab 特点:由 Google 提供的基于云端的 Jupyter 笔记本环境,提…...

Java SE与Java EE

Java SE&#xff08;Java 平台标准版&#xff09; Java SE 是 Java 平台的核心&#xff0c;提供了 Java 语言的基础功能。它包含了 Java 开发工具包&#xff08;JDK&#xff09;&#xff0c;其中有 Java 编译器&#xff08;javac&#xff09;、Java 虚拟机&#xff08;JVM&…...

02_linux系统命令

一、绝对路径与相对路径 1.以 ./ 开始的路径名是相对路径 2.以 / 开始的路径是绝对路径. 相对路径:会随着用户当前所在的目录发生改变. 绝对路径:不会根据用户所在的路径而改变. 3.gcc 编译器 编译器把高级语言(C语言/JAVA语言/C语言)生成二进制代码的一种工具.gcc 是专用…...

【leetcode hot 100 11】移动零

一、暴力解法&#xff1a;两个 for 循环&#xff0c;外层循环遍历所有可能的左边界&#xff0c;内层循环遍历所有可能的右边界 class Solution {public int maxArea(int[] height) {int max_area0;for(int i0; i<height.length; i){for(int ji1; j<height.length; j){in…...

AI绘画软件Stable Diffusion详解教程(2):Windows系统本地化部署操作方法(专业版)

一、事前准备 1、一台配置不错的电脑&#xff0c;英伟达显卡&#xff0c;20系列起步&#xff0c;建议显存6G起步&#xff0c;安装win10或以上版本&#xff0c;我的显卡是40系列&#xff0c;16G显存&#xff0c;所以跑大部分的模型都比较快&#xff1b; 2、科学上网&#xff0…...

轨迹控制--odrive的位置控制---负载设置

轨迹控制 此模式使您可以平滑地使电机旋转&#xff0c;从一个位置加速&#xff0c;匀速和减速到另一位置。 使用位置控制时&#xff0c;控制器只是试图尽可能快地到达设定点。 使用轨迹控制模式可以使您更灵活地调整反馈增益&#xff0c;以消除干扰&#xff0c;同时保持平稳的运…...

【安卓逆向】逆向APP界面UI修改再安装

1.背景 有一客户找到我&#xff0c;说能不能把APP首页的底部多余界面去掉。 逆向实战 想要去除安卓应用软件中的内容&#xff0c;需要对APP逆向进行修改再打包。 通过工具 MIT管理器工具 提取APK包&#xff0c;点击apk文件&#xff0c;点击查看反编译apk。 搜索关键字。这里关键…...

SAP Webide系列(7)- 优化FreeStyle新建项目预设模板

目录 一、背景 二、优化目标 三、定位调整点 四、调整步骤 五、效果展示 六、附言 一、背景 在每次通过Webide进行FreeStyle方式自开发SAP UI5应用的时候&#xff0c;新建项目&#xff0c;得到的模板文件都是只有很少的内容&#xff08;没有路由配置、没有设置默认全屏等…...

python读取sqlite温度数据,并画出折线图

需求&#xff1a; 在Windows下请用python画出折线图&#xff0c;x轴是时间&#xff0c;y轴是温度temperature 和体感温度feels_like_temperature 。可以选择县市近1小时&#xff0c;近1天&#xff0c;近1个月的。sqlite文件weather_data.db当前目录下&#xff0c;建表结构如下…...

URL 对字母大小写敏感么?

URL 的不同部分对大小写的敏感性不同&#xff1a; 协议部分: 不区分大小写 例如&#xff1a;http:// 和 HTTP:// 被视为相同域名部分&#xff08;主机名&#xff09;: 不区分大小写 例如&#xff1a;example.com 和 ExAmPle.CoM 被视为相同路径部分 例如&#xff1a;example.c…...

【Linux网络编程】高效I/O--select/poll服务器

目录 多路转接之select select服务器实现 获取连接 handlerEvent select服务器代码链接 select的优缺点 多路转接之poll poll服务器实现(select服务器改写) poll的优缺点 多路转接之select select的作用 I/O的本质 等 拷贝 多路转接就是通过同时等待多个文件描述…...

2024年第十五届蓝桥杯大赛软件赛省赛Python大学A组真题解析

文章目录 试题A: 拼正方形(本题总分:5 分)解析答案试题B: 召唤数学精灵(本题总分:5 分)解析答案试题C: 数字诗意解析答案试题A: 拼正方形(本题总分:5 分) 【问题描述】 小蓝正在玩拼图游戏,他有7385137888721 个2 2 的方块和10470245 个1 1 的方块,他需要从中挑出一些…...

C语言实战项目(1)---------->猜数字游戏

在学习完循环和选择结构之后&#xff0c;我们可以做一个猜数字游戏。在此项目之前&#xff0c;如果还不会C语言的if语句、switch语句等组成选择结构的语句&#xff0c;while循环、for循环、do-while循环等组成循环结构的语句。可以参考我之前的博客&#xff1a; C语言&#xf…...

web理论总结

1.网页&#xff1b;可以承载各种网址应用和信息的容器&#xff0c;所有可视化的内容都会通过网页展示给用户。 2.网页主要由文字&#xff0c;图像和超链接等元素构成。还可以包含音频&#xff0c;视频以及动画等。 网页分为静态网页&#xff0c;动态网页大部分网页静动结合。…...

Failed to start The PHP FastCGI Process Manager.

报错如下&#xff1a; Job for php-fpm.service failed because the control process exited with error code. See "systemctl status php-fpm.service" and "journalctl -xe" for details. 2月 25 21:49:00 nginx systemd[1]: Starting The PHP FastC…...

001 Kafka入门及安装

Kafka入门及安装 文章目录 Kafka入门及安装1.介绍Kafka的基本概念和核心组件 2.安装1.docker快速安装zookeeper安装kafka安装 添加topic删除topickafka-ui安装 来源参考的deepseek&#xff0c;如有侵权联系立删 1.介绍 Kafka的基本概念和核心组件 Kafka是分布式流处理平台&a…...

【paddle】详解 padde.autograd.backward

目录 backward二次函数案例paddle 代码pytorch 代码 backward paddle.autograd.backward(tensors, grad_tensorsNone, retain_graphFalse)[源代码] 参数 tensors (list[Tensor]) – 将要计算梯度的 Tensors 列表。Tensors 中不能包含有相同的 Tensor。grad_tensors (None|list…...

飞腾腾锐D2000 + OpenHarmony 4.1release部署deepseek大模型

简介 1.1 飞腾腾锐D2000 飞腾腾锐D2000是一款面向桌面应用的高性能通用处理&#xff0c;集成8个飞腾自主研发的高能效处理器核FTC663&#xff0c;兼 容64位ARMv8指令集并支持ARM64和ARM32两种执行模式&#xff0c;支持单精度、双精度浮点运算指令和ASIMD处理 指令&#xff0c;主…...

【REST2SQL】15银河麒麟系统下达梦数据库部署REST2SQL

【REST2SQL】01RDB关系型数据库REST初设计 【REST2SQL】02 GO连接Oracle数据库 【REST2SQL】03 GO读取JSON文件 【REST2SQL】04 REST2SQL第一版Oracle版实现 【REST2SQL】05 GO 操作 达梦 数据库 【REST2SQL】06 GO 跨包接口重构代码 【REST2SQL】07 GO 操作 Mysql 数据库 【RE…...

晶体管输出光耦和逻辑输出光耦

晶体管输出光耦&#xff08;非线性&#xff09;和逻辑输出光耦&#xff08;线性&#xff09;的区别&#xff1a; 逻辑输出光耦的电流传输特性曲线是非线性的&#xff0c;适合于开关信号的传输&#xff0c;不适合于传输模拟量&#xff1b; 光电晶体管输出光耦的电流传输特性是线…...

智能生活综合平台需求规格说明书

一、引言 1.1 编写目的 本需求规格说明书旨在明确智能生活综合平台的功能、性能、用户界面等方面的需求&#xff0c;为平台的开发、测试和部署提供详细的指导&#xff0c;确保平台能够满足用户在社交、购物、教育、医疗、政务等多个领域的智能生活需求&#xff0c;提升用户的…...

绕过过滤order by

一、常见绕过技术 1、注释符截断 利用注释符&#xff08;如 --、#&#xff09;截断后续查询&#xff0c;消除过滤逻辑的影响。 ORDER BY 1-- 若原查询为 SELECT * FROM table ORDER BY 用户输入&#xff0c;注入后可能忽略后续过滤逻辑。 2、大小写混淆/编码绕过 若过滤是大…...

计算机网络之传输层(传输层的功能)

一、数据分段与重组 传输层从会话层接收数据&#xff0c;并将其分割成较小的数据段&#xff0c;以适应网络层的最大传输单元&#xff08;MTU&#xff09;限制。在目的端&#xff0c;传输层负责将这些数据段重新组合成原始数据&#xff0c;确保数据的完整性和正确性。 二、端口…...