一、环境介绍
操作系统: 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
为了更加方便处理视频帧,代码里重写了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
6.2 widget.cpp
#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