操作系统: win10 64位
QT版本: QT5.12.6
编译器: MinGW 32
QMediaPlayer是Qt提供的一个跨平台媒体播放器类,它没有自带解码库,而是对平台相关的播放器框架做了封装,提供了平台无关的API。所以使用QMediaPlayer播放视频,需要提前安装解码库。 在win系统下可以下载K-Lite_Codec_Pack或者LAVFilters解码库安装。
下载地址:https://download.csdn.net/download/xiaolong1126626497/15702561
工程完整源码下载: https://download.csdn.net/download/xiaolong1126626497/15804157
1. 支持命令行传入视频播放
2. 支持图像旋转播放
3. 支持查看媒体信息
4. 支持选择GPU加速解码
5. 支持快进、快退
6. 支持预览画面(鼠标放在进度条上查看画面缩略图)
7. 支持单帧播放,就是一帧一帧的点击切换画面
8. 支持画面拍照、截图
9. 支持复位到视频首页
10. 支持音量调整
11. 支持拖拽文件到窗口播放
12. 默认打开视频不会自动播放。 自动显示在第一帧,视频放完停留在最后一帧。
为了更加方便处理视频帧,代码里重写了QAbstractVideoSurface 类,接收解码的视频帧,自己完成绘制。
头文件:
#ifndef MEDIAVIDEOFRAME_H
#define MEDIAVIDEOFRAME_H
#include <QAbstractVideoSurface>
class MediaVideoFrame : public QAbstractVideoSurface
{
Q_OBJECT
public:
explicit MediaVideoFrame(QObject *parent = nullptr);
signals:
void SendOneFrame(QImage img);
public slots:
// QAbstractVideoSurface interface
public:
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const;
bool present(const QVideoFrame &frame);
};
#endif // MEDIAVIDEOFRAME_H
源文件:
#include "MediaVideoFrame.h"
MediaVideoFrame::MediaVideoFrame(QObject *parent) : QAbstractVideoSurface(parent)
{
}
QList<QVideoFrame::PixelFormat> MediaVideoFrame::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
{
QList<QVideoFrame::PixelFormat> list;
list<<QVideoFrame::Format_RGB32; //支持的格式
return list;
}
bool MediaVideoFrame::present(const QVideoFrame &frame)
{
if(frame.isValid())
{
QVideoFrame cloneFrame(frame);
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
QImage img(cloneFrame.bits(),cloneFrame.width(),cloneFrame.height(),QVideoFrame::imageFormatFromPixelFormat(cloneFrame.pixelFormat()));
cloneFrame.unmap();
emit SendOneFrame(img);
return true;
}
return false;
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QFile>
#include <QFileDialog>
#include <QActionGroup>
#include <QMenu>
#include <QApplication>
#include <QClipboard>
#include <QStyle>
#include <QToolTip>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QMouseEvent>
#include "work_thread.h"
#include "dialog_waitcomplete.h"
#include <QDesktopWidget>
#include <QMessageBox>
#include <QMediaPlayer>
#include "MediaVideoFrame.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
enum MENU_ITEM
{
MENU_COPY_CLIPBOARD = 0,
MENU_SAVE_FILE_SYSTEM,
MENU_EDIT_SUBTITLE,
MENU_SHOW_SUBTITLE,
MENU_PLAY_NARRATOR,
MENU_TO_KeyFrame, //转为关键帧
MENU_APP_INFO,//APP信息介绍
};
enum MEDIA_STATE
{
MEDIA_NOLOAD,//视频未加载
MEDIA_LOAD,//视频加载
};
//媒体状态信息
struct MediaStateInfo
{
MEDIA_STATE state;
QString mediaName;
};
//配置文件名称
#define ConfigFile "cofnig.ini"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QString filename=nullptr,QWidget *parent = nullptr);
~Widget();
//加载样式表
void SetStyle(const QString &qssFile);
//UI界面初始化
void UI_InitConfig();
//加载视频文件
bool load_video_file(bool flag, QString file_path);
//保存配置
void SaveConfig();
//读取配置
void ReadConfig();
//播放器初始化
void MediaPlayer_InitConfig(void);
signals:
void s_pause_state(bool pause_state,qint64 current_time);
void s_PlayPosTime(qint64 pos_time);
protected:
bool eventFilter(QObject *obj, QEvent *event);
void dragEnterEvent(QDragEnterEvent *e);
void dropEvent(QDropEvent *e);
private slots:
void slot_stateChanged(QMediaPlayer::State);
void slot_PlayError(QMediaPlayer::Error);
void slot_wheelEvent(bool type);
void seek();
void seek(int);
void onTimeSliderHover(int pos, int value);
void onTimeSliderLeave();
void slot_onMediaSnapshot(QAction *action);
void slot_onConfigurationFunction(QAction *action);
void slot_onMediaRotate(QAction *action);
void slot_onSetTimeSpeed(QAction *action);
void updateSliderPosition(qint64 value);
void media_durationChanged(qint64);
void on_toolButton_load_clicked();
void on_toolButton_init_load_clicked();
void on_MediaPauseBtn_clicked();
void on_MediaResetBtn_clicked();
void on_MediaPrevBtn_clicked();
void on_MediaPlayBtn_clicked();
void on_ReverseFrameBtn_clicked();
void on_ForwardFrameBtn_clicked();
void on_VolumeBtn_clicked();
void on_horizontalSlider_AudioValue_valueChanged(int value);
void on_MediaSpeedBtn_clicked();
void on_MediaRotateBtn_clicked();
void on_MediaSnapshotBtn_clicked();
void on_toolButton_set_clicked();
void on_toolButton_media_info_clicked();
void on_checkBox_gpu_set_clicked(bool checked);
private:
Ui::Widget *ui;
struct MediaStateInfo MediaInfo;
QActionGroup *m_TimeSpeedGrp;
QMenu m_SpeedMenu;
QActionGroup *m_RotateGrp;
QMenu m_RotateMenu;
QActionGroup *m_SnapshotGrp;
QMenu m_SnapshotMenu;
QActionGroup *m_ConfigurationFunctionGrp;
QMenu m_ConfigurationFunctionMenu;
//视频名称
char video_name[1024]; //视频名称
//工作线程
CWorkThread *work_thread;
QMediaPlayer *MeadiaPlayer;
MediaVideoFrame *VideoFrame;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QString filename,QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//设置窗口的标题名称
this->setWindowTitle("视频播放器-K-Lite_Codec_Pack");
//加载样式表
SetStyle(":/resource/ECRS_VideoPlayer.qss");
//读取配置文件
ReadConfig();
//播放器初始化
MediaPlayer_InitConfig();
//UI界面相关初始化
UI_InitConfig();
//如果构造函数传入的视频文件就直接加载
if(!filename.isEmpty())
{
load_video_file(0,filename);
}
}
Widget::~Widget()
{
delete ui;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 加载样式表
*/
void Widget::SetStyle(const QString &qssFile)
{
QFile file(qssFile);
if (file.open(QFile::ReadOnly))
{
QByteArray qss=file.readAll();
qApp->setStyleSheet(qss);
file.close();
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: UI界面初始化
*/
void Widget::UI_InitConfig()
{
//音量滑块范围设置
ui->horizontalSlider_AudioValue->setMaximum(10);
ui->horizontalSlider_AudioValue->setMinimum(0);
//播放速度设置
ui->MediaSpeedBtn->setCheckable(true);
m_TimeSpeedGrp = new QActionGroup(this);
QStringList strSpeedItems;
strSpeedItems << tr("0.03X") << tr("0.06X") << tr("0.1X") << tr("0.15X") << tr("0.2X") << tr("0.3X") << tr("0.4X") << tr("0.5X") << tr("0.6X") << tr("0.8X") << tr("1.0X") << tr("1.25X") << tr("1.5X") << tr("2.0X") << tr("4.0X") << tr("8.0X");
float speeds[] = { 0.03, 0.06, 0.1, 0.15, 0.2, 0.3, 0.4, 0.5,0.6,0.8,1.0,1.25,1.5,2.0,4.0,8.0};
for (int i = 0; i < strSpeedItems.size(); i++)
{
QAction *pSpeedItem = m_SpeedMenu.addAction(strSpeedItems.at(i));
pSpeedItem->setData(QVariant::fromValue(speeds[i]));
pSpeedItem->setCheckable(true);
m_TimeSpeedGrp->addAction(pSpeedItem);
if (i == 10)
{
pSpeedItem->setChecked(true);
}
}
connect(m_TimeSpeedGrp, SIGNAL(triggered(QAction *)), this, SLOT(slot_onSetTimeSpeed(QAction *)));
//图像的旋转方向
m_RotateGrp = new QActionGroup(this);
QStringList strDegrees;
strDegrees << tr("0~") << tr("90~") << tr("180~") << tr("270~");
int Degrees[] = {0, 90, 180, 270 };
for (int i = 0; i < strDegrees.size(); i++)
{
QAction *pItem = m_RotateMenu.addAction(strDegrees.at(i));
pItem->setData(QVariant::fromValue(Degrees[i]));
pItem->setCheckable(true);
m_RotateGrp->addAction(pItem);
}
connect(m_RotateGrp, SIGNAL(triggered(QAction *)), this, SLOT(slot_onMediaRotate(QAction *)));
//功能设置
// ui->toolButton_set->setCheckable(true);
m_ConfigurationFunctionGrp = new QActionGroup(this);
QAction *pToKeyFrame = m_ConfigurationFunctionMenu.addAction(tr("转为关键帧"));
QAction *pAppInfo = m_ConfigurationFunctionMenu.addAction(tr("功能说明"));
pToKeyFrame->setData(MENU_TO_KeyFrame); //保存到剪切板
pAppInfo->setData(MENU_APP_INFO); //保存到文件
m_ConfigurationFunctionGrp->addAction(pToKeyFrame); //添加到分组
m_ConfigurationFunctionGrp->addAction(pAppInfo); //添加到分组
connect(m_ConfigurationFunctionGrp, SIGNAL(triggered(QAction *)), this, SLOT(slot_onConfigurationFunction(QAction *)));
//截图保存
// ui->MediaSnapshotBtn->setCheckable(true);
m_SnapshotGrp = new QActionGroup(this);
QAction *pClipboard = m_SnapshotMenu.addAction(tr("保存到剪切板"));
QAction *pFileDirectory = m_SnapshotMenu.addAction(tr("保存到文件"));
pClipboard->setData(MENU_COPY_CLIPBOARD); //保存到剪切板
pFileDirectory->setData(MENU_SAVE_FILE_SYSTEM); //保存到文件
m_SnapshotGrp->addAction(pClipboard); //添加到分组
m_SnapshotGrp->addAction(pFileDirectory); //添加到分组
connect(m_SnapshotGrp, SIGNAL(triggered(QAction *)), this, SLOT(slot_onMediaSnapshot(QAction *)));
//安装事件监听器 事件筛选器是接收发送到此对象的所有事件的对象
ui->horizontalSlider_PlayPosition->installEventFilter(this);
ui->widget_videoDisplay->installEventFilter(this);
//状态信息初始化
MediaInfo.state=MEDIA_NOLOAD;
//工具提示信息
ui->toolButton_init_load->setToolTip(tr("加载视频,也可以直接将视频文件拖拽到窗口"));
ui->toolButton_load->setToolTip(tr("加载视频,也可以直接将视频文件拖拽到窗口"));
ui->toolButton_media_info->setToolTip(tr("媒体信息"));
ui->MediaPrevBtn->setToolTip(tr("快退"));
ui->MediaPlayBtn->setToolTip(tr("快进"));
ui->MediaPauseBtn->setToolTip(tr("暂停/继续"));
ui->MediaSpeedBtn->setToolTip(tr("倍速选择"));
ui->MediaResetBtn->setToolTip(tr("复位"));
ui->MediaSnapshotBtn->setToolTip(tr("截图"));
ui->MediaRotateBtn->setToolTip(tr("画面旋转"));
ui->ReverseFrameBtn->setToolTip(tr("左一帧"));
ui->ForwardFrameBtn->setToolTip(tr("右一帧"));
ui->VolumeBtn->setToolTip(tr("静音切换"));
ui->checkBox_gpu_set->setToolTip(tr("GPU硬件解码支持(需要重启软件才能生效)"));
//播放进度条滑块初始化
connect(ui->horizontalSlider_PlayPosition, SIGNAL(onLeave()), SLOT(onTimeSliderLeave()));
connect(ui->horizontalSlider_PlayPosition, SIGNAL(onHover(int,int)), SLOT(onTimeSliderHover(int,int)));
connect(ui->horizontalSlider_PlayPosition, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
connect(ui->horizontalSlider_PlayPosition, SIGNAL(sliderPressed()), SLOT(seek()));
this->setMouseTracking(true);
connect(this, SIGNAL(s_pause_state(bool,qint64)),ui->widget_videoDisplay,SLOT(slot_pause_state(bool,qint64)));
connect(this, SIGNAL(s_PlayPosTime(qint64)),ui->widget_videoDisplay,SLOT(slot_PlayPosTime(qint64)));
//工作线程
work_thread =new CWorkThread;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-15
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: MediaPlayer初始化
*/
void Widget::MediaPlayer_InitConfig(void)
{
/*多媒体类*/
MeadiaPlayer = new QMediaPlayer(this);
VideoFrame = new MediaVideoFrame;
/*设置视频解码输出的位置*/
MeadiaPlayer->setVideoOutput(VideoFrame);
//关联播放器的视频帧显示
connect(VideoFrame, SIGNAL(SendOneFrame(QImage)),ui->widget_videoDisplay,SLOT(slotGetOneFrame(QImage)));
//添加播放进度的信号
connect(MeadiaPlayer, SIGNAL(positionChanged(qint64)),this, SLOT(updateSliderPosition(qint64)));
//添加获取播放总时间的信号
connect(MeadiaPlayer, SIGNAL(durationChanged(qint64)),this, SLOT(media_durationChanged(qint64)));
//关联鼠标事件
connect(ui->widget_videoDisplay, SIGNAL(s_wheelEvent(bool)),this,SLOT(slot_wheelEvent(bool)));
//关联错误提示信号
connect(MeadiaPlayer, SIGNAL(error(QMediaPlayer::Error)),this,SLOT(slot_PlayError(QMediaPlayer::Error)));
//状态发生改变
connect(MeadiaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)),this,SLOT(slot_stateChanged(QMediaPlayer::State)));
//设置更新间隔
MeadiaPlayer->setNotifyInterval(100);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-15
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 设置播放器
*/
void Widget::slot_PlayError(QMediaPlayer::Error error)
{
QString text;
if(QMediaPlayer::NoError==error)
{
text="正常解码!";
}
else if(QMediaPlayer::ResourceError==error)
{
text="媒体资源无法解析。";
}
else if(QMediaPlayer::FormatError==error)
{
text="不支持该媒体资源的格式,没有解码器!";
}
else if(QMediaPlayer::NetworkError==error)
{
text="发生网络错误。";
}
else if(QMediaPlayer::AccessDeniedError==error)
{
text="没有适当的权限来播放媒体资源。";
}
else if(QMediaPlayer::ServiceMissingError==error)
{
text="没有找到有效的播放服务,播放无法继续。";
}
QMessageBox::critical(this,"播放器提示",text,QMessageBox::Ok,QMessageBox::Ok);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-15
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 状态发生改变
*/
void Widget::slot_stateChanged(QMediaPlayer::State state)
{
switch (state)
{
case QMediaPlayer::StoppedState:
qDebug()<<"视频停止播放";
break;
case QMediaPlayer::PlayingState:
qDebug()<<"视频继续播放";
ui->MediaPauseBtn->setChecked(false);
break;
case QMediaPlayer::PausedState:
qDebug()<<"视频暂停播放";
ui->MediaPauseBtn->setChecked(true);
break;
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 更新播放进度
*/
void Widget::updateSliderPosition(qint64 value)
{
//设置进度条的时间
ui->horizontalSlider_PlayPosition->setValue(int(value));
//通知播放器显示窗体
emit s_PlayPosTime(value);
//设置右上角的时间
ui->label_current_Time->setText(QTime(0, 0, 0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss")));
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-15
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 媒体总时间
*/
void Widget::media_durationChanged(qint64 value)
{
//最大值最小值
ui->horizontalSlider_PlayPosition->setMinimum(int(0));
ui->horizontalSlider_PlayPosition->setMaximum(int(value));
ui->label_Total_Time->setText(QTime(0, 0, 0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss")));
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 加载视频文件 flag=0 重新加载文件 flag=1 重新开始播放
QString file_path 这参数可以传入文件名称,因为窗口支持拖放文件进来
返回值: true 成功 false 失败
*/
bool Widget::load_video_file(bool flag,QString file_path)
{
if(flag==false)
{
QString filename=file_path;
if(filename.isEmpty())
{
filename=QFileDialog::getOpenFileName(this,"选择播放的视频","D:/",tr("*.mp4 *.wmv *.*"));
}
strncpy(video_name,filename.toUtf8().data(),sizeof(video_name));
ui->toolButton_init_load->setVisible(false);
}
//判断文件是否存在
if(QFileInfo::exists(video_name)==false)
{
return false;
}
else
{
MediaInfo.state=MEDIA_LOAD;
MediaInfo.mediaName=video_name;
}
//每次加载新文件设置播放进度条为0
ui->horizontalSlider_PlayPosition->setValue(0);
qDebug()<<"停止原视频";
//停止播放
MeadiaPlayer->stop();
//播放视频
MeadiaPlayer->setMedia(QUrl::fromLocalFile(video_name));
MeadiaPlayer->pause();
qDebug()<<"当期播放视频:"<<video_name;
//设置当前播放的视频名称
QFileInfo info(video_name);
ui->label_FileName->setText(QString("%1").arg(info.fileName()));
return true;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 加载视频文件
*/
void Widget::on_toolButton_load_clicked()
{
qDebug()<<"加载视频文件状态:"<<load_video_file(0,"");
}
void Widget::on_toolButton_init_load_clicked()
{
load_video_file(0,"");
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 暂停播放
*/
void Widget::on_MediaPauseBtn_clicked()
{
//暂停状态
if (MeadiaPlayer->state()==QMediaPlayer::PausedState)
{
//通知播放器
s_pause_state(true,ui->horizontalSlider_PlayPosition->value());
//继续播放
MeadiaPlayer->play();
return;
}
else
{
//暂停播放
MeadiaPlayer->pause();
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 重新开始播放
*/
void Widget::on_MediaResetBtn_clicked()
{
//加重新开始播放
load_video_file(true,"");
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 快退播放
*/
void Widget::on_MediaPrevBtn_clicked()
{
//得到播放进度的当前位置
int value=ui->horizontalSlider_PlayPosition->value();
int m_unit = MeadiaPlayer->notifyInterval();//获取更新间隔时间
//设置每次点击快退的距离
MeadiaPlayer->setPosition(value-m_unit*10);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 快进播放
*/
void Widget::on_MediaPlayBtn_clicked()
{
//得到播放进度的当前位置
int value=ui->horizontalSlider_PlayPosition->value();
int m_unit = MeadiaPlayer->notifyInterval();//获取更新间隔时间
//设置每次点击快退的距离
MeadiaPlayer->setPosition(value+m_unit*10);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 向左一帧
*/
void Widget::on_ReverseFrameBtn_clicked()
{
//得到播放进度的当前位置
int value=ui->horizontalSlider_PlayPosition->value();
int m_unit = MeadiaPlayer->notifyInterval();//获取更新间隔时间
//设置每次点击快退的距离
MeadiaPlayer->setPosition(value-m_unit);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 向右一帧
*/
void Widget::on_ForwardFrameBtn_clicked()
{
//得到播放进度的当前位置
int value=ui->horizontalSlider_PlayPosition->value();
int m_unit = MeadiaPlayer->notifyInterval();//获取更新间隔时间
//设置每次点击快退的距离
MeadiaPlayer->setPosition(value+m_unit);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 音量设置
*/
void Widget::on_VolumeBtn_clicked()
{
bool checked=ui->VolumeBtn->isChecked();
if(checked)
{
//静音
//音量设置
MeadiaPlayer->setVolume(0);
}
else
{
//设置正常音量
int volume_val=ui->horizontalSlider_AudioValue->value();
//音量设置
MeadiaPlayer->setVolume(int(volume_val));
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 音量设置
*/
void Widget::on_horizontalSlider_AudioValue_valueChanged(int value)
{
//音量设置
MeadiaPlayer->setVolume(value);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 播放速度设置菜单选择
*/
void Widget::slot_onSetTimeSpeed(QAction *action)
{
action->setChecked(true);
ui->MediaSpeedBtn->setToolTip(action->text());
ui->MediaSpeedBtn->setText(action->text());
//设置速度 正常速度是1.0
MeadiaPlayer->setPlaybackRate(qreal(action->data().toFloat()));
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了速度设置按钮
*/
void Widget::on_MediaSpeedBtn_clicked()
{
QPoint ptWgt = ui->MediaSpeedBtn->mapToGlobal(QPoint(0, 0));
ptWgt -= QPoint(10, 180);
QAction *pSelect = m_SpeedMenu.exec(ptWgt);
if (pSelect == nullptr)
return;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了旋转选择菜单
*/
void Widget::slot_onMediaRotate(QAction *action)
{
action->setChecked(true);
ui->MediaRotateBtn->setToolTip(action->text());
ui->widget_videoDisplay->Set_Rotate(action->data().toInt());
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了画面旋转菜单
*/
void Widget::on_MediaRotateBtn_clicked()
{
QPoint ptWgt = ui->MediaRotateBtn->mapToGlobal(QPoint(0, 0));
ptWgt -= QPoint(10, 94);
QAction *pSelect = m_RotateMenu.exec(ptWgt);
if (pSelect == nullptr)
return;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-08
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了功能设置菜单
*/
void Widget::slot_onConfigurationFunction(QAction *action)
{
if (action == nullptr)
return;
//得到按下的序号
MENU_ITEM item = MENU_ITEM(action->data().toInt());
//转视频为关键帧
if (item == MENU_TO_KeyFrame)
{
qDebug()<<"转视频为关键帧";
//创建窗口,显示导出的过程信息,并绑定信号.
Dialog_WaitComplete wait;
connect(work_thread, &CWorkThread::exit_close,&wait, &Dialog_WaitComplete::dialog_close);
QString video_file=QFileDialog::getOpenFileName(this,"选择打开的视频文件","D:/",tr("*.*"));
//filename==选择文件的绝对路径
if(video_file.isEmpty())return;
QFileInfo info(video_file);
QString output_video=info.absolutePath()+info.baseName()+"_KeyFrame"+"."+info.suffix();
qDebug()<<"output_video:"<<output_video;
//拼接命令
QString strCmd=QCoreApplication::applicationDirPath(); //得到程序的执行路径
strCmd += QString("/ffmpeg_422.exe -i %1 -strict -2 -qscale 0 -intra %2")
.arg(video_file) //输入视频文件路径
.arg(output_video); //输出的文件名称
qDebug()<<"strCmd:"<<strCmd;
//判断输出文件是否已经存在
QFileInfo out_file(output_video);
if(out_file.exists()) //文件存在就不需要转换
{
QFile::remove(output_video);
//return;
}
//先退出线程
work_thread->quit();
work_thread->wait();
work_thread->SetRunCmd(strCmd);
work_thread->start(); //执行线程
//设置遮罩窗口
QWidget mask_window(this);
//设置窗体的背景色,这里的百分比就是透明度
mask_window.setStyleSheet(QString("background-color: rgba(0, 0, 0, 50%);"));
mask_window.setGeometry(this->rect()); //设置当前窗口
mask_window.show();
//显示状态窗口
wait.SetText("请等待...");
wait.show();
wait.exec();
//关闭遮罩窗口
mask_window.close();
}
//APP功能介绍
else if(item==MENU_APP_INFO)
{
qDebug()<<"APP功能介绍";
QString text=
"Bug提交: QQ1126626497\n"
"1. 支持命令行传入视频播放\n"
"2. 支持图像旋转播放\n"
"3. 支持查看媒体信息\n"
"4. 支持选择GPU加速解码\n"
"5. 支持快进、快退\n"
"6. 支持预览画面(鼠标放在进度条上查看画面缩略图)\n"
" 7. 支持单帧播放,就是一帧一帧的点击切换画面\n"
" 8. 支持画面拍照、截图\n"
" 9. 支持复位到视频首页\n"
"10. 支持音量调整\n"
"11. 支持拖拽文件到窗口播放\n"
"12. 默认打开视频不会自动播放。自动显示在第一帧,视频放完停留在最后一帧。\n"
"13. 支持视频转关键帧输出.方便鼠标滚轮单帧跳转\n";
QMessageBox::about(this,"播放器功能介绍",text);
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了截图菜单
*/
void Widget::slot_onMediaSnapshot(QAction *action)
{
if (action == nullptr)
return;
//得到按下的序号
MENU_ITEM item = MENU_ITEM(action->data().toInt());
QImage Pic=ui->widget_videoDisplay->GetImage();
if (Pic.isNull() || Pic.height() <= 0)return;
//保存到剪切板
if (item == MENU_COPY_CLIPBOARD)
{
QApplication::clipboard()->setImage(Pic);
}
//保存到文件
else if (item == MENU_SAVE_FILE_SYSTEM)
{
QString strFile = QDateTime::currentDateTime().toString("yyyyMMddHHmmss") + ".png";
QString strFileName = QFileDialog::getSaveFileName(nullptr, "保存图片", strFile, "PNG(*.png);;BMP(*.bmp);;JPEG(*.jpg *.jpeg)");
if (!strFileName.isEmpty())
{
strFileName = QDir::toNativeSeparators(strFileName);
QFileInfo fInfo(strFileName);
Pic.save(strFileName, fInfo.completeSuffix().toStdString().c_str(), 80);
}
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了截图按钮
*/
void Widget::on_MediaSnapshotBtn_clicked()
{
QPoint ptWgt = ui->MediaSnapshotBtn->mapToGlobal(QPoint(0, 0));
ptWgt -= QPoint(10, 48);
QAction *pSelect = m_SnapshotMenu.exec(ptWgt);
if (pSelect == nullptr)
return;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 拦截事件
*/
bool Widget::eventFilter(QObject *obj, QEvent *event)
{
//判断是否在视频窗口范围内按下的鼠标
if(obj==ui->widget_videoDisplay)
{
//视频加载成功才能进行下面的操作
if(MediaInfo.state==MEDIA_LOAD)
{
//判断是否是鼠标事件
if (event->type()==QEvent::MouseButtonPress)
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
//如果按下的是左键
if(mouseEvent->button() == Qt::LeftButton)
{
//如果是暂停状态
if(MeadiaPlayer->state()==QMediaPlayer::PausedState)
{
//继续播放
MeadiaPlayer->play();
//通知播放器
s_pause_state(true,ui->horizontalSlider_PlayPosition->value());
}
else
{
//暂停播放
MeadiaPlayer->pause();
}
}
}
}
}
return QObject::eventFilter(obj,event);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-08
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 点击了设置按钮
*/
void Widget::on_toolButton_set_clicked()
{
QPoint ptWgt = ui->toolButton_set->mapToGlobal(QPoint(0, 0));
ptWgt -= QPoint(10, 48);
QAction *pSelect = m_ConfigurationFunctionMenu.exec(ptWgt);
if (pSelect == nullptr)
return;
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 查看媒体信息
*/
void Widget::on_toolButton_media_info_clicked()
{
}
void Widget::onTimeSliderHover(int pos, int value)
{
}
void Widget::onTimeSliderLeave()
{
}
void Widget::seek(int value)
{
MeadiaPlayer->setPosition(qint64(value));
}
void Widget::seek()
{
seek(ui->horizontalSlider_PlayPosition->value());
}
void Widget::dragEnterEvent(QDragEnterEvent *e)
{
if (e->mimeData()->hasUrls())
{
e->acceptProposedAction();
}
}
void Widget::dropEvent(QDropEvent *e)
{
foreach (const QUrl &url, e->mimeData()->urls())
{
QString fileName = url.toLocalFile();
qDebug() << "拖入的文件名称:" << fileName;
//加载视频文件
load_video_file(false,fileName);
}
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 保存配置
*/
void Widget::SaveConfig()
{
//从UI界面获取用户的个性化配置参数
bool val=ui->checkBox_gpu_set->isChecked();
/*保存数据到文件,方便下次加载*/
QString text;
text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
QFile filesrc(text);
filesrc.open(QIODevice::WriteOnly);
QDataStream out(&filesrc);
out << val; //序列化
filesrc.flush();
filesrc.close();
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 读取配置
*/
void Widget::ReadConfig()
{
// //读取配置文件
// QString text;
// text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
// bool val;
// //判断文件是否存在
// if(QFile::exists(text))
// {
// QFile filenew(text);
// filenew.open(QIODevice::ReadOnly);
// QDataStream in(&filenew); // 从文件读取序列化数据
// in >> val; //提取写入的数据
// filenew.close();
// //设置界面值
// ui->checkBox_gpu_set->setChecked(val);
// }
//因为使用的系统接口,默认就是使用GPU
ui->checkBox_gpu_set->setChecked(true);
}
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: GPU支持选择
*/
void Widget::on_checkBox_gpu_set_clicked(bool checked)
{
SaveConfig();
}
/*
工程: ECRS_VideoPlayer
日期: 2021-03-07
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 鼠标滚轮事件
true 向前
false 向后
*/
void Widget::slot_wheelEvent(bool type)
{
//鼠标滚轮向前
if(type)
{
on_ForwardFrameBtn_clicked();
}
//鼠标滚轮向后
else
{
on_ReverseFrameBtn_clicked();
}
}
注意: 运行的时候,需要把QT安装目录下的“mediaservice” 目录拷贝到程序运行目录下。
比如我的目录路径:C:\Qt\Qt5.12.6\5.12.6\mingw73_32\plugins\mediaservice
解码器安装之后的目录如下:
最后程序打包的时候,可以把解码库一起打包放在程序执行目录下,注册3个ax文件即可。
可以将以下代码写入到xxx.bat文件中,直接运行。 注意: 要用管理员身份运行。
cd /d %~dp0
@regsvr32 LAVAudio.ax
@regsvr32 LAVSplitter.ax
@regsvr32 LAVVideo.ax
@exit