16

Does anyone know how to implement seeking by seconds (or milliseconds) in FFmpeg. I currently have a loop running through the frames of a video using av_read_frame() and I want to determine what time this frame should be at in seconds. If it gets to a certain point then I want to seek to a later point in the video. By the way it is not a video player, just processing the frames. Ive heard I should be able to get the dts or pts from the packet but its always returning 0.

DRiFTy
  • 11,269
  • 11
  • 61
  • 77
  • 1
    Be aware that in a codec which isn't "I Frame only," you may get garbage for the first few frames after a seek until ffmpeg gets enough information to give you a full frame. It's been a while since I tried, but AFAIK, this is still true. ffmpeg assumes you are either a player, and don't care about a few frames going wrong, or you are processing straight through and getting all frames sequentially. If that isn't the case, you may run into issues on things like MPEG4. – wrosecrans Nov 04 '11 at 03:30
  • @wrosecrans Thanks a lot for that info. I came across some weirdness like you explained and I learned that trying to seek to a key frame was the way to go. If you try to seek to part of the video that isn't a key frame then you get some strange output for a second. – DRiFTy Nov 04 '11 at 12:29

1 Answers1

14

NOTE: This is out of date, it should still work but there is now av_seek_frame() to do it officially.

I didn't write this but here is some code from a sample I have

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;
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • 1
    Awesome! Thanks for the reply! I think this looks like it should work. I will give it a try as soon as I get back to my computer and post back my results. I didnt know about the avformat_seek_file, i was using av_seek_frame... do you know what the difference is by any chance? – DRiFTy Mar 10 '11 at 21:56
  • Sorry - I just did the video writing part of the code, I haven't even tested the reader. – Martin Beckett Mar 10 '11 at 22:08
  • 1
    The method that Martin Beckett posted did work, and allowed me to seek forward in the video to a specific frame based on milliseconds. – DRiFTy Mar 14 '11 at 16:39
  • 1
    Can someone please show how `LastLastFrameNumber` and `LastFrameNumber` are set or calculated? – Ryan Jun 15 '11 at 19:19
  • @Ryan you get the total number of frames (LastLast...) when you open the file - it's in the avi header. LastFrameNumber is just the previous frame you read. – Martin Beckett Jun 15 '11 at 19:22
  • So when you say AVI this method will only work on AVI files? Not a H264 MPEG? – Ryan Jun 15 '11 at 19:31
  • Avi is the container format which may contain a H264 source. FFMpeg also handles MP4 containers which also have the number of frames in the header. I don't know about ogg or mkv – Martin Beckett Jun 15 '11 at 19:33
  • Just a note that not all formats have the total number of frames (or even the total duration of the video) stored in them, so you can't always rely on that for every video. – Cornstalks Apr 04 '12 at 18:12
  • 2
    I don't believe this code will work for formats that are not AVI. I have not tested the code but I have observed timestamps are not equivalent to frame numbers with MOV files as this code implies. Also `avformat_seek_file()` currently has a note in the documentation saying not to use it. – koan Aug 02 '14 at 19:50
  • `av_seek_frame` seems to be the current way to seek. – Sam Sep 07 '15 at 09:14
  • 1
    doesn't seem to work for me using fixed frame number 0, using a H265. – StarShine Aug 22 '16 at 22:13