QT software development: Design of video player based on libVLC kernel

1, Environment introduction

Operating system: win10 64 bit

QT version: Qt5 twelve point six

Compiler: MinGW 32

libvlc version: 3.0.12

Complete project download address (compile and run after downloading): VLC_ Core_ VideoPlayer. Zip QT document class resources - CSDN Download

2, Player operation effect and function introduction

Function introduction of the player:

1. Image rotation playback (90 °, 0 °, 180 °, 360 °)

2. Save video screenshots to local

3. Double speed switching and speed switching will not change the sound color

4. Volume adjustment and mute switching

5. Fast forward and fast backward support

6. Click the button to load the file and drag the file with the mouse to play

7. Replay button support

8. Pause and continue switching

9. Single frame switching, previous frame and next frame

10. Play streaming media and enter the streaming media link

11. The progress bar is displayed, and it supports any mouse click to jump to the click position

12. The playback time is updated and displayed in real time

Other functions can be added by yourself

3, Introduction to libVLC

3.1 download SDK files of VLC

VLC official website address: VLC: Official site - Free multimedia solutions for all OS! - VideoLAN

Download address of all VLC versions: http://ftp.heanet.ie/pub/videolan/vlc

SDK download address of 3.0.12: Index of /pub/videolan/vlc/last/win32

3.2 introduction to libvlc

Introduction to the official website:

libVLC is the core engine and VLC media player The interface of the multimedia framework based on.

libVLC is modularized into hundreds of plug-ins that can be loaded at run time. This architecture provides great flexibility for developers (VLC developers and developers using the Library). It allows developers to use VLCfunction Create a wide range of multimedia applications.

  • Play each media file format, each codec and each streaming media protocol.
  • Runs on a variety of platforms, from desktops (Windows, Linux, Mac) to mobile devices (Android, iOS) and TVs.
  • Hardware and efficient decoding on each platform, up to 8K.
  • Remote file systems (SMB, FTP, SFTP, NFS...) And servers (UPnP, DLNA).
  • Use menu navigation to play audio CD s, DVD s, and Blu ray.
  • Support HDR, including tone mapping of SDR stream.
  • Audio pass through with SPDIF and HDMI, including audio HD codec, such as DD +, TrueHD or DTS-HD.
  • Support video and audio filters.
  • Support 360 degree video and 3D audio playback, including Ambisonics.
  • It can project and stream to remote renderers, such as Chromecast and UPnP renderers.

libVLC is a C library that can be embedded into your own applications. It is suitable for most popular operating system platforms, including mobile devices and desktop devices. It's in lgpl2 1 with permission.

libVLC versioning is essentially associated with VLC application versioning. The main stable version of libVLC is version 3, and the preview / development version is version 4.

libVLC's various programming language bindings can be used to seamlessly use the library in the ecosystem of your choice.

VideoLAN binding

  • For C + + libvlcpp
  • For Apple platform VLCKit , use Objective-C/Swift.
  • For Android platform libvlcjni , using Java/Kotlin.
  • LibVLCSharp Suitable for most operating system platforms NET/Mono.

Community binding

  • vlcj Desktop platform for using Java.
  • python-vlc Desktop platform for using Python.
  • vlc-rs Use the Rust programming language.
  • libvlc-go Use Go programming language.

4, Player programming

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

    //Sets the title name of the window
    this->setWindowTitle("VLC Kernel video player");

    //Get VLC version number
    qDebug()<<"VLC Kernel version:"<<libvlc_get_version();
    qDebug()<<"compile LIBVLC Compiler version of:"<<libvlc_get_compiler();

    //Load style sheet
    SetStyle(":/resource/VideoPlayer.qss");

    //Read configuration file
    ReadConfig();

    //Player initialization
    VLC_InitConfig();

    //UI interface related initialization
    UI_InitConfig();

    //If the constructor passes in the video file, it will be loaded directly
    if(!filename.isEmpty())
    {
        load_video_file(0,filename);
    }

    //Drag and drop files require
    setAcceptDrops(true);
}


Widget::~Widget()
{
    delete ui;
    libvlc_release(vlc_base); //Reduce the reference count of the libvlc instance and destroy it
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Functions: loading style sheets
*/
void Widget::SetStyle(const QString &qssFile)
{
    QFile file(qssFile);
    if (file.open(QFile::ReadOnly))
    {
        QByteArray qss=file.readAll();
        qApp->setStyleSheet(qss);
        file.close();
    }
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: UI interface initialization
*/
void Widget::UI_InitConfig()
{
    //Volume slider range setting
    ui->horizontalSlider_AudioValue->setMaximum(100);
    ui->horizontalSlider_AudioValue->setMinimum(0);
    //Install event filter
    ui->horizontalSlider_AudioValue->installEventFilter(this);

    //Playback speed setting
    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 *)));


    //Rotation direction of image
    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 *)));

    //Function settings
   // ui->toolButton_set->setCheckable(true);
    m_ConfigurationFunctionGrp = new QActionGroup(this);
    QAction *pToKeyFrame = m_ConfigurationFunctionMenu.addAction(tr("Reserved 1"));
    QAction *pAppInfo = m_ConfigurationFunctionMenu.addAction(tr("Reserved 2"));
    pToKeyFrame->setData(MENU_TO_KeyFrame); //Save to clipboard
    pAppInfo->setData(MENU_APP_INFO); //Save to file
    m_ConfigurationFunctionGrp->addAction(pToKeyFrame); //Add to group
    m_ConfigurationFunctionGrp->addAction(pAppInfo); //Add to group
    connect(m_ConfigurationFunctionGrp, SIGNAL(triggered(QAction *)), this, SLOT(slot_onConfigurationFunction(QAction *)));

    //Screenshot saving
    // ui->MediaSnapshotBtn->setCheckable(true);
    m_SnapshotGrp = new QActionGroup(this);
    QAction *pClipboard = m_SnapshotMenu.addAction(tr("Save screenshot to clipboard"));
    QAction *pFileDirectory = m_SnapshotMenu.addAction(tr("Save screenshot to file"));
    pClipboard->setData(MENU_COPY_CLIPBOARD); //Save to clipboard
    pFileDirectory->setData(MENU_SAVE_FILE_SYSTEM); //Save to file
    m_SnapshotGrp->addAction(pClipboard); //Add to group
    m_SnapshotGrp->addAction(pFileDirectory); //Add to group
    connect(m_SnapshotGrp, SIGNAL(triggered(QAction *)), this, SLOT(slot_onMediaSnapshot(QAction *)));

    //Install event listeners event filters are objects that receive all events sent to this object
    ui->horizontalSlider_PlayPosition->installEventFilter(this);
    ui->widget_videoDisplay->installEventFilter(this);

    //Status information initialization
    MediaInfo.state=MEDIA_NOLOAD;

    //Tooltip information
    ui->toolButton_load->setToolTip(tr("Load video,You can also drag the video file directly to the window"));
    ui->MediaPrevBtn->setToolTip(tr("Rewind for 5 seconds"));
    ui->MediaPlayBtn->setToolTip(tr("Fast forward 5 seconds"));
    ui->MediaPauseBtn->setToolTip(tr("suspend/continue"));
    ui->MediaSpeedBtn->setToolTip(tr("Multiple speed selection"));
    ui->MediaResetBtn->setToolTip(tr("Play from scratch"));
    ui->MediaSnapshotBtn->setToolTip(tr("screenshot"));
    ui->MediaRotateBtn->setToolTip(tr("Picture rotation"));
    ui->ReverseFrameBtn->setToolTip(tr("Previous frame"));
    ui->ForwardFrameBtn->setToolTip(tr("Next frame"));
    ui->VolumeBtn->setToolTip(tr("Mute switching"));
    ui->toolButton_link->setToolTip(tr("Streaming media link"));
    //Playback progress bar slider initialization
    connect(ui->horizontalSlider_PlayPosition, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
    connect(ui->horizontalSlider_PlayPosition, SIGNAL(sliderPressed()), SLOT(seek()));

    this->setMouseTracking(true);
}


/*
Project: ECRS_VideoPlayer
 Date: March 15, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: MediaPlayer initialization
*/
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 related initialization
    //vlc_base=libvlc_new(0, nullptr); // Create and initialize the libvlc instance
    vlc_base=libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    if(!vlc_base)
    {
        qDebug()<<"libvlc_new Execution error.";
    }

    //Image buffer request space
    ctx.pixels = new uchar[MAX_WIDTH * MAX_HEIGHT * 4];
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: update playback progress
*/
void Widget::updateSliderPosition(qint64 value)
{
     //Set the time for the progress bar
     ui->horizontalSlider_PlayPosition->setValue(int(value));

     //Set the time in the upper right corner
     ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(value)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
}


/*
Project: ECRS_VideoPlayer
 Date: March 15, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: total media time
*/
void Widget::media_durationChanged(qint64 value)
{
    //Max min
    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")));
}


/*
Project: QtVLC_Player
 Date: March 24, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: VLC event callback
*/
void Widget::vlcEvents(const libvlc_event_t *ev, void *param)
{
    libvlc_time_t play_ms_pos=0;
    switch (ev->type){
    case libvlc_MediaPlayerTimeChanged:
         //Get the playback position of the current video
         play_ms_pos=libvlc_media_player_get_time(pThis->vlc_mediaPlayer);
         //Set the time in the upper right corner
         pThis->ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(play_ms_pos)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
         //Set progress bar
         pThis->ui->horizontalSlider_PlayPosition->setValue(int(play_ms_pos));
         break;
    case libvlc_MediaPlayerEndReached:
         qDebug() << "VLC Play complete.";
        break;
    case libvlc_MediaPlayerStopped:
        qDebug() << "VLC stop playing";
        //Get the playback position of the current video
        play_ms_pos=libvlc_media_player_get_time(pThis->vlc_mediaPlayer);
        //Set the time in the upper right corner
        pThis->ui->label_current_Time->setText(QTime(0, 0, 0,0).addMSecs(int(play_ms_pos)).toString(QString::fromLatin1("HH:mm:ss:zzz")));
        //Set progress bar
        pThis->ui->horizontalSlider_PlayPosition->setValue(int( pThis->ui->horizontalSlider_PlayPosition->maximum()));
        break;
    case libvlc_MediaPlayerPlaying:
        qDebug() << "VLC Start playing";
        break;
    case libvlc_MediaPlayerPaused:
        qDebug() << "VLC Pause playback";
        break;
    case libvlc_MediaParsedChanged:
        qDebug() << "Get metadata";
        int state=ev->u.media_parsed_changed.new_status;
        if(libvlc_media_parsed_status_done==state)
        {
            qDebug()<<"Media metadata:"<<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();
    // Tell VLC to put the decoded data into the buffer
    *planes = ctx->pixels;
    return nullptr;
}


//Get argb frame
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();
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: load video file flag=0 reload file flag=1 restart playback
QString file_path This parameter can pass in the file name because the window supports dragging and dropping files in
 Return value: true success false failure
*/
bool Widget::load_video_file(bool flag,QString file_path)
{
    if(flag==false)
    {
        QString filename=file_path;
        if(filename.isEmpty())
        {
            //Get the path. If there is no default path, use the current C disk root directory
            if(open_dir_path.isEmpty())
            {
                open_dir_path="C:/";
            }

            filename=QFileDialog::getOpenFileName(this,"Select a video to play",open_dir_path,tr("*.mkv *.flv *.mp4 *.wmv *.*"));
            if(filename.isEmpty())return false;

            open_dir_path=QFileInfo(filename).path();
            qDebug()<<"route:"<<open_dir_path;
        }
        media_filename=filename;
    }

    //Determine whether the file exists
    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()<<"Streaming media played:"<<media_filename;
        }
    }
    else
    {
        /* Turn / to the right diagonal bar under windows */
        std::replace(media_filename.begin(), media_filename.end(), QChar('/'), QChar('\\'));
        qDebug()<<"Play local media:"<<media_filename;
    }

    MediaInfo.state=MEDIA_LOAD;
    MediaInfo.mediaName=media_filename;


    //Set the name of the currently playing video
    QFileInfo info(media_filename);
    ui->label_FileName->setText(QString("%1").arg(info.fileName()));


    //Wide HD 0
    video_width=0;
    video_height=0;

    /*Create media for a specific file path*/
    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 Execution error.";
        return false;
    }

    /*Creates a player object based on a given media object*/
    if(vlc_mediaPlayer)libvlc_media_player_release(vlc_mediaPlayer);
    vlc_mediaPlayer=libvlc_media_player_new_from_media(vlc_media);

     //Set callback to extract frames or display them on the interface.
     libvlc_video_set_callbacks(vlc_mediaPlayer, vlc_lock, vlc_unlock, display, &ctx);

    /*Get media player event manager*/
    libvlc_event_manager_t *em = libvlc_media_player_event_manager(vlc_mediaPlayer);
    libvlc_event_attach(em, libvlc_MediaPlayerTimeChanged, vlcEvents, this); //Schedule change
    libvlc_event_attach(em, libvlc_MediaPlayerEndReached, vlcEvents, this); //Play complete
    libvlc_event_attach(em, libvlc_MediaPlayerStopped, vlcEvents, this); //stop it
    libvlc_event_attach(em, libvlc_MediaPlayerPlaying, vlcEvents, this); //Start playing
    libvlc_event_attach(em, libvlc_MediaPlayerPaused, vlcEvents, this); //suspend
    libvlc_event_attach(em, libvlc_MediaParsedChanged, vlcEvents, this); //Get metadata

    /*Play media files*/
    if(vlc_mediaPlayer)libvlc_media_player_play(vlc_mediaPlayer);

    //Wait for VLC to parse the file Otherwise, the following time acquisition is unsuccessful
    QThread::msleep(100);

    //Get the total length of media files, ms
    libvlc_time_t length = libvlc_media_player_get_length(vlc_mediaPlayer);
    qDebug()<<"Total length of media files:"<<length;
    ui->label_Total_Time->setText(QTime(0, 0, 0,0).addMSecs(int(length)).toString(QString::fromLatin1("HH:mm:ss:zzz")));

    //Each time a new file is loaded, set the playback progress bar to 0
    ui->horizontalSlider_PlayPosition->setValue(0);
    //Set the scope of the progress bar
    ui->horizontalSlider_PlayPosition->setMaximum(int(length));
    ui->horizontalSlider_PlayPosition->setMinimum(0);

    //Gets the location of the current media playback
    libvlc_time_t current_movie_time=libvlc_media_player_get_time(vlc_mediaPlayer);
    qDebug()<<"Gets the location of the current media playback:"<<current_movie_time;

    libvlc_video_get_size(vlc_mediaPlayer,0,&video_width,&video_height);

    qDebug()<<"Video size:"<<"width:"<<video_width<<"height:"<<video_height;

    memset(ctx.pixels, 0, MAX_WIDTH * MAX_HEIGHT * 4);

    //Format image color
    libvlc_video_set_format(vlc_mediaPlayer, "RV32", video_width, video_height, video_width * 4);

    libvlc_media_add_option(vlc_media, ":rtsp=tcp"); //Connection mode
    libvlc_media_add_option(vlc_media, ":network-caching=200"); //cache

    //Set the button status to play status
    ui->MediaPauseBtn->setChecked(false);

    //Hide label controls
    ui->label->setVisible(false);

    //Get metadata information of media -- asynchronous mode
    libvlc_media_parse_with_options(vlc_media,libvlc_media_parse_local,1000);

    qDebug()<<"Media metadata acquisition---Media title:"<<libvlc_media_get_meta(vlc_media,libvlc_meta_Title);

    //Gets the basic stream description of the media descriptor
    libvlc_media_track_t **tracks;

    //The number of streams obtained by normal video is 2 One video stream one audio stream
    if(libvlc_media_tracks_get(vlc_media,&tracks))
    {
        qDebug()<<"Video width:"<<tracks[0]->video->i_width;
        qDebug()<<"Video high:"<<tracks[0]->video->i_height;

        //The video taken by the mobile phone is flipped (the width and height are reversed), and it needs to be flipped back when playing on the computer
        qDebug()<<"Degree of rotation:"<<tracks[0]->video->i_orientation;

        //Rotate 90 ° counterclockwise
        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"); //Connection mode
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"); //Hardware decoding
libvlc_media_add_option(vlc_media, ":network-caching=200"); //cache
libvlc_media_add_option(vlc_media, ":prefetch-buffer-size=1024");  //Prefetch buffer size 512K
libvlc_media_add_option(vlc_media, ":prefetch-read-size=65535");   //Preload read size 64K
*/
    return true;
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: load video file
*/
void Widget::on_toolButton_load_clicked()
{
    qDebug()<<"Load video file status:"<<load_video_file(0,"");
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: pause playback
*/
void Widget::on_MediaPauseBtn_clicked()
{
    //Pause and resume
     if(vlc_mediaPlayer)libvlc_media_player_pause(vlc_mediaPlayer);
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: restart playback
*/
void Widget::on_MediaResetBtn_clicked()
{
    //Add and restart playback
    load_video_file(true,"");
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: rewind playback
*/
void Widget::on_MediaPrevBtn_clicked()
{
    //Get the current position of the playback progress
    int value=ui->horizontalSlider_PlayPosition->value();
    seek(value-1000*5);
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: fast forward playback
*/
void Widget::on_MediaPlayBtn_clicked()
{
    //Get the current position of the playback progress
    int value=ui->horizontalSlider_PlayPosition->value();
    seek(value+1000*5);
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: one frame to the left
*/
void Widget::on_ReverseFrameBtn_clicked()
{
    //Get the current position of the playback progress
    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")));
        //Jump player
        float f_value=(float)value/(float)ui->horizontalSlider_PlayPosition->maximum();
         qDebug()<<"f_value:"<<f_value;
        libvlc_media_player_set_position(vlc_mediaPlayer,f_value);
    }
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: one frame to the right
*/
void Widget::on_ForwardFrameBtn_clicked()
{
    //Get the current position of the playback progress
    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")));
        //Jump player
        float f_value=(float)value/(float)ui->horizontalSlider_PlayPosition->maximum();
         qDebug()<<"f_value:"<<f_value;
        libvlc_media_player_set_position(vlc_mediaPlayer,f_value);
    }
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: volume setting
*/
void Widget::on_VolumeBtn_clicked()
{
    bool checked=ui->VolumeBtn->isChecked();
    if(checked)
    {
        //Mute
        if(vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer,0);
    }
    else
    {
       //Set normal volume
        int volume_val=ui->horizontalSlider_AudioValue->value();
        //Volume Setting 
        if(vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer,volume_val);
    }
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: volume setting
*/
void Widget::on_horizontalSlider_AudioValue_valueChanged(int value)
{
    //Volume Setting 
    if(vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer,value);
}


/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: playback speed setting menu selection
*/
void Widget::slot_onSetTimeSpeed(QAction *action)
{
    action->setChecked(true);
    ui->MediaSpeedBtn->setToolTip(action->text());
    ui->MediaSpeedBtn->setText(action->text());

    /*Set playback rate*/
    if(vlc_mediaPlayer)libvlc_media_player_set_rate(vlc_mediaPlayer,float(action->data().toFloat()));
}



/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the speed setting button
*/
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;
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the rotation selection menu
*/
void Widget::slot_onMediaRotate(QAction *action)
{
    action->setChecked(true);
    ui->MediaRotateBtn->setToolTip(action->text());

    ui->widget_videoDisplay->Set_Rotate(action->data().toInt());
}

/*
Project: ECRS_VideoPlayer
 Date: February 23, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the screen rotation menu
*/
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;
}

/*
Project: ECRS_VideoPlayer
 Date: March 8, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the function setting menu
*/
void Widget::slot_onConfigurationFunction(QAction *action)
{
    if (action == nullptr)
        return;

    //Get the sequence number pressed
    MENU_ITEM item = MENU_ITEM(action->data().toInt());

    //Convert video to keyframe
    if (item == MENU_TO_KeyFrame)
    {

    }
    //APP function introduction
    else if(item==MENU_APP_INFO)
    {

    }
}

/*
Project: ECRS_VideoPlayer
 Date: February 24, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the screenshot menu
*/
void Widget::slot_onMediaSnapshot(QAction *action)
{
    if (action == nullptr)
        return;

    //Get the sequence number pressed
    MENU_ITEM item = MENU_ITEM(action->data().toInt());

    QImage Pic=ui->widget_videoDisplay->GetImage();
    if (Pic.isNull() || Pic.height() <= 0)return;

    //Save to clipboard
    if (item == MENU_COPY_CLIPBOARD)
    {
        QApplication::clipboard()->setImage(Pic);
    }

    //Save to file
    else if (item == MENU_SAVE_FILE_SYSTEM)
    {
        QString strFile = QDateTime::currentDateTime().toString("yyyyMMddHHmmss") + ".png";
        QString strFileName = QFileDialog::getSaveFileName(nullptr, "Save picture", 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);
        }
    }
}

/*
Project: ECRS_VideoPlayer
 Date: February 24, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the screenshot button
*/
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;
}



/*
Project: ECRS_VideoPlayer
 Date: February 24, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: intercept events
*/
bool Widget::eventFilter(QObject *obj, QEvent *event)
{

    //Determine whether to press the mouse within the video window
    if(obj==ui->widget_videoDisplay)
    {

    }

    //Click the volume slider
    else if(obj==ui->horizontalSlider_AudioValue)
    {
        if (event->type()==QEvent::MouseButtonPress)           //Judgment type
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
            if (mouseEvent->button() == Qt::LeftButton)	//Judge left key
            {
                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);
}


/*
Project: ASS_SubtitleVideoPlayer
 Date: June 15, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: key event
*/
void Widget::keyPressEvent(QKeyEvent *event)
{
    switch(event->key())
    {
    case Qt::Key_Space:
        //The following operations can only be performed after the video is loaded successfully
        if(MediaInfo.state==MEDIA_LOAD)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Space)
            {

                if(vlc_mediaPlayer)
                {
                    //Pause and continue switching
                    libvlc_media_player_pause(vlc_mediaPlayer);

                    //4 means pause, 3 means continue, and 5 means stop
                    int state=libvlc_media_player_get_state(vlc_mediaPlayer);
                    if(state==4 || state==5)
                    {
                        //Set button status
                        ui->MediaPauseBtn->setChecked(false);
                    }
                    else if(state==3)
                    {
                        //Set button status
                        ui->MediaPauseBtn->setChecked(true);
                    }
                }
            }
        }
        break;
    }
}


/*
Project: ECRS_VideoPlayer
 Date: March 8, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: click the setting button
*/
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()<<"Jump position:"<<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")));
        //Jump player
        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() << "Drag in file name:" << fileName;
        //Load video file
        load_video_file(false,fileName);
    }
}


/*
Project: ECRS_VideoPlayer
 Date: February 24, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: save configuration
*/
void Widget::SaveConfig()
{
    //Obtain the user's personalized configuration parameters from the UI interface
    /*Save the data to a file for next loading*/
    QString text;
    text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;
    QFile filesrc(text);
    filesrc.open(QIODevice::WriteOnly);
    QDataStream out(&filesrc);
    out << open_dir_path;  //serialize
    filesrc.flush();
    filesrc.close();
}

/*
Project: ECRS_VideoPlayer
 Date: February 24, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: read configuration
*/
void Widget::ReadConfig()
{
    //Read configuration file
    QString text;
    text=QCoreApplication::applicationDirPath()+"/"+ConfigFile;

    //Determine whether the file exists
    if(QFile::exists(text))
    {
        QFile filenew(text);
        filenew.open(QIODevice::ReadOnly);
        QDataStream in(&filenew); // Read serialized data from file
        in >>open_dir_path; //Extract written data
        filenew.close();
    }
}


/*
Project: ASS_SubtitleVideoPlayer
 Date: June 16, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: window closing event
*/
void Widget::closeEvent(QCloseEvent *event) //Window close event
{
    int ret = QMessageBox::question(this, tr("Important tips"),
    tr("Exit player?"),
    QMessageBox::Yes | QMessageBox::No);

    if(ret==QMessageBox::Yes)
    {
        SaveConfig();
        event->accept(); //Accept event
    }
    else
    {
        event->ignore(); //Clear event
    }
}


/*
Engineering: VLC_Core_VideoPlayer
 Date: July 26, 2021
 Author: brother DS Bruce Lee
 Environment: win10 Qt5 12.6 MinGW32
 Function: open link
*/
void Widget::on_toolButton_link_clicked()
{
    bool ok;
    QString text = QInputDialog::getText(this, tr("Streaming media playback"),tr("Please enter the streaming media address:"), QLineEdit::Normal,
    tr("rtmp://58.200.131.2:1935/livetv/cctv14"),&ok);
    if (ok && !text.isEmpty())
    {
        //load_video_file(0,text);
//Open RTMP streaming media: libvlc_media_new_location (inst, "rtmp://10.0.0.4:554/cam ");

    }
}

Added by JKinBlack on Wed, 12 Jan 2022 04:45:38 +0200