[MPlayer-dev-eng] [Patch] Even better seeking in ogg ;-)

Michael Behrisch behrisch at informatik.hu-berlin.de
Fri Feb 27 15:18:14 CET 2004


On Wed, Feb 25, 2004 at 09:47:36PM +0100, Moritz Bunkus wrote:
> Heya,
> 
> hmm...
> 
> http://www.bunkus.org/bob-short.ogm
> (Upload will be done in 10minutes, it's a 20MB sample, over a minute long)
> 
> Without -forceidx seeking works fine, but mplayer can segfault when you
> seek backwards to before the start.

fixed, annoying division by zero if final_granulepos is not set.
I made the range for seeking for final_granulepos more than three times 
as big (270K). Is this OK? How far can one go? In the file You gave are 
granulepos dists up to 400K. If this is also fine please increase the 270000
further.

> 
> With -forceidx seeking does not work at all: mplayer always jumps to the
> start.
> 

Your file starts with invalid granulepos. I made the scanner jump over the 
first if it is larger than the second. Please try again also with the
File 1 from the last mail. I think it is the same problem with an invalid 
granulepos, although I do not know whether the first granulepos may occur 
as late as 12s. So maybe there are invalid granulepos also later in ogm-files?

Michael
-------------- next part --------------
--- libmpdemux/demux_ogg.old	2004-02-02 10:58:12.000000000 +0100
+++ libmpdemux/demux_ogg.c	2004-02-26 21:45:12.000000000 +0100
@@ -444,7 +444,8 @@
   return 1;
 }
 
-/// Build a table of all syncpoints to make seeking easier
+/// if -forceidx build a table of all syncpoints to make seeking easier
+/// otherwise try to get at least the final_granulepos
 void demux_ogg_scan_stream(demuxer_t* demuxer) {
   ogg_demuxer_t* ogg_d = demuxer->priv;
   stream_t *s = demuxer->stream;
@@ -463,7 +464,8 @@
   stream_seek(s,demuxer->movi_start);
   }
   else {
-    stream_seek(s,demuxer->movi_end-20*BLOCK_SIZE);
+    //the 270000 are just a wild guess
+    stream_seek(s,max(demuxer->movi_start,demuxer->movi_end-270000));
   }
   ogg_sync_reset(sync);
 
@@ -516,7 +518,12 @@
       demux_ogg_read_packet(os,&op,context,&pts,&flags);
       if(flags || (os->vorbis && op.granulepos >= 0)) {
         if(index_mode == 2) {
+          if(ogg_d->num_syncpoint == 1 && ogg_d->syncpoints[0].granulepos > op.granulepos) {
+            ogg_d->num_syncpoint--;
+            mp_msg(MSGT_DEMUX,MSGL_INFO,"Invalid granulepos %lld at start\n",ogg_d->syncpoints[0].granulepos);
+          } else {
 	ogg_d->syncpoints = (ogg_syncpoint_t*)realloc(ogg_d->syncpoints,(ogg_d->num_syncpoint+1)*sizeof(ogg_syncpoint_t));
+          }
 	ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos = op.granulepos;
 	ogg_d->syncpoints[ogg_d->num_syncpoint].page_pos = (ogg_page_continued(page) && p == 0) ? last_pos : pos;
 	ogg_d->num_syncpoint++;
@@ -1072,10 +1079,10 @@
   sh_audio_t* sh_audio = demuxer->audio->sh;
   ogg_packet op;
   float rate;
-  int i,sp,first;
+  int i,sp,first,precision=1,do_seek=1;
   vorbis_info* vi = NULL;
-  int64_t gp = 0;
-  off_t pos;
+  int64_t gp = 0, old_gp;
+  off_t pos, old_pos;
 
   if(demuxer->video->id >= 0) {
     ds = demuxer->video;
@@ -1089,37 +1096,52 @@
   os = &ogg_d->subs[ds->id];
   oss = &os->stream;
 
-  if(ogg_d->syncpoints) {
+  old_gp = os->lastpos;
+  old_pos = ogg_d->pos;
+
+  //calculate the granulepos to seek to
     gp = flags & 1 ? 0 : os->lastpos;
-    if(flags & 2)
-      gp += ogg_d->syncpoints[ogg_d->num_syncpoint].granulepos * rel_seek_secs;
+  if(flags & 2) {
+    if (ogg_d->final_granulepos > 0)
+      gp += ogg_d->final_granulepos * rel_seek_secs;
       else
+      gp += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) * os->lastpos / ogg_d->pos;
+  } else
       gp += rel_seek_secs * rate;
+  if (gp < 0) gp = 0;
 
+  //calculate the filepos to seek to
+  if(ogg_d->syncpoints) {
     for(sp = 0; sp < ogg_d->num_syncpoint ; sp++) {
       if(ogg_d->syncpoints[sp].granulepos >= gp) break;
     }
 
-    if(sp >= ogg_d->num_syncpoint)
-      return;
+    if(sp >= ogg_d->num_syncpoint) return;
+    if (sp > 0 && ogg_d->syncpoints[sp].granulepos - gp > gp - ogg_d->syncpoints[sp-1].granulepos)
+      sp--;
+    if (ogg_d->syncpoints[sp].granulepos == os->lastpos) {
+      if (sp > 0 && gp < os->lastpos) sp--;
+      if (sp < ogg_d->num_syncpoint-1 && gp > os->lastpos) sp++;
+    }
     pos = ogg_d->syncpoints[sp].page_pos;
-
+    precision = 0;
   } else {
     pos = flags & 1 ? 0 : ogg_d->pos;
     if(flags & 2)
       pos += (demuxer->movi_end - demuxer->movi_start) * rel_seek_secs;
     else {
-      if (ogg_d->final_granulepos > 0)
+      if (ogg_d->final_granulepos > 0) {
         pos += rel_seek_secs * (demuxer->movi_end - demuxer->movi_start) / (ogg_d->final_granulepos / rate);
-    else
+      } else if (os->lastpos > 0) {
       pos += rel_seek_secs * ogg_d->pos / (os->lastpos / rate);
     }
-    if (pos < 0)
-      pos = 0;
-    else if (pos > (demuxer->movi_end - demuxer->movi_start))
-      return;
   }
+    if (pos < 0) pos = 0;
+    if (pos > (demuxer->movi_end - demuxer->movi_start)) return;
+  } // if(ogg_d->syncpoints)
 
+  while(1) {
+    if (do_seek) {
   stream_seek(demuxer->stream,pos+demuxer->movi_start);
   ogg_sync_reset(sync);
   for(i = 0 ; i < ogg_d->num_sub ; i++) {
@@ -1128,68 +1150,87 @@
   }
   ogg_d->pos = pos;
   ogg_d->last_size = 0;
-
+      /* we just guess that we reached correct granulepos, in case a
+         subsequent search occurs before we read a valid granulepos */
+      os->lastpos = gp;
   first = 1;
-  while(1) {
+      do_seek=0;
+    }
     int np;
     ogg_d->pos += ogg_d->last_size;
     ogg_d->last_size = 0;
     np = ogg_sync_pageseek(sync,page);
 
-    if(np < 0)
-      ogg_d->pos -= np;
+    if(np < 0) ogg_d->pos -= np;
     if(np <= 0) { // We need more data
       char* buf = ogg_sync_buffer(sync,BLOCK_SIZE);
       int len = stream_read(demuxer->stream,buf,BLOCK_SIZE);
        if(len == 0 && demuxer->stream->eof) {
-	mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to seek !!!!\n");
+        mp_msg(MSGT_DEMUX,MSGL_ERR,"EOF while trying to seek!\n");
 	break;
       }
       ogg_sync_wrote(sync,len);
       continue;
     }
     ogg_d->last_size = np;
-    if(ogg_page_serialno(page) != oss->serialno)
-      continue;
-
-    if(ogg_stream_pagein(oss,page) != 0)
+    if(ogg_page_serialno(page) != oss->serialno || ogg_stream_pagein(oss,page) != 0)
       continue;
 
      while(1) {
       np = ogg_stream_packetout(oss,&op);
-      if(np < 0)
-	continue;
-      else if(np == 0)
-	break;
+      if(np < 0) continue;
+      if(np == 0) break;
       if (first) { /* Discard the first packet as it's probably broken,
            and we don't have any other means to decide whether it is
            complete or not. */
         first = 0;
         break;
       }
-      
+      if (op.granulepos >= 0) os->lastpos = op.granulepos;
+      if (precision && op.granulepos >= 0) {
+        //prepare another seek because we are probably not precise enough
+        precision--;
+        if (ogg_d->final_granulepos > 0) {
+          pos = ogg_d->pos + (gp - op.granulepos)
+                 * (demuxer->movi_end - demuxer->movi_start) / ogg_d->final_granulepos;
+        } else if (old_gp > 0) {
+          pos = ogg_d->pos + (gp - op.granulepos) * old_pos / old_gp;
+        }
+        if (pos < 0) pos = 0;
+        if (pos < (demuxer->movi_end - demuxer->movi_start)) {
+          do_seek=1;
+          break;
+        }
+      }
+      if (op.granulepos >= 0 && pos > 0 && pos < old_pos
+          && 2 * (old_gp - op.granulepos) < old_gp - gp) {
+        /* prepare another seek because looking for a syncpoint
+           destroyed the backward search */
+        pos = old_pos - 1.5 * (old_pos - pos);
+        if (pos < 0) pos = 0;
+        if (pos < (demuxer->movi_end - demuxer->movi_start)) {
+          do_seek=1;
+          break;
+        }
+      }
       /* the detection of keyframes for theora is somewhat a hack: granulepos
        for theora packets is `keyframeNumber<<shift | iframeNumber'; `shift'
        is *variable*, with its excact value encoded in the theora header.
        This code just hopes that it is greater than 9 and that keyframes
        distance will never overflow 512. */
-      if( (((*op.packet & PACKET_IS_SYNCPOINT) && !os->theora) || 
-	   os->vorbis || (os->theora && (op.granulepos&0x1FF) == 0)) &&
-	  (!ogg_d->syncpoints || op.granulepos >= gp) ) {
+      if(!precision && ( ((*op.packet & PACKET_IS_SYNCPOINT) && !os->theora) ||
+           os->vorbis || (os->theora && (op.granulepos&0x1FF) == 0) ) ) {
         ogg_sub.lines = 0;
         vo_sub = &ogg_sub;
         vo_osd_changed(OSDTYPE_SUBTITLE);
         clear_sub = -1;
 	demux_ogg_add_packet(ds,os,&op);
-	if(sh_audio)
-	  resync_audio_stream(sh_audio); 
+        if(sh_audio) resync_audio_stream(sh_audio);
 	return;
       }
      }
   }
-
   mp_msg(MSGT_DEMUX,MSGL_ERR,"Can't find the good packet :(\n");  
-
 }
 
 void demux_close_ogg(demuxer_t* demuxer) {


More information about the MPlayer-dev-eng mailing list