天天看点

QT软件开发:基于libVLC内核设计视频播放器

一、环境介绍

操作系统: win10 64位

QT版本:  QT5.12.6

编译器:  MinGW 32

libvlc版本:  3.0.12

完整工程下载地址(下载即可编译运行): VLC_Core_VideoPlayer.zip-QT文档类资源-CSDN下载

二、播放器运行效果与功能介绍

QT软件开发:基于libVLC内核设计视频播放器
QT软件开发:基于libVLC内核设计视频播放器
QT软件开发:基于libVLC内核设计视频播放器
QT软件开发:基于libVLC内核设计视频播放器
QT软件开发:基于libVLC内核设计视频播放器

播放器的功能介绍:

1. 图像旋转播放(90°、0°、180°、360°)

2. 视频画面截图保存到本地

3. 倍速切换、速度切换不会改变声音音色

4. 音量调整,静音切换

5. 快进、快退支持

6. 点击按钮加载文件、鼠标拖拽文件进行播放

7. 重播按钮支持

8. 暂停与继续切换

9. 单帧切换、前一帧、后一帧

10. 流媒体播放,输入流媒体链接

11. 进度条显示,支持鼠标点击任意跳转到点击位置

12. 播放时间实时更新显示

其他功能可自己增加.....

三、libVLC介绍

3.1 下载VLC的SDK文件

VLC官网地址: VLC: Official site - Free multimedia solutions for all OS! - VideoLAN

所有的VLC版本下载地址:

http://ftp.heanet.ie/pub/videolan/vlc 3.0.12的SDK下载地址: Index of /pub/videolan/vlc/last/win32
QT软件开发:基于libVLC内核设计视频播放器
QT软件开发:基于libVLC内核设计视频播放器
QT软件开发:基于libVLC内核设计视频播放器
 3.2 libvlc介绍
QT软件开发:基于libVLC内核设计视频播放器

来至官网的介绍:

libVLC是核心引擎,也是VLC 媒体播放器所基于的多媒体框架的接口。

libVLC被模块化为数百个插件,可以在运行时加载。这种架构为开发人员(VLC 开发人员和使用该库的开发人员)提供了极大的灵活性。它允许开发人员使用VLC功能创建范围广泛的多媒体应用程序。

播放每种媒体文件格式、每种编解码器和每种流媒体协议。

在各种平台上运行,从桌面(Windows、Linux、Mac)到移动设备(Android、iOS)和电视。

每个平台上的硬件和高效解码,高达 8K。

远程文件系统(SMB、FTP、SFTP、NFS...)和服务器(UPnP、DLNA)的网络浏览。

使用菜单导航播放音频 CD、DVD 和蓝光。

支持 HDR,包括 SDR 流的色调映射。

具有 SPDIF 和 HDMI 的音频直通,包括音频高清编解码器,如 DD+、TrueHD 或 DTS-HD。

支持视频和音频过滤器。

支持 360 度视频和 3D 音频播放,包括 Ambisonics。

能够投射和流式传输到远程渲染器,如 Chromecast 和 UPnP 渲染器。

libVLC是一个 C 库,可以嵌入到您自己的应用程序中。它适用于大多数流行的操作系统平台,包括移动设备和桌面设备。它在LGPL2.1 许可下。

libVLC版本控制本质上与 VLC 应用程序版本控制相关联。libVLC 当前稳定的主要版本是 version 3,预览/开发版本是 version 4。

libVLC 的各种编程语言绑定可用于在您选择的生态系统中无缝使用该库。

VideoLAN 绑定

用于 C++ 的libvlcpp

用于 Apple 平台的VLCKit,使用 Objective-C/Swift。

用于 Android 平台的libvlcjni,使用 Java/Kotlin。

LibVLCSharp适用于大多数操作系统平台,使用 .NET/Mono。

社区绑定

vlcj用于使用 Java 的桌面平台。

python-vlc用于使用 Python 的桌面平台。

vlc-rs使用 Rust 编程语言。

libvlc-go使用 Go 编程语言。

QT软件开发:基于libVLC内核设计视频播放器
四、播放器程序设计

#include "widget.h"
#include "ui_widget.h"
 
Widget* Widget::pThis = nullptr;
 
Widget::Widget(QString filename,QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
 
    pThis=this;
 
    //设置窗口的标题名称
    this->setWindowTitle("VLC内核视频播放器");
 
    //获取VLC版本号
    qDebug()<<"VLC内核版本:"<<libvlc_get_version();
    qDebug()<<"编译LIBVLC的编译器版本:"<<libvlc_get_compiler();
 
    //加载样式表
    SetStyle(":/resource/VideoPlayer.qss");
 
    //读取配置文件
    ReadConfig();
 
    //播放器初始化
    VLC_InitConfig();
 
    //UI界面相关初始化
    UI_InitConfig();
 
    //如果构造函数传入的视频文件就直接加载
    if(!filename.isEmpty())
    {
        load_video_file(0,filename);
    }
 
    //拖放文件需要使用
    setAcceptDrops(true);
}
 
 
Widget::~Widget()
{
    delete ui;
    libvlc_release(vlc_base); //减少libvlc实例的引用计数,并销毁它
}
 
 
/*
工程: 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(100);
    ui->horizontalSlider_AudioValue->setMinimum(0);
    //安装事件过滤器
    ui->horizontalSlider_AudioValue->installEventFilter(this);
 
    //播放速度设置
    ui->MediaSpeedBtn->setCheckable(true);
    m_TimeSpeedGrp = new QActionGroup(this);
 
    QStringList strSpeedItems;
    strSpeedItems << tr("0.5X") << tr("1.0X") << tr("1.5X") << tr("2.0X") << tr("4.0X");
    float speeds[] = {0.5,1.0,1.5,2.0,4.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 == 1)
        {
            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);
        if (i == 0)
        {
            pItem->setChecked(true);
        }
    }
    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("保留1"));
    QAction *pAppInfo = m_ConfigurationFunctionMenu.addAction(tr("保留2"));
    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_load->setToolTip(tr("加载视频,也可以直接将视频文件拖拽到窗口"));
    ui->MediaPrevBtn->setToolTip(tr("快退5秒"));
    ui->MediaPlayBtn->setToolTip(tr("快进5秒"));
    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->toolButton_link->setToolTip(tr("流媒体链接"));
    //播放进度条滑块初始化
    connect(ui->horizontalSlider_PlayPosition, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
    connect(ui->horizontalSlider_PlayPosition, SIGNAL(sliderPressed()), SLOT(seek()));
 
    this->setMouseTracking(true);
}
 
 
/*
工程: ECRS_VideoPlayer
日期: 2021-03-15
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: MediaPlayer初始化
*/
void Widget::VLC_InitConfig(void)
{  
    const char *tempArg = "";
   // tempArg = "--demux=h264";
 
    const char *vlc_args[9] = {"-I", "dummy", "--no-osd", "--no-stats", "--ignore-config", "--no-video-on-top", "--no-video-title-show", "--no-snapshot-preview", tempArg};
 
    //VLC相关的初始化
    //vlc_base=libvlc_new(0, nullptr); //创建并初始化libvlc实例
    vlc_base=libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    if(!vlc_base)
    {
        qDebug()<<"libvlc_new 执行错误.";
    }
 
    //图像缓冲区申请空间
    ctx.pixels = new uchar[MAX_WIDTH * MAX_HEIGHT * 4];
}
 
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 更新播放进度
*/
void Widget::updateSliderPosition(qint64 value)
{
     //设置进度条的时间
     ui->horizontalSlider_PlayPosition->setValue(int(value));
 
     //设置右上角的时间
     ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
}
 
 
/*
工程: 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,0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
}
 
 
/*
工程: QtVLC_Player
日期: 2021-03-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: VLC的事件回调
*/
void Widget::vlcEvents(const libvlc_event_t *ev, void *param)
{
    libvlc_time_t play_ms_pos=0;
    switch (ev->type){
    case libvlc_MediaPlayerTimeChanged:
         //获取当前视频的播放位置
         play_ms_pos=libvlc_media_player_get_time(pThis->vlc_mediaPlayer);
         //设置右上角的时间
         pThis->ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(play_ms_pos)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
         //设置进度条
         pThis->ui->horizontalSlider_PlayPosition->setValue(int(play_ms_pos));
         break;
    case libvlc_MediaPlayerEndReached:
         qDebug() << "VLC播放完毕.";
        break;
    case libvlc_MediaPlayerStopped:
        qDebug() << "VLC停止播放";
        //获取当前视频的播放位置
        play_ms_pos=libvlc_media_player_get_time(pThis->vlc_mediaPlayer);
        //设置右上角的时间
        pThis->ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(play_ms_pos)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
        //设置进度条
        pThis->ui->horizontalSlider_PlayPosition->setValue(int( pThis->ui->horizontalSlider_PlayPosition->maximum()));
        break;
    case libvlc_MediaPlayerPlaying:
        qDebug() << "VLC开始播放";
        break;
    case libvlc_MediaPlayerPaused:
        qDebug() << "VLC暂停播放";
        break;
    case libvlc_MediaParsedChanged:
        qDebug() << "获取到元数据";
        int state=ev->u.media_parsed_changed.new_status;
        if(libvlc_media_parsed_status_done==state)
        {
            qDebug()<<"媒体元数据:"<<libvlc_media_get_meta(pThis->vlc_media,libvlc_meta_Date);
        }
        break;
    }
}
 
void Widget::display(void *opaque, void *picture)
{
    (void)opaque;
}
 
void *Widget::vlc_lock(void *opaque, void **planes)
{
    struct Context *ctx = (struct Context *)opaque;
    ctx->mutex.lock();
    // 告诉 VLC 将解码的数据放到缓冲区中
    *planes = ctx->pixels;
    return nullptr;
}
 
 
//获取 argb 帧
void Widget::vlc_unlock(void *opaque, void *picture, void *const *planes)
{
    struct Context *ctx = (struct Context *)opaque;
    unsigned char *data = static_cast<unsigned char *>(*planes);
 
    quint32 w=pThis->video_width;
    quint32 h=pThis->video_height;
 
    if(w>0 && h>0 && data!=nullptr)
    {
        QImage image(data, int(w),int(h), QImage::Format_RGB32);
 
        if(!image.isNull())
        {
            pThis->ui->widget_videoDisplay->slotGetOneFrame(image.copy());
        }
    }
    ctx->mutex.unlock();
}
 
 
/*
工程: 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())
        {
            //获取路径,如果没有默认路径,就使用当前C盘根目录
            if(open_dir_path.isEmpty())
            {
                open_dir_path="C:/";
            }
 
            filename=QFileDialog::getOpenFileName(this,"选择播放的视频",open_dir_path,tr("*.mkv *.flv *.mp4 *.wmv *.*"));
            if(filename.isEmpty())return false;
 
            open_dir_path=QFileInfo(filename).path();
            qDebug()<<"路径:"<<open_dir_path;
        }
        media_filename=filename;
    }
 
    //判断文件是否存在
    if(QFileInfo::exists(media_filename)==false)
    {
        if(media_filename.contains("rtsp:", Qt::CaseInsensitive)==false&&
           media_filename.contains("rtmp:", Qt::CaseInsensitive)==false)
        {
            return false;
        }
        else
        {
 
             qDebug()<<"播放的流媒体:"<<media_filename;
        }
    }
    else
    {
        /* 将 / 转为windows下的右斜杆 */
        std::replace(media_filename.begin(), media_filename.end(), QChar('/'), QChar('\\'));
        qDebug()<<"播放的本地媒体:"<<media_filename;
    }
 
    MediaInfo.state=MEDIA_LOAD;
    MediaInfo.mediaName=media_filename;
 
 
    //设置当前播放的视频名称
    QFileInfo info(media_filename);
    ui->label_FileName->setText(QString("%1").arg(info.fileName()));
 
 
    //宽高清0
    video_width=0;
    video_height=0;
 
    /*为特定文件路径创建媒体*/
    if(vlc_media)libvlc_media_release(vlc_media);
    vlc_media=libvlc_media_new_path(vlc_base,media_filename.toUtf8());
    if(vlc_media==nullptr)
    {
        qDebug()<<"libvlc_media_new_path 执行错误.";
        return false;
    }
 
    /*根据给定的媒体对象创建一个播放器对象*/
    if(vlc_mediaPlayer)libvlc_media_player_release(vlc_mediaPlayer);
    vlc_mediaPlayer=libvlc_media_player_new_from_media(vlc_media);
 
     //设置回调,用于提取帧或者在界面上显示。
     libvlc_video_set_callbacks(vlc_mediaPlayer, vlc_lock, vlc_unlock, display, &ctx);
 
    /*获取媒体播放器事件管理器*/
    libvlc_event_manager_t *em = libvlc_media_player_event_manager(vlc_mediaPlayer);
    libvlc_event_attach(em, libvlc_MediaPlayerTimeChanged, vlcEvents, this); //进度改变
    libvlc_event_attach(em, libvlc_MediaPlayerEndReached, vlcEvents, this); //播放完毕
    libvlc_event_attach(em, libvlc_MediaPlayerStopped, vlcEvents, this); //停止
    libvlc_event_attach(em, libvlc_MediaPlayerPlaying, vlcEvents, this); //开始播放
    libvlc_event_attach(em, libvlc_MediaPlayerPaused, vlcEvents, this); //暂停
    libvlc_event_attach(em, libvlc_MediaParsedChanged, vlcEvents, this); //获取到元数据
 
    /*播放媒体文件*/
    if(vlc_mediaPlayer)libvlc_media_player_play(vlc_mediaPlayer);
 
    //等待VLC解析文件.否则下面的时间获取不成功
    QThread::msleep(100);
 
    //获取媒体文件总长度 ms
    libvlc_time_t length = libvlc_media_player_get_length(vlc_mediaPlayer);
    qDebug()<<"媒体文件总长度:"<<length;
    ui->label_Total_Time->setText(QTime(0, 0, 0,0).addMSecs(int(length)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
 
    //每次加载新文件设置播放进度条为0
    ui->horizontalSlider_PlayPosition->setValue(0);
    //设置进度条的范围
    ui->horizontalSlider_PlayPosition->setMaximum(int(length));
    ui->horizontalSlider_PlayPosition->setMinimum(0);
 
    //获取当前媒体播放的位置
    libvlc_time_t current_movie_time=libvlc_media_player_get_time(vlc_mediaPlayer);
    qDebug()<<"获取当前媒体播放的位置:"<<current_movie_time;
 
    libvlc_video_get_size(vlc_mediaPlayer,0,&video_width,&video_height);
 
    qDebug()<<"视频尺寸:"<<"width:"<<video_width<<"height:"<<video_height;
 
    memset(ctx.pixels, 0, MAX_WIDTH * MAX_HEIGHT * 4);
 
    //设置图像颜色格式
    libvlc_video_set_format(vlc_mediaPlayer, "RV32", video_width, video_height, video_width * 4);
 
    libvlc_media_add_option(vlc_media, ":rtsp=tcp"); //连接方式
    libvlc_media_add_option(vlc_media, ":network-caching=200"); //缓存
 
    //设置按钮状态为播放状态
    ui->MediaPauseBtn->setChecked(false);
 
    //隐藏标签控件
    ui->label->setVisible(false);
 
    //获取媒体的元数据信息--异步方式
    libvlc_media_parse_with_options(vlc_media,libvlc_media_parse_local,1000);
 
    qDebug()<<"媒体元数据获取---媒体标题:"<<libvlc_media_get_meta(vlc_media,libvlc_meta_Title);
 
    //获取媒体描述符的基本流描述
    libvlc_media_track_t **tracks;
 
    //正常的视频获取的流的数量是2. 一个视频流  一个音频流
    if(libvlc_media_tracks_get(vlc_media,&tracks))
    {
        qDebug()<<"视频宽:"<<tracks[0]->video->i_width;
        qDebug()<<"视频高:"<<tracks[0]->video->i_height;
 
        //手机拍的视频是翻转(宽高反过来的),电脑上播放需要翻转回去
        qDebug()<<"旋转度数:"<<tracks[0]->video->i_orientation;
 
        //逆时针旋转90°
        if(tracks[0]->video->i_orientation==libvlc_video_orient_right_top)
        {
           // video_width=tracks[0]->video->i_height;
           // video_height=tracks[0]->video->i_width;
            //ui->widget_videoDisplay->Set_Rotate(90);
 
            //libvlc_media_add_option(vlc_media, ":transform-type=90");
        }
 
       // libvlc_video_set_scale(vlc_mediaPlayer,1);
    }
 
 
/*
libvlc_media_add_option(vlc_media, ":rtsp=tcp"); //连接方式
libvlc_media_add_option(vlc_media, ":codec=ffmpeg");
libvlc_media_add_option(vlc_media, ":avcodec-threads=1");
libvlc_media_add_option(vlc_media, ":avcodec-hw=any"); //硬件解码
libvlc_media_add_option(vlc_media, ":network-caching=200"); //缓存
libvlc_media_add_option(vlc_media, ":prefetch-buffer-size=1024");  //预装取缓冲大小512K
libvlc_media_add_option(vlc_media, ":prefetch-read-size=65535");   //预装取读取大小64K
*/
    return true;
}
 
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 加载视频文件
*/
void Widget::on_toolButton_load_clicked()
{
    qDebug()<<"加载视频文件状态:"<<load_video_file(0,"");
}
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 暂停播放
*/
void Widget::on_MediaPauseBtn_clicked()
{
    //暂停与继续
     if(vlc_mediaPlayer)libvlc_media_player_pause(vlc_mediaPlayer);
}
 
/*
工程: 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();
    seek(value-1000*5);
}
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 快进播放
*/
void Widget::on_MediaPlayBtn_clicked()
{
    //得到播放进度的当前位置
    int value=ui->horizontalSlider_PlayPosition->value();
    seek(value+1000*5);
}
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 向左一帧
*/
void Widget::on_ReverseFrameBtn_clicked()
{
    //得到播放进度的当前位置
    int value=ui->horizontalSlider_PlayPosition->value();
    value+=100;
    if(value<=ui->horizontalSlider_PlayPosition->maximum())
    {
        ui->horizontalSlider_PlayPosition->setValue(value);
        ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
        //跳转播放器
        float f_value=(float)value/(float)ui->horizontalSlider_PlayPosition->maximum();
         qDebug()<<"f_value:"<<f_value;
        libvlc_media_player_set_position(vlc_mediaPlayer,f_value);
    }
}
 
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 向右一帧
*/
void Widget::on_ForwardFrameBtn_clicked()
{
    //得到播放进度的当前位置
    int value=ui->horizontalSlider_PlayPosition->value();
    value-=100;
    if(value>=0)
    {
        ui->horizontalSlider_PlayPosition->setValue(value);
        ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
        //跳转播放器
        float f_value=(float)value/(float)ui->horizontalSlider_PlayPosition->maximum();
         qDebug()<<"f_value:"<<f_value;
        libvlc_media_player_set_position(vlc_mediaPlayer,f_value);
    }
}
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 音量设置
*/
void Widget::on_VolumeBtn_clicked()
{
    bool checked=ui->VolumeBtn->isChecked();
    if(checked)
    {
        //静音
        if(vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer,0);
    }
    else
    {
       //设置正常音量
        int volume_val=ui->horizontalSlider_AudioValue->value();
        //音量设置
        if(vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer,volume_val);
    }
}
 
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-23
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 音量设置
*/
void Widget::on_horizontalSlider_AudioValue_valueChanged(int value)
{
    //音量设置
    if(vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer,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());
 
    /*设置播放速率*/
    if(vlc_mediaPlayer)libvlc_media_player_set_rate(vlc_mediaPlayer,float(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, 94);
    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)
    {
 
    }
    //APP功能介绍
    else if(item==MENU_APP_INFO)
    {
 
    }
}
 
/*
工程: 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)
    {
 
    }
 
    //点击的是音量滑块
    else if(obj==ui->horizontalSlider_AudioValue)
    {
        if (event->type()==QEvent::MouseButtonPress)           //判断类型
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            if (mouseEvent->button() == Qt::LeftButton) //判断左键
            {
                int value = QStyle::sliderValueFromPosition(ui->horizontalSlider_AudioValue->minimum(), ui->horizontalSlider_AudioValue->maximum(), mouseEvent->pos().x(), ui->horizontalSlider_AudioValue->width());
                ui->horizontalSlider_AudioValue->setValue(value);
            }
        }
    }
    return QObject::eventFilter(obj,event);
}
 
 
/*
工程: ASS_SubtitleVideoPlayer
日期: 2021-06-15
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 按键事件
*/
void Widget::keyPressEvent(QKeyEvent *event)
{
    switch(event->key())
    {
    case Qt::Key_Space:
        //视频加载成功才能进行下面的操作
        if(MediaInfo.state==MEDIA_LOAD)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Space)
            {
 
                if(vlc_mediaPlayer)
                {
                    //暂停与继续切换
                    libvlc_media_player_pause(vlc_mediaPlayer);
 
                    //4表示暂停  3表示继续   5表示停止
                    int state=libvlc_media_player_get_state(vlc_mediaPlayer);
                    if(state==4 || state==5)
                    {
                        //设置按钮状态
                        ui->MediaPauseBtn->setChecked(false);
                    }
                    else if(state==3)
                    {
                        //设置按钮状态
                        ui->MediaPauseBtn->setChecked(true);
                    }
                }
            }
        }
        break;
    }
}
 
 
/*
工程: 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;
}
 
void Widget::seek(int value)
{
    if(vlc_mediaPlayer)
    {
        float pos=value*1.0/ui->horizontalSlider_PlayPosition->maximum();
        libvlc_media_player_set_position(vlc_mediaPlayer,pos);
        qDebug()<<"跳转的位置:"<<pos;
 
 
        ui->horizontalSlider_PlayPosition->setValue(value);
        ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
        //跳转播放器
        float f_value=(float)value/(float)ui->horizontalSlider_PlayPosition->maximum();
         qDebug()<<"f_value:"<<f_value;
        libvlc_media_player_set_position(vlc_mediaPlayer,f_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界面获取用户的个性化配置参数
    /*保存数据到文件,方便下次加载*/
    QString text;
    text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
    QFile filesrc(text);
    filesrc.open(QIODevice::WriteOnly);
    QDataStream out(&filesrc);
    out << open_dir_path;  //序列化
    filesrc.flush();
    filesrc.close();
}
 
/*
工程: ECRS_VideoPlayer
日期: 2021-02-24
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 读取配置
*/
void Widget::ReadConfig()
{
    //读取配置文件
    QString text;
    text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
 
    //判断文件是否存在
    if(QFile::exists(text))
    {
        QFile filenew(text);
        filenew.open(QIODevice::ReadOnly);
        QDataStream in(&filenew); // 从文件读取序列化数据
        in >>open_dir_path; //提取写入的数据
        filenew.close();
    }
}
 
 
/*
工程: ASS_SubtitleVideoPlayer
日期: 2021-06-16
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 窗口关闭事件
*/
void Widget::closeEvent(QCloseEvent *event) //窗口关闭事件
{
    int ret = QMessageBox::question(this, tr("重要提示"),
    tr("是否退出播放器?"),
    QMessageBox::Yes | QMessageBox::No);
 
    if(ret==QMessageBox::Yes)
    {
        SaveConfig();
        event->accept(); //接受事件
    }
    else
    {
        event->ignore(); //清除事件
    }
}
 
 
/*
工程: VLC_Core_VideoPlayer
日期: 2021-07-26
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 打开链接
*/
void Widget::on_toolButton_link_clicked()
{
    bool ok;
    QString text = QInputDialog::getText(this, tr("流媒体播放"),tr("请输入流媒体地址:"), QLineEdit::Normal,
    tr("rtmp://58.200.131.2:1935/livetv/cctv14"),&ok);
    if (ok && !text.isEmpty())
    {
        //load_video_file(0,text);
//打开RTMP流媒体:libvlc_media_new_location (inst, "rtmp://10.0.0.4:554/cam");
 
    }
}