一、问题描述与解决思路
在上几个章节,介绍了如何使用libvlc开发一款视频播放器,libvlc除了具备普通播放器该有的功能,还有很多其他强大的功能,比如:播放rtsp\rtmp流媒体视频、录制画面为视频、各种滤镜等等;
在开发视频播放器的过程中,肯定是需要显示视频的事实播放进度的,这个功能在上篇文章里已经介绍了,采用libvlc的回调事件来获取当前视频的播放进度。但是这个回调事件里,得到的进度不够精细,固定一秒只能返回2次左右,也就是500ms一次,靠回调返回的时间直接给进度条控件赋值,会感觉不细腻,进度条一跳一跳的,不细腻。 libvlc本身接口上没有提供修改时间间隔精度的功能,这里采用的方法是: 采用系统时间补偿的思路,计算当前播放的位置。
下面这个是libvlc事件里打印的时间:
pos: 58152
pos: 58652
pos: 59151
pos: 59402
pos: 59900
pos: 60151
pos: 60401
pos: 60902
pos: 61154
pos: 61651
.......
可以看到,时间的间隔差不多是500ms返回一次。
这是当前正在播放视频的媒体详细信息:
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "High",
"codec_type": "video",
"codec_time_base": "125/5994",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1280,
"height": 720,
"coded_width": 1280,
"coded_height": 720,
"has_b_frames": 2,
"sample_aspect_ratio": "1:1",
"display_aspect_ratio": "16:9",
"pix_fmt": "yuv420p",
"level": 41,
"chroma_location": "left",
"refs": 1,
"is_avc": "true",
"nal_length_size": "4",
"r_frame_rate": "2997/125",
"avg_frame_rate": "2997/125",
"time_base": "1/11988",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 67452500,
"duration": "5626.668335",
"bit_rate": "1498216",
"bits_per_raw_sample": "8",
"nb_frames": "134905",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"creation_time": "2018-01-25T18:44:17.000000Z",
"language": "und",
"handler_name": "VideoHandler"
}
},
{
"index": 1,
"codec_name": "aac",
"codec_long_name": "AAC (Advanced Audio Coding)",
"profile": "LC",
"codec_type": "audio",
"codec_time_base": "1/48000",
"codec_tag_string": "mp4a",
"codec_tag": "0x6134706d",
"sample_fmt": "fltp",
"sample_rate": "48000",
"channels": 6,
"channel_layout": "5.1",
"bits_per_sample": 0,
"r_frame_rate": "0/0",
"avg_frame_rate": "0/0",
"time_base": "1/48000",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 270081024,
"duration": "5626.688000",
"bit_rate": "224000",
"max_bit_rate": "224000",
"nb_frames": "263753",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"creation_time": "2018-01-25T18:44:17.000000Z",
"language": "eng",
"handler_name": "SoundHandler"
}
}
]
}
解决办法:
采用系统时间计算当前播放时间:
/*获取当前的播放时间*/
qint64 Widget::libvlc_GetCurrentPlayerPos()
{
static qint64 lastPlayTime = 0;
static qint64 lastPlayTimeMSecs = 0;
if(vlc_mediaPlayer==nullptr)
{
return 0;
}
qint64 currentTime = libvlc_media_player_get_time(vlc_mediaPlayer);
if (lastPlayTime == currentTime && lastPlayTime != 0)
{
currentTime += QDateTime::currentMSecsSinceEpoch() - lastPlayTimeMSecs;
}
else
{
lastPlayTime = currentTime;
lastPlayTimeMSecs = QDateTime::currentMSecsSinceEpoch();
}
return currentTime;
}
开启一个定时器,定时调用该函数获取当前播放器的时间,想要多精确就看定时器多久获取一次了。我这里定时50ms获取一次时间,打印的效果如下:
pos: 1088
pos: 1155
pos: 1218
pos: 1281
pos: 1342
pos: 1418
pos: 1480
pos: 1544
pos: 1604
pos: 1656
pos: 1719
pos: 1782
pos: 1842
pos: 1905
pos: 1966
pos: 2029
pos: 2091
pos: 2152
pos: 2213
pos: 2273
pos: 2337
pos: 2398
pos: 2460
pos: 2521
pos: 2582
pos: 2651
pos: 2712
pos: 2789
pos: 2850
pos: 2901
pos: 2961
pos: 3019
pos: 3083
pos: 3154
pos: 3212
pos: 3281
pos: 3344
pos: 3403
pos: 3463
pos: 3535
pos: 3598
pos: 3651
pos: 3712
pos: 3773
50ms更新一次,进度条就很平滑了。
二、完整的代码
下面就贴出了和本章节主要解决的问题的相关代码。
2.1 widge.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <vlc/vlc.h>
#include <QDebug>
#include <QFileDialog>
#include <QMoveEvent>
#include "form.h"
#include <QTime>
#include <QThread>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
qint64 libvlc_GetCurrentPlayerPos();
........
private slots:
void timer_update();
........
protected:
........
private:
........
QTimer *timer;
};
#endif // WIDGET_H
2.2 widget.cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
................
//定时器更新
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timer_update()));
timer->start(50);
}
/*获取当前的播放时间*/
qint64 Widget::libvlc_GetCurrentPlayerPos()
{
static qint64 lastPlayTime = 0;
static qint64 lastPlayTimeMSecs = 0;
if(vlc_mediaPlayer==nullptr)
{
return 0;
}
qint64 currentTime = libvlc_media_player_get_time(vlc_mediaPlayer);
if (lastPlayTime == currentTime && lastPlayTime != 0)
{
currentTime += QDateTime::currentMSecsSinceEpoch() - lastPlayTimeMSecs;
}
else
{
lastPlayTime = currentTime;
lastPlayTimeMSecs = QDateTime::currentMSecsSinceEpoch();
}
return currentTime;
}
//播放器的播放进度时间更新
void Widget::timer_update()
{
static qint64 pos1=0;
if(vlc_mediaPlayer)
{
//处于播放状态
if(libvlc_media_player_is_playing(vlc_mediaPlayer))
{
qint64 pos=libvlc_GetCurrentPlayerPos();
if(pos1!=pos)
{
pos1=pos;
qDebug()<<"pos:"<<pos;
ui->label_t1->setText(QString("%1").arg(QTime(0, 0, 0,0).addMSecs(int(pos)).toString(QString::fromLatin1("HH:mm:ss:zzz"))));
ui->horizontalSlider_pos->setValue(pos);
}
}
}
}