libVLC 播放控制
共 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·