写在前面:
这部分对应视频课程中的4-4~4-5。实现拖动视频进度条来控制播放位置,并控制视频播放和暂停。5、进度条拖动控制视频播放位置
拖动进度条播放,需要重新写一个函数,用于指定的位置进行读取。
bool XFFmpeg::Seek(float pos)传入的参数是0~1,也就是视频的百分比,这样我们通过拖动进度条,直接将进度条的百分比直接传入seek中。
但这里存在一个问题:如果视频拖动的到的位置的帧不是关键帧,那就没办法解码。
通过使用stamp = pos *ic->streams[videoStream]->duration;得到进度条对应视频的相对位置在哪。
其中:*ic->streams[videoStream]->duration里面是视频的总长度(单位还没去看,但是乘以传入的进度条pos百分比,就可以得到相对位置了)
然后使用av_seek_frame定位。
其中:
AVSEEK_FLAG_BACKWARD是向前对齐,也就是向之前的帧对其
AVSEEK_FLAG_FRAME是定位到关键帧
这里引入这两个参数就是为了解决上面说的问题,进度条定位到的帧不是关键帧就无法进行解码,所以需要这样让视频从关键帧开始,然后当然是需要定位到当前进度之前的关键帧。
定位完视频位置后,当前的解码缓冲当中还有之前的视频帧,因此我们需要释放掉之前的缓冲区,这里调用avcodec_flush_buffers函数将缓冲区的缓冲帧清除掉。6、界面中进度条代码实现
首先创建进度条的信号槽,然后需要创建两个函数,一个是点击进度条的时候,一个是释放进度条的时候,这样做的原因在于:进度条有刷新时间,我们点击进度条的时候,它也会在刷新,所以在点击进度条的时候,我们停止刷新,释放的时候再继续刷新。
在QT开发中,凡是涉及界面的处理,都需要放到槽当中,当一个信号发生之后并不是立即处理,而是放在队列里面。这里我们定义了两个槽sliderPressed和sliderRelease。
定义一个全局变量g_isPressSlider
void Xplay::sliderPressed()//进度条按压处理
{
g_isPressSlider = true;
}
void Xplay::sliderReleased()//进度条释放处理
{
g_isPressSlider = false;
float pos = 0;
pos = (float)ui.playslider->value() / (float)(ui.playslider->maximum() + 1);
XFFmpeg::Get()->Seek(pos);
}
这样在void Xplay::timerEvent(QTimerEvent *e)函数中就可以使用这种方法来控制进度条的刷新。
if (!g_isPressSlider)
ui.playslider->setValue(rate * 1000);
进度条释放完后,就可以对视频进行定位了,这时候直接获取ui.playslider->value()的值除以总长度就是进度条的相对位置,将相对位置传入事先封装好的Seek函数即可。7、控制视频的播放
拖动播放按钮到上方,添加新的信号槽play,这样当点击播放按钮的时候,能够播放视频,且图标会切换。
定义全局变量g_isPlay 用于判断当前处于播放状态还是暂停状态,然后进行图标切换,图标切换参照之前设置图标的时候,QPushButton的设定,这样通过setStyleSheet函数来设定图标的显示。
#define PAUSE "QPushButton{border-image: url(:/Xplay/pause_normal.png);}"
#define PLAY "QPushButton{border-image: url(:/Xplay/play_normal.png);}"
void Xplay::play()
{
g_isPlay = !g_isPlay;
if (g_isPlay)//pause
ui.pushButton_2->setStyleSheet(PAUSE);
else
ui.pushButton_2->setStyleSheet(PLAY)
}
但是这里会出现一个问题,一开始的时候,显示的是播放的图标,但是状态是暂停的,所以需要稍作处理。
在选择播放视频的最后,调用切换图标的动作即可。
在void Xplay::open()函数之后添加:g_isPlay = false;
play();
8、控制视频的暂停
暂停方案选择一个简单的方法,就是直接在解码的线程当中做处理,如果是暂停状态,那么就不对视频做解码处理。
在暂停的时候,如果拖动视频,是没办法对进度条进行移动的,因为当前代码进度条的位置是通过Decode解码过程中得到的pkt来实现的,暂停视频的时候,就没办法进行解码了,因此我们需要自己实现暂停时读取pkt,才能移动进度条。
在 XFFmpeg::Seek(float pos)函数里再次计算pts = stamp * r2d(ic->streams[videoStream]->time_base) * 1000;