summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty <xiphmont@xiph.org>2003-03-29 03:07:21 +0000
committerMonty <xiphmont@xiph.org>2003-03-29 03:07:21 +0000
commit3c764af8013b5b153d3e97311d56a8741536f4cf (patch)
treeb8201ccdd984c9b5b682641996a19cca15b6593c
parenta80cb134d84683d6c4961e2c33fddf3a24b378fa (diff)
downloadtremor-3c764af8013b5b153d3e97311d56a8741536f4cf.tar.gz
Roll all recent optimizations and fixes to mainline vorbisfile into Tremor
First mainline deployment of libogg 2 (embedded into Tremor) git-svn-id: https://svn.xiph.org/trunk/Tremor@4565 0101bb08-14d6-0310-b084-bc0e0c8e3800
-rw-r--r--Makefile.am2
-rw-r--r--bitwise.c253
-rw-r--r--block.c149
-rw-r--r--framing.c1263
-rw-r--r--info.c4
-rw-r--r--ivorbiscodec.h3
-rw-r--r--ivorbisfile.h13
-rw-r--r--misc.h15
-rw-r--r--ogg.h214
-rw-r--r--synthesis.c38
-rw-r--r--vorbisfile.c670
11 files changed, 1743 insertions, 881 deletions
diff --git a/Makefile.am b/Makefile.am
index 9041c4f..d0a6c16 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ INCLUDES = -I./
lib_LTLIBRARIES = libvorbisidec.la
-libvorbisidec_la_SOURCES = mdct.c block.c window.c \
+libvorbisidec_la_SOURCES = mdct.c block.c window.c misc.c\
synthesis.c info.c \
floor1.c floor0.c vorbisfile.c \
res012.c mapping0.c registry.c codebook.c \
diff --git a/bitwise.c b/bitwise.c
index 1fefb93..ccb82b0 100644
--- a/bitwise.c
+++ b/bitwise.c
@@ -22,8 +22,6 @@
#include <stdlib.h>
#include "ogg.h"
-#define BUFFER_INCREMENT 256
-
static unsigned long mask[]=
{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f,
0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff,
@@ -33,80 +31,235 @@ static unsigned long mask[]=
0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff,
0x3fffffff,0x7fffffff,0xffffffff };
-void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){
+/* mark read process as having run off the end */
+static void _adv_halt(oggpack_buffer *b){
+ b->headptr=b->head->buffer->data+b->head->begin+b->head->length;
+ b->headend=-1;
+ b->headbit=0;
+}
+
+/* spans forward, skipping as many bytes as headend is negative; if
+ headend is zero, simply finds next byte. If we're up to the end
+ of the buffer, leaves headend at zero. If we've read past the end,
+ halt the decode process. */
+static void _span(oggpack_buffer *b){
+ while(b->headend<1){
+ if(b->head->next){
+ b->count+=b->head->length;
+ b->head=b->head->next;
+ b->headptr=b->head->buffer->data+b->head->begin-b->headend;
+ b->headend+=b->head->length;
+ }else{
+ /* we've either met the end of decode, or gone past it. halt
+ only if we're past */
+ if(b->headend<0 || b->headbit)
+ /* read has fallen off the end */
+ _adv_halt(b);
+
+ break;
+ }
+ }
+}
+
+void oggpack_readinit(oggpack_buffer *b,ogg_reference *r){
memset(b,0,sizeof(*b));
- b->buffer=b->ptr=buf;
- b->storage=bytes;
+
+ b->tail=b->head=r;
+ b->count=0;
+ b->headptr=b->head->buffer->data+b->head->begin;
+ b->headend=b->head->length;
+ _span(b);
}
+#define _lookspan() while(!end){\
+ head=head->next;\
+ if(!head) return -1;\
+ ptr=head->buffer->data + head->begin;\
+ end=head->length;\
+ }
+
/* Read in bits without advancing the bitptr; bits <= 32 */
long oggpack_look(oggpack_buffer *b,int bits){
- unsigned long ret;
unsigned long m=mask[bits];
+ unsigned long ret;
- bits+=b->endbit;
+ bits+=b->headbit;
- if(b->endbyte+4>=b->storage){
- /* not the main path */
- if(b->endbyte*8+bits>b->storage*8)return(-1);
- }
-
- ret=b->ptr[0]>>b->endbit;
- if(bits>8){
- ret|=b->ptr[1]<<(8-b->endbit);
- if(bits>16){
- ret|=b->ptr[2]<<(16-b->endbit);
- if(bits>24){
- ret|=b->ptr[3]<<(24-b->endbit);
- if(bits>32 && b->endbit)
- ret|=b->ptr[4]<<(32-b->endbit);
+ if(bits >= b->headend<<3){
+ int end=b->headend;
+ unsigned char *ptr=b->headptr;
+ ogg_reference *head=b->head;
+
+ if(end<0)return -1;
+
+ if(bits){
+ _lookspan();
+ ret=*ptr++>>b->headbit;
+ if(bits>8){
+ --end;
+ _lookspan();
+ ret|=*ptr++<<(8-b->headbit);
+ if(bits>16){
+ --end;
+ _lookspan();
+ ret|=*ptr++<<(16-b->headbit);
+ if(bits>24){
+ --end;
+ _lookspan();
+ ret|=*ptr++<<(24-b->headbit);
+ if(bits>32 && b->headbit){
+ --end;
+ _lookspan();
+ ret|=*ptr<<(32-b->headbit);
+ }
+ }
+ }
+ }
+ }
+
+ }else{
+
+ /* make this a switch jump-table */
+ ret=b->headptr[0]>>b->headbit;
+ if(bits>8){
+ ret|=b->headptr[1]<<(8-b->headbit);
+ if(bits>16){
+ ret|=b->headptr[2]<<(16-b->headbit);
+ if(bits>24){
+ ret|=b->headptr[3]<<(24-b->headbit);
+ if(bits>32 && b->headbit)
+ ret|=b->headptr[4]<<(32-b->headbit);
+ }
}
}
}
- return(m&ret);
+
+ ret&=m;
+ return ret;
}
+/* limited to 32 at a time */
void oggpack_adv(oggpack_buffer *b,int bits){
- bits+=b->endbit;
- b->ptr+=bits/8;
- b->endbyte+=bits/8;
- b->endbit=bits&7;
+ bits+=b->headbit;
+ b->headbit=bits&7;
+ b->headptr+=bits/8;
+ if((b->headend-=bits/8)<1)_span(b);
+}
+
+/* spans forward and finds next byte. Never halts */
+static void _span_one(oggpack_buffer *b){
+ while(b->headend<1){
+ if(b->head->next){
+ b->count+=b->head->length;
+ b->head=b->head->next;
+ b->headptr=b->head->buffer->data+b->head->begin;
+ b->headend=b->head->length;
+ }else
+ break;
+ }
+}
+
+static int _halt_one(oggpack_buffer *b){
+ if(b->headend<1){
+ _adv_halt(b);
+ return -1;
+ }
+ return 0;
+}
+
+int oggpack_eop(oggpack_buffer *b){
+ if(b->headend<0)return -1;
+ return 0;
}
/* bits <= 32 */
long oggpack_read(oggpack_buffer *b,int bits){
- unsigned long ret;
unsigned long m=mask[bits];
+ ogg_uint32_t ret;
- bits+=b->endbit;
+ bits+=b->headbit;
- if(b->endbyte+4>=b->storage){
- /* not the main path */
- ret=-1UL;
- if(b->endbyte*8+bits>b->storage*8)goto overflow;
- }
+ if(bits >= b->headend<<3){
+
+ if(b->headend<0)return -1;
+
+ if(bits){
+ if (_halt_one(b)) return -1;
+ ret=*b->headptr>>b->headbit;
+
+ if(bits>=8){
+ ++b->headptr;
+ --b->headend;
+ _span_one(b);
+ if(bits>8){
+ if (_halt_one(b)) return -1;
+ ret|=*b->headptr<<(8-b->headbit);
+
+ if(bits>=16){
+ ++b->headptr;
+ --b->headend;
+ _span_one(b);
+ if(bits>16){
+ if (_halt_one(b)) return -1;
+ ret|=*b->headptr<<(16-b->headbit);
+
+ if(bits>=24){
+ ++b->headptr;
+ --b->headend;
+ _span_one(b);
+ if(bits>24){
+ if (_halt_one(b)) return -1;
+ ret|=*b->headptr<<(24-b->headbit);
+
+ if(bits>=32){
+ ++b->headptr;
+ --b->headend;
+ _span_one(b);
+ if(bits>32){
+ if (_halt_one(b)) return -1;
+ if(b->headbit)ret|=*b->headptr<<(32-b->headbit);
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }else{
- ret=b->ptr[0]>>b->endbit;
- if(bits>8){
- ret|=b->ptr[1]<<(8-b->endbit);
- if(bits>16){
- ret|=b->ptr[2]<<(16-b->endbit);
- if(bits>24){
- ret|=b->ptr[3]<<(24-b->endbit);
- if(bits>32 && b->endbit){
- ret|=b->ptr[4]<<(32-b->endbit);
- }
+ ret=b->headptr[0]>>b->headbit;
+ if(bits>8){
+ ret|=b->headptr[1]<<(8-b->headbit);
+ if(bits>16){
+ ret|=b->headptr[2]<<(16-b->headbit);
+ if(bits>24){
+ ret|=b->headptr[3]<<(24-b->headbit);
+ if(bits>32 && b->headbit){
+ ret|=b->headptr[4]<<(32-b->headbit);
+ }
+ }
}
}
+
+ b->headptr+=bits/8;
+ b->headend-=bits/8;
}
+
ret&=m;
-
- overflow:
+ b->headbit=bits&7;
+ return ret;
+}
+
+long oggpack_bytes(oggpack_buffer *b){
+ return(b->count+b->headptr-b->head->buffer->data-b->head->begin+
+ (b->headbit+7)/8);
+}
- b->ptr+=bits/8;
- b->endbyte+=bits/8;
- b->endbit=bits&7;
- return(ret);
+long oggpack_bits(oggpack_buffer *b){
+ return((b->count+b->headptr-b->head->buffer->data-b->head->begin)*8+
+ b->headbit);
}
-#undef BUFFER_INCREMENT
diff --git a/block.c b/block.c
index e35ff6b..8949253 100644
--- a/block.c
+++ b/block.c
@@ -143,7 +143,7 @@ int vorbis_block_clear(vorbis_block *vb){
return(0);
}
-int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){
+static int _vds_init(vorbis_dsp_state *v,vorbis_info *vi){
int i;
codec_setup_info *ci=(codec_setup_info *)vi->codec_setup;
private_state *b=NULL;
@@ -180,11 +180,6 @@ int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){
v->lW=0; /* previous window size */
v->W=0; /* current window size */
- /* all vector indexes */
- v->centerW=ci->blocksizes[1]/2;
-
- v->pcm_current=v->centerW;
-
/* initialize all the mapping/backend lookups */
b->mode=(vorbis_look_mapping **)_ogg_calloc(ci->modes,sizeof(*b->mode));
for(i=0;i<ci->modes;i++){
@@ -193,7 +188,21 @@ int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){
b->mode[i]=_mapping_P[maptype]->look(v,ci->mode_param[i],
ci->map_param[mapnum]);
}
+ return(0);
+}
+
+int vorbis_synthesis_restart(vorbis_dsp_state *v){
+ vorbis_info *vi=v->vi;
+ codec_setup_info *ci;
+ if(!v->backend_state)return -1;
+ if(!vi)return -1;
+ ci=vi->codec_setup;
+ if(!ci)return -1;
+
+ v->centerW=ci->blocksizes[1]/2;
+ v->pcm_current=v->centerW;
+
v->pcm_returned=-1;
v->granulepos=-1;
v->sequence=-1;
@@ -202,6 +211,13 @@ int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){
return(0);
}
+int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){
+ _vds_init(v,vi);
+ vorbis_synthesis_restart(v);
+
+ return(0);
+}
+
void vorbis_dsp_clear(vorbis_dsp_state *v){
int i;
if(v){
@@ -258,7 +274,8 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
v->sequence=vb->sequence;
- {
+ if(vb->pcm){ /* no 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;
@@ -341,75 +358,75 @@ 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(b->sample_count==-1){
- b->sample_count=0;
- }else{
- b->sample_count+=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(b->sample_count==-1){
+ b->sample_count=0;
+ }else{
+ b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4;
+ }
- if(v->granulepos==-1){
- if(vb->granulepos!=-1){ /* only set if we have a position to set to */
-
- v->granulepos=vb->granulepos;
-
- /* is this a short page? */
- if(b->sample_count>v->granulepos){
- /* corner case; if this is both the first and last audio page,
- then spec says the end is cut, not beginning */
- if(vb->eofflag){
- /* trim the end */
- /* no preceeding granulepos; assume we started at zero (we'd
- have to in a short single-page stream) */
- /* granulepos could be -1 due to a seek, but that would result
- in a long coun`t, not short count */
-
- v->pcm_current-=(b->sample_count-v->granulepos);
- }else{
- /* trim the beginning */
- v->pcm_returned+=(b->sample_count-v->granulepos);
- if(v->pcm_returned>v->pcm_current)
- v->pcm_returned=v->pcm_current;
- }
+ if(v->granulepos==-1){
+ if(vb->granulepos!=-1){ /* only set if we have a position to set to */
+
+ v->granulepos=vb->granulepos;
+
+ /* is this a short page? */
+ if(b->sample_count>v->granulepos){
+ /* corner case; if this is both the first and last audio page,
+ then spec says the end is cut, not beginning */
+ if(vb->eofflag){
+ /* trim the end */
+ /* no preceeding granulepos; assume we started at zero (we'd
+ have to in a short single-page stream) */
+ /* granulepos could be -1 due to a seek, but that would result
+ in a long coun`t, not short count */
+ v->pcm_current-=(b->sample_count-v->granulepos);
+ }else{
+ /* trim the beginning */
+ v->pcm_returned+=(b->sample_count-v->granulepos);
+ if(v->pcm_returned>v->pcm_current)
+ v->pcm_returned=v->pcm_current;
}
}
- }else{
- v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4;
- if(vb->granulepos!=-1 && 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(v->granulepos>vb->granulepos){
- long extra=v->granulepos-vb->granulepos;
-
- if(extra)
- if(vb->eofflag){
- /* partial last frame. Strip the extra samples off */
- v->pcm_current-=extra;
- } /* 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;
- }
+ if(extra)
+ if(vb->eofflag){
+ /* partial last frame. Strip the extra samples off */
+ v->pcm_current-=extra;
+ } /* 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);
}
diff --git a/framing.c b/framing.c
index 15b8919..12a0de9 100644
--- a/framing.c
+++ b/framing.c
@@ -6,13 +6,12 @@
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
- * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
- function: code raw [Vorbis] packets into framed OggSquish stream and
- decode Ogg streams back into raw packets
+ function: decode Ogg streams back into raw packets
note: The CRC code is directly derived from public domain code by
Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html
@@ -23,54 +22,452 @@
#include <stdlib.h>
#include <string.h>
#include "ogg.h"
+#include "misc.h"
+
/* A complete description of Ogg framing exists in docs/framing.html */
+/* basic, centralized Ogg memory management based on linked lists of
+ references to refcounted memory buffers. References and buffers
+ are both recycled. Buffers are passed around and consumed in
+ reference form. */
+
+static ogg_buffer_state *ogg_buffer_create(void){
+ ogg_buffer_state *bs=_ogg_calloc(1,sizeof(*bs));
+ return bs;
+}
+
+/* destruction is 'lazy'; there may be memory references outstanding,
+ and yanking the buffer state out from underneath would be
+ antisocial. Dealloc what is currently unused and have
+ _release_one watch for the stragglers to come in. When they do,
+ finish destruction. */
+
+/* call the helper while holding lock */
+static void _ogg_buffer_destroy(ogg_buffer_state *bs){
+ ogg_buffer *bt;
+ ogg_reference *rt;
+
+ if(bs->shutdown){
+
+ bt=bs->unused_buffers;
+ rt=bs->unused_references;
+
+ if(!bs->outstanding){
+ _ogg_free(bs);
+ return;
+ }
+
+ while(bt){
+ ogg_buffer *b=bt;
+ bt=b->ptr.next;
+ if(b->data)_ogg_free(b->data);
+ _ogg_free(b);
+ }
+ bs->unused_buffers=0;
+ while(rt){
+ ogg_reference *r=rt;
+ rt=r->next;
+ _ogg_free(r);
+ }
+ bs->unused_references=0;
+ }
+}
+
+static void ogg_buffer_destroy(ogg_buffer_state *bs){
+ bs->shutdown=1;
+ _ogg_buffer_destroy(bs);
+}
+
+static ogg_buffer *_fetch_buffer(ogg_buffer_state *bs,long bytes){
+ ogg_buffer *ob;
+ bs->outstanding++;
+
+ /* do we have an unused buffer sitting in the pool? */
+ if(bs->unused_buffers){
+ ob=bs->unused_buffers;
+ bs->unused_buffers=ob->ptr.next;
+
+ /* if the unused buffer is too small, grow it */
+ if(ob->size<bytes){
+ ob->data=_ogg_realloc(ob->data,bytes);
+ ob->size=bytes;
+ }
+ }else{
+ /* allocate a new buffer */
+ ob=_ogg_malloc(sizeof(*ob));
+ ob->data=_ogg_malloc(bytes<16?16:bytes);
+ ob->size=bytes;
+ }
+
+ ob->refcount=1;
+ ob->ptr.owner=bs;
+ return ob;
+}
+
+static ogg_reference *_fetch_ref(ogg_buffer_state *bs){
+ ogg_reference *or;
+ bs->outstanding++;
+
+ /* do we have an unused reference sitting in the pool? */
+ if(bs->unused_references){
+ or=bs->unused_references;
+ bs->unused_references=or->next;
+ }else{
+ /* allocate a new reference */
+ or=_ogg_malloc(sizeof(*or));
+ }
+
+ or->begin=0;
+ or->length=0;
+ or->next=0;
+ return or;
+}
+
+/* fetch a reference pointing to a fresh, initially continguous buffer
+ of at least [bytes] length */
+static ogg_reference *ogg_buffer_alloc(ogg_buffer_state *bs,long bytes){
+ ogg_buffer *ob=_fetch_buffer(bs,bytes);
+ ogg_reference *or=_fetch_ref(bs);
+ or->buffer=ob;
+ return or;
+}
+
+/* enlarge the data buffer in the current link */
+static void ogg_buffer_realloc(ogg_reference *or,long bytes){
+ ogg_buffer *ob=or->buffer;
+
+ /* if the unused buffer is too small, grow it */
+ if(ob->size<bytes){
+ ob->data=_ogg_realloc(ob->data,bytes);
+ ob->size=bytes;
+ }
+}
+
+static void _ogg_buffer_mark_one(ogg_reference *or){
+ or->buffer->refcount++;
+}
+
+/* increase the refcount of the buffers to which the reference points */
+static void ogg_buffer_mark(ogg_reference *or){
+ while(or){
+ _ogg_buffer_mark_one(or);
+ or=or->next;
+ }
+}
+
+/* duplicate a reference (pointing to the same actual buffer memory)
+ and increment buffer refcount. If the desired segment begins out
+ of range, NULL is returned; if the desired segment is simply zero
+ length, a zero length ref is returned. Partial range overlap
+ returns the overlap of the ranges */
+static ogg_reference *ogg_buffer_sub(ogg_reference *or,long begin,long length){
+ ogg_reference *ret=0,*head=0;
+
+ /* walk past any preceeding fragments we don't want */
+ while(or && begin>=or->length){
+ begin-=or->length;
+ or=or->next;
+ }
+
+ /* duplicate the reference chain; increment refcounts */
+ while(or && length){
+ ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner);
+ if(head)
+ head->next=temp;
+ else
+ ret=temp;
+ head=temp;
+ head->buffer=or->buffer;
+ head->begin=or->begin+begin;
+ head->length=length;
+ if(head->length>or->length-begin)
+ head->length=or->length-begin;
+
+ begin=0;
+ length-=head->length;
+ or=or->next;
+ }
+
+ ogg_buffer_mark(ret);
+ return ret;
+}
+
+ogg_reference *ogg_buffer_dup(ogg_reference *or){
+ ogg_reference *ret=0,*head=0;
+ /* duplicate the reference chain; increment refcounts */
+ while(or){
+ ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner);
+ if(head)
+ head->next=temp;
+ else
+ ret=temp;
+ head=temp;
+ head->buffer=or->buffer;
+ head->begin=or->begin;
+ head->length=or->length;
+ or=or->next;
+ }
+
+ ogg_buffer_mark(ret);
+ return ret;
+}
+
+/* split a reference into two references; 'return' is a reference to
+ the buffer preceeding pos and 'head'/'tail' are the buffer past the
+ split. If pos is at or past the end of the passed in segment,
+ 'head/tail' are NULL */
+static ogg_reference *ogg_buffer_split(ogg_reference **tail,
+ ogg_reference **head,long pos){
+
+ /* walk past any preceeding fragments to one of:
+ a) the exact boundary that seps two fragments
+ b) the fragment that needs split somewhere in the middle */
+ ogg_reference *ret=*tail;
+ ogg_reference *or=*tail;
+
+ while(or && pos>or->length){
+ pos-=or->length;
+ or=or->next;
+ }
+
+ if(!or || pos==0){
+
+ return 0;
+
+ }else{
+
+ if(pos>=or->length){
+ /* exact split, or off the end? */
+ if(or->next){
+
+ /* a split */
+ *tail=or->next;
+ or->next=0;
+
+ }else{
+
+ /* off or at the end */
+ *tail=*head=0;
+
+ }
+ }else{
+
+ /* split within a fragment */
+ long lengthA=pos;
+ long beginB=or->begin+pos;
+ long lengthB=or->length-pos;
+
+ /* make a new reference to tail the second piece */
+ *tail=_fetch_ref(or->buffer->ptr.owner);
+
+ (*tail)->buffer=or->buffer;
+ (*tail)->begin=beginB;
+ (*tail)->length=lengthB;
+ (*tail)->next=or->next;
+ _ogg_buffer_mark_one(*tail);
+ if(head && or==*head)*head=*tail;
+
+ /* update the first piece */
+ or->next=0;
+ or->length=lengthA;
+
+ }
+ }
+ return ret;
+}
+
+static void ogg_buffer_release_one(ogg_reference *or){
+ ogg_buffer *ob=or->buffer;
+ ogg_buffer_state *bs=ob->ptr.owner;
+
+ ob->refcount--;
+ if(ob->refcount==0){
+ bs->outstanding--; /* for the returned buffer */
+ ob->ptr.next=bs->unused_buffers;
+ bs->unused_buffers=ob;
+ }
+
+ bs->outstanding--; /* for the returned reference */
+ or->next=bs->unused_references;
+ bs->unused_references=or;
+
+ _ogg_buffer_destroy(bs); /* lazy cleanup (if needed) */
+
+}
+
+/* release the references, decrease the refcounts of buffers to which
+ they point, release any buffers with a refcount that drops to zero */
+static void ogg_buffer_release(ogg_reference *or){
+ while(or){
+ ogg_reference *next=or->next;
+ ogg_buffer_release_one(or);
+ or=next;
+ }
+}
+
+static ogg_reference *ogg_buffer_pretruncate(ogg_reference *or,long pos){
+ /* release preceeding fragments we don't want */
+ while(or && pos>=or->length){
+ ogg_reference *next=or->next;
+ pos-=or->length;
+ ogg_buffer_release_one(or);
+ or=next;
+ }
+ if (or) {
+ or->begin+=pos;
+ or->length-=pos;
+ }
+ return or;
+}
+
+static ogg_reference *ogg_buffer_walk(ogg_reference *or){
+ if(!or)return NULL;
+ while(or->next){
+ or=or->next;
+ }
+ return(or);
+}
+
+/* *head is appended to the front end (head) of *tail; both continue to
+ be valid pointers, with *tail at the tail and *head at the head */
+static ogg_reference *ogg_buffer_cat(ogg_reference *tail, ogg_reference *head){
+ if(!tail)return head;
+
+ while(tail->next){
+ tail=tail->next;
+ }
+ tail->next=head;
+ return ogg_buffer_walk(head);
+}
+
+static void _positionB(oggbyte_buffer *b,int pos){
+ if(pos<b->pos){
+ /* start at beginning, scan forward */
+ b->ref=b->baseref;
+ b->pos=0;
+ b->end=b->pos+b->ref->length;
+ b->ptr=b->ref->buffer->data+b->ref->begin;
+ }
+}
+
+static void _positionF(oggbyte_buffer *b,int pos){
+ /* scan forward for position */
+ while(pos>=b->end){
+ /* just seek forward */
+ b->pos+=b->ref->length;
+ b->ref=b->ref->next;
+ b->end=b->ref->length+b->pos;
+ b->ptr=b->ref->buffer->data+b->ref->begin;
+ }
+}
+
+static int oggbyte_init(oggbyte_buffer *b,ogg_reference *or){
+ memset(b,0,sizeof(*b));
+ if(or){
+ b->ref=b->baseref=or;
+ b->pos=0;
+ b->end=b->ref->length;
+ b->ptr=b->ref->buffer->data+b->ref->begin;
+ return 0;
+ }else
+ return -1;
+}
+
+static void oggbyte_set4(oggbyte_buffer *b,ogg_uint32_t val,int pos){
+ int i;
+ _positionB(b,pos);
+ for(i=0;i<4;i++){
+ _positionF(b,pos);
+ b->ptr[pos-b->pos]=val;
+ val>>=8;
+ ++pos;
+ }
+}
+
+static unsigned char oggbyte_read1(oggbyte_buffer *b,int pos){
+ _positionB(b,pos);
+ _positionF(b,pos);
+ return b->ptr[pos-b->pos];
+}
+
+static ogg_uint32_t oggbyte_read4(oggbyte_buffer *b,int pos){
+ ogg_uint32_t ret;
+ _positionB(b,pos);
+ _positionF(b,pos);
+ ret=b->ptr[pos-b->pos];
+ _positionF(b,++pos);
+ ret|=b->ptr[pos-b->pos]<<8;
+ _positionF(b,++pos);
+ ret|=b->ptr[pos-b->pos]<<16;
+ _positionF(b,++pos);
+ ret|=b->ptr[pos-b->pos]<<24;
+ return ret;
+}
+
+static ogg_int64_t oggbyte_read8(oggbyte_buffer *b,int pos){
+ ogg_int64_t ret;
+ unsigned char t[7];
+ int i;
+ _positionB(b,pos);
+ for(i=0;i<7;i++){
+ _positionF(b,pos);
+ t[i]=b->ptr[pos++ -b->pos];
+ }
+
+ _positionF(b,pos);
+ ret=b->ptr[pos-b->pos];
+
+ for(i=6;i>=0;--i)
+ ret= ret<<8 | t[i];
+
+ return ret;
+}
+
+/* Now we get to the actual framing code */
+
int ogg_page_version(ogg_page *og){
- return((int)(og->header[4]));
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read1(&ob,4);
}
int ogg_page_continued(ogg_page *og){
- return((int)(og->header[5]&0x01));
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read1(&ob,5)&0x01;
}
int ogg_page_bos(ogg_page *og){
- return((int)(og->header[5]&0x02));
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read1(&ob,5)&0x02;
}
int ogg_page_eos(ogg_page *og){
- return((int)(og->header[5]&0x04));
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read1(&ob,5)&0x04;
}
ogg_int64_t ogg_page_granulepos(ogg_page *og){
- unsigned char *page=og->header;
- ogg_int64_t granulepos=page[13]&(0xff);
- granulepos= (granulepos<<8)|(page[12]&0xff);
- granulepos= (granulepos<<8)|(page[11]&0xff);
- granulepos= (granulepos<<8)|(page[10]&0xff);
- granulepos= (granulepos<<8)|(page[9]&0xff);
- granulepos= (granulepos<<8)|(page[8]&0xff);
- granulepos= (granulepos<<8)|(page[7]&0xff);
- granulepos= (granulepos<<8)|(page[6]&0xff);
- return(granulepos);
-}
-
-int ogg_page_serialno(ogg_page *og){
- return(og->header[14] |
- (og->header[15]<<8) |
- (og->header[16]<<16) |
- (og->header[17]<<24));
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read8(&ob,6);
+}
+
+ogg_uint32_t ogg_page_serialno(ogg_page *og){
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read4(&ob,14);
}
-long ogg_page_pageno(ogg_page *og){
- return(og->header[18] |
- (og->header[19]<<8) |
- (og->header[20]<<16) |
- (og->header[21]<<24));
+ogg_uint32_t ogg_page_pageno(ogg_page *og){
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+ return oggbyte_read4(&ob,18);
}
-
-
/* returns the number of packets that are completed on this page (if
the leading packet is begun on a previous page, but ends on this
page, it's counted */
@@ -89,13 +486,22 @@ more page packet), the return will be:
*/
int ogg_page_packets(ogg_page *og){
- int i,n=og->header[26],count=0;
+ int i;
+ int n;
+ int count=0;
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,og->header);
+
+ n=oggbyte_read1(&ob,26);
for(i=0;i<n;i++)
- if(og->header[27+i]<255)count++;
+ if(oggbyte_read1(&ob,27+i)<255)count++;
return(count);
}
-static const ogg_uint32_t crc_lookup[256]={
+/* Static CRC calculation table. See older code in CVS for dead
+ run-time initialization code. */
+
+static ogg_uint32_t crc_lookup[256]={
0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9,
0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005,
0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61,
@@ -161,190 +567,92 @@ static const ogg_uint32_t crc_lookup[256]={
0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668,
0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4};
-/* init the encode/decode logical stream state */
-
-int ogg_stream_init(ogg_stream_state *os,int serialno){
- if(os){
- memset(os,0,sizeof(*os));
- os->body_storage=6*1024;
- os->body_data=(unsigned char *)_ogg_malloc(os->body_storage*sizeof(*os->body_data));
-
- os->lacing_storage=128;
- os->lacing_vals=(int *)_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals));
- os->granule_vals=(ogg_int64_t *)_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals));
-
- os->serialno=serialno;
-
- return(0);
- }
- return(-1);
-}
-
-/* _clear does not free os, only the non-flat storage within */
-int ogg_stream_clear(ogg_stream_state *os){
- if(os){
- if(os->body_data)_ogg_free(os->body_data);
- if(os->lacing_vals)_ogg_free(os->lacing_vals);
- if(os->granule_vals)_ogg_free(os->granule_vals);
-
- memset(os,0,sizeof(*os));
- }
- return(0);
-}
-
-int ogg_stream_destroy(ogg_stream_state *os){
- if(os){
- ogg_stream_clear(os);
- _ogg_free(os);
- }
- return(0);
-}
-
-/* Helpers for ogg_stream_encode; this keeps the structure and
- what's happening fairly clear */
-
-static void _os_body_expand(ogg_stream_state *os,int needed){
- /* can we shift first? */
- if(os->body_storage<=os->body_fill+needed){
- long br=os->body_returned;
-
- os->body_fill-=br;
- if(os->body_fill)
- memmove(os->body_data,os->body_data+br,os->body_fill);
- os->body_returned=0;
- }
-
- /* still need more? */
- if(os->body_storage<=os->body_fill+needed){
- os->body_storage+=(needed+1024);
- os->body_data=(unsigned char *)_ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data));
- }
+ogg_sync_state *ogg_sync_create(void){
+ ogg_sync_state *oy=_ogg_calloc(1,sizeof(*oy));
+ memset(oy,0,sizeof(*oy));
+ oy->bufferpool=ogg_buffer_create();
+ return oy;
}
-static void _os_lacing_expand(ogg_stream_state *os,int needed){
-
- /* first check for a shift */
- if(os->lacing_storage<=os->lacing_fill+needed){
- long lr=os->lacing_returned;
-
- /* segment table */
- if(os->lacing_fill-lr){
- memmove(os->lacing_vals,os->lacing_vals+lr,
- (os->lacing_fill-lr)*sizeof(*os->lacing_vals));
- memmove(os->granule_vals,os->granule_vals+lr,
- (os->lacing_fill-lr)*sizeof(*os->granule_vals));
- }
- os->lacing_fill-=lr;
- os->lacing_packet-=lr;
- os->lacing_returned=0;
- }
-
- /* not enough? *now* realloc */
- if(os->lacing_storage<=os->lacing_fill+needed){
- os->lacing_storage+=(needed+32);
- os->lacing_vals=(int *)_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals));
- os->granule_vals=(ogg_int64_t *)_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals));
- }
-}
-
-/* checksum the page */
-/* Direct table CRC; note that this will be faster in the future if we
- perform the checksum silmultaneously with other copies */
-
-void ogg_page_checksum_set(ogg_page *og){
- if(og){
- ogg_uint32_t crc_reg=0;
- int i;
-
- /* safety; needed for API behavior, but not framing code */
- og->header[22]=0;
- og->header[23]=0;
- og->header[24]=0;
- og->header[25]=0;
-
- for(i=0;i<og->header_len;i++)
- crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]];
- for(i=0;i<og->body_len;i++)
- crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]];
-
- og->header[22]=crc_reg&0xff;
- og->header[23]=(crc_reg>>8)&0xff;
- og->header[24]=(crc_reg>>16)&0xff;
- og->header[25]=(crc_reg>>24)&0xff;
- }
-}
-
-/* DECODING PRIMITIVES: packet streaming layer **********************/
-
-/* This has two layers to place more of the multi-serialno and paging
- control in the application's hands. First, we expose a data buffer
- using ogg_sync_buffer(). The app either copies into the
- buffer, or passes it directly to read(), etc. We then call
- ogg_sync_wrote() to tell how many bytes we just added.
-
- Pages are returned (pointers into the buffer in ogg_sync_state)
- by ogg_sync_pageout(). The page is then submitted to
- ogg_stream_pagein() along with the appropriate
- ogg_stream_state* (ie, matching serialno). We then get raw
- packets out calling ogg_stream_packetout() with a
- ogg_stream_state. See the 'frame-prog.txt' docs for details and
- example code. */
-
-/* initialize the struct to a known state */
-int ogg_sync_init(ogg_sync_state *oy){
+int ogg_sync_destroy(ogg_sync_state *oy){
if(oy){
+ ogg_sync_reset(oy);
+ ogg_buffer_destroy(oy->bufferpool);
memset(oy,0,sizeof(*oy));
+ _ogg_free(oy);
}
- return(0);
+ return OGG_SUCCESS;
}
-/* clear non-flat storage within */
-int ogg_sync_clear(ogg_sync_state *oy){
- if(oy){
- if(oy->data)_ogg_free(oy->data);
- ogg_sync_init(oy);
+unsigned char *ogg_sync_bufferin(ogg_sync_state *oy, long bytes){
+
+ /* [allocate and] expose a buffer for data submission.
+
+ If there is no head fragment
+ allocate one and expose it
+ else
+ if the current head fragment has sufficient unused space
+ expose it
+ else
+ if the current head fragment is unused
+ resize and expose it
+ else
+ allocate new fragment and expose it
+ */
+
+ /* base case; fifo uninitialized */
+ if(!oy->fifo_head){
+ oy->fifo_head=oy->fifo_tail=ogg_buffer_alloc(oy->bufferpool,bytes);
+ return oy->fifo_head->buffer->data;
}
- return(0);
-}
-
-int ogg_sync_destroy(ogg_sync_state *oy){
- if(oy){
- ogg_sync_clear(oy);
- _ogg_free(oy);
+
+ /* space left in current fragment case */
+ if(oy->fifo_head->buffer->size-
+ oy->fifo_head->length-
+ oy->fifo_head->begin >= bytes)
+ return oy->fifo_head->buffer->data+
+ oy->fifo_head->length+oy->fifo_head->begin;
+
+ /* current fragment is unused, but too small */
+ if(!oy->fifo_head->length){
+ ogg_buffer_realloc(oy->fifo_head,bytes);
+ return oy->fifo_head->buffer->data+oy->fifo_head->begin;
+ }
+
+ /* current fragment used/full; get new fragment */
+ {
+ ogg_reference *new=ogg_buffer_alloc(oy->bufferpool,bytes);
+ oy->fifo_head->next=new;
+ oy->fifo_head=new;
}
- return(0);
+ return oy->fifo_head->buffer->data;
}
-char *ogg_sync_buffer(ogg_sync_state *oy, long size){
+int ogg_sync_wrote(ogg_sync_state *oy, long bytes){
+ if(!oy->fifo_head)return OGG_EINVAL;
+ if(oy->fifo_head->buffer->size-oy->fifo_head->length-oy->fifo_head->begin <
+ bytes)return OGG_EINVAL;
+ oy->fifo_head->length+=bytes;
+ oy->fifo_fill+=bytes;
+ return OGG_SUCCESS;
+}
- /* first, clear out any space that has been previously returned */
- if(oy->returned>8192){
- oy->fill-=oy->returned;
- if(oy->fill>0)
- memmove(oy->data,oy->data+oy->returned,oy->fill);
- oy->returned=0;
+static ogg_uint32_t _checksum(ogg_reference *or, int bytes){
+ ogg_uint32_t crc_reg=0;
+ int j,post;
+
+ while(or){
+ unsigned char *data=or->buffer->data+or->begin;
+ post=(bytes<or->length?bytes:or->length);
+ for(j=0;j<post;++j)
+ crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^data[j]];
+ bytes-=j;
+ or=or->next;
}
- if(size>oy->storage-oy->fill){
- /* We need to extend the internal buffer */
- long newsize=size+oy->fill+4096; /* an extra page to be nice */
-
- if(oy->data)
- oy->data=(unsigned char *)_ogg_realloc(oy->data,newsize);
- else
- oy->data=(unsigned char *)_ogg_malloc(newsize);
- oy->storage=newsize;
- }
-
- /* expose a segment at least as large as requested at the fill mark */
- return((char *)oy->data+oy->fill);
+ return crc_reg;
}
-int ogg_sync_wrote(ogg_sync_state *oy, long bytes){
- if(oy->fill+bytes>oy->storage)return(-1);
- oy->fill+=bytes;
- return(0);
-}
/* sync the stream. This is meant to be useful for finding page
boundaries.
@@ -357,97 +665,117 @@ int ogg_sync_wrote(ogg_sync_state *oy, long 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;
-
+ oggbyte_buffer page;
+ long bytes,ret=0;
+
+ ogg_page_release(og);
+
+ bytes=oy->fifo_fill;
+ oggbyte_init(&page,oy->fifo_tail);
+
if(oy->headerbytes==0){
- int headerbytes,i;
- if(bytes<27)return(0); /* not enough for a header */
+ if(bytes<27)goto sync_out; /* not enough for even a minimal 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 */
-
+ if(oggbyte_read1(&page,0)!=(int)'O' ||
+ oggbyte_read1(&page,1)!=(int)'g' ||
+ oggbyte_read1(&page,2)!=(int)'g' ||
+ oggbyte_read1(&page,3)!=(int)'S' ) goto sync_fail;
+
+ oy->headerbytes=oggbyte_read1(&page,26)+27;
+ }
+ if(bytes<oy->headerbytes)goto sync_out; /* not enough for header +
+ seg table */
+ if(oy->bodybytes==0){
+ int i;
/* count up body length in the segment table */
-
- for(i=0;i<page[26];i++)
- oy->bodybytes+=page[27+i];
- oy->headerbytes=headerbytes;
+ for(i=0;i<oy->headerbytes-27;i++)
+ oy->bodybytes+=oggbyte_read1(&page,27+i);
}
- if(oy->bodybytes+oy->headerbytes>bytes)return(0);
-
- /* The whole test page is buffered. Verify the checksum */
+ if(oy->bodybytes+oy->headerbytes>bytes)goto sync_out;
+
+ /* we have what appears to be a complete page; last test: verify
+ 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;
- ogg_page_checksum_set(&log);
-
- /* Compare */
- if(memcmp(chksum,page+22,4)){
+ ogg_uint32_t chksum=oggbyte_read4(&page,22);
+ oggbyte_set4(&page,0,22);
+
+ /* Compare checksums; memory continues to be common access */
+ if(chksum!=_checksum(oy->fifo_tail,oy->bodybytes+oy->headerbytes)){
+
/* 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);
+ at all). replace the computed checksum with the one actually
+ read in; remember all the memory is common access */
- /* Bad checksum. Lose sync */
+ oggbyte_set4(&page,chksum,22);
goto sync_fail;
}
+ oggbyte_set4(&page,chksum,22);
}
-
- /* 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);
+ /* We have a page. Set up page return. */
+ if(og){
+ /* set up page output */
+ og->header=ogg_buffer_split(&oy->fifo_tail,&oy->fifo_head,oy->headerbytes);
+ og->header_len=oy->headerbytes;
+ og->body=ogg_buffer_split(&oy->fifo_tail,&oy->fifo_head,oy->bodybytes);
+ og->body_len=oy->bodybytes;
+ }else{
+ /* simply advance */
+ oy->fifo_tail=
+ ogg_buffer_pretruncate(oy->fifo_tail,oy->headerbytes+oy->bodybytes);
+ if(!oy->fifo_tail)oy->fifo_head=0;
}
- sync_fail:
+ ret=oy->headerbytes+oy->bodybytes;
+ oy->unsynced=0;
+ oy->headerbytes=0;
+ oy->bodybytes=0;
+ oy->fifo_fill-=ret;
+
+ return ret;
+ sync_fail:
+
oy->headerbytes=0;
oy->bodybytes=0;
+ oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,1);
+ ret--;
- /* search for possible capture */
- next=(unsigned char *)memchr(page+1,'O',bytes-1);
- if(!next)
- next=oy->data+oy->fill;
+ /* search forward through fragments for possible capture */
+ while(oy->fifo_tail){
+ /* invariant: fifo_cursor points to a position in fifo_tail */
+ unsigned char *now=oy->fifo_tail->buffer->data+oy->fifo_tail->begin;
+ unsigned char *next=memchr(now, 'O', oy->fifo_tail->length);
+
+ if(next){
+ /* possible capture in this segment */
+ long bytes=next-now;
+ oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,bytes);
+ ret-=bytes;
+ break;
+ }else{
+ /* no capture. advance to next segment */
+ long bytes=oy->fifo_tail->length;
+ ret-=bytes;
+ oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,bytes);
+ }
+ }
+ if(!oy->fifo_tail)oy->fifo_head=0;
+ oy->fifo_fill+=ret;
- oy->returned=next-oy->data;
- return(-(next-page));
+ sync_out:
+ return ret;
}
/* 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
+ OGG_HOLE) 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 */
@@ -462,17 +790,17 @@ int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){
long ret=ogg_sync_pageseek(oy,og);
if(ret>0){
/* have a page */
- return(1);
+ return 1;
}
if(ret==0){
/* need more data */
- return(0);
+ return 0;
}
/* head did not start a synced page... skipped some bytes */
if(!oy->unsynced){
oy->unsynced=1;
- return(-1);
+ return OGG_HOLE;
}
/* loop. keep looking */
@@ -480,123 +808,186 @@ int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){
}
}
-/* add the incoming page to the stream state; we decompose the page
- into packet segments here as well. */
-int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){
- unsigned char *header=og->header;
- unsigned char *body=og->body;
- long bodysize=og->body_len;
- int segptr=0;
+/* clear things to an initial state. Good to call, eg, before seeking */
+int ogg_sync_reset(ogg_sync_state *oy){
- int version=ogg_page_version(og);
- int continued=ogg_page_continued(og);
- int bos=ogg_page_bos(og);
- int eos=ogg_page_eos(og);
- ogg_int64_t granulepos=ogg_page_granulepos(og);
- int serialno=ogg_page_serialno(og);
- long pageno=ogg_page_pageno(og);
- int segments=header[26];
-
- /* check the serial number */
- if(serialno!=os->serialno)return(-1);
- if(version>0)return(-1);
+ ogg_buffer_release(oy->fifo_tail);
+ oy->fifo_tail=0;
+ oy->fifo_head=0;
+ oy->fifo_fill=0;
- _os_lacing_expand(os,segments+1);
+ oy->unsynced=0;
+ oy->headerbytes=0;
+ oy->bodybytes=0;
+ return OGG_SUCCESS;
+}
- /* are we in sequence? */
- if(pageno!=os->pageno){
- int i;
+ogg_stream_state *ogg_stream_create(int serialno){
+ ogg_stream_state *os=_ogg_calloc(1,sizeof(*os));
+ os->serialno=serialno;
+ os->pageno=-1;
+ return os;
+}
- /* unroll previous partial packet (if any) */
- for(i=os->lacing_packet;i<os->lacing_fill;i++)
- os->body_fill-=os->lacing_vals[i]&0xff;
- os->lacing_fill=os->lacing_packet;
+int ogg_stream_destroy(ogg_stream_state *os){
+ if(os){
+ ogg_buffer_release(os->header_tail);
+ ogg_buffer_release(os->body_tail);
+ memset(os,0,sizeof(*os));
+ }
+ return OGG_SUCCESS;
+}
- /* make a note of dropped data in segment table */
- if(os->pageno!=-1){
- os->lacing_vals[os->lacing_fill++]=0x400;
- os->lacing_packet++;
- }
- /* are we a 'continued packet' page? If so, we'll need to skip
- some segments */
- if(continued){
- bos=0;
- for(;segptr<segments;segptr++){
- int val=header[27+segptr];
- body+=val;
- bodysize-=val;
- if(val<255){
- segptr++;
- break;
- }
- }
+#define FINFLAG 0x80000000UL
+#define FINMASK 0x7fffffffUL
+
+static void _next_lace(oggbyte_buffer *ob,ogg_stream_state *os){
+ /* search ahead one lace */
+ os->body_fill_next=0;
+ while(os->laceptr<os->lacing_fill){
+ int val=oggbyte_read1(ob,27+os->laceptr++);
+ os->body_fill_next+=val;
+ if(val<255){
+ os->body_fill_next|=FINFLAG;
+ os->clearflag=1;
+ break;
}
}
-
- if(bodysize){
- _os_body_expand(os,bodysize);
- memcpy(os->body_data+os->body_fill,body,bodysize);
- os->body_fill+=bodysize;
- }
+}
- {
- int saved=-1;
- while(segptr<segments){
- int val=header[27+segptr];
- os->lacing_vals[os->lacing_fill]=val;
- os->granule_vals[os->lacing_fill]=-1;
-
- if(bos){
- os->lacing_vals[os->lacing_fill]|=0x100;
- bos=0;
- }
+static void _span_queued_page(ogg_stream_state *os){
+ while( !(os->body_fill&FINFLAG) ){
+
+ if(!os->header_tail)break;
+
+ /* first flush out preceeding page header (if any). Body is
+ flushed as it's consumed, so that's not done here. */
+
+ if(os->lacing_fill>=0)
+ os->header_tail=ogg_buffer_pretruncate(os->header_tail,
+ os->lacing_fill+27);
+ os->lacing_fill=0;
+ os->laceptr=0;
+ os->clearflag=0;
+
+ if(!os->header_tail){
+ os->header_head=0;
+ break;
+ }else{
- if(val<255)saved=os->lacing_fill;
+ /* process/prepare next page, if any */
+
+ ogg_page og; /* only for parsing header values */
+ og.header=os->header_tail; /* only for parsing header values */
+ long pageno=ogg_page_pageno(&og);
+ oggbyte_buffer ob;
+
+ oggbyte_init(&ob,os->header_tail);
+ os->lacing_fill=oggbyte_read1(&ob,26);
- os->lacing_fill++;
- segptr++;
+ /* are we in sequence? */
+ if(pageno!=os->pageno){
+ if(os->pageno==-1) /* indicates seek or reset */
+ os->holeflag=1; /* set for internal use */
+ else
+ os->holeflag=2; /* set for external reporting */
+
+ os->body_tail=ogg_buffer_pretruncate(os->body_tail,
+ os->body_fill);
+ if(os->body_tail==0)os->body_head=0;
+ os->body_fill=0;
+
+ }
+
+ if(ogg_page_continued(&og)){
+ if(os->body_fill==0){
+ /* continued packet, but no preceeding data to continue */
+ /* dump the first partial packet on the page */
+ _next_lace(&ob,os);
+ os->body_tail=
+ ogg_buffer_pretruncate(os->body_tail,os->body_fill_next&FINMASK);
+ if(os->body_tail==0)os->body_head=0;
+ /* set span flag */
+ if(!os->spanflag && !os->holeflag)os->spanflag=2;
+ }
+ }else{
+ if(os->body_fill>0){
+ /* preceeding data to continue, but not a continued page */
+ /* dump body_fill */
+ os->body_tail=ogg_buffer_pretruncate(os->body_tail,
+ os->body_fill);
+ if(os->body_tail==0)os->body_head=0;
+ os->body_fill=0;
+
+ /* set espan flag */
+ if(!os->spanflag && !os->holeflag)os->spanflag=2;
+ }
+ }
+
+ if(os->laceptr<os->lacing_fill){
+ os->granulepos=ogg_page_granulepos(&og);
+
+ /* get current packet size & flag */
+ _next_lace(&ob,os);
+ os->body_fill+=os->body_fill_next; /* addition handles the flag fine;
+ unsigned on purpose */
+ /* ...and next packet size & flag */
+ _next_lace(&ob,os);
+
+ }
- if(val<255)os->lacing_packet=os->lacing_fill;
- }
-
- /* set the granulepos on the last granuleval of the last full packet */
- if(saved!=-1){
- os->granule_vals[saved]=granulepos;
+ os->pageno=pageno+1;
+ os->e_o_s=ogg_page_eos(&og);
+ os->b_o_s=ogg_page_bos(&og);
+
}
-
}
+}
- if(eos){
- os->e_o_s=1;
- if(os->lacing_fill>0)
- os->lacing_vals[os->lacing_fill-1]|=0x200;
- }
+/* add the incoming page to the stream state; we decompose the page
+ into packet segments here as well. */
- os->pageno=pageno+1;
+int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){
- return(0);
-}
+ int serialno=ogg_page_serialno(og);
+ int version=ogg_page_version(og);
-/* clear things to an initial state. Good to call, eg, before seeking */
-int ogg_sync_reset(ogg_sync_state *oy){
- oy->fill=0;
- oy->returned=0;
- oy->unsynced=0;
- oy->headerbytes=0;
- oy->bodybytes=0;
- return(0);
+ /* check the serial number */
+ if(serialno!=os->serialno){
+ ogg_page_release(og);
+ return OGG_ESERIAL;
+ }
+ if(version>0){
+ ogg_page_release(og);
+ return OGG_EVERSION;
+ }
+
+ /* add to fifos */
+ if(!os->body_tail){
+ os->body_tail=og->body;
+ os->body_head=ogg_buffer_walk(og->body);
+ }else{
+ os->body_head=ogg_buffer_cat(os->body_head,og->body);
+ }
+ if(!os->header_tail){
+ os->header_tail=og->header;
+ os->header_head=ogg_buffer_walk(og->header);
+ os->lacing_fill=-27;
+ }else{
+ os->header_head=ogg_buffer_cat(os->header_head,og->header);
+ }
+
+ memset(og,0,sizeof(*og));
+ return OGG_SUCCESS;
}
int ogg_stream_reset(ogg_stream_state *os){
- os->body_fill=0;
- os->body_returned=0;
- os->lacing_fill=0;
- os->lacing_packet=0;
- os->lacing_returned=0;
-
- os->header_fill=0;
+ ogg_buffer_release(os->header_tail);
+ ogg_buffer_release(os->body_tail);
+ os->header_tail=os->header_head=0;
+ os->body_tail=os->body_head=0;
os->e_o_s=0;
os->b_o_s=0;
@@ -604,61 +995,100 @@ int ogg_stream_reset(ogg_stream_state *os){
os->packetno=0;
os->granulepos=0;
- return(0);
-}
+ os->body_fill=0;
+ os->lacing_fill=0;
-static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){
+ os->holeflag=0;
+ os->spanflag=0;
+ os->clearflag=0;
+ os->laceptr=0;
+ os->body_fill_next=0;
- /* The last part of decode. We have the stream broken into packet
- segments. Now we need to group them into packets (or return the
- out of sync markers) */
+ return OGG_SUCCESS;
+}
- int ptr=os->lacing_returned;
+int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){
+ ogg_stream_reset(os);
+ os->serialno=serialno;
+ return OGG_SUCCESS;
+}
- if(os->lacing_packet<=ptr)return(0);
+static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){
- if(os->lacing_vals[ptr]&0x400){
- /* we need to tell the codec there's a gap; it might need to
- handle previous packet dependencies. */
- os->lacing_returned++;
- os->packetno++;
- return(-1);
+ ogg_packet_release(op);
+ _span_queued_page(os);
+
+ if(os->holeflag){
+ int temp=os->holeflag;
+ if(os->clearflag)
+ os->holeflag=0;
+ else
+ os->holeflag=1;
+ if(temp==2){
+ os->packetno++;
+ return OGG_HOLE;
+ }
+ }
+ if(os->spanflag){
+ int temp=os->spanflag;
+ if(os->clearflag)
+ os->spanflag=0;
+ else
+ os->spanflag=1;
+ if(temp==2){
+ os->packetno++;
+ return OGG_SPAN;
+ }
}
- if(!op && !adv)return(1); /* just using peek as an inexpensive way
+ if(!(os->body_fill&FINFLAG)) return 0;
+ if(!op && !adv)return 1; /* just using peek as an inexpensive way
to ask if there's a whole packet
waiting */
+ if(op){
+ op->b_o_s=os->b_o_s;
+ if(os->e_o_s && os->body_fill_next==0)
+ op->e_o_s=os->e_o_s;
+ else
+ op->e_o_s=0;
+ if( (os->body_fill&FINFLAG) && !(os->body_fill_next&FINFLAG) )
+ op->granulepos=os->granulepos;
+ else
+ op->granulepos=-1;
+ op->packetno=os->packetno;
+ }
- /* Gather the whole packet. We'll have no holes or a partial packet */
- {
- int size=os->lacing_vals[ptr]&0xff;
- int bytes=size;
- int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */
- int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */
-
- while(size==255){
- int val=os->lacing_vals[++ptr];
- size=val&0xff;
- if(val&0x200)eos=0x200;
- bytes+=size;
- }
+ if(adv){
+ oggbyte_buffer ob;
+ oggbyte_init(&ob,os->header_tail);
+ /* split the body contents off */
if(op){
- op->e_o_s=eos;
- op->b_o_s=bos;
- op->packet=os->body_data+os->body_returned;
- op->packetno=os->packetno;
- op->granulepos=os->granule_vals[ptr];
- op->bytes=bytes;
+ op->packet=ogg_buffer_split(&os->body_tail,&os->body_head,
+ os->body_fill&FINMASK);
+ op->bytes=os->body_fill&FINMASK;
+ }else{
+ os->body_tail=ogg_buffer_pretruncate(os->body_tail,
+ os->body_fill&FINMASK);
+ if(os->body_tail==0)os->body_head=0;
}
- if(adv){
- os->body_returned+=bytes;
- os->lacing_returned=ptr+1;
- os->packetno++;
+ /* update lacing pointers */
+ os->body_fill=os->body_fill_next;
+ _next_lace(&ob,os);
+ }else{
+ if(op){
+ op->packet=ogg_buffer_sub(os->body_tail,0,os->body_fill&FINMASK);
+ op->bytes=os->body_fill&FINMASK;
}
}
- return(1);
+
+ if(adv){
+ os->packetno++;
+ os->b_o_s=0;
+ }
+
+ return 1;
}
int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){
@@ -669,8 +1099,27 @@ int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){
return _packetout(os,op,0);
}
-void ogg_packet_clear(ogg_packet *op) {
- _ogg_free(op->packet);
- memset(op, 0, sizeof(*op));
+int ogg_packet_release(ogg_packet *op) {
+ if(op){
+ ogg_buffer_release(op->packet);
+ memset(op, 0, sizeof(*op));
+ }
+ return OGG_SUCCESS;
+}
+
+int ogg_page_release(ogg_page *og) {
+ if(og){
+ ogg_buffer_release(og->header);
+ ogg_buffer_release(og->body);
+ memset(og, 0, sizeof(*og));
+ }
+ return OGG_SUCCESS;
+}
+
+void ogg_page_dup(ogg_page *dup,ogg_page *orig){
+ dup->header_len=orig->header_len;
+ dup->body_len=orig->body_len;
+ dup->header=ogg_buffer_dup(orig->header);
+ dup->body=ogg_buffer_dup(orig->body);
}
diff --git a/info.c b/info.c
index 72398b4..941695e 100644
--- a/info.c
+++ b/info.c
@@ -6,7 +6,7 @@
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
- * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
@@ -300,7 +300,7 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op)
oggpack_buffer opb;
if(op){
- oggpack_readinit(&opb,op->packet,op->bytes);
+ oggpack_readinit(&opb,op->packet);
/* Which of the three types of header is this? */
/* Also verify header-ness, vorbis */
diff --git a/ivorbiscodec.h b/ivorbiscodec.h
index 3d637f6..d4de1fd 100644
--- a/ivorbiscodec.h
+++ b/ivorbiscodec.h
@@ -169,7 +169,8 @@ extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
ogg_packet *op);
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_restart(vorbis_dsp_state *v);
+extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op,int decodep);
extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm);
extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
diff --git a/ivorbisfile.h b/ivorbisfile.h
index 3a668f7..dd77378 100644
--- a/ivorbisfile.h
+++ b/ivorbisfile.h
@@ -26,8 +26,7 @@ extern "C"
#include <stdio.h>
#include "ivorbiscodec.h"
-#define CHUNKSIZE 8192
-
+#define CHUNKSIZE 1024
/* The function prototypes for the callbacks are basically the same as for
* the stdio functions fread, fseek, fclose, ftell.
* The one difference is that the FILE * arguments have been replaced with
@@ -56,14 +55,14 @@ typedef struct OggVorbis_File {
int seekable;
ogg_int64_t offset;
ogg_int64_t end;
- ogg_sync_state oy;
+ 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_uint32_t *serialnos;
ogg_int64_t *pcmlengths;
vorbis_info *vi;
vorbis_comment *vc;
@@ -71,13 +70,13 @@ typedef struct OggVorbis_File {
/* Decoding working state local storage */
ogg_int64_t pcm_offset;
int ready_state;
- long current_serialno;
+ ogg_uint32_t current_serialno;
int current_link;
ogg_int64_t bittrack;
ogg_int64_t samptrack;
- ogg_stream_state os; /* take physical pages, weld into a logical
+ ogg_stream_state *os; /* take physical pages, weld into a logical
stream of packets */
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
vorbis_block vb; /* local working space for packet->PCM decode */
@@ -106,7 +105,7 @@ extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i);
extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i);
extern ogg_int64_t ov_time_total(OggVorbis_File *vf,int i);
-extern int ov_raw_seek(OggVorbis_File *vf,long pos);
+extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos);
extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos);
extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos);
extern int ov_time_seek(OggVorbis_File *vf,ogg_int64_t pos);
diff --git a/misc.h b/misc.h
index 0b6dfdf..2d45c00 100644
--- a/misc.h
+++ b/misc.h
@@ -20,6 +20,21 @@
#include "ivorbiscodec.h"
#include "os_types.h"
+//#define _VDBG_GRAPHFILE "_0.m"
+extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line);
+extern void _VDBG_free(void *ptr,char *file,long line);
+
+#undef _ogg_malloc
+#undef _ogg_calloc
+#undef _ogg_realloc
+#undef _ogg_free
+
+#define _ogg_malloc(x) _VDBG_malloc(NULL,(x),__FILE__,__LINE__)
+#define _ogg_calloc(x,y) _VDBG_malloc(NULL,(x)*(y),__FILE__,__LINE__)
+#define _ogg_realloc(x,y) _VDBG_malloc((x),(y),__FILE__,__LINE__)
+#define _ogg_free(x) _VDBG_free((x),__FILE__,__LINE__)
+
+
extern void *_vorbis_block_alloc(vorbis_block *vb,long bytes);
extern void _vorbis_block_ripcord(vorbis_block *vb);
extern void _analysis_output(char *base,int i,ogg_int32_t *v,int point,
diff --git a/ogg.h b/ogg.h
index ad1d35f..85cb41b 100644
--- a/ogg.h
+++ b/ogg.h
@@ -6,7 +6,7 @@
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
- * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
@@ -23,105 +23,138 @@ extern "C" {
#include "os_types.h"
-typedef struct {
- long endbyte;
- int endbit;
+typedef struct ogg_buffer_state{
+ struct ogg_buffer *unused_buffers;
+ struct ogg_reference *unused_references;
+ int outstanding;
+ int shutdown;
+} ogg_buffer_state;
+
+typedef struct ogg_buffer {
+ unsigned char *data;
+ long size;
+ int refcount;
+
+ union {
+ ogg_buffer_state *owner;
+ struct ogg_buffer *next;
+ } ptr;
+} ogg_buffer;
+
+typedef struct ogg_reference {
+ ogg_buffer *buffer;
+ long begin;
+ long length;
+
+ struct ogg_reference *next;
+} ogg_reference;
+
+typedef struct oggpack_buffer {
+ int headbit;
+ unsigned char *headptr;
+ long headend;
+
+ /* memory management */
+ ogg_reference *head;
+ ogg_reference *tail;
+
+ /* render the byte/bit counter API constant time */
+ long count; /* doesn't count the tail */
+} oggpack_buffer;
- unsigned char *buffer;
+typedef struct oggbyte_buffer {
+ ogg_reference *baseref;
+
+ ogg_reference *ref;
unsigned char *ptr;
- long storage;
-} oggpack_buffer;
+ long pos;
+ long end;
+} oggbyte_buffer;
-/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/
+typedef struct ogg_sync_state {
+ /* decode memory management pool */
+ ogg_buffer_state *bufferpool;
-typedef struct {
- unsigned char *header;
- long header_len;
- unsigned char *body;
- long body_len;
-} ogg_page;
+ /* stream buffers */
+ ogg_reference *fifo_head;
+ ogg_reference *fifo_tail;
+ long fifo_fill;
-/* ogg_stream_state contains the current encode/decode state of a logical
- Ogg bitstream **********************************************************/
+ /* stream sync management */
+ int unsynced;
+ int headerbytes;
+ int bodybytes;
-typedef struct {
- unsigned char *body_data; /* bytes from packet bodies */
- long body_storage; /* storage elements allocated */
- long body_fill; /* elements stored; fill mark */
- long body_returned; /* elements of fill returned */
-
-
- int *lacing_vals; /* The values that will go to the segment table */
- ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact
- this way, but it is simple coupled to the
- lacing fifo */
- long lacing_storage;
- long lacing_fill;
- long lacing_packet;
- long lacing_returned;
-
- unsigned char header[282]; /* working space for header encode */
- int header_fill;
-
- int e_o_s; /* set when we have buffered the last packet in the
- logical bitstream */
- int b_o_s; /* set after we've written the initial page
- of a logical bitstream */
- long serialno;
- long pageno;
- ogg_int64_t packetno; /* sequence number for decode; the framing
- knows where there's a hole in the data,
- but we need coupling so that the codec
- (which is in a seperate abstraction
- layer) also knows about the gap */
- ogg_int64_t granulepos;
+} ogg_sync_state;
+typedef struct ogg_stream_state {
+ ogg_reference *header_head;
+ ogg_reference *header_tail;
+ ogg_reference *body_head;
+ ogg_reference *body_tail;
+
+ int e_o_s; /* set when we have buffered the last
+ packet in the logical bitstream */
+ int b_o_s; /* set after we've written the initial page
+ of a logical bitstream */
+ long serialno;
+ long pageno;
+ ogg_int64_t packetno; /* sequence number for decode; the framing
+ knows where there's a hole in the data,
+ but we need coupling so that the codec
+ (which is in a seperate abstraction
+ layer) also knows about the gap */
+ ogg_int64_t granulepos;
+
+ int lacing_fill;
+ ogg_uint32_t body_fill;
+
+ /* decode-side state data */
+ int holeflag;
+ int spanflag;
+ int clearflag;
+ int laceptr;
+ ogg_uint32_t body_fill_next;
+
} ogg_stream_state;
-/* ogg_packet is used to encapsulate the data and metadata belonging
- to a single raw Ogg/Vorbis packet *************************************/
-
typedef struct {
- unsigned char *packet;
- long bytes;
- long b_o_s;
- long e_o_s;
-
- ogg_int64_t granulepos;
-
- ogg_int64_t packetno; /* sequence number for decode; the framing
- knows where there's a hole in the data,
- but we need coupling so that the codec
- (which is in a seperate abstraction
- layer) also knows about the gap */
+ ogg_reference *packet;
+ long bytes;
+ long b_o_s;
+ long e_o_s;
+ ogg_int64_t granulepos;
+ ogg_int64_t packetno; /* sequence number for decode; the framing
+ knows where there's a hole in the data,
+ but we need coupling so that the codec
+ (which is in a seperate abstraction
+ layer) also knows about the gap */
} ogg_packet;
typedef struct {
- unsigned char *data;
- int storage;
- int fill;
- int returned;
-
- int unsynced;
- int headerbytes;
- int bodybytes;
-} ogg_sync_state;
+ ogg_reference *header;
+ int header_len;
+ ogg_reference *body;
+ long body_len;
+} ogg_page;
/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/
-extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes);
+extern void oggpack_readinit(oggpack_buffer *b,ogg_reference *r);
extern long oggpack_look(oggpack_buffer *b,int bits);
extern void oggpack_adv(oggpack_buffer *b,int bits);
extern long oggpack_read(oggpack_buffer *b,int bits);
+extern long oggpack_bytes(oggpack_buffer *b);
+extern long oggpack_bits(oggpack_buffer *b);
+extern int oggpack_eop(oggpack_buffer *b);
/* Ogg BITSTREAM PRIMITIVES: decoding **************************/
-extern int ogg_sync_init(ogg_sync_state *oy);
-extern int ogg_sync_clear(ogg_sync_state *oy);
+extern ogg_sync_state *ogg_sync_create(void);
+extern int ogg_sync_destroy(ogg_sync_state *oy);
extern int ogg_sync_reset(ogg_sync_state *oy);
-extern int ogg_sync_destroy(ogg_sync_state *oy);
-extern char *ogg_sync_buffer(ogg_sync_state *oy, long size);
+extern unsigned char *ogg_sync_bufferin(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);
@@ -131,24 +164,39 @@ extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op);
/* Ogg BITSTREAM PRIMITIVES: general ***************************/
-extern int ogg_stream_init(ogg_stream_state *os,int serialno);
-extern int ogg_stream_clear(ogg_stream_state *os);
-extern int ogg_stream_reset(ogg_stream_state *os);
+extern ogg_stream_state *ogg_stream_create(int serialno);
extern int ogg_stream_destroy(ogg_stream_state *os);
+extern int ogg_stream_reset(ogg_stream_state *os);
+extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno);
extern int ogg_stream_eos(ogg_stream_state *os);
-extern void ogg_page_checksum_set(ogg_page *og);
+extern int ogg_page_checksum_set(ogg_page *og);
extern int ogg_page_version(ogg_page *og);
extern int ogg_page_continued(ogg_page *og);
extern int ogg_page_bos(ogg_page *og);
extern int ogg_page_eos(ogg_page *og);
extern ogg_int64_t ogg_page_granulepos(ogg_page *og);
-extern int ogg_page_serialno(ogg_page *og);
-extern long ogg_page_pageno(ogg_page *og);
+extern ogg_uint32_t ogg_page_serialno(ogg_page *og);
+extern ogg_uint32_t ogg_page_pageno(ogg_page *og);
extern int ogg_page_packets(ogg_page *og);
+extern int ogg_page_getbuffer(ogg_page *og, unsigned char **buffer);
+
+extern int ogg_packet_release(ogg_packet *op);
+extern int ogg_page_release(ogg_page *og);
+
+extern void ogg_page_dup(ogg_page *d, ogg_page *s);
+
+/* Ogg BITSTREAM PRIMITIVES: return codes ***************************/
+
+#define OGG_SUCCESS 0
-extern void ogg_packet_clear(ogg_packet *op);
+#define OGG_HOLE -10
+#define OGG_SPAN -11
+#define OGG_EVERSION -12
+#define OGG_ESERIAL -13
+#define OGG_EINVAL -14
+#define OGG_EEOS -15
#ifdef __cplusplus
diff --git a/synthesis.c b/synthesis.c
index 47cd8b1..3006eee 100644
--- a/synthesis.c
+++ b/synthesis.c
@@ -6,13 +6,13 @@
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
- * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
function: single-block PCM synthesis
- last mod: $Id: synthesis.c,v 1.3 2002/10/16 07:39:56 xiphmont Exp $
+ last mod: $Id: synthesis.c,v 1.4 2003/03/29 03:07:21 xiphmont Exp $
********************************************************************/
@@ -24,7 +24,7 @@
#include "misc.h"
#include "os.h"
-int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){
+int vorbis_synthesis(vorbis_block *vb,ogg_packet *op,int decodep){
vorbis_dsp_state *vd=vb->vd;
private_state *b=(private_state *)vd->backend_state;
vorbis_info *vi=vd->vi;
@@ -34,7 +34,7 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){
/* first things first. Make sure decode is ready */
_vorbis_block_ripcord(vb);
- oggpack_readinit(opb,op->packet,op->bytes);
+ oggpack_readinit(opb,op->packet);
/* Check the packet type */
if(oggpack_read(opb,1)!=0){
@@ -62,16 +62,24 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){
vb->sequence=op->packetno-3; /* first block is third packet */
vb->eofflag=op->e_o_s;
- /* alloc pcm passback storage */
- vb->pcmend=ci->blocksizes[vb->W];
- vb->pcm=(ogg_int32_t **)_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels);
- for(i=0;i<vi->channels;i++)
- vb->pcm[i]=(ogg_int32_t *)_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i]));
-
- /* unpack_header enforces range checking */
- type=ci->map_type[ci->mode_param[mode]->mapping];
-
- return(_mapping_P[type]->inverse(vb,b->mode[mode]));
+ if(decodep){
+ /* alloc pcm passback storage */
+ vb->pcmend=ci->blocksizes[vb->W];
+ vb->pcm=(ogg_int32_t **)_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels);
+ for(i=0;i<vi->channels;i++)
+ vb->pcm[i]=(ogg_int32_t *)_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i]));
+
+ /* unpack_header enforces range checking */
+ type=ci->map_type[ci->mode_param[mode]->mapping];
+
+ return(_mapping_P[type]->inverse(vb,b->mode[mode]));
+ }else{
+ /* no pcm */
+ vb->pcmend=0;
+ vb->pcm=NULL;
+
+ return(0);
+ }
}
long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){
@@ -79,7 +87,7 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){
oggpack_buffer opb;
int mode;
- oggpack_readinit(&opb,op->packet,op->bytes);
+ oggpack_readinit(&opb,op->packet);
/* Check the packet type */
if(oggpack_read(&opb,1)!=0){
diff --git a/vorbisfile.c b/vorbisfile.c
index 433bdb0..14df0ed 100644
--- a/vorbisfile.c
+++ b/vorbisfile.c
@@ -6,12 +6,13 @@
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
- * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
+ * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 *
* BY THE Xiph.Org FOUNDATION http://www.xiph.org/ *
* *
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
+ last mod: $Id: vorbisfile.c,v 1.5 2003/03/29 03:07:21 xiphmont Exp $
********************************************************************/
@@ -56,14 +57,14 @@
* 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
-*/
+
+/* read a little more data from the file/pipe into the ogg_sync framer */
static long _get_data(OggVorbis_File *vf){
errno=0;
if(vf->datasource){
- char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE);
+ char *buffer=ogg_sync_bufferin(vf->oy,CHUNKSIZE);
long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource);
- if(bytes>0)ogg_sync_wrote(&vf->oy,bytes);
+ if(bytes>0)ogg_sync_wrote(vf->oy,bytes);
if(bytes==0 && errno)return(-1);
return(bytes);
}else
@@ -71,11 +72,11 @@ static long _get_data(OggVorbis_File *vf){
}
/* save a tiny smidge of verbosity to make the code more readable */
-static void _seek_helper(OggVorbis_File *vf,long offset){
+static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
if(vf->datasource){
(vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET);
vf->offset=offset;
- ogg_sync_reset(&vf->oy);
+ ogg_sync_reset(vf->oy);
}else{
/* shouldn't happen unless someone writes a broken callback */
return;
@@ -93,15 +94,18 @@ static void _seek_helper(OggVorbis_File *vf,long offset){
n) search for a new page beginning for n bytes
return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD)
- n) found a page at absolute offset n */
+ n) found a page at absolute offset n
+
+ produces a refcounted page */
-static long _get_next_page(OggVorbis_File *vf,ogg_page *og,int boundary){
+static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og,
+ ogg_int64_t boundary){
if(boundary>0)boundary+=vf->offset;
while(1){
long more;
if(boundary>0 && vf->offset>=boundary)return(OV_FALSE);
- more=ogg_sync_pageseek(&vf->oy,og);
+ more=ogg_sync_pageseek(vf->oy,og);
if(more<0){
/* skipped n bytes */
@@ -118,7 +122,7 @@ static long _get_next_page(OggVorbis_File *vf,ogg_page *og,int boundary){
}else{
/* got a page. Return the offset at the page beginning,
advance the internal offset past the page end */
- long ret=vf->offset;
+ ogg_int64_t ret=vf->offset;
vf->offset+=more;
return(ret);
@@ -131,19 +135,21 @@ static long _get_next_page(OggVorbis_File *vf,ogg_page *og,int boundary){
position. Much dirtier than the above as Ogg doesn't have any
backward search linkage. no 'readp' as it will certainly have to
read. */
-/* returns offset or OV_EREAD, OV_FAULT */
-static long _get_prev_page(OggVorbis_File *vf,ogg_page *og){
- long begin=vf->offset;
- long ret;
- int offset=-1;
+/* returns offset or OV_EREAD, OV_FAULT and produces a refcounted page */
+
+static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
+ ogg_int64_t begin=vf->offset;
+ ogg_int64_t end=begin;
+ ogg_int64_t ret;
+ ogg_int64_t offset=-1;
while(offset==-1){
begin-=CHUNKSIZE;
if(begin<0)
begin=0;
_seek_helper(vf,begin);
- while(vf->offset<begin+CHUNKSIZE){
- ret=_get_next_page(vf,og,begin+CHUNKSIZE-vf->offset);
+ while(vf->offset<end){
+ ret=_get_next_page(vf,og,end-vf->offset);
if(ret==OV_EREAD)return(OV_EREAD);
if(ret<0){
break;
@@ -168,20 +174,20 @@ static long _get_prev_page(OggVorbis_File *vf,ogg_page *og){
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 int _bisect_forward_serialno(OggVorbis_File *vf,
- long begin,
- long searched,
- long end,
- long currentno,
+ ogg_int64_t begin,
+ ogg_int64_t searched,
+ ogg_int64_t end,
+ ogg_uint32_t currentno,
long m){
- long endsearched=end;
- long next=end;
- ogg_page og;
- long ret;
+ ogg_int64_t endsearched=end;
+ ogg_int64_t next=end;
+ ogg_page og={0,0,0,0};
+ ogg_int64_t ret;
/* the below guards against garbage seperating the last and
first pages of two links. */
while(searched<endsearched){
- long bisect;
+ ogg_int64_t bisect;
if(endsearched-searched<CHUNKSIZE){
bisect=searched;
@@ -198,6 +204,7 @@ static int _bisect_forward_serialno(OggVorbis_File *vf,
}else{
searched=ret+og.header_len+og.body_len;
}
+ ogg_page_release(&og);
}
_seek_helper(vf,next);
@@ -205,36 +212,45 @@ static int _bisect_forward_serialno(OggVorbis_File *vf,
if(ret==OV_EREAD)return(OV_EREAD);
if(searched>=end || ret<0){
+ ogg_page_release(&og);
vf->links=m+1;
- vf->offsets=(ogg_int64_t *)_ogg_malloc((m+2)*sizeof(*vf->offsets));
+ vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets));
+ vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos));
vf->offsets[m+1]=searched;
}else{
ret=_bisect_forward_serialno(vf,next,vf->offset,
end,ogg_page_serialno(&og),m+1);
+ ogg_page_release(&og);
if(ret==OV_EREAD)return(OV_EREAD);
}
vf->offsets[m]=begin;
+ vf->serialnos[m]=currentno;
return(0);
}
/* 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,vorbis_comment *vc,
- long *serialno,ogg_page *og_ptr){
- ogg_page og;
- ogg_packet op;
- int i,ret=0;
+/* consumes the page that's passed in (if any) */
+
+static int _fetch_headers(OggVorbis_File *vf,
+ vorbis_info *vi,
+ vorbis_comment *vc,
+ ogg_uint32_t *serialno,
+ ogg_page *og_ptr){
+ ogg_page og={0,0,0,0};
+ ogg_packet op={0,0,0,0,0,0};
+ int i,ret;
if(!og_ptr){
- ret=_get_next_page(vf,&og,CHUNKSIZE);
- if(ret==OV_EREAD)return(OV_EREAD);
- if(ret<0)return OV_ENOTVORBIS;
+ ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
+ if(llret==OV_EREAD)return(OV_EREAD);
+ if(llret<0)return OV_ENOTVORBIS;
og_ptr=&og;
}
- if(serialno)*serialno=ogg_page_serialno(og_ptr);
- ogg_stream_init(&vf->os,ogg_page_serialno(og_ptr));
+ ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
+ if(serialno)*serialno=vf->os->serialno;
vf->ready_state=STREAMSET;
/* extract the initial header from the first page and verify that the
@@ -245,9 +261,9 @@ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc,
i=0;
while(i<3){
- ogg_stream_pagein(&vf->os,og_ptr);
+ ogg_stream_pagein(vf->os,og_ptr);
while(i<3){
- int result=ogg_stream_packetout(&vf->os,&op);
+ int result=ogg_stream_packetout(vf->os,&op);
if(result==0)break;
if(result==-1){
ret=OV_EBADHEADER;
@@ -264,12 +280,16 @@ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc,
goto bail_header;
}
}
+
+ ogg_packet_release(&op);
+ ogg_page_release(&og);
return 0;
bail_header:
+ ogg_packet_release(&op);
+ ogg_page_release(&og);
vorbis_info_clear(vi);
vorbis_comment_clear(vc);
- ogg_stream_clear(&vf->os);
vf->ready_state=OPENED;
return ret;
@@ -284,20 +304,22 @@ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc,
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, long dataoffset){
- ogg_page og;
- int i,ret;
+static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){
+ ogg_page og={0,0,0,0};
+ int i;
+ ogg_int64_t ret;
- vf->vi=(vorbis_info *)_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
- vf->vc=(vorbis_comment *)_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
- vf->dataoffsets=(ogg_int64_t *)_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
- vf->pcmlengths=(ogg_int64_t *)_ogg_malloc(vf->links*sizeof(*vf->pcmlengths));
- vf->serialnos=(long *)_ogg_malloc(vf->links*sizeof(*vf->serialnos));
+ vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
+ vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
+ vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
+ vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
for(i=0;i<vf->links;i++){
if(i==0){
/* we already grabbed the initial header earlier. Just set the offset */
vf->dataoffsets[i]=dataoffset;
+ _seek_helper(vf,dataoffset);
+
}else{
/* seek to the location of the initial header */
@@ -307,14 +329,62 @@ static void _prefetch_all_headers(OggVorbis_File *vf, long dataoffset){
vf->dataoffsets[i]=-1;
}else{
vf->dataoffsets[i]=vf->offset;
- ogg_stream_clear(&vf->os);
}
}
- /* get the serial number and PCM length of this link. To do this,
+ /* fetch beginning PCM offset */
+
+ if(vf->dataoffsets[i]!=-1){
+ ogg_int64_t accumulated=0,pos;
+ long lastblock=-1;
+ int result;
+
+ ogg_stream_reset_serialno(vf->os,vf->serialnos[i]);
+
+ while(1){
+ ogg_packet op={0,0,0,0,0,0};
+
+ ret=_get_next_page(vf,&og,-1);
+ if(ret<0)
+ /* this should not be possible unless the file is
+ truncated/mangled */
+ break;
+
+ if(ogg_page_serialno(&og)!=vf->serialnos[i])
+ break;
+
+ pos=ogg_page_granulepos(&og);
+
+ /* count blocksizes of all frames in the page */
+ ogg_stream_pagein(vf->os,&og);
+ while((result=ogg_stream_packetout(vf->os,&op))){
+ if(result>0){ /* ignore holes */
+ long thisblock=vorbis_packet_blocksize(vf->vi+i,&op);
+ if(lastblock!=-1)
+ accumulated+=(lastblock+thisblock)>>2;
+ lastblock=thisblock;
+ }
+ }
+ ogg_packet_release(&op);
+
+ if(pos!=-1){
+ /* pcm offset of last packet on the first audio page */
+ accumulated= pos-accumulated;
+ break;
+ }
+ }
+
+ /* less than zero? This is a stream with samples trimmed off
+ the beginning, a normal occurrence; set the offset to zero */
+ if(accumulated<0)accumulated=0;
+
+ vf->pcmlengths[i*2]=accumulated;
+ }
+
+ /* get the PCM length of this link. To do this,
get the last page of the stream */
{
- long end=vf->offsets[i+1];
+ ogg_int64_t end=vf->offsets[i+1];
_seek_helper(vf,end);
while(1){
@@ -326,14 +396,14 @@ static void _prefetch_all_headers(OggVorbis_File *vf, long dataoffset){
break;
}
if(ogg_page_granulepos(&og)!=-1){
- vf->serialnos[i]=ogg_page_serialno(&og);
- vf->pcmlengths[i]=ogg_page_granulepos(&og);
+ vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2];
break;
}
vf->offset=ret;
}
}
}
+ ogg_page_release(&og);
}
static void _make_decode_ready(OggVorbis_File *vf){
@@ -345,13 +415,16 @@ static void _make_decode_ready(OggVorbis_File *vf){
}
vorbis_block_init(&vf->vd,&vf->vb);
vf->ready_state=INITSET;
+ vf->bittrack=0;
+ vf->samptrack=0;
return;
}
static int _open_seekable2(OggVorbis_File *vf){
- long serialno=vf->current_serialno,end;
- long dataoffset=vf->offset;
- ogg_page og;
+ ogg_uint32_t serialno=vf->current_serialno;
+ ogg_uint32_t tempserialno;
+ ogg_int64_t dataoffset=vf->offset, end;
+ ogg_page og={0,0,0,0};
/* we're partially open and have a first link header state in
storage in vf */
@@ -362,28 +435,22 @@ static int _open_seekable2(OggVorbis_File *vf){
/* 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);
- if(end<0){
- ov_clear(vf);
- return(end);
- }
+ if(end<0)return(end);
/* more than one logical bitstream? */
- if(ogg_page_serialno(&og)!=serialno){
+ tempserialno=ogg_page_serialno(&og);
+ ogg_page_release(&og);
+
+ if(tempserialno!=serialno){
/* 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){
- ov_clear(vf);
- return(OV_EREAD);
- }
+ if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return(OV_EREAD);
}else{
/* Only one logical bitstream */
- if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0)){
- ov_clear(vf);
- return(OV_EREAD);
- }
+ if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return(OV_EREAD);
}
@@ -394,13 +461,9 @@ static int _open_seekable2(OggVorbis_File *vf){
/* clear out the current logical bitstream decoder */
static void _decode_clear(OggVorbis_File *vf){
- ogg_stream_clear(&vf->os);
vorbis_dsp_clear(&vf->vd);
vorbis_block_clear(&vf->vb);
vf->ready_state=OPENED;
-
- vf->bittrack=0;
- vf->samptrack=0;
}
/* fetch and process a packet. Handles the case where we're at a
@@ -414,8 +477,12 @@ static void _decode_clear(OggVorbis_File *vf){
1) got a packet
*/
-static int _process_packet(OggVorbis_File *vf,int readp){
- ogg_page og;
+static int _fetch_and_process_packet(OggVorbis_File *vf,
+ int readp,
+ int spanp){
+ ogg_page og={0,0,0,0};
+ ogg_packet op={0,0,0,0,0,0};
+ int ret=0;
/* handle one packet. Try to fetch it from current stream state */
/* extract packets from page */
@@ -425,25 +492,34 @@ static int _process_packet(OggVorbis_File *vf,int readp){
neither is a page */
if(vf->ready_state==INITSET){
while(1) {
- ogg_packet op;
- int result=ogg_stream_packetout(&vf->os,&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){
+ ret=OV_HOLE; /* hole in the data. */
+ goto cleanup;
+ }
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 */
+ if(!vorbis_synthesis(&vf->vb,&op,1)){ /* 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){
+ ret=OV_EFAULT;
+ goto cleanup;
+ }
+
vorbis_synthesis_blockin(&vf->vd,&vf->vb);
vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
vf->bittrack+=op.bytes*8;
@@ -466,15 +542,23 @@ static int _process_packet(OggVorbis_File *vf,int readp){
So, we need a previous granulepos from an in-sequence page
to have a reference point. Thus the !op.e_o_s clause
above */
-
+
+ if(vf->seekable && link>0)
+ granulepos-=vf->pcmlengths[link*2];
+ if(granulepos<0)granulepos=0; /* actually, this
+ shouldn't be possible
+ here unless the stream
+ is very broken */
+
samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
granulepos-=samples;
for(i=0;i<link;i++)
- granulepos+=vf->pcmlengths[i];
+ granulepos+=vf->pcmlengths[i*2+1];
vf->pcm_offset=granulepos;
}
- return(1);
+ ret=1;
+ goto cleanup;
}
}
else
@@ -483,16 +567,28 @@ static int _process_packet(OggVorbis_File *vf,int readp){
}
if(vf->ready_state>=OPENED){
- 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 */
+ int ret;
+ if(!readp){
+ ret=0;
+ goto cleanup;
+ }
+ if((ret=_get_next_page(vf,&og,-1))<0){
+ ret=OV_EOF; /* eof. leave unitialized */
+ goto cleanup;
+ }
+
+ /* 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)){
+ if(!spanp){
+ ret=OV_EOF;
+ goto cleanup;
+ }
+
_decode_clear(vf);
if(!vf->seekable){
@@ -527,15 +623,15 @@ static int _process_packet(OggVorbis_File *vf,int readp){
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 */
+ if(link==vf->links){
+ ret=OV_EBADLINK; /* sign of a bogus stream. error out,
+ leave machine uninitialized */
+ goto cleanup;
+ }
vf->current_link=link;
- ogg_stream_init(&vf->os,vf->current_serialno);
- ogg_stream_reset(&vf->os);
+ ogg_stream_reset_serialno(vf->os,vf->current_serialno);
vf->ready_state=STREAMSET;
}else{
@@ -543,7 +639,7 @@ static int _process_packet(OggVorbis_File *vf,int readp){
/* fetch the three header packets, build the info struct */
int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og);
- if(ret)return(ret);
+ if(ret) goto cleanup;
vf->current_link++;
link=0;
}
@@ -551,18 +647,24 @@ static int _process_packet(OggVorbis_File *vf,int readp){
_make_decode_ready(vf);
}
- ogg_stream_pagein(&vf->os,&og);
+ ogg_stream_pagein(vf->os,&og);
}
+ cleanup:
+ ogg_packet_release(&op);
+ ogg_page_release(&og);
+ return ret;
}
+/* if, eg, 64 bit stdio is configured by default, this will build with
+ fseek64 */
static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){
if(f==NULL)return(-1);
- return fseek(f,(int)off,whence);
+ return fseek(f,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 offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1);
int ret;
memset(vf,0,sizeof(*vf));
@@ -570,27 +672,28 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial,
vf->callbacks = callbacks;
/* init the framing state */
- ogg_sync_init(&vf->oy);
+ vf->oy=ogg_sync_create();
/* 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);
+ char *buffer=ogg_sync_bufferin(vf->oy,ibytes);
memcpy(buffer,initial,ibytes);
- ogg_sync_wrote(&vf->oy,ibytes);
+ ogg_sync_wrote(vf->oy,ibytes);
}
/* can we seek? Stevens suggests the seek test was portable */
- if(offset!=-1)vf->seekable=1;
+ if(offsettest!=-1)vf->seekable=1;
/* No seeking yet; Set up a 'single' (current) logical bitstream
entry for partial open */
vf->links=1;
- vf->vi=(vorbis_info *)_ogg_calloc(vf->links,sizeof(*vf->vi));
- vf->vc=(vorbis_comment *)_ogg_calloc(vf->links,sizeof(*vf->vc));
-
+ vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi));
+ vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc));
+ vf->os=ogg_stream_create(-1); /* fill in the serialno later */
+
/* 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;
@@ -620,7 +723,7 @@ int ov_clear(OggVorbis_File *vf){
if(vf){
vorbis_block_clear(&vf->vb);
vorbis_dsp_clear(&vf->vd);
- ogg_stream_clear(&vf->os);
+ ogg_stream_destroy(vf->os);
if(vf->vi && vf->links){
int i;
@@ -635,7 +738,8 @@ int ov_clear(OggVorbis_File *vf){
if(vf->pcmlengths)_ogg_free(vf->pcmlengths);
if(vf->serialnos)_ogg_free(vf->serialnos);
if(vf->offsets)_ogg_free(vf->offsets);
- ogg_sync_clear(&vf->oy);
+ ogg_sync_destroy(vf->oy);
+
if(vf->datasource)(vf->callbacks.close_func)(vf->datasource);
memset(vf,0,sizeof(*vf));
}
@@ -726,6 +830,10 @@ long ov_bitrate(OggVorbis_File *vf,int i){
int i;
for(i=0;i<vf->links;i++)
bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
+ /* This once read: return(rint(bits/ov_time_total(vf,-1)));
+ * gcc 3.x on x86 miscompiled this at optimisation level 2 and above,
+ * so this is slightly transformed to make it work.
+ */
return(bits*1000/ov_time_total(vf,-1));
}else{
if(vf->seekable){
@@ -784,7 +892,7 @@ 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;
+ ogg_int64_t acc=0;
int i;
for(i=0;i<vf->links;i++)
acc+=ov_raw_total(vf,i);
@@ -809,12 +917,12 @@ ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
acc+=ov_pcm_total(vf,i);
return(acc);
}else{
- return(vf->pcmlengths[i]);
+ return(vf->pcmlengths[i*2+1]);
}
}
/* returns: total milliseconds of content if i==-1
- seconds in that logical bitstream for i==0 to n
+ milliseconds in 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
*/
@@ -839,19 +947,26 @@ ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){
returns zero on success, nonzero on failure */
-int ov_raw_seek(OggVorbis_File *vf,long pos){
- ogg_stream_state work_os;
-
+int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
+ ogg_stream_state *work_os=NULL;
+ ogg_page og={0,0,0,0};
+ ogg_packet op={0,0,0,0,0,0};
+
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 */
+ if(pos<0 || pos>vf->end)return(OV_EINVAL);
+
+ /* don't yet clear out decoding machine (if it's initialized), in
+ the case we're in the same link. Restart the decode lapping, and
+ let _fetch_and_process_packet deal with a potential bitstream
+ boundary */
vf->pcm_offset=-1;
- _decode_clear(vf);
-
+ ogg_stream_reset_serialno(vf->os,
+ vf->current_serialno); /* must set serialno */
+ vorbis_synthesis_restart(&vf->vd);
+
_seek_helper(vf,pos);
/* we need to make sure the pcm_offset is set, but we don't want to
@@ -860,7 +975,7 @@ int ov_raw_seek(OggVorbis_File *vf,long pos){
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
+ 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
@@ -870,42 +985,46 @@ int ov_raw_seek(OggVorbis_File *vf,long pos){
*/
{
- ogg_page og;
- ogg_packet op;
int lastblock=0;
int accblock=0;
- int thisblock=-1;
- int eosflag=0;
-
- memset(&work_os,0,sizeof(work_os));/* so that it's safe to clear
- it later even if we don't
- init it */
+ int thisblock;
+ int eosflag;
+ work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */
while(1){
- if(vf->ready_state==STREAMSET){
+ if(vf->ready_state>=STREAMSET){
/* snarf/scan a packet if we can */
- int result=ogg_stream_packetout(&work_os,&op);
+ int result=ogg_stream_packetout(work_os,&op);
if(result>0){
- if(vf->vi[vf->current_link].codec_setup)
+ 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(thisblock<0){
+ ogg_stream_packetout(vf->os,NULL);
+ thisblock=0;
+ }else{
+
+ 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-vf->pcmlengths[link*2];
+ if(granulepos<0)granulepos=0;
+
+ for(i=0;i<link;i++)
+ granulepos+=vf->pcmlengths[i*2+1];
+ vf->pcm_offset=granulepos-accblock;
+ break;
+ }
+ lastblock=thisblock;
+ continue;
+ }else
+ ogg_stream_packetout(vf->os,NULL);
}
}
@@ -921,11 +1040,11 @@ int ov_raw_seek(OggVorbis_File *vf,long pos){
}
/* has our decoding just traversed a bitstream boundary? */
- if(vf->ready_state==STREAMSET)
+ 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);
- }
+ _decode_clear(vf); /* clear out stream state */
+ ogg_stream_destroy(work_os);
+ }
if(vf->ready_state<STREAMSET){
int link;
@@ -933,32 +1052,42 @@ int ov_raw_seek(OggVorbis_File *vf,long pos){
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 */
+ 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);
+ ogg_stream_reset_serialno(vf->os,vf->current_serialno);
+ ogg_stream_reset_serialno(work_os,vf->current_serialno);
vf->ready_state=STREAMSET;
}
- ogg_stream_pagein(&vf->os,&og);
- ogg_stream_pagein(&work_os,&og);
- eosflag=ogg_page_eos(&og);
+ {
+ ogg_page dup;
+ ogg_page_dup(&dup,&og);
+ eosflag=ogg_page_eos(&og);
+ ogg_stream_pagein(vf->os,&og);
+ ogg_stream_pagein(work_os,&dup);
+ }
}
}
- ogg_stream_clear(&work_os);
+ ogg_packet_release(&op);
+ ogg_page_release(&og);
+ ogg_stream_destroy(work_os);
+ vf->bittrack=0;
+ vf->samptrack=0;
return(0);
seek_error:
+ ogg_packet_release(&op);
+ ogg_page_release(&og);
+
/* dump the machine so we're in a known state */
vf->pcm_offset=-1;
- ogg_stream_clear(&work_os);
+ ogg_stream_destroy(work_os);
_decode_clear(vf);
return OV_EBADLINK;
}
@@ -971,8 +1100,10 @@ int ov_raw_seek(OggVorbis_File *vf,long pos){
arrive at the requested position. */
int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
int link=-1;
- long ret;
+ ogg_int64_t result=0;
ogg_int64_t total=ov_pcm_total(vf,-1);
+ ogg_page og={0,0,0,0};
+ ogg_packet op={0,0,0,0,0,0};
if(vf->ready_state<OPENED)return(OV_EINVAL);
if(!vf->seekable)return(OV_ENOSEEK);
@@ -980,7 +1111,7 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
/* which bitstream section does this pcm offset occur in? */
for(link=vf->links-1;link>=0;link--){
- total-=vf->pcmlengths[link];
+ total-=vf->pcmlengths[link*2+1];
if(pos>=total)break;
}
@@ -992,16 +1123,15 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
/* 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_int64_t end=vf->offsets[link+1];
+ ogg_int64_t begin=vf->offsets[link];
+ ogg_int64_t begintime = vf->pcmlengths[link*2];
+ ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime;
+ ogg_int64_t target=pos-total+begintime;
+ ogg_int64_t best=begin;
- ogg_page og;
while(begin<end){
- long bisect;
+ ogg_int64_t bisect;
if(end-begin<CHUNKSIZE){
bisect=begin;
@@ -1012,40 +1142,42 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
if(bisect<=begin)
bisect=begin+1;
}
+
_seek_helper(vf,bisect);
while(begin<end){
- ret=_get_next_page(vf,&og,end-bisect);
- if(ret==OV_EREAD) goto seek_error;
- if(ret<0){
+ result=_get_next_page(vf,&og,end-vf->offset);
+ if(result==OV_EREAD) goto seek_error;
+ if(result<0){
if(bisect<=begin+1)
end=begin; /* found it */
else{
- if(bisect==0)goto seek_error;
+ 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==-1)continue;
if(granulepos<target){
- best=ret; /* raw offset of packet with granulepos */
+ best=result; /* raw offset of packet with granulepos */
begin=vf->offset; /* raw offset of next page */
begintime=granulepos;
- if(target-begin>44100)break;
+ if(target-begintime>44100)break;
bisect=begin; /* *not* begin + 1 */
}else{
if(bisect<=begin+1)
end=begin; /* found it */
else{
if(end==vf->offset){ /* we're pretty close - we'd be stuck in */
- end=ret;
+ end=result;
bisect-=CHUNKSIZE; /* an endless loop otherwise. */
if(bisect<=begin)bisect=begin+1;
_seek_helper(vf,bisect);
}else{
- end=ret;
+ end=result;
endtime=granulepos;
break;
}
@@ -1058,103 +1190,139 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
/* 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);
+ vf->pcm_offset=-1;
- 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);
+ if(_get_next_page(vf,&og,-1)<0){
+ ogg_page_release(&og);
+ return(OV_EOF); /* shouldn't happen */
+ }
+
+ if(link!=vf->current_link){
+ /* Different link; dump entire decode machine */
+ _decode_clear(vf);
+
+ vf->current_link=link;
+ vf->current_serialno=ogg_page_serialno(&og);
+ vf->ready_state=STREAMSET;
+
+ }else{
+ vorbis_synthesis_restart(&vf->vd);
+ }
+
+ ogg_stream_reset_serialno(vf->os,vf->current_serialno);
+ 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){
+ result=ogg_stream_packetpeek(vf->os,&op);
+ if(result==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. */
+
+ _seek_helper(vf,best);
+
while(1){
- ret=_get_prev_page(vf,&og);
- if(ret<0)goto seek_error;
+ result=_get_prev_page(vf,&og);
+ if(result<0) goto seek_error;
if(ogg_page_granulepos(&og)>-1 ||
!ogg_page_continued(&og)){
- return ov_raw_seek(vf,ret);
+ return ov_raw_seek(vf,result);
}
- vf->offset=ret;
+ vf->offset=result;
}
}
- if(ret<0)goto seek_error;
+ if(result<0){
+ result = OV_EBADPACKET;
+ goto seek_error;
+ }
if(op.granulepos!=-1){
- vf->pcm_offset=op.granulepos+total;
+ vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
+ if(vf->pcm_offset<0)vf->pcm_offset=0;
+ vf->pcm_offset+=total;
break;
}else
- ret=ogg_stream_packetout(&vf->os,NULL);
+ result=ogg_stream_packetout(vf->os,NULL);
}
}
}
/* verify result */
if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){
- ret=OV_EFAULT;
+ result=OV_EFAULT;
goto seek_error;
}
+ vf->bittrack=0;
+ vf->samptrack=0;
+
+ ogg_page_release(&og);
+ ogg_packet_release(&op);
return(0);
seek_error:
+
+ ogg_page_release(&og);
+ ogg_packet_release(&op);
+
/* dump machine so we're in a known state */
vf->pcm_offset=-1;
_decode_clear(vf);
- return ret;
+ return (int)result;
}
/* seek to a sample offset relative to the decompressed pcm stream
returns zero on success, nonzero on failure */
int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
+ ogg_packet op={0,0,0,0,0,0};
+ ogg_page og={0,0,0,0};
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 */
while(1){
- ogg_packet op;
- ogg_page og;
- int ret=ogg_stream_packetpeek(&vf->os,&op);
+ int ret=ogg_stream_packetpeek(vf->os,&op);
if(ret>0){
thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
+ if(thisblock<0){
+ ogg_stream_packetout(vf->os,NULL);
+ continue; /* 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;
-
- ogg_stream_packetout(&vf->os,NULL);
-
+ /* remove the packet from packet queue and track its granulepos */
+ ogg_stream_packetout(vf->os,NULL);
+ vorbis_synthesis(&vf->vb,&op,0); /* 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 */
- vf->pcm_offset=op.granulepos;
+ vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
+ if(vf->pcm_offset<0)vf->pcm_offset=0;
for(i=0;i<vf->current_link;i++)
- vf->pcm_offset+=vf->pcmlengths[i];
+ vf->pcm_offset+=vf->pcmlengths[i*2+1];
}
-
+
lastblock=thisblock;
-
+
}else{
if(ret<0 && ret!=OV_HOLE)break;
@@ -1168,34 +1336,42 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
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);
+ if(link==vf->links){
+ ogg_page_release(&og);
+ ogg_packet_release(&op);
+ return(OV_EBADLINK);
+ }
vf->current_link=link;
- ogg_stream_init(&vf->os,vf->current_serialno);
- ogg_stream_reset(&vf->os);
+ ogg_stream_reset_serialno(vf->os,vf->current_serialno);
vf->ready_state=STREAMSET;
+ _make_decode_ready(vf);
lastblock=0;
}
- ogg_stream_pagein(&vf->os,&og);
+
+ ogg_stream_pagein(vf->os,&og);
}
}
+ vf->bittrack=0;
+ vf->samptrack=0;
/* 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){
- ogg_int32_t **pcm;
- long target=pos-vf->pcm_offset;
- long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+ ogg_int64_t target=pos-vf->pcm_offset;
+ long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
if(samples>target)samples=target;
vorbis_synthesis_read(&vf->vd,samples);
vf->pcm_offset+=samples;
if(samples<target)
- if(_process_packet(vf,1)<=0)
+ if(_fetch_and_process_packet(vf,1,1)<=0)
vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
}
+
+ ogg_page_release(&og);
+ ogg_packet_release(&op);
return 0;
}
@@ -1214,7 +1390,7 @@ int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){
/* which bitstream section does this time offset occur in? */
for(link=vf->links-1;link>=0;link--){
- pcm_total-=vf->pcmlengths[link];
+ pcm_total-=vf->pcmlengths[link*2+1];
time_total-=ov_time_total(vf,link);
if(milliseconds>=time_total)break;
}
@@ -1241,7 +1417,7 @@ int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t milliseconds){
/* which bitstream section does this time offset occur in? */
for(link=vf->links-1;link>=0;link--){
- pcm_total-=vf->pcmlengths[link];
+ pcm_total-=vf->pcmlengths[link*2+1];
time_total-=ov_time_total(vf,link);
if(milliseconds>=time_total)break;
}
@@ -1268,11 +1444,9 @@ ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
/* return time offset (milliseconds) of next PCM sample to be read */
ogg_int64_t ov_time_tell(OggVorbis_File *vf){
- /* translate time to PCM position and call ov_pcm_seek */
-
int link=0;
ogg_int64_t pcm_total=0;
- ogg_int64_t time_total=0;
+ ogg_int64_t time_total=0.f;
if(vf->ready_state<OPENED)return(OV_EINVAL);
if(vf->seekable){
@@ -1281,7 +1455,7 @@ ogg_int64_t ov_time_tell(OggVorbis_File *vf){
/* which bitstream section does this time offset occur in? */
for(link=vf->links-1;link>=0;link--){
- pcm_total-=vf->pcmlengths[link];
+ pcm_total-=vf->pcmlengths[link*2+1];
time_total-=ov_time_total(vf,link);
if(vf->pcm_offset>=pcm_total)break;
}
@@ -1369,16 +1543,18 @@ long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){
if(vf->ready_state<OPENED)return(OV_EINVAL);
while(1){
- if(vf->ready_state>=STREAMSET){
+ if(vf->ready_state==INITSET){
samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
if(samples)break;
}
/* suck in another packet */
{
- int ret=_process_packet(vf,1);
- if(ret==OV_EOF)return(0);
- if(ret<=0)return(ret);
+ int ret=_fetch_and_process_packet(vf,1,1);
+ if(ret==OV_EOF)
+ return(0);
+ if(ret<=0)
+ return(ret);
}
}
@@ -1391,18 +1567,18 @@ long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){
if(channels==1){
if(samples>(bytes_req/2))
- samples=bytes_req/2;
+ samples=bytes_req/2;
}else{
if(samples>(bytes_req/4))
- samples=bytes_req/4;
+ samples=bytes_req/4;
}
for(i=0;i<channels;i++) { /* It's faster in this order */
ogg_int32_t *src=pcm[i];
short *dest=((short *)buffer)+i;
for(j=0;j<samples;j++) {
- *dest=CLIP_TO_15(src[j]>>9);
- dest+=channels;
+ *dest=CLIP_TO_15(src[j]>>9);
+ dest+=channels;
}
}
@@ -1414,7 +1590,3 @@ long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){
return(samples);
}
}
-
-
-
-