主頁 > 移動端開發 > QT軟體開發:基于libVLC內核設計視頻播放器

QT軟體開發:基于libVLC內核設計視頻播放器

2021-07-28 17:06:06 移動端開發

一、環境介紹

作業系統: win10 64位

QT版本: QT5.12.6

編譯器: MinGW 32

libvlc版本: 3.0.12

完整工程下載地址(下載即可編譯運行): https://download.csdn.net/download/xiaolong1126626497/20479530

二、播放器運行效果與功能介紹

播放器的功能介紹:

1. 影像旋轉播放(90°、0°、180°、360°)

2. 視頻畫面截圖保存到本地

3. 倍速切換、速度切換不會改變聲音音色

4. 音量調整,靜音切換

5. 快進、快退支持

6. 點擊按鈕加載檔案、滑鼠拖拽檔案進行播放

7. 重播按鈕支持

8. 暫停與繼續切換

9. 單幀切換、前一幀、后一幀

10. 流媒體播放,輸入流媒體鏈接

11. 進度條顯示,支持滑鼠點擊任意跳轉到點擊位置

12. 播放時間實時更新顯示

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

三、libVLC介紹

3.1 下載VLC的SDK檔案

VLC官網地址: https://www.videolan.org/

所有的VLC版本下載地址: http://ftp.heanet.ie/pub/videolan/vlc

3.0.12的SDK下載地址: http://ftp.heanet.ie/pub/videolan/vlc/last/win32/

3.2 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 編程語言,

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/290469.html

標籤:其他

上一篇:Flutter 命令本質之 Flutter tools 機制原始碼深入分析

下一篇:Android基礎到進階UI 時鐘TextClock 使用+實體

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more