带有 AVSEEK_FLAG_ANY 的 ffmpeg av_seek_frame 导致灰屏
Posted
技术标签:
【中文标题】带有 AVSEEK_FLAG_ANY 的 ffmpeg av_seek_frame 导致灰屏【英文标题】:ffmpeg av_seek_frame with AVSEEK_FLAG_ANY causes grey screen 【发布时间】:2014-01-11 03:45:59 【问题描述】:问题:
omxplayer 的源代码使用AVSEEK_FLAG_BACKWARD
标志调用ffmpeg av_seek_frame()
方法。虽然不是 100% 肯定,但我相信这会寻找最接近的 i-frame。相反,我想寻找确切的位置,所以我修改了源代码,使av_seek_frame()
方法现在使用AVSEEK_FLAG_ANY
标志。现在,当电影加载时,我得到一个灰屏,通常为 1 秒钟,在此期间我可以听到音频。我已经在多台计算机上尝试过这个(我实际上正在同步它们,因此也是同时)所以这不是一个孤立的事件。我的猜测是寻找非 i 帧的计算成本更高,导致初始灰屏。
问题:如何使用 ffmpeg 指示音频等到视频准备好后再继续。
【问题讨论】:
【参考方案1】:实际上,AVSEEK_FLAG_BACKWARD
表示您希望找到最近的关键帧,其时间戳小于您正在寻找的那个。
通过使用AVSEEK_FLAG_ANY
,您可以获得与您要求的时间戳完全对应的帧。但是这一帧可能不是关键帧,这意味着它不能被完全解码。这就解释了你的“灰屏”,它会一直出现,直到到达下一个关键帧。
因此,解决方案是使用AVSEEK_FLAG_BACKWARD
向后搜索,并从此关键帧读取下一帧(例如使用av_read_frame()
),直到找到与您的时间戳对应的帧。此时,您的帧将被完全解码,不再显示为“灰屏”。
注意:看来,由于某种原因,av_seek_frame()
使用 AVSEEK_FLAG_BACKWARD
返回 next 关键帧,而我正在寻找的帧是前一个关键帧这个关键帧。否则它会返回前一个关键帧(这是我想要的)。我的解决方案是更改我给av_seek_frame()
的时间戳,以确保它将在我正在寻找的帧之前返回关键帧。
【讨论】:
我认为你的最后一段有错误 "可能不是关键帧,也就是说无法完全解码" 什么意思,只有关键帧可以完全解码? 我不是专家,但我是这样看的:视频是使用“关键帧”编码的,它们是独立的图像,以及“帧" 依赖于关键帧。换句话说,当你读取一个关键帧时,你可以解码相应的图像。但是一帧并不包含解码它所需的所有信息,因此您需要解码之前的帧(而不是关键帧)。因此,如果您“跳转”到一个帧而之前没有读取关键帧,那么您就会错过有关该帧的信息。是不是更清楚了? 我认为你在这里错了。一些帧需要解码其他帧。 “keyframe”实际上是一个I-frame(参见here)。 寻找第一个出现在你想要的之前的关键帧(或者另一个离它不太远的关键帧,只要它出现在之前一个你想要的)。然后,一次读一帧,直到达到目标(这实际上是我答案的第三段=))。【参考方案2】:用一些代码完成 JonesV 的回答:
void seekFrame(unsigned frameIndex)
// Seek is done on packet dts
int64_t target_dts_usecs = (int64_t)round(frameIndex
* (double)m_video_stream->r_frame_rate.den
/ m_video_stream->r_frame_rate.num * AV_TIME_BASE);
// Remove first dts: when non zero seek should be more accurate
auto first_dts_usecs = (int64_t)round(m_video_stream->first_dts
* (double)m_video_stream->time_base.num
/ m_video_stream->time_base.den * AV_TIME_BASE);
target_dts_usecs += first_dts_usecs;
int rv = av_seek_frame(
m_format_ctx, -1, target_dts_usecs, AVSEEK_FLAG_BACKWARD);
if (rv < 0)
throw exception("Failed to seek");
avcodec_flush_buffers(m_codec_ctx);
然后您可以开始解码检查 AVPacket.dts
与原始目标 dts,在 AVStream.time_base
上计算。到达目标 dts 后,下一个解码帧应该是所需的帧。
【讨论】:
auto first_dts_usecs = (int64_t)round(m_video_stream->first_dts * (double)m_video_stream->time_base.num / m_video_stream->time_base.den * AV_TIME_BASE)
的行是什么?
@csguy 据我记得,第一个 dts 可能不为零,在这种情况下,它应该被考虑并作为偏移量。我不知道av_seek_frame
时间戳是否仍应被视为微秒,但可以肯定的是,很久以前它就在我的测试中。针对新版本的 ffmpeg 仔细验证此代码的正确性。以上是关于带有 AVSEEK_FLAG_ANY 的 ffmpeg av_seek_frame 导致灰屏的主要内容,如果未能解决你的问题,请参考以下文章