Single link files seeking fixes:
authorMonty <xiphmont@xiph.org>
Thu, 28 Feb 2002 04:12:48 +0000 (04:12 +0000)
committerMonty <xiphmont@xiph.org>
Thu, 28 Feb 2002 04:12:48 +0000 (04:12 +0000)
fix pcm exact seeking at very beginning (a rejected packet was being
interpreted as a negative pcm offset) and very end of files (short
final frames require processing from previous page's granulepos to get
length of final frame correct)

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

examples/encoder_example.c
examples/seeking_example.c
include/vorbis/codec.h
lib/block.c
lib/mapping0.c
lib/synthesis.c
lib/vorbisfile.c

index 12ed716..3fd78c4 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: simple example encoder
- last mod: $Id: encoder_example.c,v 1.36 2002/01/23 16:04:55 segher Exp $
+ last mod: $Id: encoder_example.c,v 1.37 2002/02/28 04:12:47 xiphmont Exp $
 
  ********************************************************************/
 
@@ -97,7 +97,7 @@ int main(){
   /* (quality mode .4: 44kHz stereo coupled, roughly 128kbps VBR) */
   vorbis_info_init(&vi);
 
-  vorbis_encode_init_vbr(&vi,2,44100,.1); /* max compression */
+  vorbis_encode_init_vbr(&vi,2,44100,.4); /* max compression */
 
   /* add a comment */
   vorbis_comment_init(&vc);
index 31e5835..7a9b89f 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: illustrate seeking, and test it too
- last mod: $Id: seeking_example.c,v 1.12 2001/12/20 01:00:24 segher Exp $
+ last mod: $Id: seeking_example.c,v 1.13 2002/02/28 04:12:47 xiphmont Exp $
 
  ********************************************************************/
 
@@ -54,6 +54,16 @@ void _verify(OggVorbis_File *ov,ogg_int64_t pos,
   for(j=0;j<bread;j++){
     if(buffer[j]!=bigassbuffer[j+pos*2]){
       printf("data position after seek doesn't match pcm position\n");
+
+      {
+       FILE *f=fopen("a.m","w");
+       for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)buffer[j]);
+       fclose(f);
+       f=fopen("b.m","w");
+       for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)bigassbuffer[j+pos*2]);
+       fclose(f);
+      }
+
       exit(1);
     }
   }
@@ -94,7 +104,6 @@ int main(){
     
     /* 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 */
@@ -107,6 +116,8 @@ int main(){
       }else{
        pcmlength=i/2;
       }
+      fprintf(stderr,"\rloading.... [%ld left]              ",
+             (long)(pcmlength*2-i));
     }
     
     /* Exercise all the real seeking cases; ov_raw_seek,
@@ -114,7 +125,7 @@ int main(){
        on pcm_seek */
     {
       ogg_int64_t length=ov.end;
-      printf("testing raw seeking to random places in %ld bytes....\n",
+      printf("\rtesting raw seeking to random places in %ld bytes....\n",
             (long)length);
     
       for(i=0;i<1000;i++){
@@ -172,7 +183,7 @@ int main(){
          exit(1);
        }
        if(ov_pcm_tell(&ov)!=val){
-         printf("Decalred position didn't perfectly match request: %ld != %ld\n",
+         printf("Declared position didn't perfectly match request: %ld != %ld\n",
                 (long)val,(long)ov_pcm_tell(&ov));
          exit(1);
        }
index eb12ddd..117e7ef 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: libvorbis codec headers
- last mod: $Id: codec.h,v 1.39 2001/12/12 09:45:23 xiphmont Exp $
+ last mod: $Id: codec.h,v 1.40 2002/02/28 04:12:47 xiphmont Exp $
 
  ********************************************************************/
 
@@ -201,6 +201,7 @@ extern int      vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
 
 extern int      vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi);
 extern int      vorbis_synthesis(vorbis_block *vb,ogg_packet *op);
+extern int      vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op);
 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);
index b89f70a..46d16c8 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: PCM data vector blocking, windowing and dis/reassembly
- last mod: $Id: block.c,v 1.58 2002/01/22 11:59:00 xiphmont Exp $
+ last mod: $Id: block.c,v 1.59 2002/02/28 04:12:48 xiphmont Exp $
 
  Handle windowing, overlap-add, etc of the PCM vectors.  This is made
  more amusing by Vorbis' current two allowed block sizes.
@@ -620,23 +620,19 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
   codec_setup_info *ci=vi->codec_setup;
   int i,j;
 
+  if(!vb)return(OV_EINVAL);
   if(v->pcm_current>v->pcm_returned  && v->pcm_returned!=-1)return(OV_EINVAL);
-
+    
   v->lW=v->W;
   v->W=vb->W;
   v->nW=-1;
-
-  v->glue_bits+=vb->glue_bits;
-  v->time_bits+=vb->time_bits;
-  v->floor_bits+=vb->floor_bits;
-  v->res_bits+=vb->res_bits;
-
+  
   if(v->sequence+1 != vb->sequence)v->granulepos=-1; /* out of sequence;
-                                                     lose count */
-
+                                                       lose count */
   v->sequence=vb->sequence;
   
-  {
+  if(vb->pcm){  /* not pcm to process if vorbis_synthesis_trackonly 
+                  was called on block */
     int n=ci->blocksizes[v->W]/2;
     int n0=ci->blocksizes[0]/2;
     int n1=ci->blocksizes[1]/2;
@@ -644,12 +640,17 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
     int thisCenter;
     int prevCenter;
     
+    v->glue_bits+=vb->glue_bits;
+    v->time_bits+=vb->time_bits;
+    v->floor_bits+=vb->floor_bits;
+    v->res_bits+=vb->res_bits;
+    
     if(v->centerW){
       thisCenter=n1;
       prevCenter=0;
     }else{
-      thisCenter=0;
-      prevCenter=n1;
+       thisCenter=0;
+       prevCenter=n1;
     }
     
     /* v->pcm is now used like a two-stage double buffer.  We don't want
@@ -709,7 +710,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
     /* deal with initial packet state; we do this using the explicit
        pcm_returned==-1 flag otherwise we're sensitive to first block
        being short or long */
-
+    
     if(v->pcm_returned==-1){
       v->pcm_returned=thisCenter;
       v->pcm_current=thisCenter;
@@ -719,57 +720,56 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
        ci->blocksizes[v->lW]/4+
        ci->blocksizes[v->W]/4;
     }
+    
+  }
 
-    /* track the frame number... This is for convenience, but also
-       making sure our last packet doesn't end with added padding.  If
-       the last packet is partial, the number of samples we'll have to
-       return will be past the vb->granulepos.
-       
-       This is not foolproof!  It will be confused if we begin
-       decoding at the last page after a seek or hole.  In that case,
-       we don't have a starting point to judge where the last frame
-       is.  For this reason, vorbisfile will always try to make sure
-       it reads the last two marked pages in proper sequence */
-
-    if(v->granulepos==-1)
-      if(vb->granulepos==-1){
-       v->granulepos=0;
-      }else{
-       v->granulepos=vb->granulepos;
-      }
-    else{
-      v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4;
-      if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){
-
-       if(v->granulepos>vb->granulepos){
-         long extra=v->granulepos-vb->granulepos;
-
-         if(vb->eofflag){
-           /* partial last frame.  Strip the extra samples off */
-           v->pcm_current-=extra;
-         }else if(vb->sequence == 1){
-           /* ^^^ argh, this can be 1 from seeking! */
-
-
-           /* partial first frame.  Discard extra leading samples */
-           v->pcm_returned+=extra;
-           if(v->pcm_returned>v->pcm_current)
-             v->pcm_returned=v->pcm_current;
-           
-         }
+  /* track the frame number... This is for convenience, but also
+     making sure our last packet doesn't end with added padding.  If
+     the last packet is partial, the number of samples we'll have to
+     return will be past the vb->granulepos.
+     
+     This is not foolproof!  It will be confused if we begin
+     decoding at the last page after a seek or hole.  In that case,
+     we don't have a starting point to judge where the last frame
+     is.  For this reason, vorbisfile will always try to make sure
+     it reads the last two marked pages in proper sequence */
+  
+  if(v->granulepos==-1){
+    if(vb->granulepos!=-1){ /* only set if we have a position to set to */
+      v->granulepos=vb->granulepos;
+    }
+  }else{
+    v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4;
+    if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){
+      
+      if(v->granulepos>vb->granulepos){
+       long extra=v->granulepos-vb->granulepos;
+       
+       if(vb->eofflag){
+         /* partial last frame.  Strip the extra samples off */
+         v->pcm_current-=extra;
+       }else if(vb->sequence == 1){
+         /* ^^^ argh, this can be 1 from seeking! */
          
-       }/* else{ Shouldn't happen *unless* the bitstream is out of
-           spec.  Either way, believe the bitstream } */
-       v->granulepos=vb->granulepos;
-      }
+         
+         /* partial first frame.  Discard extra leading samples */
+         v->pcm_returned+=extra;
+         if(v->pcm_returned>v->pcm_current)
+           v->pcm_returned=v->pcm_current;
+         
+       } /* else {Shouldn't happen *unless* the bitstream is out of
+            spec.  Either way, believe the bitstream } */
+      } /* else {Shouldn't happen *unless* the bitstream is out of
+          spec.  Either way, believe the bitstream } */
+      v->granulepos=vb->granulepos;
     }
-    
-    /* Update, cleanup */
-    
-    if(vb->eofflag)v->eofflag=1;
   }
   
+  /* Update, cleanup */
+  
+  if(vb->eofflag)v->eofflag=1;
   return(0);
+  
 }
 
 /* pcm==NULL indicates we just want the pending samples, no more */
@@ -787,9 +787,9 @@ int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){
   return(0);
 }
 
-int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){
-  if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL);
-  v->pcm_returned+=bytes;
+int vorbis_synthesis_read(vorbis_dsp_state *v,int n){
+  if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL);
+  v->pcm_returned+=n;
   return(0);
 }
 
index 6237c8c..22a97a7 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: channel mapping 0 implementation
- last mod: $Id: mapping0.c,v 1.44 2002/01/22 11:59:00 xiphmont Exp $
+ last mod: $Id: mapping0.c,v 1.45 2002/02/28 04:12:48 xiphmont Exp $
 
  ********************************************************************/
 
@@ -681,7 +681,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_look_mapping *l){
        pcm[j]=0.f;
 
   }
-           
+
   /* all done! */
   return(0);
 }
@@ -697,3 +697,4 @@ vorbis_func_mapping mapping0_exportbundle={
   &mapping0_forward,
   &mapping0_inverse
 };
+
index 52cf51a..6a032d3 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: single-block PCM synthesis
- last mod: $Id: synthesis.c,v 1.25 2001/12/20 01:00:30 segher Exp $
+ last mod: $Id: synthesis.c,v 1.26 2002/02/28 04:12:48 xiphmont Exp $
 
  ********************************************************************/
 
@@ -73,6 +73,53 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){
   return(_mapping_P[type]->inverse(vb,b->mode[mode]));
 }
 
+/* used to track pcm position without actually performing decode.
+   Useful for sequential 'fast forward' */
+int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){
+  vorbis_dsp_state     *vd=vb->vd;
+  backend_lookup_state *b=vd->backend_state;
+  vorbis_info          *vi=vd->vi;
+  codec_setup_info     *ci=vi->codec_setup;
+  oggpack_buffer       *opb=&vb->opb;
+  int                   mode;
+  /* first things first.  Make sure decode is ready */
+  _vorbis_block_ripcord(vb);
+  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);
+  }
+
+  /* read our mode and pre/post windowsize */
+  mode=oggpack_read(opb,b->modebits);
+  if(mode==-1)return(OV_EBADPACKET);
+  
+  vb->mode=mode;
+  vb->W=ci->mode_param[mode]->blockflag;
+  if(vb->W){
+    vb->lW=oggpack_read(opb,1);
+    vb->nW=oggpack_read(opb,1);
+    if(vb->nW==-1)   return(OV_EBADPACKET);
+  }else{
+    vb->lW=0;
+    vb->nW=0;
+  }
+  
+  /* more setup */
+  vb->granulepos=op->granulepos;
+  vb->sequence=op->packetno-3; /* first block is third packet */
+  vb->eofflag=op->e_o_s;
+
+  /* no pcm */
+  vb->pcmend=0;
+  vb->pcm=NULL;
+
+  return(0);
+}
+
 long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){
   codec_setup_info     *ci=vi->codec_setup;
   oggpack_buffer       opb;
index 0185546..8ae4357 100644 (file)
@@ -11,7 +11,7 @@
  ********************************************************************
 
  function: stdio-based convenience library for opening/seeking/decoding
- last mod: $Id: vorbisfile.c,v 1.55 2002/01/22 08:06:08 xiphmont Exp $
+ last mod: $Id: vorbisfile.c,v 1.56 2002/02/28 04:12:48 xiphmont Exp $
 
  ********************************************************************/
 
@@ -416,7 +416,9 @@ static void _decode_clear(OggVorbis_File *vf){
            1) got a packet 
 */
 
-static int _process_packet(OggVorbis_File *vf,int readp){
+static int _fetch_and_process_packet(OggVorbis_File *vf,
+                                    ogg_packet *op_in,
+                                    int readp){
   ogg_page og;
 
   /* handle one packet.  Try to fetch it from current stream state */
@@ -428,31 +430,37 @@ static int _process_packet(OggVorbis_File *vf,int readp){
     if(vf->ready_state==INITSET){
       while(1) {
        ogg_packet op;
-       int result=ogg_stream_packetout(&vf->os,&op);
+       ogg_packet *op_ptr=(op_in?op_in:&op);
+       int result=ogg_stream_packetout(&vf->os,op_ptr);
        ogg_int64_t granulepos;
 
+       op_in=NULL;
        if(result==-1)return(OV_HOLE); /* hole in the data. */
        if(result>0){
          /* got a packet.  process it */
-         granulepos=op.granulepos;
-         if(!vorbis_synthesis(&vf->vb,&op)){ /* lazy check for lazy
-                                                 header handling.  The
-                                                 header packets aren't
-                                                 audio, so if/when we
-                                                 submit them,
-                                                 vorbis_synthesis will
-                                                 reject them */
+         granulepos=op_ptr->granulepos;
+         if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy
+                                                   header handling.  The
+                                                   header packets aren't
+                                                   audio, so if/when we
+                                                   submit them,
+                                                   vorbis_synthesis will
+                                                   reject them */
 
            /* suck in the synthesis data and track bitrate */
            {
              int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+             /* for proper use of libvorbis within libvorbisfile,
+                 oldsamples will always be zero. */
+             if(oldsamples)return(OV_EFAULT);
+             
              vorbis_synthesis_blockin(&vf->vd,&vf->vb);
              vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
-             vf->bittrack+=op.bytes*8;
+             vf->bittrack+=op_ptr->bytes*8;
            }
          
            /* update the pcm offset. */
-           if(granulepos!=-1 && !op.e_o_s){
+           if(granulepos!=-1 && !op_ptr->e_o_s){
              int link=(vf->seekable?vf->current_link:0);
              int i,samples;
            
@@ -466,7 +474,7 @@ static int _process_packet(OggVorbis_File *vf,int readp){
                 granulepos declares the last frame in the stream, and the
                 last packet of the last page may be a partial frame.
                 So, we need a previous granulepos from an in-sequence page
-                to have a reference point.  Thus the !op.e_o_s clause
+                to have a reference point.  Thus the !op_ptr->e_o_s clause
                 above */
            
              samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
@@ -1125,6 +1133,7 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
   int thisblock,lastblock=0;
   int ret=ov_pcm_seek_page(vf,pos);
   if(ret<0)return(ret);
+  _make_decode_ready(vf);
 
   /* discard leading packets we don't need for the lapping of the
      position we want; don't decode them */
@@ -1136,17 +1145,22 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
     int ret=ogg_stream_packetpeek(&vf->os,&op);
     if(ret>0){
       thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
+      if(thisblock<0)thisblock=0; /* non audio packet */
       if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
-
+      
       if(vf->pcm_offset+((thisblock+
                          vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break;
-
+      
+      /* remove the packet from packet queue and track its granulepos */
       ogg_stream_packetout(&vf->os,NULL);
+      vorbis_synthesis_trackonly(&vf->vb,&op);  /* set up a vb with
+                                                   only tracking, no
+                                                   pcm_decode */
+      vorbis_synthesis_blockin(&vf->vd,&vf->vb); 
       
-
       /* end of logical stream case is hard, especially with exact
-         length positioning. */
-
+        length positioning. */
+      
       if(op.granulepos>-1){
        int i;
        /* always believe the stream markers */
@@ -1154,9 +1168,9 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
        for(i=0;i<vf->current_link;i++)
          vf->pcm_offset+=vf->pcmlengths[i];
       }
-
+       
       lastblock=thisblock;
-
+      
     }else{
       if(ret<0 && ret!=OV_HOLE)break;
       
@@ -1176,15 +1190,16 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
        ogg_stream_init(&vf->os,vf->current_serialno);
        ogg_stream_reset(&vf->os); 
        vf->ready_state=STREAMSET;      
+       _make_decode_ready(vf);
        lastblock=0;
       }
+
       ogg_stream_pagein(&vf->os,&og);
     }
   }
 
   /* 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;
@@ -1195,7 +1210,7 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
     vf->pcm_offset+=samples;
     
     if(samples<target)
-      if(_process_packet(vf,1)<=0)
+      if(_fetch_and_process_packet(vf,NULL,1)<=0)
        vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
   }
   return 0;
@@ -1393,7 +1408,7 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int *bitstream){
 
     /* suck in another packet */
     {
-      int ret=_process_packet(vf,1);
+      int ret=_fetch_and_process_packet(vf,NULL,1);
       if(ret==OV_EOF)return(0);
       if(ret<=0)return(ret);
     }
@@ -1419,7 +1434,7 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length,
 
     /* suck in another packet */
     {
-      int ret=_process_packet(vf,1);
+      int ret=_fetch_and_process_packet(vf,NULL,1);
       if(ret==OV_EOF)return(0);
       if(ret<=0)return(ret);
     }