libVLC 播放控制

proginn468312

共 1672字,需浏览 4分钟

 ·

2020-08-23 14:03

虽然之前介绍了 libVLC 的工作流程,但只能实现简单的播放。与真正的媒体播放器相比,还相差甚远,因为它连一些基本的控制都没有,像播放/暂停、停止、跳播、快进/快退、音量调节、静音等。


为了让我们的播放器更加专业一些,现在是时候加上这些功能了!




1

包装器


为了和 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};
};


接口较多,挑一些核心的介绍一下。



2

订阅事件


这一步很关键,因为要实时获取媒体信息的话,必须订阅相关事件。比如,播放时间发生变化时,需要更新 UI 上的当前时间,这时就需要监听 libvlc_MediaPlayerTimeChanged 事件。


有关播放状态、音量、是否静音、媒体时长等相关的事件为以下几个:


void Player::attachEvents()
{
    // 事件列表
    QList 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(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;
    }
}



3

播放控制


当然了,最主要的是控制入口,这是外部调用的主要方式。



播放


对于播放来说,需要判断一下当前的状态;如果当前处于暂停状态,则恢复播放;否则,直接进行播放:


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



跳播


跳播相对来说复杂一些,因为需要将位置和时长对应起来。跳播的实现方式有两种,任选一种都可以:


  • 跳播到指定时间:使用 libvlc_media_player_set_time()

  • 跳播到指定位置:使用 libvlc_media_player_set_position()


以方式一为例,假设我们的播放滑块取值范围是 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));
}


大概的功能先介绍到这里,后面的更精彩,敬请期待吧!


·END·
 

作者:一去、二三里
爱学习,爱编程,爱生活。
欢迎来撩,一起畅谈程序人生!

点个在看,么么哒!

浏览 258
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报