av_seek_frame() 定位为什么不准呢?

初次学习和使用ffmpeg,电脑系统有点老,没办法使用最新版的ffmpeg 3.3,只能从别处下载了一个2.8版的用用,官网提供的历史版本都没有我电脑可用的版本。

花了两天时间学习并写了一个简单的处理视频的程序,实现视频的截屏保存为jpg图片。

本来想用SDL写个播放器,但写着写着,感觉实现视频、音频同步什么的有点难,有点累,就没再继续。

今天在研究视频定位(seek)的时候,在测试代码时发现问题:

我测试了不同格式的视频: mp4,flv,mov,3gp,wmv,mkv,avi

定位,我用的是 av_seek_frame(),如果定位:wmv,avi,3gp 等,我只需简单的使用:

int64_t seekTime_us = (int64_t)8 * AV_TIME_BASE; //单位: 微秒, 必须加 (int64_t),否则有可能发生数据溢出。
av_seek_frame(pFormatCtx, -1 , seekTime_us, AVSEEK_FLAG_BACKWARD); // AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY | AVSEEK_FLAG_FRAME

结果:定位很准确。

但上面的代码用来定位:mp4,flv,mov,mkv等,经常会遇到定位不准的情况,误差在[0-5秒]左右。

在网上搜索了半天,找到下面另一种计算方法,经测试和上面的效果其实是一样的,误差在[0-5秒]左右,还不如上面的简单。

int startSecond = 8;
int64_t seekTime_us = startSecond * AV_TIME_BASE; //单位: 微秒
int64_t targetFrame = av_rescale_q(seekTime_us, AV_TIME_BASE_Q, pFormatCtx->streams[videoStream]->time_base);

int64_t seekStreamDuration = pFormatCtx->streams[videoStream]->duration;
int flags = AVSEEK_FLAG_BACKWARD; //默认使用
if(targetFrame > 0 && targetFrame < seekStreamDuration)
{
    // H.264 I frames don't always register as "keyframes" in FFmpeg
    flags |= AVSEEK_FLAG_ANY; //加了这个,有时定位更不准
}

av_seek_frame(pFormatCtx, videoStream, targetFrame, flags);
avcodec_flush_buffers(pCodecCtx);

我又换了个方式:先计算视频的总帧数(frameCount),然后通过fps计算出8秒位置的frame_index, 不用av_seek_frame,直接通过while(av_read_frame()),跳过frame_index,来实现seek,这个方法定位非常准,但不科学。因为如果要定位到60秒,我用的电脑光是花费在while(av_read_frame())上的时间就将近4秒,效率太低了,只适合定位开头几十秒内。

谁能告诉我,怎样用 av_seek_frame 来定位 mp4,flv 格式的视频精准一点,不要定位到8秒,显示的是5秒时的内容。

原文地址:https://www.cnblogs.com/personnel/p/7265775.html