[xiph-cvs] cvs commit: vorbis/lib block.c synthesis.c vorbisfile.c

Monty xiphmont at xiph.org
Sat May 26 22:52:24 PDT 2001



xiphmont    01/05/26 22:52:23

  Modified:    examples Tag: monty-branch-20010404 seeking_example.c
               include/vorbis Tag: monty-branch-20010404 codec.h
                        vorbisfile.h
               lib      Tag: monty-branch-20010404 block.c synthesis.c
                        vorbisfile.c
  Log:
  New seeking example/tets script (that will find both vorbisfile bugs
  and bad bitstreams)
  
  Completed vorbisfile optimizations, many seeking bugfixes.
  
  Fixed one bug in libvorbis (vorbis_synthesis_pcmout gave an invalid
  positive answer if vorbis_synthesis_blockin had not yet been called)
  
  Monty

Revision  Changes    Path
No                   revision

No                   revision

1.7.4.1   +145 -12   vorbis/examples/seeking_example.c

Index: seeking_example.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis/examples/seeking_example.c,v
retrieving revision 1.7
retrieving revision 1.7.4.1
diff -u -r1.7 -r1.7.4.1
--- seeking_example.c	2001/02/26 03:50:38	1.7
+++ seeking_example.c	2001/05/27 05:52:20	1.7.4.1
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: illustrate seeking, and test it too
- last mod: $Id: seeking_example.c,v 1.7 2001/02/26 03:50:38 xiphmont Exp $
+ last mod: $Id: seeking_example.c,v 1.7.4.1 2001/05/27 05:52:20 xiphmont Exp $
 
  ********************************************************************/
 
@@ -21,28 +21,161 @@
 #include "vorbis/vorbisfile.h"
 #include "../lib/misc.h"
 
+
+void _verify(OggVorbis_File *ov,ogg_int64_t pos,
+	     ogg_int64_t val,ogg_int64_t pcmval,
+	     ogg_int64_t pcmlength,
+	     char *bigassbuffer){
+  int j;
+  long bread;
+  char buffer[4096];
+  int dummy;
+
+  /* verify the raw position, the pcm position and position decode */
+  if(val!=-1 && ov_raw_tell(ov)<val){
+    printf("raw position out of tolerance: requested %ld, got %ld\n",
+	   (long)val,(long)ov_raw_tell(ov));
+    exit(1);
+  }
+  if(pcmval!=-1 && ov_pcm_tell(ov)>pcmval){
+    printf("pcm position out of tolerance: requested %ld, got %ld\n",
+	   (long)pcmval,(long)ov_pcm_tell(ov));
+    exit(1);
+  }
+  pos=ov_pcm_tell(ov);
+  if(pos<0 || pos>pcmlength){
+    printf("pcm position out of bounds: got %ld\n",(long)pos);
+    exit(1);
+  }
+  bread=ov_read(ov,buffer,4096,1,1,1,&dummy);
+  for(j=0;j<bread;j++){
+    if(buffer[j]!=bigassbuffer[j+pos*2]){
+      printf("data position after seek doesn't match pcm position\n");
+      exit(1);
+    }
+  }
+}
+
 int main(){
   OggVorbis_File ov;
-  int i;
+  int i,ret;
+  ogg_int64_t pcmlength;
+  char *bigassbuffer;
+  int dummy;
 
   /* open the file/pipe on stdin */
   if(ov_open(stdin,&ov,NULL,-1)<0){
     printf("Could not open input as an OggVorbis file.\n\n");
     exit(1);
   }
-  
-  /* print details about each logical bitstream in the input */
+
   if(ov_seekable(&ov)){
-    double length=ov_time_total(&ov,-1);
-    printf("testing seeking to random places in %g seconds....\n",length);
-    for(i=0;i<100;i++){
-      double val=(double)rand()/RAND_MAX*length;
-      ov_time_seek(&ov,val);
-      printf("\r\t%d [%gs]...     ",i,val);
-      fflush(stdout);
+
+    /* to simplify our own lives, we want to assume the whole file is
+       stereo.  Verify this to avoid potentially mystifying users
+       (pissing them off is OK, just don't confuse them) */
+    for(i=0;i<ov.links;i++){
+      vorbis_info *vi=ov_info(&ov,i);
+      if(vi->channels!=2){
+	printf("Sorry; right now seeking_test can only use Vorbis files\n"
+	       "that are entirely stereo.\n\n");
+	exit(1);
+      }
+    }
+    
+    /* because we want to do sample-level verification that the seek
+       does what it claimed, decode the entire file into memory */
+    printf("loading....\n");
+    fflush(stdout);
+    pcmlength=ov_pcm_total(&ov,-1);
+    bigassbuffer=malloc(pcmlength*2); /* w00t */
+    i=0;
+    while(i<pcmlength*2){
+      int ret=ov_read(&ov,bigassbuffer+i,pcmlength*2-i,1,1,1,&dummy);
+      if(ret<0)continue;
+      if(ret){
+	i+=ret;
+      }else{
+	pcmlength=i/2;
+      }
+    }
+    
+    /* Exercise all the real seeking cases; ov_raw_seek,
+       ov_pcm_seek_page and ov_pcm_seek.  time seek is just a wrapper
+       on pcm_seek */
+    {
+      ogg_int64_t length=ov.end;
+      printf("testing raw seeking to random places in %ld bytes....\n",
+	     (long)length);
+    
+      for(i=0;i<1000;i++){
+	ogg_int64_t val=(double)rand()/RAND_MAX*length;
+	ogg_int64_t pos;
+	printf("\r\t%d [raw position %ld]...     ",i,(long)val);
+	fflush(stdout);
+	ret=ov_raw_seek(&ov,val);
+	if(ret<0){
+	  printf("seek failed: %d\n",ret);
+	  exit(1);
+	}
+
+	_verify(&ov,pos,val,-1,pcmlength,bigassbuffer);
+
+      }
+    }
+
+    printf("\r");
+    {
+      ogg_int64_t length=ov.end;
+      printf("testing pcm page seeking to random places in %ld samples....\n",
+	     (long)pcmlength);
+    
+      for(i=0;i<1000;i++){
+	ogg_int64_t val=(double)rand()/RAND_MAX*pcmlength;
+	ogg_int64_t pos;
+	printf("\r\t%d [pcm position %ld]...     ",i,(long)val);
+	fflush(stdout);
+	ret=ov_pcm_seek_page(&ov,val);
+	if(ret<0){
+	  printf("seek failed: %d\n",ret);
+	  exit(1);
+	}
+
+	_verify(&ov,pos,-1,val,pcmlength,bigassbuffer);
+
+      }
+    }
+    
+    printf("\r");
+    {
+      ogg_int64_t length=ov.end;
+      printf("testing pcm exact seeking to random places in %ld samples....\n",
+	     (long)pcmlength);
+    
+      for(i=0;i<1000;i++){
+	ogg_int64_t val=(double)rand()/RAND_MAX*pcmlength;
+	ogg_int64_t pos;
+	printf("\r\t%d [pcm position %ld]...     ",i,(long)val);
+	fflush(stdout);
+	ret=ov_pcm_seek(&ov,val);
+	if(ret<0){
+	  printf("seek failed: %d\n",ret);
+	  exit(1);
+	}
+	if(ov_pcm_tell(&ov)!=val){
+	  printf("Decalred position didn't perfectly match request: %ld != %ld\n",
+		 (long)val,(long)ov_pcm_tell(&ov));
+	  exit(1);
+	}
+
+	_verify(&ov,pos,-1,val,pcmlength,bigassbuffer);
+
+      }
     }
     
-    printf("\r                                   \nOK.\n\n");
+    printf("\r                                           \nOK.\n\n");
+
+
   }else{
     printf("Standard input was not seekable.\n");
   }

No                   revision

No                   revision

1.37.4.1  +2 -1      vorbis/include/vorbis/codec.h

Index: codec.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis/include/vorbis/codec.h,v
retrieving revision 1.37
retrieving revision 1.37.4.1
diff -u -r1.37 -r1.37.4.1
--- codec.h	2001/02/26 03:50:39	1.37
+++ codec.h	2001/05/27 05:52:21	1.37.4.1
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: libvorbis codec headers
- last mod: $Id: codec.h,v 1.37 2001/02/26 03:50:39 xiphmont Exp $
+ last mod: $Id: codec.h,v 1.37.4.1 2001/05/27 05:52:21 xiphmont Exp $
 
  ********************************************************************/
 
@@ -199,6 +199,7 @@
 extern int      vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
 extern int      vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm);
 extern int      vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
+extern long     vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op);
 
 /* Vorbis ERRORS and return codes ***********************************/
 

1.13.4.1  +22 -12    vorbis/include/vorbis/vorbisfile.h

Index: vorbisfile.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis/include/vorbis/vorbisfile.h,v
retrieving revision 1.13
retrieving revision 1.13.4.1
diff -u -r1.13 -r1.13.4.1
--- vorbisfile.h	2001/02/26 03:50:39	1.13
+++ vorbisfile.h	2001/05/27 05:52:21	1.13.4.1
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.h,v 1.13 2001/02/26 03:50:39 xiphmont Exp $
+ last mod: $Id: vorbisfile.h,v 1.13.4.1 2001/05/27 05:52:21 xiphmont Exp $
 
  ********************************************************************/
 
@@ -43,27 +43,32 @@
   long   (*tell_func)  (void *datasource);
 } ov_callbacks;
 
+#define  NOTOPEN   0
+#define  PARTOPEN  1
+#define  OPENED    2
+#define  STREAMSET 3
+#define  INITSET   4
 
 typedef struct OggVorbis_File {
-  void             *datasource; /* Pointer to a FILE *, etc. */
+  void            *datasource; /* Pointer to a FILE *, etc. */
   int              seekable;
-  ogg_int64_t          offset;
-  ogg_int64_t          end;
+  ogg_int64_t      offset;
+  ogg_int64_t      end;
   ogg_sync_state   oy; 
 
   /* If the FILE handle isn't seekable (eg, a pipe), only the current
      stream appears */
   int              links;
-  ogg_int64_t          *offsets;
-  ogg_int64_t          *dataoffsets;
-  long             *serialnos;
-  ogg_int64_t          *pcmlengths;
-  vorbis_info      *vi;
-  vorbis_comment   *vc;
+  ogg_int64_t     *offsets;
+  ogg_int64_t     *dataoffsets;
+  long            *serialnos;
+  ogg_int64_t     *pcmlengths;
+  vorbis_info     *vi;
+  vorbis_comment  *vc;
 
   /* Decoding working state local storage */
-  ogg_int64_t          pcm_offset;
-  int              decode_ready;
+  ogg_int64_t      pcm_offset;
+  int              ready_state;
   long             current_serialno;
   int              current_link;
 
@@ -83,6 +88,11 @@
 extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
 extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf,
                 char *initial, long ibytes, ov_callbacks callbacks);
+
+extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes);
+extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf,
+		char *initial, long ibytes, ov_callbacks callbacks);
+extern int ov_test_open(OggVorbis_File *vf);
 
 extern long ov_bitrate(OggVorbis_File *vf,int i);
 extern long ov_bitrate_instant(OggVorbis_File *vf);

No                   revision

No                   revision

1.47.4.1  +2 -2      vorbis/lib/block.c

Index: block.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis/lib/block.c,v
retrieving revision 1.47
retrieving revision 1.47.4.1
diff -u -r1.47 -r1.47.4.1
--- block.c	2001/03/26 23:27:42	1.47
+++ block.c	2001/05/27 05:52:22	1.47.4.1
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: PCM data vector blocking, windowing and dis/reassembly
- last mod: $Id: block.c,v 1.47 2001/03/26 23:27:42 xiphmont Exp $
+ last mod: $Id: block.c,v 1.47.4.1 2001/05/27 05:52:22 xiphmont Exp $
 
  Handle windowing, overlap-add, etc of the PCM vectors.  This is made
  more amusing by Vorbis' current two allowed block sizes.
@@ -787,7 +787,7 @@
 /* pcm==NULL indicates we just want the pending samples, no more */
 int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){
   vorbis_info *vi=v->vi;
-  if(v->pcm_returned<v->centerW){
+  if(v->pcm_returned>-1 && v->pcm_returned<v->centerW){
     if(pcm){
       int i;
       for(i=0;i<vi->channels;i++)

1.21.4.1  +29 -1     vorbis/lib/synthesis.c

Index: synthesis.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis/lib/synthesis.c,v
retrieving revision 1.21
retrieving revision 1.21.4.1
diff -u -r1.21 -r1.21.4.1
--- synthesis.c	2001/02/26 03:50:43	1.21
+++ synthesis.c	2001/05/27 05:52:22	1.21.4.1
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: single-block PCM synthesis
- last mod: $Id: synthesis.c,v 1.21 2001/02/26 03:50:43 xiphmont Exp $
+ last mod: $Id: synthesis.c,v 1.21.4.1 2001/05/27 05:52:22 xiphmont Exp $
 
  ********************************************************************/
 
@@ -70,6 +70,34 @@
   type=ci->map_type[ci->mode_param[mode]->mapping];
 
   return(_mapping_P[type]->inverse(vb,b->mode[mode]));
+}
+
+long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){
+  codec_setup_info     *ci=vi->codec_setup;
+  oggpack_buffer       opb;
+  int                  type,mode,i;
+ 
+  oggpack_readinit(&opb,op->packet,op->bytes);
+
+  /* Check the packet type */
+  if(oggpack_read(&opb,1)!=0){
+    /* Oops.  This is not an audio data packet */
+    return(OV_ENOTAUDIO);
+  }
+
+  {
+    int modebits=0;
+    int v=ci->modes;
+    while(v>1){
+      modebits++;
+      v>>=1;
+    }
+
+    /* read our mode and pre/post windowsize */
+    mode=oggpack_read(&opb,modebits);
+  }
+  if(mode==-1)return(OV_EBADPACKET);
+  return(ci->blocksizes[ci->mode_param[mode]->blockflag]);
 }
 
 

1.44.4.1  +463 -215  vorbis/lib/vorbisfile.c

Index: vorbisfile.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis/lib/vorbisfile.c,v
retrieving revision 1.44
retrieving revision 1.44.4.1
diff -u -r1.44 -r1.44.4.1
--- vorbisfile.c	2001/03/27 07:04:51	1.44
+++ vorbisfile.c	2001/05/27 05:52:22	1.44.4.1
@@ -7,11 +7,11 @@
  *                                                                  *
  * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
  * by the XIPHOPHORUS Company http://www.xiph.org/                  *
-
+ *                                                                  *
  ********************************************************************
 
  function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.c,v 1.44 2001/03/27 07:04:51 xiphmont Exp $
+ last mod: $Id: vorbisfile.c,v 1.44.4.1 2001/05/27 05:52:22 xiphmont Exp $
 
  ********************************************************************/
 
@@ -57,7 +57,8 @@
  * grokking near the end of the file */
 
 /* read a little more data from the file/pipe into the ogg_sync framer */
-#define CHUNKSIZE 4096
+#define CHUNKSIZE 8500 /* a shade over 8k; anyone using pages well
+                          over 8k gets what they deserve */
 static long _get_data(OggVorbis_File *vf){
   errno=0;
   if(vf->datasource){
@@ -233,6 +234,7 @@
 
   if(serialno)*serialno=ogg_page_serialno(og_ptr);
   ogg_stream_init(&vf->os,ogg_page_serialno(og_ptr));
+  vf->ready_state=STREAMSET;
   
   /* extract the initial header from the first page and verify that the
      Ogg bitstream is in fact Vorbis data */
@@ -279,24 +281,19 @@
    able to open and use damaged bitstreams as well as we can.  Just
    watch out for missing information for links in the OggVorbis_File
    struct */
-static void _prefetch_all_headers(OggVorbis_File *vf,vorbis_info *first_i,
-				  vorbis_comment *first_c,
-				  long dataoffset){
+static void _prefetch_all_headers(OggVorbis_File *vf, long dataoffset){
   ogg_page og;
   int i,ret;
   
-  vf->vi=_ogg_calloc(vf->links,sizeof(vorbis_info));
-  vf->vc=_ogg_calloc(vf->links,sizeof(vorbis_info));
+  vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(vorbis_info));
+  vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(vorbis_info));
   vf->dataoffsets=_ogg_malloc(vf->links*sizeof(ogg_int64_t));
   vf->pcmlengths=_ogg_malloc(vf->links*sizeof(ogg_int64_t));
   vf->serialnos=_ogg_malloc(vf->links*sizeof(long));
   
   for(i=0;i<vf->links;i++){
-    if(first_i && first_c && i==0){
-      /* we already grabbed the initial header earlier.  This just
-         saves the waste of grabbing it again */
-      memcpy(vf->vi+i,first_i,sizeof(vorbis_info));
-      memcpy(vf->vc+i,first_c,sizeof(vorbis_comment));
+    if(i==0){
+      /* we already grabbed the initial header earlier.  Just set the offset */
       vf->dataoffsets[i]=dataoffset;
     }else{
 
@@ -320,7 +317,7 @@
       while(1){
         ret=_get_prev_page(vf,&og);
         if(ret<0){
-	  /* this should not be possible, actually */
+	  /* this should not be possible */
           vorbis_info_clear(vf->vi+i);
           vorbis_comment_clear(vf->vc+i);
           break;
@@ -330,39 +327,32 @@
           vf->pcmlengths[i]=ogg_page_granulepos(&og);
           break;
         }
+	vf->offset=ret;
       }
     }
   }
 }
 
 static void _make_decode_ready(OggVorbis_File *vf){
-  if(vf->decode_ready)return;
+  if(vf->ready_state!=STREAMSET)return;
   if(vf->seekable){
     vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link);
   }else{
     vorbis_synthesis_init(&vf->vd,vf->vi);
   }    
   vorbis_block_init(&vf->vd,&vf->vb);
-  vf->decode_ready=1;
+  vf->ready_state=INITSET;
   return;
 }
 
-static int _open_seekable(OggVorbis_File *vf){
-  vorbis_info initial_i;
-  vorbis_comment initial_c;
-  long serialno,end;
-  int ret;
-  long dataoffset;
+static int _open_seekable2(OggVorbis_File *vf){
+  long serialno=vf->current_serialno,end;
+  long dataoffset=vf->offset;
   ogg_page og;
-  
-  /* is this even vorbis...? */
-  ret=_fetch_headers(vf,&initial_i,&initial_c,&serialno,NULL);
-  dataoffset=vf->offset;
-  ogg_stream_clear(&vf->os);
-  if(ret<0)return(ret);
-  
+
+  /* we're partially open and have a first link header state in
+     storage in vf */
   /* we can seek, so set out learning all about this file */
-  vf->seekable=1;
   (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
   vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
   
@@ -370,7 +360,7 @@
      Most OggVorbis files will contain a single logical bitstream */
   end=_get_prev_page(vf,&og);
   if(end<0){
-    ogg_stream_clear(&vf->os);
+    ov_clear(vf);
     return(end);
   }
 
@@ -380,7 +370,7 @@
     /* Chained bitstream. Bisect-search each logical bitstream
        section.  Do so based on serial number only */
     if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0){
-      ogg_stream_clear(&vf->os);
+      ov_clear(vf);
       return(OV_EREAD);
     }
 
@@ -388,30 +378,15 @@
 
     /* Only one logical bitstream */
     if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0)){
-      ogg_stream_clear(&vf->os);
+      ov_clear(vf);
       return(OV_EREAD);
     }
 
   }
 
-  _prefetch_all_headers(vf,&initial_i,&initial_c,dataoffset);
+  /* the initial header memory is referenced by vf after; don't free it */
+  _prefetch_all_headers(vf,dataoffset);
   return(ov_raw_seek(vf,0));
-
-}
-
-static int _open_nonseekable(OggVorbis_File *vf){
-  int ret;
-  /* we cannot seek. Set up a 'single' (current) logical bitstream entry  */
-  vf->links=1;
-  vf->vi=_ogg_calloc(vf->links,sizeof(vorbis_info));
-  vf->vc=_ogg_calloc(vf->links,sizeof(vorbis_info));
-
-  /* Try to fetch the headers, maintaining all the storage */
-  if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0)
-    return(ret);
-  _make_decode_ready(vf);
-
-  return 0;
 }
 
 /* clear out the current logical bitstream decoder */ 
@@ -419,7 +394,7 @@
   ogg_stream_clear(&vf->os);
   vorbis_dsp_clear(&vf->vd);
   vorbis_block_clear(&vf->vb);
-  vf->decode_ready=0;
+  vf->ready_state=OPENED;
 
   vf->bittrack=0.f;
   vf->samptrack=0.f;
@@ -445,11 +420,11 @@
     
     /* process a packet if we can.  If the machine isn't loaded,
        neither is a page */
-    if(vf->decode_ready){
+    if(vf->ready_state==INITSET){
       ogg_packet op;
       int result=ogg_stream_packetout(&vf->os,&op);
       ogg_int64_t granulepos;
-      
+
       if(result==-1)return(OV_HOLE); /* hole in the data. */
       if(result>0){
         /* got a packet.  process it */
@@ -499,18 +474,26 @@
         }
       }
     }
-
-    if(!readp)return(0);
-    if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* eof. leave unitialized */
 
-    /* bitrate tracking; add the header's bytes here, the body bytes
-       are done by packet above */
-    vf->bittrack+=og.header_len*8;
-
-    /* has our decoding just traversed a bitstream boundary? */
-    if(vf->decode_ready){
-      if(vf->current_serialno!=ogg_page_serialno(&og)){
-	_decode_clear(vf);
+    if(vf->ready_state>=STREAMSET){
+      if(!readp)return(0);
+      if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* eof. 
+							leave unitialized */
+      
+      /* bitrate tracking; add the header's bytes here, the body bytes
+	 are done by packet above */
+      vf->bittrack+=og.header_len*8;
+      
+      /* has our decoding just traversed a bitstream boundary? */
+      if(vf->ready_state==INITSET){
+	if(vf->current_serialno!=ogg_page_serialno(&og)){
+	  _decode_clear(vf);
+	  
+	  if(!vf->seekable){
+	    vorbis_info_clear(vf->vi);
+	    vorbis_comment_clear(vf->vc);
+	  }
+	}
       }
     }
 
@@ -526,33 +509,37 @@
        we're now nominally at the header of the next bitstream
     */
 
-    if(!vf->decode_ready){
+    if(vf->ready_state!=INITSET){ 
       int link;
-      if(vf->seekable){
-	vf->current_serialno=ogg_page_serialno(&og);
-	
-	/* match the serialno to bitstream section.  We use this rather than
-	   offset positions to avoid problems near logical bitstream
-	   boundaries */
-	for(link=0;link<vf->links;link++)
-	  if(vf->serialnos[link]==vf->current_serialno)break;
-	if(link==vf->links)return(OV_EBADLINK); /* sign of a bogus
-						   stream.  error out,
-						   leave machine
-						   uninitialized */
-	
-	vf->current_link=link;
-	
-	ogg_stream_init(&vf->os,vf->current_serialno);
-	ogg_stream_reset(&vf->os); 
-	
-      }else{
-	/* we're streaming */
-	/* fetch the three header packets, build the info struct */
-	
-	_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og);
-	vf->current_link++;
-	link=0;
+
+      if(vf->ready_state<STREAMSET){
+	if(vf->seekable){
+	  vf->current_serialno=ogg_page_serialno(&og);
+	  
+	  /* match the serialno to bitstream section.  We use this rather than
+	     offset positions to avoid problems near logical bitstream
+	     boundaries */
+	  for(link=0;link<vf->links;link++)
+	    if(vf->serialnos[link]==vf->current_serialno)break;
+	  if(link==vf->links)return(OV_EBADLINK); /* sign of a bogus
+						     stream.  error out,
+						     leave machine
+						     uninitialized */
+	  
+	  vf->current_link=link;
+	  
+	  ogg_stream_init(&vf->os,vf->current_serialno);
+	  ogg_stream_reset(&vf->os); 
+	  vf->ready_state=STREAMSET;
+	  
+	}else{
+	  /* we're streaming */
+	  /* fetch the three header packets, build the info struct */
+	  
+	  _fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og);
+	  vf->current_link++;
+	  link=0;
+	}
       }
       
       _make_decode_ready(vf);
@@ -561,9 +548,65 @@
   }
 }
 
-/**********************************************************************
- * The helpers are over; it's all toplevel interface from here on out */
- 
+static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
+  if(f==NULL)return(-1);
+  return fseek(f,(int)off,whence);
+}
+
+static int _ov_open1(void *f,OggVorbis_File *vf,char *initial,
+		     long ibytes, ov_callbacks callbacks){
+  long offset=(f?callbacks.seek_func(f,0,SEEK_CUR):-1);
+  int ret;
+
+  memset(vf,0,sizeof(OggVorbis_File));
+  vf->datasource=f;
+  vf->callbacks = callbacks;
+
+  /* init the framing state */
+  ogg_sync_init(&vf->oy);
+
+  /* perhaps some data was previously read into a buffer for testing
+     against other stream types.  Allow initialization from this
+     previously read data (as we may be reading from a non-seekable
+     stream) */
+  if(initial){
+    char *buffer=ogg_sync_buffer(&vf->oy,ibytes);
+    memcpy(buffer,initial,ibytes);
+    ogg_sync_wrote(&vf->oy,ibytes);
+  }
+
+  /* can we seek? Stevens suggests the seek test was portable */
+  if(offset!=-1)vf->seekable=1;
+
+  /* No seeking yet; Set up a 'single' (current) logical bitstream
+     entry for partial open */
+  vf->links=1;
+  vf->vi=_ogg_calloc(vf->links,sizeof(vorbis_info));
+  vf->vc=_ogg_calloc(vf->links,sizeof(vorbis_info));
+  
+  /* Try to fetch the headers, maintaining all the storage */
+  if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){
+    vf->datasource=NULL;
+    ov_clear(vf);
+  }else
+    vf->ready_state=PARTOPEN;
+  return(ret);
+}
+
+static int _ov_open2(OggVorbis_File *vf){
+  vf->ready_state=OPENED;
+  if(vf->seekable){
+    int ret=_open_seekable2(vf);
+    if(ret){
+      vf->datasource=NULL;
+      ov_clear(vf);
+    }
+    return(ret);
+  }
+  return 0;
+}
+
+
 /* clear out the OggVorbis_File struct */
 int ov_clear(OggVorbis_File *vf){
   if(vf){
@@ -594,11 +637,6 @@
   return(0);
 }
 
-static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
-  if(f==NULL)return(-1);
-  return fseek(f,(int)off,whence);
-}
-
 /* inspects the OggVorbis file and finds/documents all the logical
    bitstreams contained in it.  Tries to be tolerant of logical
    bitstream sections that are truncated/woogie. 
@@ -607,6 +645,13 @@
             0) OK
 */
 
+int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes,
+    ov_callbacks callbacks){
+  int ret=_ov_open1(f,vf,initial,ibytes,callbacks);
+  if(ret)return ret;
+  return _ov_open2(vf);
+}
+
 int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
   ov_callbacks callbacks = {
     (size_t (*)(void *, size_t, size_t, void *))  fread,
@@ -618,42 +663,32 @@
   return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks);
 }
   
+/* Only partially open the vorbis file; test for Vorbisness, and load
+   the headers for the first chain.  Do not seek (although test for
+   seekability).  Use ov_test_open to finish opening the file, else
+   ov_clear to close/free it. Same return codes as open. */
 
-int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes,
+int ov_test_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes,
     ov_callbacks callbacks)
 {
-  long offset=(f?callbacks.seek_func(f,0,SEEK_CUR):-1);
-  int ret;
-
-  memset(vf,0,sizeof(OggVorbis_File));
-  vf->datasource=f;
-  vf->callbacks = callbacks;
-
-  /* init the framing state */
-  ogg_sync_init(&vf->oy);
+  return _ov_open1(f,vf,initial,ibytes,callbacks);
+}
 
-  /* perhaps some data was previously read into a buffer for testing
-     against other stream types.  Allow initialization from this
-     previously read data (as we may be reading from a non-seekable
-     stream) */
-  if(initial){
-    char *buffer=ogg_sync_buffer(&vf->oy,ibytes);
-    memcpy(buffer,initial,ibytes);
-    ogg_sync_wrote(&vf->oy,ibytes);
-  }
+int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
+  ov_callbacks callbacks = {
+    (size_t (*)(void *, size_t, size_t, void *))  fread,
+    (int (*)(void *, ogg_int64_t, int))              _fseek64_wrap,
+    (int (*)(void *))                             fclose,
+    (long (*)(void *))                            ftell
+  };
 
-  /* can we seek? Stevens suggests the seek test was portable */
-  if(offset!=-1){
-    ret=_open_seekable(vf);
-  }else{
-    ret=_open_nonseekable(vf);
-  }
-  if(ret){
-    vf->datasource=NULL;
-    ov_clear(vf);
-  }
-  return(ret);
+  return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks);
 }
+  
+int ov_test_open(OggVorbis_File *vf){
+  if(vf->ready_state!=PARTOPEN)return(OV_EINVAL);
+  return _ov_open2(vf);
+}
 
 /* How many logical bitstreams in this physical bitstream? */
 long ov_streams(OggVorbis_File *vf){
@@ -675,6 +710,7 @@
    vorbis_info structs */
 
 long ov_bitrate(OggVorbis_File *vf,int i){
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(i>=vf->links)return(OV_EINVAL);
   if(!vf->seekable && i!=0)return(ov_bitrate(vf,0));
   if(i<0){
@@ -706,10 +742,13 @@
 }
 
 /* returns the actual bitrate since last call.  returns -1 if no
-   additional data to offer since last call (or at beginning of stream) */
+   additional data to offer since last call (or at beginning of stream),
+   EINVAL if stream is only partially open 
+*/
 long ov_bitrate_instant(OggVorbis_File *vf){
   int link=(vf->seekable?vf->current_link:0);
   long ret;
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(vf->samptrack==0)return(OV_FALSE);
   ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5;
   vf->bittrack=0.f;
@@ -730,9 +769,11 @@
 
 /* returns: total raw (compressed) length of content if i==-1
             raw (compressed) length of that logical bitstream for i==0 to n
-	    -1 if the stream is not seekable (we can't know the length)
+	    OV_EINVAL if the stream is not seekable (we can't know the length)
+	    or if stream is only partially open
 */
 ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
   if(i<0){
     long acc=0;
@@ -745,11 +786,13 @@
   }
 }
 
-/* returns: total PCM length (samples) of content if i==-1
-            PCM length (samples) of that logical bitstream for i==0 to n
-	    -1 if the stream is not seekable (we can't know the length)
+/* returns: total PCM length (samples) of content if i==-1 PCM length
+	    (samples) of that logical bitstream for i==0 to n
+	    OV_EINVAL if the stream is not seekable (we can't know the
+	    length) or only partially open 
 */
 ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
   if(i<0){
     ogg_int64_t acc=0;
@@ -764,9 +807,11 @@
 
 /* returns: total seconds of content if i==-1
             seconds in that logical bitstream for i==0 to n
-	    -1 if the stream is not seekable (we can't know the length)
+	    OV_EINVAL if the stream is not seekable (we can't know the
+	    length) or only partially open 
 */
 double ov_time_total(OggVorbis_File *vf,int i){
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
   if(i<0){
     double acc=0;
@@ -780,69 +825,132 @@
 }
 
 /* seek to an offset relative to the *compressed* data. This also
-   immediately sucks in and decodes pages to update the PCM cursor. It
-   will cross a logical bitstream boundary, but only if it can't get
-   any packets out of the tail of the bitstream we seek to (so no
-   surprises). 
+   scans packets to update the PCM cursor. It will cross a logical
+   bitstream boundary, but only if it can't get any packets out of the
+   tail of the bitstream we seek to (so no surprises).
 
    returns zero on success, nonzero on failure */
 
 int ov_raw_seek(OggVorbis_File *vf,long pos){
-  int flag=0;
-  if(!vf->seekable)return(OV_ENOSEEK); /* don't dump machine if we can't seek */
+  ogg_stream_state work_os;
+
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(!vf->seekable)
+    return(OV_ENOSEEK); /* don't dump machine if we can't seek */
+
   if(pos<0 || pos>vf->offsets[vf->links])return(OV_EINVAL);
 
   /* clear out decoding machine state */
   vf->pcm_offset=-1;
   _decode_clear(vf);
   
-  /* seek */
   _seek_helper(vf,pos);
 
-  /* we need to make sure the pcm_offset is set.  We use the
-     _fetch_packet helper to process one packet with readp set, then
-     call it until it returns '0' with readp not set (the last packet
-     from a page has the 'granulepos' field set, and that's how the
-     helper updates the offset */
-
-  while(!flag){
-    switch(_process_packet(vf,1)){
-    case 0:case OV_EOF:
-      /* oh, eof. There are no packets remaining.  Set the pcm offset to
-	 the end of file */
-      vf->pcm_offset=ov_pcm_total(vf,-1);
-      return(0);
-    case OV_HOLE:
-      break;
-    case OV_EBADLINK:
-      goto seek_error;
-    default:
-      /* all OK */
-      flag=1;
-      break;
-    }
-  }
-  
-  while(1){
-    /* don't have to check each time through for the updated granule;
-       it's always the last complete packet on a page */
-    switch(_process_packet(vf,0)){
-    case 0:case OV_EOF:
-      /* the offset is set unless it's a bogus bitstream with no
-         offset information but that's not our fault.  We still run
-         gracefully, we're just missing the offset */
-      return(0);
-    case OV_EBADLINK:
-      goto seek_error;
-    default:
-      /* continue processing packets */
-      break;
+  /* we need to make sure the pcm_offset is set, but we don't want to
+     advance the raw cursor past good packets just to get to the first
+     with a granulepos.  That's not equivalent behavior to beginning
+     decoding as immediately after the seek position as possible.
+
+     So, a hack.  We use two stream states; a local scratch state and
+     a the shared vf->os stream state.  We use the local state to
+     scan, and the shared state as a buffer for later decode. 
+
+     Unfortuantely, on the last page we still advance to last packet
+     because the granulepos on the last page is not necessarily on a
+     packet boundary, and we need to make sure the granpos is
+     correct. 
+  */
+
+  {
+    ogg_page og;
+    ogg_packet op;
+    int lastblock=0;
+    int accblock=0;
+    int thisblock;
+    int eosflag;
+
+    memset(&work_os,0,sizeof(work_os));/* so that it's safe to clear
+					  it later even if we don't
+					  init it */
+
+    while(1){
+      if(vf->ready_state==STREAMSET){
+	/* snarf/scan a packet if we can */
+	int result=ogg_stream_packetout(&work_os,&op);
+      
+	if(result>0){
+
+	  if(vf->vi[vf->current_link].codec_setup)
+	    thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
+	  if(eosflag)
+	    ogg_stream_packetout(&vf->os,NULL);
+	  else
+	    if(lastblock)accblock+=(lastblock+thisblock)>>2;
+
+	  if(op.granulepos!=-1){
+	    int i,link=vf->current_link;
+	    ogg_int64_t granulepos=op.granulepos;
+	    
+	    for(i=0;i<link;i++)
+	      granulepos+=vf->pcmlengths[i];
+	    vf->pcm_offset=granulepos-accblock;
+	    break;
+	  }
+	  lastblock=thisblock;
+	  continue;
+	}
+      }
+      
+      if(!lastblock){
+	if(_get_next_page(vf,&og,-1)<0){
+	  vf->pcm_offset=ov_pcm_total(vf,-1);
+	  break;
+	}
+      }else{
+	/* huh?  Bogus stream with packets but no granulepos */
+	vf->pcm_offset=-1;
+	break;
+      }
+      
+      /* has our decoding just traversed a bitstream boundary? */
+      if(vf->ready_state==STREAMSET)
+	if(vf->current_serialno!=ogg_page_serialno(&og)){
+	_decode_clear(vf); /* clear out stream state */
+	ogg_stream_clear(&work_os);
+      }
+
+      if(vf->ready_state<STREAMSET){
+	int link;
+	
+	vf->current_serialno=ogg_page_serialno(&og);
+	for(link=0;link<vf->links;link++)
+	  if(vf->serialnos[link]==vf->current_serialno)break;
+	if(link==vf->links)goto seek_error; /* sign of a bogus stream.
+					       error out, leave
+					       machine uninitialized */
+	vf->current_link=link;
+	
+	ogg_stream_init(&vf->os,vf->current_serialno);
+	ogg_stream_reset(&vf->os); 
+	ogg_stream_init(&work_os,vf->current_serialno);
+	ogg_stream_reset(&work_os); 
+	vf->ready_state=STREAMSET;
+	
+      }
+    
+      ogg_stream_pagein(&vf->os,&og);
+      ogg_stream_pagein(&work_os,&og);
+      eosflag=ogg_page_eos(&og);
     }
   }
-  
+
+  ogg_stream_clear(&work_os);
+  return(0);
+
  seek_error:
   /* dump the machine so we're in a known state */
   vf->pcm_offset=-1;
+  ogg_stream_clear(&work_os);
   _decode_clear(vf);
   return OV_EBADLINK;
 }
@@ -858,9 +966,10 @@
   long ret;
   ogg_int64_t total=ov_pcm_total(vf,-1);
 
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable)return(OV_ENOSEEK);
   if(pos<0 || pos>total)return(OV_EINVAL);
-
+ 
   /* which bitstream section does this pcm offset occur in? */
   for(link=vf->links-1;link>=0;link--){
     total-=vf->pcmlengths[link];
@@ -872,46 +981,117 @@
      missing pages or incorrect frame number information in the
      bitstream could make our task impossible.  Account for that (it
      would be an error condition) */
+
+  /* new search algorithm by HB (Nicholas Vinen) */
   {
     ogg_int64_t target=pos-total;
     long end=vf->offsets[link+1];
     long begin=vf->offsets[link];
+    ogg_int64_t endtime = vf->pcmlengths[link];
+    ogg_int64_t begintime = 0;
     long best=begin;
-
+    
     ogg_page og;
     while(begin<end){
       long bisect;
-    
+      
       if(end-begin<CHUNKSIZE){
         bisect=begin;
       }else{
-	bisect=(end+begin)/2;
+	/* take a (pretty decent) guess. */
+	bisect=begin + 
+	  (target-begintime)*(end-begin)/(endtime-begintime) - CHUNKSIZE;
+	if(bisect<=begin)
+	  bisect=begin+1;
       }
-    
       _seek_helper(vf,bisect);
-      ret=_get_next_page(vf,&og,end-bisect);
-      switch(ret){
-      case OV_FALSE: case OV_EOF:
-	end=bisect;
-	break;
-      case OV_EREAD:
-	goto seek_error;
-      default:
-	{
+    
+      while(begin<end){
+	ret=_get_next_page(vf,&og,end-bisect);
+	if(ret==OV_EREAD) goto seek_error;
+	if(ret<0){
+	  if(bisect<=begin+1)
+	    end=begin; /* found it */
+	  else{
+	    if(bisect==0)goto seek_error;
+	    bisect-=CHUNKSIZE;
+	    if(bisect<=begin)bisect=begin+1;
+	    _seek_helper(vf,bisect);
+	  }
+	}else{
           ogg_int64_t granulepos=ogg_page_granulepos(&og);
           if(granulepos<target){
             best=ret;  /* raw offset of packet with granulepos */ 
-	    begin=vf->offset; /* raw offset of next packet */
+	    begin=vf->offset; /* raw offset of next page */
+	    begintime=granulepos;
+	    
+	    if(target-begin>44100)break;
+	    bisect=begin; /* *not* begin + 1 */
           }else{
-	    end=bisect;
+	    if(bisect<=begin+1)
+	      end=begin;  /* found it */
+	    else{
+	      if(end==vf->offset){ /* we're pretty close - we'd be stuck in */
+		end=ret;
+		bisect-=CHUNKSIZE; /* an endless loop otherwise. */
+		if(bisect<=begin)bisect=begin+1;
+		_seek_helper(vf,bisect);
+	      }else{
+		end=ret;
+		endtime=granulepos;
+		break;
+	      }
+	    }
           }
         }
       }
     }
 
-    /* found our page. seek to it (call raw_seek). */
-    
-    if((ret=ov_raw_seek(vf,best)))goto seek_error;
+    /* found our page. seek to it, update pcm offset. Easier case than
+       raw_seek, don't keep packets preceeding granulepos. */
+    {
+      ogg_page og;
+      ogg_packet op;
+      /* clear out decoding machine state */
+      _decode_clear(vf);  
+      /* seek */
+      _seek_helper(vf,best);
+      
+      if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* shouldn't happen */
+      vf->current_serialno=ogg_page_serialno(&og);
+      vf->current_link=link;
+      
+      ogg_stream_init(&vf->os,vf->current_serialno);
+      ogg_stream_reset(&vf->os); 
+      vf->ready_state=STREAMSET;
+      ogg_stream_pagein(&vf->os,&og);
+
+      /* pull out all but last packet; the one with granulepos */
+      while(1){
+	ret=ogg_stream_packetpeek(&vf->os,&op);
+	if(ret==0){
+	  /* !!! the packet finishing this page originated on a
+             preceeding page. Keep fetching previous pages until we
+             get one with a granulepos or without the 'continued' flag
+             set.  Then just use raw_seek for simplicity. */
+	  while(1){
+	    ret=_get_prev_page(vf,&og);
+	    if(ret<0)goto seek_error;
+	    if(ogg_page_granulepos(&og)>-1 ||
+	       !ogg_page_continued(&og)){
+	      return ov_raw_seek(vf,ret);
+	    }
+	    vf->offset=ret;
+	  }
+	}
+	if(ret<0)goto seek_error;
+	if(op.granulepos!=-1 && !op.e_o_s){
+	  vf->pcm_offset=op.granulepos+total;
+	  break;
+	}else
+	  ret=ogg_stream_packetout(&vf->os,NULL);
+      }
+    }
   }
   
   /* verify result */
@@ -932,11 +1112,71 @@
    returns zero on success, nonzero on failure */
 
 int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
+  int thisblock,lastblock=0,blockacc=0;
   int ret=ov_pcm_seek_page(vf,pos);
   if(ret<0)return(ret);
-  
+
+  /* discard leading packets we don't need for the lapping of the
+     position we want; don't decode them */
+#if 0
+  while(1){
+    ogg_packet op;
+    ogg_page og;
+
+    int ret=ogg_stream_packetpeek(&vf->os,&op);
+    if(ret>0){
+      thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
+
+      if(blockacc+
+	 ((lastblock+thisblock)>>2)+
+	 (thisblock>>1)+vf->pcm_offset>pos)break;
+
+      ogg_stream_packetout(&vf->os,NULL);
+      /* end of logical stream case is hard, especially with exact
+         length positioning. */
+
+      if(op.granulepos>-1){
+	int i;
+	/* always believe the stream markers */
+	vf->pcm_offset=op.granulepos;
+	for(i=0;i<vf->current_link;i++)
+	  vf->pcm_offset+=vf->pcmlengths[i];
+	blockacc=0;
+      }else
+	if(lastblock)blockacc+=(lastblock+thisblock)>>2;
+
+      lastblock=thisblock;
+    }else{
+      if(ret<0 && ret!=OV_HOLE)break;
+      
+      /* suck in a new page */
+      if(_get_next_page(vf,&og,-1)<0)break;
+      if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf);
+      
+      if(vf->ready_state<STREAMSET){
+	int link;
+	
+	vf->current_serialno=ogg_page_serialno(&og);
+	for(link=0;link<vf->links;link++)
+	  if(vf->serialnos[link]==vf->current_serialno)break;
+	if(link==vf->links)return(OV_EBADLINK);
+	vf->current_link=link;
+	
+	ogg_stream_init(&vf->os,vf->current_serialno);
+	ogg_stream_reset(&vf->os); 
+	vf->ready_state=STREAMSET;      
+	lastblock=0;
+      }
+      ogg_stream_pagein(&vf->os,&og);
+    }
+  }
+
+  if(lastblock)vf->pcm_offset+=blockacc;
+#endif
+
   /* discard samples until we reach the desired position. Crossing a
      logical bitstream boundary with abandon is OK. */
+  _make_decode_ready(vf);
   while(vf->pcm_offset<pos){
     float **pcm;
     long target=pos-vf->pcm_offset;
@@ -947,7 +1187,7 @@
     vf->pcm_offset+=samples;
     
     if(samples<target)
-      if(_process_packet(vf,1)==0)
+      if(_process_packet(vf,1)<=0)
         vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
   }
   return 0;
@@ -962,6 +1202,7 @@
   ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
   double time_total=ov_time_total(vf,-1);
 
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable)return(OV_ENOSEEK);
   if(seconds<0 || seconds>time_total)return(OV_EINVAL);
   
@@ -988,6 +1229,7 @@
   ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
   double time_total=ov_time_total(vf,-1);
 
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(!vf->seekable)return(OV_ENOSEEK);
   if(seconds<0 || seconds>time_total)return(OV_EINVAL);
   
@@ -1008,11 +1250,13 @@
 /* tell the current stream offset cursor.  Note that seek followed by
    tell will likely not give the set offset due to caching */
 ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   return(vf->offset);
 }
 
 /* return PCM offset (sample) of next PCM sample to be read */
 ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   return(vf->pcm_offset);
 }
 
@@ -1024,6 +1268,7 @@
   ogg_int64_t pcm_total=0;
   double time_total=0.f;
   
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
   if(vf->seekable){
     pcm_total=ov_pcm_total(vf,-1);
     time_total=ov_time_total(vf,-1);
@@ -1050,20 +1295,17 @@
 vorbis_info *ov_info(OggVorbis_File *vf,int link){
   if(vf->seekable){
     if(link<0)
-      if(vf->decode_ready)
+      if(vf->ready_state>=STREAMSET)
         return vf->vi+vf->current_link;
       else
-	return NULL;
+      return vf->vi;
     else
       if(link>=vf->links)
         return NULL;
       else
         return vf->vi+link;
   }else{
-    if(vf->decode_ready)
-      return vf->vi;
-    else
-      return NULL;
+    return vf->vi;
   }
 }
 
@@ -1071,20 +1313,17 @@
 vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
   if(vf->seekable){
     if(link<0)
-      if(vf->decode_ready)
+      if(vf->ready_state>=STREAMSET)
         return vf->vc+vf->current_link;
       else
-	return NULL;
+	return vf->vc;
     else
       if(link>=vf->links)
         return NULL;
       else
         return vf->vc+link;
   }else{
-    if(vf->decode_ready)
-      return vf->vc;
-    else
-      return NULL;
+    return vf->vc;
   }
 }
 
@@ -1117,7 +1356,7 @@
                  word) word size for output.  currently 1 (byte) or 
                        2 (16 bit short)
 
-   return values: <0) error/hole in data (OV_HOLE)
+   return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
                    0) EOF
                    n) number of bytes of PCM actually returned.  The
                    below works on a packet-by-packet basis, so the
@@ -1131,8 +1370,17 @@
   int i,j;
   int host_endian = host_is_big_endian();
 
+  if(vf->ready_state<OPENED)return(OV_EINVAL);
+  if(vf->ready_state==OPENED)return(OV_EOF); /* stream is always
+						initialized after
+						other calls (after
+						open)... unless there
+						was no page at the end
+						to initialize state
+						with. */
+
   while(1){
-    if(vf->decode_ready){
+    if(vf->ready_state>=STREAMSET){
       float **pcm;
       long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
       if(samples){

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list