diff options
author | Monty <xiphmont@xiph.org> | 2003-03-29 03:07:21 +0000 |
---|---|---|
committer | Monty <xiphmont@xiph.org> | 2003-03-29 03:07:21 +0000 |
commit | 3c764af8013b5b153d3e97311d56a8741536f4cf (patch) | |
tree | b8201ccdd984c9b5b682641996a19cca15b6593c | |
parent | a80cb134d84683d6c4961e2c33fddf3a24b378fa (diff) | |
download | tremor-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.am | 2 | ||||
-rw-r--r-- | bitwise.c | 253 | ||||
-rw-r--r-- | block.c | 149 | ||||
-rw-r--r-- | framing.c | 1263 | ||||
-rw-r--r-- | info.c | 4 | ||||
-rw-r--r-- | ivorbiscodec.h | 3 | ||||
-rw-r--r-- | ivorbisfile.h | 13 | ||||
-rw-r--r-- | misc.h | 15 | ||||
-rw-r--r-- | ogg.h | 214 | ||||
-rw-r--r-- | synthesis.c | 38 | ||||
-rw-r--r-- | vorbisfile.c | 670 |
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 \ @@ -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 @@ -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); } @@ -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); } @@ -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); @@ -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, @@ -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); } } - - - - |