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();elsem_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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTprivate: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"));elseui->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"));elseui->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++开发指南