QT多媒体开发(三):使用QMediaPlayer播放视频
QMediaPlayer 类不仅能播放音频文件,也可以播放各种常见的视频文件,如 MP4 文件和 WMV 文件。QMediaPlayer 能对视频文件进行解码,并在某个界面组件上显示视频帧。
使用 QMediaPlayer 播放视频时,必须用函数 setVideoOutput()设置用于显示视频的界面组件, 有 QVideoWidget 和 QGraphicsVideoItem 两种显示视频的组件:QVideoWidget 是 QWidget 的类, 是普通的界面组件;QGraphicsVideoItem 是适用于图形/视图架构的图形项。
注意还需要使用setAudioOutput()设置音频输出通道,否则播放视频时会没有声音。
使用QVideoWidget 类
由于使用QVideoWidget 实现全屏显示有一个问题,程序无法退出全屏显示,按 ESC 键也不起作用。所以可以从QVideoWidget 继承并自定义了一个类 TMyVideoWidget。
然后为这个自定义类重写keyPressEvent()事件和mousePressEvent()事件。实现在全屏状态下按 ESC 键可以退出全屏状态,在组件上点击鼠标可以暂停播放或继续播放。
该类的完整实现如下所示:
#ifndef TMYVIDEOWIDGET_H
#define TMYVIDEOWIDGET_H
#include <QObject>
#include <QWidget>
#include <QMediaPlayer>
#include <QVideoWidget>
class TMyVideoWidget : public QVideoWidget
{
Q_OBJECT
private:
QMediaPlayer *m_player;
protected:
void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QMouseEvent *event);
public:
TMyVideoWidget(QWidget *parent =nullptr);
void setMediaPlayer(QMediaPlayer *player);
};
#endif // TMYVIDEOWIDGET_H
#include "tmyvideowidget.h"
#include <QKeyEvent>
#include <QMouseEvent>
void TMyVideoWidget::keyPressEvent(QKeyEvent *event)
{//按键事件处理函数,ESC退出全屏状态
if ((event->key() == Qt::Key_Escape)&&(isFullScreen()))
{
setFullScreen(false); //退出全屏状态
event->accept();
QVideoWidget::keyPressEvent(event);
}
}
void TMyVideoWidget::mousePressEvent(QMouseEvent *event)
{//鼠标事件处理函数,单击时暂停或继续播放
if (event->button()==Qt::LeftButton)
{
if (m_player->playbackState()==QMediaPlayer::PlayingState)
m_player->pause();
else
m_player->play();
}
QVideoWidget::mousePressEvent(event);
}
TMyVideoWidget::TMyVideoWidget(QWidget *parent):QVideoWidget(parent)
{
}
void TMyVideoWidget::setMediaPlayer(QMediaPlayer *player)
{//设置播放器
m_player=player;
}
主窗口头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtMultimedia>
#include "tmyvideowidget.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QMediaPlayer *player; //视频播放器
QString durationTime;
QString positionTime;
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
//自定义槽函数
void do_stateChanged(QMediaPlayer::PlaybackState state);
void do_durationChanged(qint64 duration);
void do_positionChanged(qint64 position);
void on_btnAdd_clicked();
void on_btnPlay_clicked();
void on_btnPause_clicked();
void on_btnStop_clicked();
void on_sliderVolumn_valueChanged(int value);
void on_btnSound_clicked();
void on_sliderPosition_valueChanged(int value);
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
在主窗口构造函数中创建QMediaPlayer。使用setAudioOutput()函数设置音频输出通道,使用setVideoOutput()设置视频显示组件(上述自定义类),并设置显示组件关联的播放器。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
player = new QMediaPlayer(this); //创建视频播放器
QAudioOutput *audioOutput= new QAudioOutput(this);
player->setAudioOutput(audioOutput); //设置音频输出通道
player->setVideoOutput(ui->videoWidget); //设置视频显示组件
ui->videoWidget->setMediaPlayer(player); //设置显示组件的关联播放器
connect(player,&QMediaPlayer::playbackStateChanged,this, &MainWindow::do_stateChanged);
connect(player,&QMediaPlayer::positionChanged,this, &MainWindow::do_positionChanged);
connect(player,&QMediaPlayer::durationChanged,this, &MainWindow::do_durationChanged);
}
与音频播放一样,设置QMediaPlayer发射信号对应的槽函数。详细参考上一期音频播放
void MainWindow::do_stateChanged(QMediaPlayer::PlaybackState state)
{//播放器状态变化
bool isPlaying = (state==QMediaPlayer::PlayingState);
ui->btnPlay->setEnabled(!isPlaying);
ui->btnPause->setEnabled(isPlaying);
ui->btnStop->setEnabled(isPlaying);
}
void MainWindow::do_durationChanged(qint64 duration)
{//文件时长变化
ui->sliderPosition->setMaximum(duration);
int secs=duration/1000; //秒
int mins=secs/60; //分钟
secs=secs % 60; //余数秒
durationTime=QString::asprintf("%d:%d",mins,secs);
ui->LabRatio->setText(positionTime+"/"+durationTime);
}
void MainWindow::do_positionChanged(qint64 position)
{//文件播放位置变化
if (ui->sliderPosition->isSliderDown())
return; //如果正在拖动滑条,退出
ui->sliderPosition->setSliderPosition(position);
int secs=position/1000; //秒
int mins=secs/60; //分钟
secs=secs % 60; //余数秒
positionTime=QString::asprintf("%d:%d",mins,secs);
ui->LabRatio->setText(positionTime+"/"+durationTime);
}
其他部分代码如下:
void MainWindow::on_btnAdd_clicked()
{//打开文件
QString curPath=QDir::homePath();
QString dlgTitle="选择视频文件";
QString filter="视频文件(*.wmv, *.mp4);;所有文件(*.*)";
QString aFile=QFileDialog::getOpenFileName(this,dlgTitle,curPath,filter);
if (aFile.isEmpty())
return;
QFileInfo fileInfo(aFile);
ui->labCurMedia->setText(fileInfo.fileName());
player->setSource(QUrl::fromLocalFile(aFile)); //设置播放文件
player->play();
}
void MainWindow::on_btnPlay_clicked()
{//播放
player->play();
}
void MainWindow::on_btnPause_clicked()
{//暂停
player->pause();
}
void MainWindow::on_btnStop_clicked()
{//停止
player->stop();
}
void MainWindow::on_sliderVolumn_valueChanged(int value)
{//调节音量
player->audioOutput()->setVolume(value/100.0);
}
void MainWindow::on_btnSound_clicked()
{//静音按钮
bool mute=player->audioOutput()->isMuted();
player->audioOutput()->setMuted(!mute);
if (mute)
ui->btnSound->setIcon(QIcon(":/images/images/volumn.bmp"));
else
ui->btnSound->setIcon(QIcon(":/images/images/mute.bmp"));
}
void MainWindow::on_sliderPosition_valueChanged(int value)
{//播放位置
player->setPosition(value);
}
void MainWindow::on_pushButton_clicked()
{//全屏按钮
ui->videoWidget->setFullScreen(true);
}
可以看到与播放音频时实现类似功能的代码基本相同。
使用QGraphicsVideoItem类
QGraphicsVideoItem 是 继承自 QGraphicsItem 的类,是适用于图形/视图架构的视频输出组件。因此,使用 QGraphicsVideoItem 组件显示视频时,可以在显示场景中将其和其他图形项组合显示,可以使用 QGraphicsItem 类的放 大、缩小、拖动、旋转等功能。
主窗口头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtMultimedia>
#include <QGraphicsVideoItem>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QMediaPlayer *player;
QGraphicsVideoItem *videoItem; //视频显示图形项
QString durationTime;
QString positionTime;
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
//自定义槽函数
void do_stateChanged(QMediaPlayer::PlaybackState state);
void do_durationChanged(qint64 duration);
void do_positionChanged(qint64 position);
void on_btnAdd_clicked();
void on_btnPlay_clicked();
void on_btnPause_clicked();
void on_btnStop_clicked();
void on_sliderVolumn_valueChanged(int value);
void on_btnSound_clicked();
void on_sliderPosition_valueChanged(int value);
void on_btnZoomIn_clicked();
void on_btnZoomOut_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
在主窗口构造函数中,像之前一样创建QMediaPlayer播放器并设置音频输出通道
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
player = new QMediaPlayer(this); //创建播放器
QAudioOutput *audioOutput= new QAudioOutput(this);
player->setAudioOutput(audioOutput); //设置音频输出通道
QGraphicsScene *scene = new QGraphicsScene(this); //创建场景
ui->graphicsView->setScene(scene); //为视图设置场景
videoItem = new QGraphicsVideoItem; //常见视频显示图形项
videoItem->setSize(QSizeF(360, 240));
videoItem->setFlags(QGraphicsItem::ItemIsMovable
| QGraphicsItem::ItemIsSelectable);
// | QGraphicsItem::ItemIsFocusable);
scene->addItem(videoItem);
player->setVideoOutput(videoItem); //设置视频显示图形项
connect(player,&QMediaPlayer::playbackStateChanged,this, &MainWindow::do_stateChanged);
connect(player,&QMediaPlayer::positionChanged,this, &MainWindow::do_positionChanged);
connect(player,&QMediaPlayer::durationChanged,this, &MainWindow::do_durationChanged);
QGraphicsSimpleTextItem *item2=new QGraphicsSimpleTextItem("海风吹,海浪涌");
QFont font=item2->font();
font.setPointSize(20);
item2->setFont(font);
item2->setPos(0,0);
item2->setBrush(QBrush(Qt::blue));
item2->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
// | QGraphicsItem::ItemIsFocusable);
scene->addItem(item2);
// //一个圆,中心位于scene的边缘
// QGraphicsEllipseItem *item3=new QGraphicsEllipseItem(50,50,50,50); //矩形框内创建椭圆,绘图项的局部坐标,左上角(-100,-50),宽200,高100
// item3->setPos(100,100);
// item3->setBrush(QBrush(Qt::red));
// item3->setFlags(QGraphicsItem::ItemIsMovable
// | QGraphicsItem::ItemIsSelectable
// | QGraphicsItem::ItemIsFocusable);
// scene->addItem(item3);
}
唯一需要注意的是,显示视频用的组件是QGraphicsVideoItem类型的,因此在使用setVideoOutput()函数时传入的参数是QGraphicsVideoItem的图形项。QGraphicsView组件已经嵌入在主窗口中,设置好场景和图形项后,使用方式与往期博客讲的图形/视图架构一致,视频窗口在这里也是一个图形项,在主窗口构造函数中可以设置初始化大小。
视频播放的其他部分与QVideoWidget 播放视频的例子基本一致,只是对于在QGraphicsView上显示的视频,可以扩展一些其他操作。
void MainWindow::do_stateChanged(QMediaPlayer::PlaybackState state)
{
bool isPlaying = (state==QMediaPlayer::PlayingState);
ui->btnPlay->setEnabled(!isPlaying);
ui->btnPause->setEnabled(isPlaying);
ui->btnStop->setEnabled(isPlaying);
}
void MainWindow::do_durationChanged(qint64 duration)
{
ui->sliderPosition->setMaximum(duration);
int secs=duration/1000;//秒
int mins=secs/60; //分钟
secs=secs % 60;//余数秒
durationTime=QString::asprintf("%d:%d",mins,secs);
ui->LabRatio->setText(positionTime+"/"+durationTime);
}
void MainWindow::do_positionChanged(qint64 position)
{
if (ui->sliderPosition->isSliderDown())
return;
ui->sliderPosition->setSliderPosition(position);
int secs=position/1000; //秒
int mins=secs/60; //分钟
secs=secs % 60; //余数秒
positionTime=QString::asprintf("%d:%d",mins,secs);
ui->LabRatio->setText(positionTime+"/"+durationTime);
}
void MainWindow::on_btnAdd_clicked()
{
QString curPath=QDir::homePath();
QString dlgTitle="选择视频文件";
QString filter="视频文件(*.wmv, *.mp4);;所有文件(*.*)";
QString aFile=QFileDialog::getOpenFileName(this,dlgTitle,curPath,filter);
if (aFile.isEmpty())
return;
QFileInfo fileInfo(aFile);
ui->LabCurMedia->setText(fileInfo.fileName());
player->setSource(QUrl::fromLocalFile(aFile));
player->play();
}
void MainWindow::on_btnPlay_clicked()
{
player->play();
}
void MainWindow::on_btnPause_clicked()
{
player->pause();
}
void MainWindow::on_btnStop_clicked()
{
player->stop();
}
void MainWindow::on_sliderVolumn_valueChanged(int value)
{
player->audioOutput()->setVolume(value);
}
void MainWindow::on_btnSound_clicked()
{
bool mute=player->audioOutput()->isMuted();
player->audioOutput()->setMuted(!mute);
if (mute)
ui->btnSound->setIcon(QIcon(":/images/images/volumn.bmp"));
else
ui->btnSound->setIcon(QIcon(":/images/images/mute.bmp"));
}
void MainWindow::on_sliderPosition_valueChanged(int value)
{
player->setPosition(value);
}
void MainWindow::on_btnZoomIn_clicked()
{//放大
qreal factor=videoItem->scale();
videoItem->setScale(factor+0.1);
}
void MainWindow::on_btnZoomOut_clicked()
{//缩小
qreal factor=videoItem->scale();
if (factor>=0.2)
videoItem->setScale(factor-0.1);
}
参考
QT6 C++开发指南