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

Michael Behrisch behrisch at informatik.hu-berlin.de
Wed Feb 25 10:28:48 CET 2004


On Sat, Feb 21, 2004 at 05:40:54PM +0100, Moritz Bunkus wrote:
> Heya,
> 
> some quick tests result:
> ...
> 
> Looks like this patch needs some refinement. But I DO appreciate your
> trying to get OGM seeking working properly.

I did not have any of those problems with the files I tested with
(but this includes only ogg vorbis and dvd backups made with mencoder
(lavc) and ogmmerge). Anyway, I did some improvements (hopefully).
If it still does not work, can you give me a link where to find
good test instances or put some of yours on public webspace.
If your tests include Theora, I suppose they will not work, but I
think theora seeking does not even work with the current mplayer
(at least when -forceidx is used).

BTW Thanks for encouraging me

Michael

-------------- next part --------------
--- libmpdemux/demux_ogg.old	2004-02-02 10:58:12.000000000 +0100
+++ libmpdemux/demux_ogg.c	2004-02-25 07:30:15.000000000 +0100
@@ -1072,10 +1072,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,21 +1089,33 @@
   os = &ogg_d->subs[ds->id];
   oss = &os->stream;
 
-  if(ogg_d->syncpoints) {
+  old_gp = os->lastpos;
+  old_pos = ogg_d->pos;
+
     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;
 
+  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)
@@ -1114,12 +1126,12 @@
     else
       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 +1140,81 @@
   }
   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) {
+        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
+          pos = ogg_d->pos + (gp - op.granulepos) * ogg_d->pos / op.granulepos;
+        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) {
+        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