First cut on chaining_example.c
authorMonty <xiphmont@xiph.org>
Tue, 2 Nov 1999 11:17:38 +0000 (11:17 +0000)
committerMonty <xiphmont@xiph.org>
Tue, 2 Nov 1999 11:17:38 +0000 (11:17 +0000)
Monty

svn path=/trunk/vorbis/; revision=157

lib/Makefile.in
lib/block.c
lib/chaining_example.c
lib/codec.h
lib/framing.c

index aeb880e..aee6429 100644 (file)
@@ -1,6 +1,6 @@
 # vorbis makefile configured for use with gcc on any platform
 
-# $Id: Makefile.in,v 1.12 1999/10/18 09:41:07 xiphmont Exp $
+# $Id: Makefile.in,v 1.13 1999/11/02 11:17:34 xiphmont Exp $
 
 ###############################################################################
 #                                                                             #
@@ -34,7 +34,7 @@ LFILES =      framing.o mdct.o smallft.o block.o envelope.o window.o\
                lsp.o lpc.o analysis.o synthesis.o psy.o info.o bitwise.o\
                spectrum.o
 
-EFILES =       encoder_example.o decoder_example.o
+EFILES =       encoder_example.o decoder_example.o chaining_example.o
 
 all:
        $(MAKE) target CFLAGS="$(OPT)"
@@ -48,7 +48,7 @@ analysis:
 profile: 
        $(MAKE) target CFLAGS="$(PROFILE)"
 
-target:        libvorbis.a encoder_example decoder_example
+target:        libvorbis.a encoder_example decoder_example chaining_example
 
 selftest:      
        $(MAKE) clean
@@ -67,6 +67,10 @@ decoder_example:     $(EFILES) libvorbis.a
        $(CC) $(CFLAGS) $(LDFLAGS) decoder_example.o libvorbis.a -o \
                        decoder_example -lm
 
+chaining_example:      $(EFILES) libvorbis.a
+       $(CC) $(CFLAGS) $(LDFLAGS) chaining_example.o libvorbis.a -o \
+                       chaining_example -lm
+
 libvorbis.a:   $(LFILES)       
                $(AR) -r libvorbis.a $(LFILES)
                $(RANLIB) libvorbis.a
index 47674ce..ffa574e 100644 (file)
@@ -111,8 +111,9 @@ int vorbis_block_clear(vorbis_block *vb){
       free(vb->pcm[i]);
     free(vb->pcm);
   }
-  if(vb->vd->analysisp)
-    _oggpack_writeclear(&vb->opb);
+  if(vb->vd)
+    if(vb->vd->analysisp)
+      _oggpack_writeclear(&vb->opb);
 
   memset(vb,0,sizeof(vorbis_block));
   return(0);
index 3f3f95f..df48835 100644 (file)
  function: simple example of opening/seeking chained bitstreams
  author: Monty <xiphmont@mit.edu>
  modifications by: Monty
- last modification date: Oct 29 1999
+ last modification date: Nov 02 1999
 
  ********************************************************************/
 
-
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
 #include <stdio.h>
 #include <math.h>
 #include "codec.h"
    multiplexing] is not allowed in Vorbis) */
 
 /* A Vorbis file can be played beginning to end (streamed) without
-   worrying ahead of time about chaining.  If we have the whole file,
-   however, and want random access (seeking/scrubbing) or desire to
-   know the total length/time of a file, we need to account for the
-   possibility of chaining. */
+   worrying ahead of time about chaining (see decoder_example.c).  If
+   we have the whole file, however, and want random access
+   (seeking/scrubbing) or desire to know the total length/time of a
+   file, we need to account for the possibility of chaining. */
 
 /* We can handle things a number of ways; we can determine the entire
-   bitstream structure right off the bat, or piece it together later.
+   bitstream structure right off the bat, or find pieces on demand.
    This example determines and caches structure for the entire
-   bitstream, but builds a virtual decoder on the fly when moving from
-   one link of the chain to another */
+   bitstream, but builds a virtual decoder on the fly when moving
+   between links in the chain */
 
 /* There are also different ways to implement seeking.  Enough
    information exists in an Ogg bitstream to seek to
    want course navigation through the stream. */
 
 typedef struct {
+  FILE             *f;
+  int              seekable;
+  long             offset;
+  long             end;
+  ogg_sync_state   oy; 
 
+  /* If the FILE handle isn't seekable (eg, a pipe), only the current
+     stream appears */
+  int              links;
+  long             *offsets;
+  long             *serialnos;
+  size64           *pcmlengths;
+  vorbis_info      *vi;
 
-
-
-} Vorbis_File;
-
-typedef struct {
-  ogg_sync_state   oy; /* sync and verify incoming physical bitstream */
+  /* Decoding working state local storage */
+  int ready;
   ogg_stream_state os; /* take physical pages, weld into a logical
                           stream of packets */
-  ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
-  ogg_packet       op; /* one raw packet of data for decode */
-  
-  vorbis_info      vi; /* struct that stores all the static vorbis bitstream
-                          settings */
   vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
   vorbis_block     vb; /* local working space for packet->PCM decode */
+
+} OggVorbis_File;
+
+
+/*************************************************************************
+ * Many, many internal helpers.  The intention is not to be confusing; 
+ * rampant duplication and monolithic function implementation would be 
+ * harder to understand anyway.  The high level functions are last.  Begin
+ * grokking near the end of the file */
+
+/* read a little more data from the file/pipe into the ogg_sync framer */
+#define CHUNKSIZE 4096
+static long _get_data(OggVorbis_File *vf){
+  char *buffer=ogg_sync_buffer(&vf->oy,4096);
+  long bytes=fread(buffer,1,4096,vf->f);
+  ogg_sync_wrote(&vf->oy,bytes);
+  return(bytes);
+}
+
+/* save a tiny smidge of verbosity to make the code more readable */
+static void _seek_helper(OggVorbis_File *vf,long offset){
+  fseek(vf->f,offset,SEEK_SET);
+  vf->offset=offset;
+  ogg_sync_reset(&vf->oy);
+}
+
+/* The read/seek functions track absolute position within the stream */
+
+/* from the head of the stream, get the next page.  boundary specifies
+   if the function is allowed to fetch more data from the stream (and
+   how much) or only use internally buffered data.
+
+   boundary: -1) unbounded search
+              0) read no additional data; use cached only
+             n) search for a new page beginning for n bytes
+
+   return:   -1) did not find a page 
+              n) found a page at absolute offset n */
+
+static long _get_next_page(OggVorbis_File *vf,ogg_page *og,int boundary){
+  if(boundary>0)boundary+=vf->offset;
+  while(1){
+    long more;
+
+    if(boundary>0 && vf->offset>=boundary)return(-1);
+    more=ogg_sync_pageseek(&vf->oy,og);
+    
+    if(more<0){
+      /* skipped n bytes */
+      vf->offset-=more;
+    }else{
+      if(more==0){
+       /* send more paramedics */
+       if(!boundary)return(-1);
+       if(_get_data(vf)<=0)return(-1);
+      }else{
+       /* got a page.  Return the offset at the page beginning,
+           advance the internal offset past the page end */
+       long ret=vf->offset;
+       vf->offset+=more;
+       return(ret);
+       
+      }
+    }
+  }
+}
+
+/* find the latest page beginning before the current stream cursor
+   position. Much dirtier than the above as Ogg doesn't have any
+   backward search linkage.  no 'readp' as it will certainly have to
+   read. */
+static long _get_prev_page(OggVorbis_File *vf,ogg_page *og){
+  long begin=vf->offset;
+  long ret;
+  int offset=-1;
+
+  while(offset==-1){
+    begin-=CHUNKSIZE;
+    _seek_helper(vf,begin);
+    while(vf->offset<begin+CHUNKSIZE){
+      ret=_get_next_page(vf,og,begin+CHUNKSIZE-vf->offset);
+      if(ret==-1){
+       break;
+      }else{
+       offset=ret;
+      }
+    }
+  }
+
+  /* we have the offset.  Actually snork and hold the page now */
+  _seek_helper(vf,offset);
+  ret=_get_next_page(vf,og,CHUNKSIZE);
+  if(ret==-1){
+    /* this shouldn't be possible */
+    fprintf(stderr,"Missed page fencepost at end of logical bitstream. "
+           "Exiting.\n");
+    exit(1);
+  }
+  return(offset);
+}
+
+/* finds each bitstream link one at a time using a bisection search
+   (has to begin by knowing the offset of the lb's initial page).
+   Recurses for each link so it can alloc the link storage after
+   finding them all, then unroll and fill the cache at the same time */
+static void _bisect_forward_serialno(OggVorbis_File *vf,
+                                    long begin,
+                                    long searched,
+                                    long end,
+                                    long currentno,
+                                    long m){
+  long endsearched=end;
+  long next=end;
+  ogg_page og;
   
-  char *buffer;
-  int  bytes;
-  int i;
-  int eos=0;
+  /* the below guards against garbage seperating the last and
+     first pages of two links. */
+  while(searched<endsearched){
+    long bisect;
+    long ret;
+    
+    if(endsearched-searched<CHUNKSIZE){
+      bisect=searched;
+    }else{
+      bisect=(searched+endsearched)/2;
+    }
+    
+    _seek_helper(vf,bisect);
+    ret=_get_next_page(vf,&og,-1);
+    if(ret<0 || ogg_page_serialno(&og)!=currentno){
+      endsearched=bisect;
+      if(ret>=0)next=ret;
+    }else{
+      searched=ret+og.header_len+og.body_len;
+    }
+  }
+  if(searched>=end){
+    vf->links=m+1;
+    vf->offsets=malloc((m+2)*sizeof(long));
+    vf->offsets[m+1]=searched;
+  }else{
+    _bisect_forward_serialno(vf,next,next,
+                            end,ogg_page_serialno(&og),m+1);
+  }
+  
+  vf->offsets[m]=begin;
+}
 
+/* uses the local ogg_stream storage in vf; this is important for
+   non-streaming input sources */
+static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,long *serialno){
+  ogg_page og;
+  ogg_packet op;
+  int i,ret;
 
+  ret=_get_next_page(vf,&og,CHUNKSIZE);
+  if(ret==-1){
+    fprintf(stderr,"Did not find initial header for bitstream.\n");
+    return -1;
+  }
+  
+  if(serialno)*serialno=ogg_page_serialno(&og);
+  ogg_stream_init(&vf->os,ogg_page_serialno(&og));
+  
+  /* extract the initial header from the first page and verify that the
+     Ogg bitstream is in fact Vorbis data */
+  
+  vorbis_info_init(vi);
+  
+  i=0;
+  while(i<3){
+    ogg_stream_pagein(&vf->os,&og);
+    while(i<3){
+      int result=ogg_stream_packetout(&vf->os,&op);
+      if(result==0)break;
+      if(result==-1){
+       fprintf(stderr,"Corrupt header in logical bitstream.  "
+               "Exiting.\n");
+       goto bail_header;
+      }
+      if(vorbis_info_headerin(vi,&op)){
+       fprintf(stderr,"Illegal header in logical bitstream.  "
+               "Exiting.\n");
+       goto bail_header;
+      }
+      i++;
+    }
+    if(i<3)
+      if(_get_next_page(vf,&og,1)<0){
+       fprintf(stderr,"Missing header in logical bitstream.  "
+               "Exiting.\n");
+       goto bail_header;
+      }
+  }
+  return 0; 
 
+ bail_header:
+  vorbis_info_clear(vi);
+  ogg_stream_clear(&vf->os);
+  return -1;
+}
 
-} Vorbis_Decode;
+/* last step of the OggVorbis_File initialization; get all the
+   vorbis_info structs and PCM positions.  Only called by the seekable
+   initialization (local stream storage is hacked slightly; pay
+   attention to how that's done) */
+static void _prefetch_all_headers(OggVorbis_File *vf,vorbis_info *first){
+  ogg_page og;
+  int i,ret;
+  
+  vf->vi=malloc(vf->links*sizeof(vorbis_info));
+  vf->pcmlengths=malloc(vf->links*sizeof(size64));
+  vf->serialnos=malloc(vf->links*sizeof(long));
+  
+  for(i=0;i<vf->links;i++){
+    if(first && i==0){
+      /* we already grabbed the initial header earlier.  This just
+         saves the waste of grabbing it again */
+      memcpy(vf->vi+i,first,sizeof(vorbis_info));
+      memset(first,0,sizeof(vorbis_info));
+    }else{
 
+      /* seek to the location of the initial header */
 
-int vorbis_open_file(FILE *f,Vorbis_File *vf){
+      _seek_helper(vf,vf->offsets[i]);
+      if(_fetch_headers(vf,vf->vi+i,NULL)==-1){
+       vorbis_info_clear(vf->vi+i);
+       fprintf(stderr,"Error opening logical bitstream #%d.\n\n",i+1);
+    
+       ogg_stream_clear(&vf->os); /* clear local storage.  This is not
+                                     done in _fetch_headers, as that may
+                                     be called in a non-seekable stream
+                                     (in which case, we need to preserve
+                                     the stream local storage) */
+      }
+    }
 
+    /* get the serial number and PCM length of this link. To do this,
+       get the last page of the stream */
+    {
+      long end=vf->offsets[i+1];
+      _seek_helper(vf,end);
 
+      while(1){
+       ret=_get_prev_page(vf,&og);
+       if(ret==-1){
+         /* this should not be possible */
+         fprintf(stderr,"Could not find last page of logical "
+                 "bitstream #%d\n\n",i);
+         vorbis_info_clear(vf->vi+i);
+         break;
+       }
+       if(ogg_page_frameno(&og)!=-1){
+         vf->serialnos[i]=ogg_page_serialno(&og);
+         vf->pcmlengths[i]=ogg_page_frameno(&og);
+         break;
+       }
+      }
+    }
+  }
 }
 
-int vorbis_clear_file(Vorbis_File *vf){
+static int _open_seekable(OggVorbis_File *vf){
+  vorbis_info initial;
+  long serialno,end;
+  int ret;
+  ogg_page og;
+  
+  /* is this even vorbis...? */
+  ret=_fetch_headers(vf,&initial,&serialno);
+  ogg_stream_clear(&vf->os);
+  if(ret==-1)return(-1);
+  
+  /* we can seek, so set out learning all about this file */
+  vf->seekable=1;
+  fseek(vf->f,0,SEEK_END); /* Yes, I know I used lseek earlier. */
+  vf->offset=vf->end=ftell(vf->f);
+  
+  /* We get the offset for the last page of the physical bitstream.
+     Most OggVorbis files will contain a single logical bitstream */
+  end=_get_prev_page(vf,&og);
 
+  /* moer than one logical bitstream? */
+  if(ogg_page_serialno(&og)!=serialno){
 
+    /* Chained bitstream. Bisect-search each logical bitstream
+       section.  Do so based on serial number only */
+    _bisect_forward_serialno(vf,0,0,end+1,serialno,0);
 
+  }else{
+
+    /* Only one logical bitstream */
+    _bisect_forward_serialno(vf,0,end,end+1,serialno,0);
+
+  }
+
+  _prefetch_all_headers(vf,&initial);
+
+  return(0);
+}
+
+static int _open_nonseekable(OggVorbis_File *vf){
+  vorbis_info vi;
+  long serialno;
+
+  /* Try to fetch the headers, maintaining all the storage */
+  if(_fetch_headers(vf,&vi,&serialno)==-1)return(-1);
+  
+  /* we cannot seek. Set up a 'single' (current) logical bitstream entry  */
+  vf->links=1;
+  vf->vi=malloc(sizeof(vorbis_info));
+  vf->serialnos=malloc(sizeof(long));
+  
+  memcpy(vf->vi,&vi,sizeof(vorbis_info));
+  vf->serialnos[0]=serialno;
+  return 0;
+}
+/**********************************************************************
+ * The helpers are over; it's all toplevel interface from here on out */
+/* clear out the OggVorbis_File struct */
+int ov_clear(OggVorbis_File *vf){
+  if(vf){
+    vorbis_block_clear(&vf->vb);
+    vorbis_dsp_clear(&vf->vd);
+    ogg_stream_clear(&vf->os);
+    
+    if(vf->vi && vf->links){
+      int i;
+      for(i=0;i<vf->links;i++)
+       vorbis_info_clear(vf->vi+i);
+      free(vf->vi);
+    }
+    if(vf->pcmlengths)free(vf->pcmlengths);
+    if(vf->serialnos)free(vf->serialnos);
+    if(vf->offsets)free(vf->offsets);
+    ogg_sync_clear(&vf->oy);
+    if(vf->f)fclose(vf->f);
+    memset(vf,0,sizeof(OggVorbis_File));
+  }
+  return(0);
 }
 
+/* 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. */
+int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){
+  long offset=lseek(fileno(f),0,SEEK_CUR);
+  int ret;
+
+  memset(vf,0,sizeof(OggVorbis_File));
+  vf->f=f;
+
+  /* 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){
+    ret=_open_seekable(vf);
+  }else{
+    ret=_open_nonseekable(vf);
+  }
+  if(ret){
+    vf->f=NULL;
+    ov_clear(vf);
+  }
+  return(ret);
+}
+
+double ov_lbtime(OggVorbis_File *vf,int i){
+  if(i>=0 && i<vf->links)
+    return((float)(vf->pcmlengths[i])/vf->vi[i].rate);
+  return(0);
+}
+
+long ov_totaltime(OggVorbis_File *vf){
+  double acc=0;
+  int i;
+  for(i=0;i<vf->links;i++)
+    acc+=ov_lbtime(vf,i);
+  return(acc);
+}
+
+/* seek to an offset relative to the *compressed* data */
+int ov_seek_stream;
+
+
+/* seek to an offset relative to the decompressed *output* stream */
+int ov_seek_pcm;
+
+
+int main(){
+  OggVorbis_File ov;
+  int i;
+
+  /* open the file/pipe on stdin */
+  if(ov_open(stdin,&ov,NULL,-1)==-1){
+    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){
+    printf("Input bitstream contained %d logical bitstream section(s).\n",
+          ov.links);
+    printf("Total bitstream playing time: %ld seconds\n\n",
+          (long)ov_totaltime(&ov));
+
+  }else{
+    printf("Standard input was not seekable.\n"
+          "First logical bitstream information:\n\n");
+  }
+
+  for(i=0;i<ov.links;i++){
+    printf("\tlogical bitstream section %d information:\n",i+1);
+    printf("\t\tsample rate: %ldHz\n",ov.vi[i].rate);
+    printf("\t\tchannels: %d\n",ov.vi[i].channels);
+    printf("\t\tserial number: %ld\n",ov.serialnos[i]);
+    printf("\t\traw length: %ldbytes\n",(ov.offsets?
+                                    ov.offsets[i+1]-ov.offsets[i]:
+                                    -1));
+    printf("\t\tplay time: %lds\n\n",(long)ov_lbtime(&ov,i));
+  }
+  
+  ov_clear(&ov);
+  return 0;
+}
+
+
+
+
+
index 3815e04..412c1e1 100644 (file)
@@ -102,7 +102,7 @@ typedef struct {
 
 typedef struct vorbis_info{
   int channels;
-  int rate;
+  long rate;
   int version;
 
   char **user_comments;
@@ -329,6 +329,7 @@ extern int    ogg_sync_reset(ogg_sync_state *oy);
 
 extern char  *ogg_sync_buffer(ogg_sync_state *oy, long size);
 extern int    ogg_sync_wrote(ogg_sync_state *oy, long bytes);
+extern long   ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
 extern int    ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og);
 extern int    ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
 extern int    ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
index 2cbaf66..ce12459 100644 (file)
@@ -417,9 +417,108 @@ int ogg_sync_wrote(ogg_sync_state *oy, long bytes){
   return(0);
 }
 
-/* sync the stream.  If we had to recapture, return -1.  If we
-   couldn't capture at all, return 0.  If a page was already framed
-   and ready to go, return 1 and fill in the page struct.
+/* sync the stream.  This is meant to be useful for finding page
+   boundaries.
+
+   return values for this:
+  -n) skipped n bytes
+   0) page not ready; more data (no bytes skipped)
+   n) page synced at current location; page length n bytes
+   
+*/
+
+long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){
+  unsigned char *page=oy->data+oy->returned;
+  unsigned char *next;
+  long bytes=oy->fill-oy->returned;
+  
+  if(oy->headerbytes==0){
+    int headerbytes,i;
+    if(bytes<27)return(0); /* not enough for a header */
+    
+    /* verify capture pattern */
+    if(memcmp(page,"OggS",4))goto sync_fail;
+    
+    headerbytes=page[26]+27;
+    if(bytes<headerbytes)return(0); /* not enough for header + seg table */
+    
+    /* count up body length in the segment table */
+    
+    for(i=0;i<page[26];i++)
+      oy->bodybytes+=page[27+i];
+    oy->headerbytes=headerbytes;
+  }
+  
+  if(oy->bodybytes+oy->headerbytes>bytes)return(0);
+  
+  /* The whole test page is buffered.  Verify the checksum */
+  {
+    /* Grab the checksum bytes, set the header field to zero */
+    char chksum[4];
+    ogg_page log;
+    
+    memcpy(chksum,page+22,4);
+    memset(page+22,0,4);
+    
+    /* set up a temp page struct and recompute the checksum */
+    log.header=page;
+    log.header_len=oy->headerbytes;
+    log.body=page+oy->headerbytes;
+    log.body_len=oy->bodybytes;
+    _os_checksum(&log);
+    
+    /* Compare */
+    if(memcmp(chksum,page+22,4)){
+      /* D'oh.  Mismatch! Corrupt page (or miscapture and not a page
+        at all) */
+      /* replace the computed checksum with the one actually read in */
+      memcpy(page+22,chksum,4);
+      
+      /* Bad checksum. Lose sync */
+      goto sync_fail;
+    }
+  }
+  
+  /* yes, have a whole page all ready to go */
+  {
+    unsigned char *page=oy->data+oy->returned;
+    long bytes;
+
+    if(og){
+      og->header=page;
+      og->header_len=oy->headerbytes;
+      og->body=page+oy->headerbytes;
+      og->body_len=oy->bodybytes;
+    }
+
+    oy->unsynced=0;
+    oy->returned+=(bytes=oy->headerbytes+oy->bodybytes);
+    oy->headerbytes=0;
+    oy->bodybytes=0;
+    return(bytes);
+  }
+  
+ sync_fail:
+  
+  oy->headerbytes=0;
+  oy->bodybytes=0;
+  
+  /* search for possible capture */
+  next=memchr(page+1,'O',bytes-1);
+  if(!next)
+    next=oy->data+oy->fill;
+
+  oy->returned=next-oy->data;
+  return(-(next-page));
+}
+
+/* sync the stream and get a page.  Keep trying until we find a page.
+   Supress 'sync errors' after reporting the first.
+
+   return values:
+   -1) recapture (hole in data)
+    0) need more data
+    1) page returned
 
    Returns pointers into buffered data; invalidated by next call to
    _stream, _clear, _init, or _buffer */
@@ -431,87 +530,23 @@ int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){
      frame */
 
   while(1){
-    unsigned char *page=oy->data+oy->returned;
-    long bytes=oy->fill-oy->returned;
-      
-    if(oy->headerbytes==0){
-      int headerbytes,i;
-      if(bytes<27)return(0); /* not enough for a header */
-
-      /* verify capture pattern */
-      if(memcmp(page,"OggS",4))goto sync_fail;
-      
-      headerbytes=page[26]+27;
-      if(bytes<headerbytes)return(0); /* not enough for header + seg table */
-
-      /* count up body length in the segment table */
-
-      for(i=0;i<page[26];i++)
-       oy->bodybytes+=page[27+i];
-      oy->headerbytes=headerbytes;
+    long ret=ogg_sync_pageseek(oy,og);
+    if(ret>0){
+      /* have a page */
+      return(1);
     }
-    
-    if(oy->bodybytes+oy->headerbytes>bytes)return(0);
-
-    /* The whole test page is buffered.  Verify the checksum */
-    {
-      /* Grab the checksum bytes, set the header field to zero */
-      char chksum[4];
-      ogg_page log;
-      
-      memcpy(chksum,page+22,4);
-      memset(page+22,0,4);
-      
-      /* set up a temp page struct and recompute the checksum */
-      log.header=page;
-      log.header_len=oy->headerbytes;
-      log.body=page+oy->headerbytes;
-      log.body_len=oy->bodybytes;
-      _os_checksum(&log);
-       
-      /* Compare */
-      if(memcmp(chksum,page+22,4)){
-       /* D'oh.  Mismatch! Corrupt page (or miscapture and not a page
-          at all) */
-       /* replace the computed checksum with the one actually read in */
-       memcpy(page+22,chksum,4);
-         
-       /* Bad checksum. Lose sync */
-       goto sync_fail;
-      }
+    if(ret==0){
+      /* need more data */
+      return(0);
     }
-       
-    og->header=page;
-    og->header_len=oy->headerbytes;
-    og->body=page+oy->headerbytes;
-    og->body_len=oy->bodybytes;
-    
-    oy->unsynced=0;
-    oy->returned+=oy->headerbytes+oy->bodybytes;
-    oy->headerbytes=0;
-    oy->bodybytes=0;
-
-    return(1);
-
-  sync_fail:
     
-    oy->headerbytes=0;
-    oy->bodybytes=0;
-
-    /* search for possible capture */
-    
-    page=memchr(page+1,'O',bytes-1);
-    if(page)
-      oy->returned=page-oy->data;
-    else
-      oy->returned=oy->fill;
-
+    /* head did not start a synced page... skipped some bytes */
     if(!oy->unsynced){
       oy->unsynced=1;
       return(-1);
     }
 
-    /* loop; verifier above will take care of determining capture or not */
+    /* loop. keep looking */
 
   }
 }