如何在FFmpeg C/C++中进行查找

16

有没有人知道如何在FFmpeg中按秒(或毫秒)实现查找功能。我目前正在使用av_read_frame()循环读取视频帧,并想确定这一帧应该在几秒钟内。如果它达到一定点,则希望跳转到视频的后面某个时间点。顺便说一下,这不是一个视频播放器,只是处理视频帧。我听说可以从packet中获取dts或pts,但它总是返回0。


1
请注意,在非“仅I帧”的编解码器中,进行跳转后的前几帧可能会出现垃圾数据,直到ffmpeg获取足够的信息来提供完整的帧。虽然我已经有一段时间没有尝试过了,但据我所知,这仍然是正确的。ffmpeg假定您要么是播放器,不关心一些帧出错,要么是直接处理并按顺序获取所有帧。如果不是这种情况,则可能会在MPEG4等方面遇到问题。 - wrosecrans
@wrosecrans 非常感谢您提供的信息。我遇到了一些像您解释的那样奇怪的问题,并且我学到了尝试寻找关键帧是正确的方法。如果您尝试寻找不是关键帧的视频部分,那么在一秒钟内会得到一些奇怪的输出。 - DRiFTy
1个回答

14

注意:这段代码已经过时,但仍然可以使用,现在有av_seek_frame()来正式实现。

以下是我拥有的示例代码:

bool seekMs(int tsms)
{
   //printf("**** SEEK TO ms %d. LLT: %d. LT: %d. LLF: %d. LF: %d. LastFrameOk: %d\n",tsms,LastLastFrameTime,LastFrameTime,LastLastFrameNumber,LastFrameNumber,(int)LastFrameOk);

   // Convert time into frame number
   DesiredFrameNumber = ffmpeg::av_rescale(tsms,pFormatCtx->streams[videoStream]->time_base.den,pFormatCtx->streams[videoStream]->time_base.num);
   DesiredFrameNumber/=1000;

   return seekFrame(DesiredFrameNumber);
}

bool seekFrame(ffmpeg::int64_t frame)
{

   //printf("**** seekFrame to %d. LLT: %d. LT: %d. LLF: %d. LF: %d. LastFrameOk: %d\n",(int)frame,LastLastFrameTime,LastFrameTime,LastLastFrameNumber,LastFrameNumber,(int)LastFrameOk);

   // Seek if:
   // - we don't know where we are (Ok=false)
   // - we know where we are but:
   //    - the desired frame is after the last decoded frame (this could be optimized: if the distance is small, calling decodeSeekFrame may be faster than seeking from the last key frame)
   //    - the desired frame is smaller or equal than the previous to the last decoded frame. Equal because if frame==LastLastFrameNumber we don't want the LastFrame, but the one before->we need to seek there
   if( (LastFrameOk==false) || ((LastFrameOk==true) && (frame<=LastLastFrameNumber || frame>LastFrameNumber) ) )
   {
      //printf("\t avformat_seek_file\n");
      if(ffmpeg::avformat_seek_file(pFormatCtx,videoStream,0,frame,frame,AVSEEK_FLAG_FRAME)<0)
         return false;

      avcodec_flush_buffers(pCodecCtx);

      DesiredFrameNumber = frame;
      LastFrameOk=false;
   }
   //printf("\t decodeSeekFrame\n");

   return decodeSeekFrame(frame);

   return true;
}

1
太棒了!感谢回复!我认为这看起来应该可以工作。我会尽快尝试并发布我的结果。我不知道avformat_seek_file,我一直在使用av_seek_frame……你知道它们的区别吗? - DRiFTy
1
Martin Beckett发布的方法确实有效,让我能够根据毫秒数向前搜索视频中的特定帧。 - DRiFTy
1
有人能展示一下 LastLastFrameNumberLastFrameNumber 是如何被设置或计算的吗? - Ryan
2
我不相信这段代码适用于除AVI以外的格式。虽然我没有测试过这段代码,但我观察到MOV文件的时间戳与帧数并不等同于这段代码所示。此外,“avformat_seek_file()”目前在文档中有一条注释,建议不要使用它。 - koan
1
使用固定帧数0,使用H265编码似乎对我无效。 - StarShine
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接