libVLC 播放控制

2021-02-14 高效程式設計師

雖然之前介紹了 libVLC 的工作流程,但只能實現簡單的播放。與真正的媒體播放器相比,還相差甚遠,因為它連一些基本的控制都沒有,像播放/暫停、停止、跳播、快進/快退、音量調節、靜音等。

為了讓我們的播放器更加專業一些,現在是時候加上這些功能了!

包裝器

為了和 UI 分離,需要單獨定義一個 Player 類,作為 libVLC 的一個包裝器,它的主要作用是提供基本的媒體播放控制功能!

將上述所描述的接口添加進來,同時,再定義一些對應的信號,當有事件發生時,進行通知:

class Player : public QObject
{
    Q_OBJECT

public:
    // 播放狀態
    typedef enum State {
        Idle,
        Opening,
        Buffering,
        Playing,
        Paused,
        Stopped,
        Ended,
        Error
    } State;

    explicit Player(QObject *parent = nullptr);
    ~Player();
    // 設置視頻輸出窗口
    void setVideoWindow(QWidget *window);
    // 獲取當前狀態
    Player::State state();

Q_SIGNALS:
    // 總時長發生變化
    void durationChanged(qint64 dur);
    // 當前時間發生變化
    void timeChanged(qint64 time);
    // 播放位置發生變化
    void positionChanged(float pos);
    // 狀態發生變化
    void stateChanged(Player::State state);

public Q_SLOTS:
    // 打開文件
    void openFile(const QString &file);
    // 設置音量
    void setVolume(int vol);
    // 跳播
    void seek(int pos);
    // 播放
    void play();
    // 暫停
    void pause();
    // 停止
    void stop();

private:
    // 訂閱事件
    void attachEvents();

private:
    libvlc_instance_t *m_instance          {nullptr};
    libvlc_media_player_t *m_player        {nullptr};
    libvlc_media_t *m_media                {nullptr};
    libvlc_event_manager_t *m_eventManager {nullptr};

    QWidget *m_videoWindow           {nullptr};
};

接口較多,挑一些核心的介紹一下。

訂閱事件

這一步很關鍵,因為要實時獲取媒體信息的話,必須訂閱相關事件。比如,播放時間發生變化時,需要更新 UI 上的當前時間,這時就需要監聽 libvlc_MediaPlayerTimeChanged 事件。

有關播放狀態、音量、是否靜音、媒體時長等相關的事件為以下幾個:

void Player::attachEvents()
{
    // 事件列表
    QList<libvlc_event_e> events;
    events << libvlc_MediaPlayerOpening
           << libvlc_MediaPlayerBuffering
           << libvlc_MediaPlayerPlaying
           << libvlc_MediaPlayerPaused
           << libvlc_MediaPlayerStopped
           << libvlc_MediaPlayerEncounteredError
           << libvlc_MediaPlayerMuted
           << libvlc_MediaPlayerUnmuted
           << libvlc_MediaPlayerAudioVolume
           << libvlc_MediaPlayerLengthChanged
           << libvlc_MediaPlayerTimeChanged
           << libvlc_MediaPlayerPositionChanged;

    // 訂閱事件
    foreach (const libvlc_event_e &e, events) {
        libvlc_event_attach(m_eventManager, e, handleEvents, this);
    }
}

緊接著,需要在回調函數中對這些事件進行處理,可以將這些信息保存起來,也可以通過信號的形式發射出去:

// 回調函數,用於事件處理
static void handleEvents(const libvlc_event_t *event, void *userData)
{
    Player *player = static_cast<Player *>(userData);
    switch (event->type) {
    // 播放狀態改變
    case libvlc_MediaPlayerOpening:
    case libvlc_MediaPlayerBuffering:
        break;
    case libvlc_MediaPlayerPlaying: {
        emit player->stateChanged(Player::Playing);
        break;
    }
    case libvlc_MediaPlayerPaused: {
        emit player->stateChanged(Player::Paused);
        break;
    }
    case libvlc_MediaPlayerStopped: {
        emit player->stateChanged(Player::Stopped);
        break;
    }
    case libvlc_MediaPlayerEncounteredError: {
        emit player->stateChanged(Player::Error);
        break;
    }
    // 時長改變
    case libvlc_MediaPlayerLengthChanged: {
        qint64 dur = event->u.media_player_length_changed.new_length;
        emit player->durationChanged(dur);
        break;
    }
    // 播放時間改變
    case libvlc_MediaPlayerTimeChanged: {
        qint64 time = event->u.media_player_time_changed.new_time;
        emit player->timeChanged(time);
        break;
    }
    // 播放位置改變
    case libvlc_MediaPlayerPositionChanged: {
        float pos = event->u.media_player_position_changed.new_position;
        emit player->positionChanged(pos);
        break;
    }
    default:
        break;
    }
}

播放控制

當然了,最主要的是控制入口,這是外部調用的主要方式。

對於播放來說,需要判斷一下當前的狀態;如果當前處於暫停狀態,則恢復播放;否則,直接進行播放:

void Player::play()
{
    WId curWId = 0;
    if (nullptr != m_videoWindow)
        curWId = m_videoWindow->winId();

    // 指定輸出窗口
#if defined (Q_OS_WIN)
    libvlc_media_player_set_hwnd(m_player, (void*)curWId);
#elif defined(Q_OS_MAC)
    libvlc_media_player_set_nsobject(m_player, (void *)curWId);
#else
    libvlc_media_player_set_xwindow(m_player, curWId);
#endif

    // 恢復或者播放
    if (state() == Player::Paused) {
        libvlc_media_player_set_pause(m_player, false);
    } else {
        libvlc_media_player_play(m_player);
    }
}

和上面的恢復播放一樣,暫停也使用的是 libvlc_media_player_set_pause() 接口,只不過第二個參數為 true:

void Player::pause()
{
    if (libvlc_media_player_can_pause(m_player))
        libvlc_media_player_set_pause(m_player, true);
}

如果想停止播放,直接使用 libvlc_media_player_stop() 即可,比較簡單:

void Player::stop()
{
    libvlc_media_player_stop(m_player);
}

在設置音量時,需要注意一下, libvlc_audio_set_volume() 的第二個參數是指音量的百分比。如果為 0,則表示靜音;如果是 100,則是 0dB:

void Player::setVolume(int vol)
{
    libvlc_audio_set_volume(m_player, vol);
}

跳播相對來說複雜一些,因為需要將位置和時長對應起來。跳播的實現方式有兩種,任選一種都可以:

以方式一為例,假設我們的播放滑塊取值範圍是 0 - 100,那麼 float(pos)/100 就是 pos 所在位置佔總範圍的百分比。將這個結果乘以總時長 float(duration),算出的就是 pos 位置對應的時間。有了這個時間,就可以使用 libvlc_media_player_set_time() 進行跳播了:

void Player::seek(int pos)
{
    libvlc_media_t *curMedia = libvlc_media_player_get_media(m_player);
    if (nullptr == curMedia)
        return;

    libvlc_time_t duration = libvlc_media_get_duration(curMedia);
    float ms = float(pos)/100 * float(duration);
    libvlc_media_player_set_time(m_player, libvlc_time_t(ms));
}

大概的功能先介紹到這裡,後面的更精彩,敬請期待吧!

點個在看,麼麼噠!

相關焦點

  • 最簡單的 libVLC 播放器
    由於 libVLC 是跨平臺的,因此在編寫程序時,應儘可能的兼容多個系統。除此之外,我們後續還會添加一些 GUI 交互。所以從本節開始,會逐漸引入 Qt(當然了,你也可以選擇自己擅長的其他 GUI 框架)。為了介紹 libVLC 的用法,我們從最簡單的播放器開始著手,來播放一個本地媒體或者網絡串流,同時也能了解它的主要工作流程。
  • 將 libVLC 視頻渲染到 QWidget 中
    在 libVLC 中,要實現這個功能並不難,只要調用平臺相關的接口,並將媒體播放器和窗口句柄傳遞進去就可以了,這樣媒體播放器就會把視頻呈現到該窗口上。主要接口一定要區分開下面這幾個接口,因為在不同平臺上(Windows/Linux/Mac OS)調用的不一樣:
  • 自定義基於 VLC 的視頻播放器
    我們只需要 libvlc.dll、libvlccore.dll 和 plugins 目錄,所以可以忽略其它文件。libvlc.dll、libvlccore.dll 文件中是 VLC 的庫函數,plugins 目錄裡是 VLC 對各種不同媒體文件提供支持的插件。第二步:映射、封裝 dll 庫函數。
  • VLC-Qt 入門指南
    VLC-Qt 是一個結合了 Qt 應用程式和 libVLC 的免費開源庫,它包含了用於媒體播放的核心類,以及用於更快速地進行媒體播放器開發的一些
  • vlc_for_android(基於git-3.0.0)快速集成並播放電視節目直播
    VLC是用的很廣泛的多媒體播放器框架,我之前也寫過關於《VLC源碼及各modules功能介紹》,VLC較其他多媒體播放框架要複雜更多。
  • Ubuntu 16.04下安裝VLC並解決sudo apt-get build-dep vlc失敗問題
    最近嘗試在Ubuntu 16.04中使用VLC播放視頻,試了很多方法,要麼是編譯後打不開圖形界面,要麼是可以接受組播流,但不能正常解碼為視頻,最後終於找到方法,和大家分享一下,希望對你有幫助。1、安裝vlc編譯中的一些工具2、獲取源碼wget ftp://ftp.videolan.org/pub/videolan/vlc/2.0.6/vlc-2.0.6.tar.xz(這個支持ubuntu 13.04,ubuntu 14.04LTS安裝時,make不成功)wget http://download.videolan.org/pub/videolan
  • (二十四)小眾但好用:免費開源跨平臺的播放器 VLC
    作為一個每次看視頻都要打開的播放器,能做到讓人不忍直視。將 vlt 文件複製到 vlc 安裝目錄的 skins 目錄下。在播放器上點「滑鼠右鍵」-「界面」-「偏好設置」。vlc 還有許多快捷鍵,除了方向鍵控制音量大小和快進快退,這些快捷鍵你也許可以用的上。
  • 修復 「VLC is Unable to Open the MRL」 錯誤 | Linux 中國
    一個使用 VLC 的技巧是使用 VLC 播放 YouTube 和其他在線視頻。這可以幫助你觀看帶有字幕的在線視頻。
  • Ubuntu 16.04下編譯安裝VLC
    最近嘗試在Ubuntu 16.04中使用VLC播放視頻,試了很多方法,要麼是編譯後打不開圖形界面,要麼是可以接受組播流,但不能正常解碼為視頻,最後終於找到方法,和大家分享一下,希望對你有幫助。1、安裝vlc編譯中的一些工具sudo apt-get install git libtool build-essential pkg-config autoconf2、獲取源碼wget ftp://ftp.videolan.org/pub/videolan/vlc/2.0.6/vlc-2.0.6.tar.xz(這個支持ubuntu 13.04
  • VLC 播放器 官方正式純潔版
    最具特色的功能是可以邊下載邊觀看Divx媒體文件,並可以播放播放不完全的AVI文件▪與其他多媒體播放工具相比,VLC Media Player程序最拿手的本領就是播放殘缺視頻。在用該工具播放下載不完整的視頻電影時,首先依次單擊菜單欄中的File/Open File命令,在彈出的如圖5所示的設置窗口中,單擊Browse按鈕,在隨後出現的文件選擇對話框中,將殘缺的視頻文件選中並導入;倘若發現殘缺視頻文件的擴展名不是VLC Media Player程序所支持的話,必須人為地將其修改過來。
  • VLC 3.0.2發布,修復150多個錯誤(如何安裝)
    VLC媒體播放器3.0.2在幾天前發布。該版本自3.0.0發布以來修復了超過150個錯誤,並提高了macOS上的解碼速度。:使用硬體加速時修復帶有字幕的快照當圖片尺寸是奇數時,修正綠色/缺失行/列macOS的硬體解碼方面的重大改進,速度明顯更快,支持更多示例並消除崩潰/死鎖提高DxVA2和D3D11解碼的兼容性,在尋找並修復損壞的驅動程序黑名單時修復崩潰修正SSA字幕強制對齊修復E-AC-3口吃修復macOS上的MIDI播放
  • VLC多媒體播放器使用及下載
    VLC多媒體播放器是一款多媒體播放器。VLC播放器支持數十種音頻、視頻格式的解碼,讓用戶不用再辛苦轉碼才能觀看罕見格式。
  • 這個真香-能播放數字衛星頻道免費高清視頻播放器
    ,播放性能非常強悍,無需安裝任何第三方編碼器就能直接流暢播放包括MPEG-2、MPEG-4、H.264、MKV、WebM、WMV、MP3、DVD、CD/VCD和藍光在內的幾乎所有播放大多數多媒體文件,威航軟體園測試後發現vlc media player 64播放器絕對的純淨沒有任何商業行為,能為廣大用戶帶來超爽無打擾的視聽播放體驗。
  • VLC 3.0 「Vetinari」正式發布,有史以來最大的更新
    被稱為「Vetinari」的VLC 3.0可能是全球數百萬計算機和移動用戶使用的VLC媒體播放器應用程式中有史以來最大的更新。它具有許多現代功能和技術,包括支持蘋果的iPhone X,360度視頻播放,3D音頻支持和HDR視頻支持。
  • Fedora 22 用戶如何安裝 VLC media player
    Fedora 22 安裝好了,感覺很不錯,不過還有點問題,播放rmvb視頻的時候聲音與畫面不同步,以前也遇到過,還好 VLC
  • 如何解決 VLC 視頻嵌入字幕中遇到的錯誤
    沒有可播放的流你可能選擇了奇怪的輸出設置。你要仔細檢查你是否選擇了正確的視頻和音頻編解碼器。另外,請記住,一些媒體播放器可能沒有所有的編解碼器。此外,確保在所有要播放的系統中都測試過了。如果你遇到了一些錯誤,這裡的提示和技巧應該可以至少幫助你解決一些,包括無法播放的流、丟失或重複的字幕、缺少編解碼器和 Kdenlive 解決方法、YouTube 上傳錯誤、隱藏的 VLC 命令行選項,還有一些其他東西。是的,這些對於一段文字來說是很多的。幸運的是,這些都是好東西。保重,網際網路的孩子們。如果你有任何其他要求,我將來的 VLC 文章應該會涵蓋,請隨意給我發郵件。
  • 實現 Bilibili 視頻播放Chrome 媒體控制效果
    正文從這開始~~Chrome 最新的版本更新了媒體控制菜單的按鈕 UI,也就是每次我們的網頁產生聲音或者視頻時候,菜單欄頭像附近,方便我們在多個 Tab 進行控制時候加的,方便我們操作這些網頁 Tab 下面的音視頻。尤其最新的樣式實現非常酷炫,加入對封面的支持,這樣整體效果看著非常棒。
  • 這些簡單的技巧使 VLC 更加出色 | Linux 中國
    如果 VLC 不是最好的播放器,那它也是最好的開源視頻播放器之一。大多數人不知道的是,它不僅僅是視頻播放器。你可以進行許多複雜的任務,如直播視頻、捕捉設備等。只需打開菜單,你就可以看到它有多少選項。我們有一個詳細的教程,討論一些專業的 VLC 技巧,但這些對於普通用戶太複雜。