如何在 FFmpeg C/C++ 中寻找

Posted

技术标签:

【中文标题】如何在 FFmpeg C/C++ 中寻找【英文标题】:How to seek in FFmpeg C/C++ 【发布时间】:2011-07-12 19:27:44 【问题描述】:

有谁知道如何在 FFmpeg 中实现按秒(或毫秒)搜索。我目前有一个使用 av_read_frame() 遍历视频帧的循环,我想确定该帧应该在几秒钟内的时间。如果它到达某个点,那么我想在视频中寻找到稍后的点。顺便说一句,它不是视频播放器,只是处理帧。我听说我应该能够从数据包中获取 dts 或 pts,但它总是返回 0。

【问题讨论】:

请注意,在不是“仅 I Frame”的编解码器中,您可能会在搜索后的前几帧中得到垃圾,直到 ffmpeg 获得足够的信息来为您提供完整的帧。我已经有一段时间没有尝试过了,但是AFAIK,这仍然是真的。 ffmpeg 假设您是玩家,并且不关心出现错误的几帧,或者您正在直接处理并按顺序获取所有帧。如果不是这样,您可能会遇到 MPEG4 等问题。 @wrosecrans 非常感谢您提供的信息。正如你所解释的那样,我遇到了一些奇怪的事情,我了解到尝试寻找关键帧是要走的路。如果您尝试寻找不是关键帧的视频部分,那么您会在一秒钟内得到一些奇怪的输出。 【参考方案1】:

注意:这已经过时了,它应该仍然可以工作,但现在有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;

【讨论】:

太棒了!谢谢回复!我认为这看起来应该可行。我会在我回到我的电脑并发回我的结果后立即尝试一下。我不知道 avformat_seek_file,我使用的是 av_seek_frame...你知道有什么区别吗? Martin Beckett 发布的方法确实有效,并允许我在视频中以毫秒为单位向前搜索到特定帧。 有人可以说明LastLastFrameNumberLastFrameNumber 是如何设置或计算的吗? 我不相信这段代码适用于非 AVI 格式。我没有测试过代码,但我观察到时间戳不等同于 MOV 文件的帧号,正如这段代码所暗示的那样。另外avformat_seek_file() 目前在文档中有一条说明不要使用它。 使用 H265 的固定帧号 0 似乎对我不起作用。

以上是关于如何在 FFmpeg C/C++ 中寻找的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 FFmpeg (C/C++) 将原始 pcm_f32le 音频编码为 AAC 编码音频?

如何在Android用FFmpeg+SDL2.0解码显示图像

如何在ffmpeg中使用字节而不是文件路径?

FFMPEG 如何将 MJPEG 编码数据复用到 mp4 或 avi 容器 c++

FFmpeg集成

解码后的音频数据如何存储在 ffmpeg AVFrame 中?