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

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

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

相关文章

WPF学习之Prism(二)

前言 学习一下Prism。 1.Prism Prism框架提供了一套丰富的工具、类和模块&#xff0c;帮助开发人员实现以下功能&#xff1a; 模块化&#xff1a;Prism框架支持将应用程序拆分为多个模块&#xff0c;每个模块具有自己的功能和视图。这种模块化的设计使得应用程序更加灵活和…

前端实现rsa加密功能

本文将从web和小程序两个端来实现rsa的加密功能。 一般项目的登录密码、身份证号以及一些用户敏感信息等在传输的时候需要使用加密传输&#xff0c;一般来说&#xff0c;前端只会得到后端给的公钥&#xff0c;而rsa加密&#xff0c;可以用公钥加密&#xff0c;也可以用私钥加密…

VidSketch:具有扩散控制的手绘草图驱动视频生成

浙大提出的VidSketch是第一个能够仅通过任意数量的手绘草图和简单的文本提示来生成高质量视频动画的应用程序。该方法训练是在单个 RTX4090 GPU 上进行的&#xff0c;针对每个动作类别使用一个小型、高质量的数据集。VidSketch方法使所有用户都能使用简洁的文本提示和直观的手绘…

STM32——HAL库开发笔记23(定时器4—输入捕获)(参考来源:b站铁头山羊)

定时器有四个通道&#xff0c;这些通道既可以用来作为输入&#xff0c;又可以作为输出。做输入的时候&#xff0c;可以使用定时器对外部输入的信号的时间参数进行测量&#xff1b;做输出的时候&#xff0c;可以使用定时器向外输出精确定时的方波信号。 一、输入捕获 的基本原理…

Jquery详解

一.Jquery介绍 1.jQuery 是一个快速、简洁的 JavaScript 库&#xff0c;它极大地简化了 HTML 文档遍历、事件处理、动画效果和 AJAX 交互等操作&#xff0c;使开发者能够更轻松地创建动态和交互性强的网页。对原生js的封装,提供了很多时间,调用Api即可,并且对浏览器做了兼容性…

【EB-06】SystemCreator dbc转arxml

SystemCreator dbc转arxml 1. SystemCreator 意义2. SystemCreator使用方法2.1 实现步骤2.2 参考官方文档方法1. SystemCreator 意义 EB Tresos 对dbc直接导入的支持不是很完善,dbc也不是AUTOSAR标准的数据库文件,EB建议所有通信矩阵通过ARXML交互比较合理(AUTOSAR定义的)…

LeetCode225.用队列实现栈

LeetCode225.用队列实现栈 文章目录 LeetCode225.用队列实现栈题目描述实现1:实现2: 题目描述 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#xff0c;并支持普通栈的全部四种操作&#xff08;push、top、pop 和 empty&#xff09;。 实现 MyStack…

【Linux】vim 设置

【Linux】vim 设置 零、起因 刚学Linux&#xff0c;有时候会重装Linux系统&#xff0c;然后默认的vi不太好用&#xff0c;需要进行一些设置&#xff0c;本文简述如何配置一个好用的vim。 壹、软件安装 sudo apt-get install vim贰、配置路径 对所有用户生效&#xff1a; …

【FL0091】基于SSM和微信小程序的社区二手物品交易小程序

&#x1f9d1;‍&#x1f4bb;博主介绍&#x1f9d1;‍&#x1f4bb; 全网粉丝10W,CSDN全栈领域优质创作者&#xff0c;博客之星、掘金/知乎/b站/华为云/阿里云等平台优质作者、专注于Java、小程序/APP、python、大数据等技术领域和毕业项目实战&#xff0c;以及程序定制化开发…

Javaweb后端数据库多表关系一对多,外键,一对一

多表关系 一对多 多的表里&#xff0c;要有一表里的主键 外键 多的表上&#xff0c;添加外键 一对一 多对多 案例

seacmsv9注入管理员账号密码+orderby+limit

seacmsv9注入管理员账号密码 seacms介绍 海洋影视管理系统&#xff08;seacms&#xff0c;海洋cms&#xff09;是一套专为不同需求的站长而设计的视频点播系统&#xff0c;采用的是 php5.Xmysql 的架构&#xff0c;使用 fofa 搜索可以看到存在 400的记录&#xff1a; 因为sea…

开源基准测试模拟器:BlueROV2 水下机器人的控制(更改Z方向控制器)

开源基准测试模拟器:BlueROV2 水下机器人的控制(更改Z方向控制器) 将原有项目的z方向控制器由自适应滑膜控制器(ASMC)更改为自抗扰控制器(ADRC) 原Z控制器 更改为ADRC后图像 原自适应滑膜控制器代码 function u =

【苍穹外卖】问题笔记

【DAY1 】 1.VCS找不到 好吧&#xff0c;发现没安git 接着发现安全模式有问题&#xff0c;点开代码信任此项目 2.导入初始文件&#xff0c;全员爆红 好像没maven&#xff0c;配一个 并在设置里设置好maven 3.启用注解&#xff0c;见新手苍穹 pom.xml改lombok版本为1.1…

项目实践 之 pdf简历的解析和填充(若依+vue3)

文章目录 环境背景最终效果前端讲解左侧模块解析右侧上传模块解析前端步骤 后端讲解代码前端 环境背景 若依前后端分离框架 vue最后边附有代码哦 最终效果 前端讲解 左侧模块解析 1、左侧表单使用el-form 注意&#xff1a; 1、prop出现的字段&#xff0c;需要保证是该类所…

Web自动化之Selenium控制已经打开的浏览器(Chrome,Edge)

在使用selenium进行web自动化或爬虫的时候,经常会面临登录的情况,对于这种情况,我们可以利用Selenium控制已经打开的浏览器&#xff0c;从而避免每次都需要重新打开浏览器并进行登录的繁琐步骤。 目录 说明 启动浏览器 注意 --user-data-dir说明 代码设定 代码 改进代…

千峰React:案例一

做这个案例捏 因为需要用到样式&#xff0c;所以创建一个样式文件&#xff1a; //29_实战.module.css .active{text-decoration:line-through } 然后创建jsx文件&#xff0c;修改main文件&#xff1a;导入Todos&#xff0c;写入Todos组件 import { StrictMode } from react …

自动驾驶FSD技术的核心算法与软件实现

引言&#xff1a;FSD技术的定义与发展背景 在当今快速发展的科技领域中&#xff0c;自动驾驶技术已经成为全球关注的焦点之一。其中&#xff0c;“FSD”&#xff08;Full Self-Driving&#xff0c;全自动驾驶&#xff09;代表了这一领域的最高目标——让车辆在无需人类干预的情…

Go红队开发—并发编程

文章目录 并发编程go协程chan通道无缓冲通道有缓冲通道创建⽆缓冲和缓冲通道 等协程sync.WaitGroup同步Runtime包Gosched()Goexit() 区别 同步变量sync.Mutex互斥锁atomic原子变量 SelectTicker定时器控制并发数量核心机制 并发编程阶段练习重要的细节端口扫描股票监控 并发编程…

【嵌入式原理设计】实验六:倒车控制设计

目录 一、实验目的 二、实验环境 三、实验内容 四、实验记录及处理 五、实验小结 六、成果文件提取链接 一、实验目的 熟悉和掌握各模块联合控制的工作方式 二、实验环境 Win10ESP32实验开发板 三、实验内容 1、用串口和OLED显示当前小车与障碍物的距离值&#xff1b…

探索浮点数在内存中的存储(附带快速计算补码转十进制)

目录 一、浮点数在内存中的存储 1、常见的浮点数&#xff1a; 2、浮点数存储规则&#xff1a; 3、内存中无法精确存储&#xff1a; 4、移码与指数位E&#xff1a; 5、指数E的三种情况&#xff1a; 二、快速计算补码转十进制 1、第一种方法讨论&#xff1a; 2、第二种方…