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

news2025/2/27 13:34:41

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应用开发中的其他应用,开发不易珍惜每一分原创和劳动成果,同时注意平时开发过程中的经验积累总结。

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

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

相关文章

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) 模型的重要扩展…

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;建表结构如下…

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

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

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

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

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…

【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; 光电晶体管输出光耦的电流传输特性是线…

绕过过滤order by

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

面试八股文--数据库基础知识总结(1)

1、数据库的定义 数据库&#xff08;DataBase&#xff0c;DB&#xff09;简单来说就是数据的集合数据库管理系统&#xff08;Database Management System&#xff0c;DBMS&#xff09;是一种操纵和管理数据库的大型软件&#xff0c;通常用于建立、使用和维护数据库。数据库系统…

机试刷题_1614. 括号的最大嵌套深度【python】

1614. 括号的最大嵌套深度 class Solution:def maxDepth(self, s: str) -> int:maxD 0if not s:return maxDstack []for char in s:if char(:stack.append(char)maxD max(maxD,len(stack))elif char) :stack.pop()return maxD

VM虚拟机安装与配置Ubuntu Linux操作系统详细教程~

一、下载VM虚拟机 VMware16.0.zip百度网盘下载链接:https://pan.baidu.com/s/1-l-CcAVNINqhRLSiQ26R7w?pwd=tznn 提取码: tznn 二、软件介绍 VMware(虚拟机)是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统,通过它可在一台电脑上同…

免费PDF工具

Smallpdf.com - A Free Solution to all your PDF Problems Smallpdf - the platform that makes it super easy to convert and edit all your PDF files. Solving all your PDF problems in one place - and yes, free. https://smallpdf.com/#rappSmallpdf.com-解决您所有PD…