快进功能的实现主要通过使用av_seek_frame函数来实现快进的功能
一、函数原型
int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
int flags);
二、参数解释
AVFormatContext *s 解码的格式上下文
int stream_index 对应的数据流
int64_t timestamp 时间戳,单位为TIMEBASE(需要手动转换)
int flags 标志位
/***********标志位参数************/
AVSEEK_FLAG_BACKWARD = 1 往后找
AVSEEK_FLAG_BYTE = 2 按照字节来移动位置
AVSEEK_FLAG_ANY = 4 针对frame来说的,就跳转那一帧,不找关键帧
AVSEEK_FLAG_FRAME = 8 表示往后找,找到关键帧
flags是二进制表示的,可以同时取2个,用或来操作。‘|’
三、代码实现
void VideoPlayer::seek(double T)
{
av_seek_frame(pFormatCtx, indexAudio, (T + ptsAudio / 1000) / av_q2d(pAudioCodeCtx->time_base), AVSEEK_FLAG_BACKWARD);
}
只需一行代码就可以实现我们想要的功能了,但是由于我们使用了缓存,导致视频的调转并不是立即执行的,它会先显示完缓存中packet包后才开始跳转,所以我们需要清除队列中缓存
四、清除队列
void PacketQueue::initQueue()
{
AVPacketList *pkt, *pkt1;
SDL_LockMutex(mutex);
if (nb_packets == capacity)
{
SDL_CondSignal(full);
}
for (pkt = first_pkt; pkt != NULL; pkt = pkt1) {
pkt1 = pkt->next;
av_free_packet(&pkt->pkt);
av_freep(&pkt);
}
last_pkt = NULL;
first_pkt = NULL;
nb_packets = 0;
size = 0;
SDL_UnlockMutex(mutex);
}
由于清除队列的时候会存在,队列中继续读取数据与插入数据的情况,所以我们需要设置标记,还暂停那些对队列的操作
void VideoPlayer::seek(double T)
{
bStop = TRUE;
SDL_PauseAudio(1);
qAudio->initQueue();
qVideo->initQueue();
av_seek_frame(pFormatCtx, indexAudio, (T + ptsAudio / 1000) / av_q2d(pAudioCodeCtx->time_base), AVSEEK_FLAG_BACKWARD);
bStop = FALSE;
SDL_PauseAudio(0);
}
五、视频暂停
通过上述的标志位实现读取,显示线程的暂停
注意:需要暂停音频SDL_PauseAudio(1);
void VideoPlayer::stop()
{
bStop = !bStop;
if (bStop)
{
SDL_PauseAudio(1);
}
else
{
SDL_PauseAudio(0);
}
}