Try out a new seek bisect optimization in vorbisfile.
[platform/upstream/libvorbis.git] / lib / vorbisfile.c
index e1682d6..a3569c9 100644 (file)
@@ -22,6 +22,9 @@
 #include <math.h>
 
 #include "vorbis/codec.h"
+
+/* we don't need or want the static callback symbols here */
+#define OV_EXCLUDE_STATIC_CALLBACKS
 #include "vorbis/vorbisfile.h"
 
 #include "os.h"
 
 /* read a little more data from the file/pipe into the ogg_sync framer
 */
-#define CHUNKSIZE 65536
+#define CHUNKSIZE 65536 /* greater-than-page-size granularity seeking */
+#define READSIZE 2048 /* a smaller read size is needed for low-rate streaming. */
 
 static long _get_data(OggVorbis_File *vf){
   errno=0;
   if(!(vf->callbacks.read_func))return(-1);
   if(vf->datasource){
-    char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE);
-    long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource);
+    char *buffer=ogg_sync_buffer(&vf->oy,READSIZE);
+    long bytes=(vf->callbacks.read_func)(buffer,1,READSIZE,vf->datasource);
     if(bytes>0)ogg_sync_wrote(&vf->oy,bytes);
     if(bytes==0 && errno)return(-1);
     return(bytes);
@@ -226,8 +230,8 @@ static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf,
 
   ogg_int64_t prefoffset=-1;
   ogg_int64_t offset=-1;
-  ogg_int64_t ret_serialno;
-  ogg_int64_t ret_gran;
+  ogg_int64_t ret_serialno=-1;
+  ogg_int64_t ret_gran=-1;
 
   while(offset==-1){
     begin-=CHUNKSIZE;
@@ -675,8 +679,13 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
   /* extract packets from page */
   while(1){
 
-    /* process a packet if we can.  If the machine isn't loaded,
-       neither is a page */
+    if(vf->ready_state==STREAMSET){
+      int ret=_make_decode_ready(vf);
+      if(ret<0)return ret;
+    }
+
+    /* process a packet if we can. */
+
     if(vf->ready_state==INITSET){
       while(1) {
               ogg_packet op;
@@ -844,11 +853,6 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
           link=0;
         }
       }
-
-      {
-        int ret=_make_decode_ready(vf);
-        if(ret<0)return ret;
-      }
     }
 
     /* the buffered page is the data we want, and we're ready for it;
@@ -1255,7 +1259,9 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
     int lastblock=0;
     int accblock=0;
     int thisblock=0;
-    int eosflag=0;
+    int lastflag=0;
+    int firstflag=0;
+    ogg_int64_t pagepos=-1;
 
     ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */
     ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE
@@ -1276,7 +1282,14 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
               thisblock=0;
             }else{
 
-              if(eosflag)
+              /* We can't get a guaranteed correct pcm position out of the
+                 last page in a stream because it might have a 'short'
+                 granpos, which can only be detected in the presence of a
+                 preceeding page.  However, if the last page is also the first
+                 page, the granpos rules of a first page take precedence.  Not
+                 only that, but for first==last, the EOS page must be treated
+                 as if its a normal first page for the stream to open/play. */
+              if(lastflag && !firstflag)
                 ogg_stream_packetout(&vf->os,NULL);
               else
                 if(lastblock)accblock+=(lastblock+thisblock)>>2;
@@ -1290,6 +1303,7 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
               for(i=0;i<link;i++)
                 granulepos+=vf->pcmlengths[i*2+1];
               vf->pcm_offset=granulepos-accblock;
+              if(vf->pcm_offset<0)vf->pcm_offset=0;
               break;
             }
             lastblock=thisblock;
@@ -1300,7 +1314,8 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
       }
 
       if(!lastblock){
-        if(_get_next_page(vf,&og,-1)<0){
+        pagepos=_get_next_page(vf,&og,-1);
+        if(pagepos<0){
           vf->pcm_offset=ov_pcm_total(vf,-1);
           break;
         }
@@ -1341,12 +1356,13 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
         ogg_stream_reset_serialno(&vf->os,serialno);
         ogg_stream_reset_serialno(&work_os,serialno);
         vf->ready_state=STREAMSET;
-
+        firstflag=(pagepos<=vf->dataoffsets[link]);
       }
 
       ogg_stream_pagein(&vf->os,&og);
       ogg_stream_pagein(&work_os,&og);
-      eosflag=ogg_page_eos(&og);
+      lastflag=ogg_page_eos(&og);
+
     }
   }
 
@@ -1411,13 +1427,14 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
         bisect=begin +
           (ogg_int64_t)((double)(target-begintime)*(end-begin)/(endtime-begintime))
           - CHUNKSIZE;
-        if(bisect<=begin)
-          bisect=begin+1;
+        if(bisect>begin+CHUNKSIZE){
+          result=_seek_helper(vf,bisect);
+          if(result) goto seek_error;
+        }else{
+          bisect=begin;
+        }
       }
 
-      result=_seek_helper(vf,bisect);
-      if(result) goto seek_error;
-
       while(begin<end){
         result=_get_next_page(vf,&og,end-vf->offset);
         if(result==OV_EREAD) goto seek_error;