无法使用avformat_seek_file获取第一帧

时间:2014-01-31 08:24:58

标签: c ffmpeg libavformat

我想使用libav在视频中寻找任意帧。更确切地说,使用函数avformat_seek_file,它显然在内部使用av_seek_frame

我想进行向后搜索(即在我寻找的之前获得最接近的可能框架),这样我就可以前进直到找到精确我想要的那个。为此,我使用如下函数:

avformat_seek_file(..., ...,
                   std::numeric_limits<boost::int64_t>::min(),
                   target_pts,
                   target_pts,
                   ...);

这意味着我对找到我的target_pts之后的帧没有任何容忍度,但我对以前的任何帧感到满意。

我正在使用Big Buck Bunny videos进行测试。使用480p H.264视频,我可以毫无问题地寻找任何pts。但是使用480p OGG视频,我不能。实际上,我可以在pts = 73之后寻找任何帧,但不是之前。寻求pts = 0将视频设置为pts = 73

有人可能会认为该流实际上是从pts = 73开始的,但这不是<stream>.start_time返回的内容。此外,如果我只加载视频并按顺序读取帧,我可以毫无问题地获得前73帧。问题是我永远无法使用avformat_seek_file返回其中一个框架。

最后一点:如果我使用标志AVSEEK_FLAG_ANY,那么它是有效的。但这可能导致我只解码我想要的帧的一部分,这对我来说不是解决方案。

有人可以解释这种奇怪的行为吗?

1 个答案:

答案 0 :(得分:1)

这可能是一个老问题,但这个答案适用于遇到同样问题的人。

旧的av_seek_frame函数使用AVSEEK_FLAG_BACKWARD标志来寻找时间戳小于或等于目标时间戳的帧。默认情况下,此功能将搜索时间戳等于或大于目标时间戳的帧。新avformat_seek_file函数的文档表明该标志被忽略。

  

如果flags包含AVSEEK_FLAG_ANY,则将非关键帧视为   关键帧(所有解复用器可能不支持)。如果是旗帜   包含AVSEEK_FLAG_BACKWARD,它被忽略。

正如我们在下面的源代码中看到的那样。

2156     if (s->seek2any>0)
2157         flags |= AVSEEK_FLAG_ANY; 
2158     flags &= ~AVSEEK_FLAG_BACKWARD;

如您所述,当avformat_seek_file函数回退到av_seek_frame并且需要再次设置AVSEEK_FLAG_BACKWARD标志时,会出现问题。仅当目标时间戳和最小时间戳之间的差异大于最大时间戳和目标时间戳之间的差异时,才会发生这种情况。

 2187     // Fall back on old API if new is not implemented but old is.
 2188     // Note the old API has somewhat different semantics.
 2189     if (s->iformat->read_seek || 1) {
 2190         int dir = (ts - (uint64_t)min_ts > (uint64_t)max_ts - ts ? AVSEEK_FLAG_BACKWARD : 0);
 2191         int ret = av_seek_frame(s, stream_index, ts, flags | dir);
 2192         if (ret<0 && ts != min_ts && max_ts != ts) {
 2193             ret = av_seek_frame(s, stream_index, dir ? max_ts : min_ts, flags | dir);
 2194             if (ret >= 0)
 2195                 ret = av_seek_frame(s, stream_index, ts, flags | (dir^AVSEEK_FLAG_BACKWARD));
 2196         }
 2197         return ret;
 2198     }

换句话说,ts - min_ts溢出,因为您正在使用最小的int64值,以确保永远不会设置AVSEEK_FLAG_BACKWARD标志。为什么不用零呢?