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