QT应用编程:基于QMediaPlayer开发音视频播放器

简介: QT应用编程:基于QMediaPlayer开发音视频播放器

一、环境介绍

操作系统: 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




三、播放器效果

image.png

image.png

四、功能介绍

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;
}

六、源码代码

6.1 widget.h

#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

image.png

解码器安装之后的目录如下:

image.png

最后程序打包的时候,可以把解码库一起打包放在程序执行目录下,注册3个ax文件即可。

image.png

可以将以下代码写入到xxx.bat文件中,直接运行。 注意:  要用管理员身份运行。

cd /d %~dp0
@regsvr32 LAVAudio.ax
@regsvr32 LAVSplitter.ax
@regsvr32 LAVVideo.ax
@exit


相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
3月前
|
开发工具 C++
qt开发技巧与三个问题点
本文介绍了三个Qt开发中的常见问题及其解决方法,并提供了一些实用的开发技巧。
|
3月前
|
4月前
|
C++
C++ Qt开发:QUdpSocket网络通信组件
QUdpSocket是Qt网络编程中一个非常有用的组件,它提供了在UDP协议下进行数据发送和接收的能力。通过简单的方法和信号,可以轻松实现基于UDP的网络通信。不过,需要注意的是,UDP协议本身不保证数据的可靠传输,因此在使用QUdpSocket时,可能需要在应用层实现一些机制来保证数据的完整性和顺序,或者选择在适用的场景下使用UDP协议。
196 2
Qt开发网络嗅探器02
Qt开发网络嗅探器02
|
4月前
|
网络协议 容器
Qt开发网络嗅探器03
Qt开发网络嗅探器03
|
4月前
|
API 开发工具 C语言
C语言与图形界面:利用GTK+、Qt等库创建GUI应用。
C语言与图形界面:利用GTK+、Qt等库创建GUI应用。
228 0
|
5月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
194 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
4月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
106 0
|
4月前
|
存储 运维 监控
Qt开发网络嗅探器01
Qt开发网络嗅探器01
|
4月前
【qt】多窗口开发
【qt】多窗口开发
68 0