第八章:ffmpeg和QT开发播放器之视频播放控制(2)

写在前面:

    这部分对应视频课程中的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;


猜你喜欢

转载自blog.csdn.net/tainjau/article/details/79846639