diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-02-14 12:21:33 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-02-14 12:21:33 +1100 |
| commit | 7ec0ff2589ffd5774e78f9e6b436ea55be45deb1 (patch) | |
| tree | 4bc335bf6474bff2babbcbd4690231320b52520f /lib/tremor/vorbisfile.c | |
| parent | b31bc07555fdd862181d8d6ed551163cea89bc62 (diff) | |
| download | tangara-fw-7ec0ff2589ffd5774e78f9e6b436ea55be45deb1.tar.gz | |
Switch to the lowmem tremor branch
in addition to using slightly less memory, this branch also doesn't seem
to have the same issues with `-O2` builds that the main branch has.
Diffstat (limited to 'lib/tremor/vorbisfile.c')
| -rw-r--r-- | lib/tremor/vorbisfile.c | 2279 |
1 files changed, 950 insertions, 1329 deletions
diff --git a/lib/tremor/vorbisfile.c b/lib/tremor/vorbisfile.c index cd4814df..79cdefcd 100644 --- a/lib/tremor/vorbisfile.c +++ b/lib/tremor/vorbisfile.c @@ -1,18 +1,18 @@ /******************************************************************** * * - * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * + * THIS FILE IS PART OF THE TremorOggVorbis 'TREMOR' CODEC SOURCE CODE. * * * * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * * 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-2014 * + * THE TremorOggVorbis '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$ + last mod: $Id: vorbisfile.c,v 1.6.2.5 2003/11/20 06:16:17 xiphmont Exp $ ********************************************************************/ @@ -22,12 +22,19 @@ #include <string.h> #include <math.h> -#include "ivorbiscodec.h" +#include "codec_internal.h" #include "ivorbisfile.h" #include "os.h" #include "misc.h" +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 /* serialno and link set, but not to current link */ +#define LINKSET 4 /* serialno and link set to current link */ +#define INITSET 5 + /* A 'chained bitstream' is a Vorbis bitstream that contains more than one logical bitstream arranged end to end (the only form of Ogg multiplexing allowed in a Vorbis bitstream; grouping [parallel @@ -52,42 +59,35 @@ we only want coarse navigation through the stream. */ /************************************************************************* - * Many, many internal helpers. The intention is not to be confusing; - * rampant duplication and monolithic function implementation would be + * Many, many internal helpers. The intention is not to be confusing; + * rampant duplication and monolithic function implementation would be * harder to understand anyway. The high level functions are last. Begin * grokking near the end of the file */ -/* read a little more data from the file/pipe into the ogg_sync framer */ -static long _get_data(OggVorbis_File *vf){ +/* read a little more data from the file/pipe into the tremor_ogg_sync framer */ +static long _get_data(TremorOggVorbis_File *vf){ errno=0; - if(!(vf->callbacks.read_func))return(-1); if(vf->datasource){ - char *buffer=ogg_sync_buffer(&vf->oy,READSIZE); - long bytes=(vf->callbacks.read_func)(buffer,1,READSIZE,vf->datasource); - if(bytes>0)ogg_sync_wrote(&vf->oy,bytes); - if(bytes==0 && errno)return(-1); - return(bytes); + unsigned char *buffer=tremor_ogg_sync_bufferin(vf->oy,CHUNKSIZE); + long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); + if(bytes>0)tremor_ogg_sync_wrote(vf->oy,bytes); + if(bytes==0 && errno)return -1; + return bytes; }else - return(0); + return 0; } /* save a tiny smidge of verbosity to make the code more readable */ -static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ - if(vf->datasource){ - /* only seek if the file position isn't already there */ - if(vf->offset != offset){ - if(!(vf->callbacks.seek_func)|| - (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) - return OV_EREAD; - vf->offset=offset; - ogg_sync_reset(&vf->oy); - } +static void _seek_helper(TremorOggVorbis_File *vf,tremor_ogg_int64_t offset){ + if(vf->datasource){ + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET); + vf->offset=offset; + tremor_ogg_sync_reset(vf->oy); }else{ /* shouldn't happen unless someone writes a broken callback */ - return OV_EFAULT; + return; } - return 0; } /* The read/seek functions track absolute position within the stream */ @@ -98,310 +98,221 @@ static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ boundary: -1) unbounded search 0) read no additional data; use cached only - n) search for a new page beginning for n bytes + 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 ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, - ogg_int64_t boundary){ +static tremor_ogg_int64_t _get_next_page(TremorOggVorbis_File *vf,tremor_ogg_page *og, + tremor_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); - + if(boundary>0 && vf->offset>=boundary)return OV_FALSE; + more=tremor_ogg_sync_pageseek(vf->oy,og); + if(more<0){ /* skipped n bytes */ vf->offset-=more; }else{ if(more==0){ - /* send more paramedics */ - if(!boundary)return(OV_FALSE); - { - long ret=_get_data(vf); - if(ret==0)return(OV_EOF); - if(ret<0)return(OV_EREAD); - } + /* send more paramedics */ + if(!boundary)return OV_FALSE; + { + long ret=_get_data(vf); + if(ret==0)return OV_EOF; + if(ret<0)return OV_EREAD; + } }else{ - /* got a page. Return the offset at the page beginning, + /* got a page. Return the offset at the page beginning, advance the internal offset past the page end */ - ogg_int64_t ret=vf->offset; - vf->offset+=more; - return(ret); - + tremor_ogg_int64_t ret=vf->offset; + vf->offset+=more; + return ret; + } } } } -/* find the latest page beginning before the passed in 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 ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_int64_t begin,ogg_page *og){ - ogg_int64_t end = begin; - ogg_int64_t ret; - ogg_int64_t offset=-1; +/* find the latest page beginning before the current stream cursor + position. Much dirtier than the above as Ogg doesn't have any + backward search linkage. no 'readp' as it will certainly have to + read. */ +/* returns offset or OV_EREAD, OV_FAULT and produces a refcounted page */ + +static tremor_ogg_int64_t _get_prev_page(TremorOggVorbis_File *vf,tremor_ogg_page *og){ + tremor_ogg_int64_t begin=vf->offset; + tremor_ogg_int64_t end=begin; + tremor_ogg_int64_t ret; + tremor_ogg_int64_t offset=-1; while(offset==-1){ begin-=CHUNKSIZE; if(begin<0) begin=0; - - ret=_seek_helper(vf,begin); - if(ret)return(ret); - + _seek_helper(vf,begin); while(vf->offset<end){ - memset(og,0,sizeof(*og)); ret=_get_next_page(vf,og,end-vf->offset); - if(ret==OV_EREAD)return(OV_EREAD); + if(ret==OV_EREAD)return OV_EREAD; if(ret<0){ - break; + break; }else{ - offset=ret; + offset=ret; } } } - /* In a fully compliant, non-multiplexed stream, we'll still be - holding the last page. In multiplexed (or noncompliant streams), - we will probably have to re-read the last page we saw */ - if(og->header_len==0){ - ret=_seek_helper(vf,offset); - if(ret)return(ret); - - ret=_get_next_page(vf,og,CHUNKSIZE); - if(ret<0) - /* this shouldn't be possible */ - return(OV_EFAULT); - } + /* we have the offset. Actually snork and hold the page now */ + _seek_helper(vf,offset); + ret=_get_next_page(vf,og,CHUNKSIZE); + if(ret<0) + /* this shouldn't be possible */ + return OV_EFAULT; - return(offset); + return offset; } -static void _add_serialno(ogg_page *og,ogg_uint32_t **serialno_list, int *n){ - ogg_uint32_t s = ogg_page_serialno(og); - (*n)++; - - if(*serialno_list){ - *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n)); - }else{ - *serialno_list = _ogg_malloc(sizeof(**serialno_list)); +/* finds each bitstream link one at a time using a bisection search + (has to begin by knowing the offset of the lb's initial page). + Recurses for each link so it can alloc the link storage after + finding them all, then unroll and fill the cache at the same time */ +static int _bisect_forward_serialno(TremorOggVorbis_File *vf, + tremor_ogg_int64_t begin, + tremor_ogg_int64_t searched, + tremor_ogg_int64_t end, + tremor_ogg_uint32_t currentno, + long m){ + tremor_ogg_int64_t endsearched=end; + tremor_ogg_int64_t next=end; + tremor_ogg_page og={0,0,0,0}; + tremor_ogg_int64_t ret; + + /* the below guards against garbage seperating the last and + first pages of two links. */ + while(searched<endsearched){ + tremor_ogg_int64_t bisect; + + if(endsearched-searched<CHUNKSIZE){ + bisect=searched; + }else{ + bisect=(searched+endsearched)/2; + } + + _seek_helper(vf,bisect); + ret=_get_next_page(vf,&og,-1); + if(ret==OV_EREAD)return OV_EREAD; + if(ret<0 || tremor_ogg_page_serialno(&og)!=currentno){ + endsearched=bisect; + if(ret>=0)next=ret; + }else{ + searched=ret+og.header_len+og.body_len; + } + tremor_ogg_page_release(&og); } - (*serialno_list)[(*n)-1] = s; -} - -/* returns nonzero if found */ -static int _lookup_serialno(ogg_uint32_t s, ogg_uint32_t *serialno_list, int n){ - if(serialno_list){ - while(n--){ - if(*serialno_list == s) return 1; - serialno_list++; - } + _seek_helper(vf,next); + ret=_get_next_page(vf,&og,-1); + if(ret==OV_EREAD)return OV_EREAD; + + if(searched>=end || ret<0){ + tremor_ogg_page_release(&og); + vf->links=m+1; + vf->offsets=_tremor_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); + vf->serialnos=_tremor_ogg_malloc(vf->links*sizeof(*vf->serialnos)); + vf->offsets[m+1]=searched; + }else{ + ret=_bisect_forward_serialno(vf,next,vf->offset, + end,tremor_ogg_page_serialno(&og),m+1); + tremor_ogg_page_release(&og); + if(ret==OV_EREAD)return OV_EREAD; } + + vf->offsets[m]=begin; + vf->serialnos[m]=currentno; return 0; } -static int _lookup_page_serialno(ogg_page *og, ogg_uint32_t *serialno_list, int n){ - ogg_uint32_t s = ogg_page_serialno(og); - return _lookup_serialno(s,serialno_list,n); -} - -/* performs the same search as _get_prev_page, but prefers pages of - the specified serial number. If a page of the specified serialno is - spotted during the seek-back-and-read-forward, it will return the - info of last page of the matching serial number instead of the very - last page. If no page of the specified serialno is seen, it will - return the info of last page and alter *serialno. */ -static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ogg_int64_t begin, - ogg_uint32_t *serial_list, int serial_n, - int *serialno, ogg_int64_t *granpos){ - ogg_page og; - ogg_int64_t end=begin; - ogg_int64_t ret; - - ogg_int64_t prefoffset=-1; - ogg_int64_t offset=-1; - ogg_int64_t ret_serialno=-1; - ogg_int64_t ret_gran=-1; - - while(offset==-1){ - begin-=CHUNKSIZE; - if(begin<0) - begin=0; - - ret=_seek_helper(vf,begin); - if(ret)return(ret); - - while(vf->offset<end){ - ret=_get_next_page(vf,&og,end-vf->offset); - if(ret==OV_EREAD)return(OV_EREAD); - if(ret<0){ - break; - }else{ - ret_serialno=ogg_page_serialno(&og); - ret_gran=ogg_page_granulepos(&og); - offset=ret; - - if((ogg_uint32_t)ret_serialno == *serialno){ - prefoffset=ret; - *granpos=ret_gran; - } - - if(!_lookup_serialno((ogg_uint32_t)ret_serialno,serial_list,serial_n)){ - /* we fell off the end of the link, which means we seeked - back too far and shouldn't have been looking in that link - to begin with. If we found the preferred serial number, - forget that we saw it. */ - prefoffset=-1; - } - } - } +static int _decode_clear(TremorOggVorbis_File *vf){ + if(vf->ready_state==INITSET){ + vorbis_dsp_destroy(vf->vd); + vf->vd=0; + vf->ready_state=STREAMSET; } - - /* we're not interested in the page... just the serialno and granpos. */ - if(prefoffset>=0)return(prefoffset); - - *serialno = ret_serialno; - *granpos = ret_gran; - return(offset); - + + if(vf->ready_state>=STREAMSET){ + vorbis_info_clear(&vf->vi); + vorbis_comment_clear(&vf->vc); + vf->ready_state=OPENED; + } + return 0; } -/* uses the local ogg_stream storage in vf; this is important for +/* uses the local tremor_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, - ogg_uint32_t **serialno_list, int *serialno_n, - ogg_page *og_ptr){ - ogg_page og; - ogg_packet op; +/* consumes the page that's passed in (if any) */ +/* state is LINKSET upon successful return */ + +static int _fetch_headers(TremorOggVorbis_File *vf, + vorbis_info *vi, + vorbis_comment *vc, + tremor_ogg_uint32_t *serialno, + tremor_ogg_page *og_ptr){ + tremor_ogg_page og={0,0,0,0}; + tremor_ogg_packet op={0,0,0,0,0,0}; int i,ret; - int allbos=0; + + if(vf->ready_state>OPENED)_decode_clear(vf); if(!og_ptr){ - ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); - if(llret==OV_EREAD)return(OV_EREAD); - if(llret<0)return(OV_ENOTVORBIS); + tremor_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; } + tremor_ogg_stream_reset_serialno(vf->os,tremor_ogg_page_serialno(og_ptr)); + if(serialno)*serialno=vf->os->serialno; + + /* extract the initial header from the first page and verify that the + Ogg bitstream is in fact Vorbis data */ + vorbis_info_init(vi); vorbis_comment_init(vc); - vf->ready_state=OPENED; - - /* extract the serialnos of all BOS pages + the first set of vorbis - headers we see in the link */ - - while(ogg_page_bos(og_ptr)){ - if(serialno_list){ - if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){ - /* a dupe serialnumber in an initial header packet set == invalid stream */ - if(*serialno_list)_ogg_free(*serialno_list); - *serialno_list=0; - *serialno_n=0; - ret=OV_EBADHEADER; - goto bail_header; + + i=0; + while(i<3){ + tremor_ogg_stream_pagein(vf->os,og_ptr); + while(i<3){ + int result=tremor_ogg_stream_packetout(vf->os,&op); + if(result==0)break; + if(result==-1){ + ret=OV_EBADHEADER; + goto bail_header; } - - _add_serialno(og_ptr,serialno_list,serialno_n); - } - - if(vf->ready_state<STREAMSET){ - /* we don't have a vorbis stream in this link yet, so begin - prospective stream setup. We need a stream to get packets */ - ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr)); - ogg_stream_pagein(&vf->os,og_ptr); - - if(ogg_stream_packetout(&vf->os,&op) > 0 && - vorbis_synthesis_idheader(&op)){ - /* vorbis header; continue setup */ - vf->ready_state=STREAMSET; - if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ - ret=OV_EBADHEADER; - goto bail_header; - } + if((ret=vorbis_dsp_headerin(vi,vc,&op))){ + goto bail_header; } + i++; } - - /* get next page */ - { - ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE); - if(llret==OV_EREAD){ - ret=OV_EREAD; - goto bail_header; - } - if(llret<0){ - ret=OV_ENOTVORBIS; - goto bail_header; + if(i<3) + if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ + ret=OV_EBADHEADER; + goto bail_header; } - - /* if this page also belongs to our vorbis stream, submit it and break */ - if(vf->ready_state==STREAMSET && - vf->os.serialno == ogg_page_serialno(og_ptr)){ - ogg_stream_pagein(&vf->os,og_ptr); - break; - } - } } - if(vf->ready_state!=STREAMSET){ - ret = OV_ENOTVORBIS; - goto bail_header; - } - - while(1){ - - i=0; - while(i<2){ /* get a page loop */ - - while(i<2){ /* get a packet loop */ - - int result=ogg_stream_packetout(&vf->os,&op); - if(result==0)break; - if(result==-1){ - ret=OV_EBADHEADER; - goto bail_header; - } - - if((ret=vorbis_synthesis_headerin(vi,vc,&op))) - goto bail_header; - - i++; - } - - while(i<2){ - if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ - ret=OV_EBADHEADER; - goto bail_header; - } - - /* if this page belongs to the correct stream, go parse it */ - if(vf->os.serialno == ogg_page_serialno(og_ptr)){ - ogg_stream_pagein(&vf->os,og_ptr); - break; - } - - /* if we never see the final vorbis headers before the link - ends, abort */ - if(ogg_page_bos(og_ptr)){ - if(allbos){ - ret = OV_EBADHEADER; - goto bail_header; - }else - allbos=1; - } - - /* otherwise, keep looking */ - } - } - - return 0; - } + tremor_ogg_packet_release(&op); + tremor_ogg_page_release(&og); + vf->ready_state=LINKSET; + return 0; bail_header: + tremor_ogg_packet_release(&op); + tremor_ogg_page_release(&og); vorbis_info_clear(vi); vorbis_comment_clear(vc); vf->ready_state=OPENED; @@ -409,401 +320,321 @@ static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, return ret; } -/* Starting from current cursor position, get initial PCM offset of - next page. Consumes the page in the process without decoding - audio, however this is only called during stream parsing upon - seekable open. */ -static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ - ogg_page og; - ogg_int64_t accumulated=0; - long lastblock=-1; - int result; - int serialno = vf->os.serialno; - - while(1){ - ogg_packet op; - if(_get_next_page(vf,&og,-1)<0) - break; /* should not be possible unless the file is truncated/mangled */ - - if(ogg_page_bos(&og)) break; - if(ogg_page_serialno(&og)!=serialno) continue; - - /* 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(vi,&op); - if(lastblock!=-1) - accumulated+=(lastblock+thisblock)>>2; - lastblock=thisblock; - } - } - - if(ogg_page_granulepos(&og)!=-1){ - /* pcm offset of last packet on the first audio page */ - accumulated= ogg_page_granulepos(&og)-accumulated; - break; - } +/* we no longer preload all vorbis_info (and the associated + codec_setup) structs. Call this to seek and fetch the info from + the bitstream, if needed */ +static int _set_link_number(TremorOggVorbis_File *vf,int link){ + if(link != vf->current_link) _decode_clear(vf); + if(vf->ready_state<STREAMSET){ + _seek_helper(vf,vf->offsets[link]); + tremor_ogg_stream_reset_serialno(vf->os,vf->serialnos[link]); + vf->current_serialno=vf->serialnos[link]; + vf->current_link=link; + return _fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL); } - - /* 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; - - return accumulated; + return 0; } -/* finds each bitstream link one at a time using a bisection search - (has to begin by knowing the offset of the lb's initial page). - Recurses for each link so it can alloc the link storage after - finding them all, then unroll and fill the cache at the same time */ -static int _bisect_forward_serialno(OggVorbis_File *vf, - ogg_int64_t begin, - ogg_int64_t searched, - ogg_int64_t end, - ogg_int64_t endgran, - int endserial, - ogg_uint32_t *currentno_list, - int currentnos, - long m){ - ogg_int64_t pcmoffset; - ogg_int64_t dataoffset=searched; - ogg_int64_t endsearched=end; - ogg_int64_t next=end; - ogg_int64_t searchgran=-1; - ogg_page og; - ogg_int64_t ret,last; - int serialno = vf->os.serialno; - - /* invariants: - we have the headers and serialnos for the link beginning at 'begin' - we have the offset and granpos of the last page in the file (potentially - not a page we care about) - */ - - /* Is the last page in our list of current serialnumbers? */ - if(_lookup_serialno(endserial,currentno_list,currentnos)){ - - /* last page is in the starting serialno list, so we've bisected - down to (or just started with) a single link. Now we need to - find the last vorbis page belonging to the first vorbis stream - for this link. */ - searched = end; - while(endserial != serialno){ - endserial = serialno; - searched=_get_prev_page_serial(vf,searched,currentno_list,currentnos,&endserial,&endgran); - } - - vf->links=m+1; - if(vf->offsets)_ogg_free(vf->offsets); - if(vf->serialnos)_ogg_free(vf->serialnos); - if(vf->dataoffsets)_ogg_free(vf->dataoffsets); - - vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); - vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); - vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); - vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); - vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); - vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); - - vf->offsets[m+1]=end; - vf->offsets[m]=begin; - vf->pcmlengths[m*2+1]=(endgran<0?0:endgran); - - }else{ - - /* last page is not in the starting stream's serial number list, - so we have multiple links. Find where the stream that begins - our bisection ends. */ - - ogg_uint32_t *next_serialno_list=NULL; - int next_serialnos=0; - vorbis_info vi; - vorbis_comment vc; - int testserial = serialno+1; +static int _set_link_number_preserve_pos(TremorOggVorbis_File *vf,int link){ + tremor_ogg_int64_t pos=vf->offset; + int ret=_set_link_number(vf,link); + if(ret)return ret; + _seek_helper(vf,pos); + if(pos<vf->offsets[link] || pos>=vf->offsets[link+1]) + vf->ready_state=STREAMSET; + return 0; +} - /* the below guards against garbage seperating the last and - first pages of two links. */ - while(searched<endsearched){ - ogg_int64_t bisect; +/* last step of the TremorOggVorbis_File initialization; get all the offset + positions. Only called by the seekable initialization (local + stream storage is hacked slightly; pay attention to how that's + done) */ + +/* this is void and does not propogate errors up because we want to be + able to open and use damaged bitstreams as well as we can. Just + watch out for missing information for links in the TremorOggVorbis_File + struct */ +static void _prefetch_all_offsets(TremorOggVorbis_File *vf, tremor_ogg_int64_t dataoffset){ + tremor_ogg_page og={0,0,0,0}; + int i; + tremor_ogg_int64_t ret; + + vf->dataoffsets=_tremor_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); + vf->pcmlengths=_tremor_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); - if(endsearched-searched<CHUNKSIZE){ - bisect=searched; - }else{ - bisect=(searched+endsearched)/2; - } + }else{ - ret=_seek_helper(vf,bisect); - if(ret)return(ret); + /* seek to the location of the initial header */ - last=_get_next_page(vf,&og,-1); - if(last==OV_EREAD)return(OV_EREAD); - if(last<0 || !_lookup_page_serialno(&og,currentno_list,currentnos)){ - endsearched=bisect; - if(last>=0)next=last; + _seek_helper(vf,vf->offsets[i]); + if(_fetch_headers(vf,&vf->vi,&vf->vc,NULL,NULL)<0){ + vf->dataoffsets[i]=-1; }else{ - searched=vf->offset; + vf->dataoffsets[i]=vf->offset; } } - /* Bisection point found */ - /* for the time being, fetch end PCM offset the simple way */ - searched = next; - while(testserial != serialno){ - testserial = serialno; - searched = _get_prev_page_serial(vf,searched,currentno_list,currentnos,&testserial,&searchgran); - } - - ret=_seek_helper(vf,next); - if(ret)return(ret); + /* fetch beginning PCM offset */ - ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL); - if(ret)return(ret); - serialno = vf->os.serialno; - dataoffset = vf->offset; + if(vf->dataoffsets[i]!=-1){ + tremor_ogg_int64_t accumulated=0,pos; + long lastblock=-1; + int result; - /* this will consume a page, however the next bisection always - starts with a raw seek */ - pcmoffset = _initial_pcmoffset(vf,&vi); + tremor_ogg_stream_reset_serialno(vf->os,vf->serialnos[i]); - ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial, - next_serialno_list,next_serialnos,m+1); - if(ret)return(ret); - - if(next_serialno_list)_ogg_free(next_serialno_list); + while(1){ + tremor_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(tremor_ogg_page_serialno(&og)!=vf->serialnos[i]) + break; + + pos=tremor_ogg_page_granulepos(&og); + + /* count blocksizes of all frames in the page */ + tremor_ogg_stream_pagein(vf->os,&og); + while((result=tremor_ogg_stream_packetout(vf->os,&op))){ + if(result>0){ /* ignore holes */ + long thisblock=vorbis_packet_blocksize(&vf->vi,&op); + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } + } + tremor_ogg_packet_release(&op); + + if(pos!=-1){ + /* pcm offset of last packet on the first audio page */ + accumulated= pos-accumulated; + break; + } + } - vf->offsets[m+1]=next; - vf->serialnos[m+1]=serialno; - vf->dataoffsets[m+1]=dataoffset; + /* 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->vi[m+1]=vi; - vf->vc[m+1]=vc; + vf->pcmlengths[i*2]=accumulated; + } - vf->pcmlengths[m*2+1]=searchgran; - vf->pcmlengths[m*2+2]=pcmoffset; - vf->pcmlengths[m*2+3]-=pcmoffset; - if(vf->pcmlengths[m*2+3]<0)vf->pcmlengths[m*2+3]=0; + /* get the PCM length of this link. To do this, + get the last page of the stream */ + { + tremor_ogg_int64_t end=vf->offsets[i+1]; + _seek_helper(vf,end); + while(1){ + ret=_get_prev_page(vf,&og); + if(ret<0){ + /* this should not be possible */ + vorbis_info_clear(&vf->vi); + vorbis_comment_clear(&vf->vc); + break; + } + if(tremor_ogg_page_granulepos(&og)!=-1){ + vf->pcmlengths[i*2+1]=tremor_ogg_page_granulepos(&og)-vf->pcmlengths[i*2]; + break; + } + vf->offset=ret; + } + } } - return(0); + tremor_ogg_page_release(&og); } -static int _make_decode_ready(OggVorbis_File *vf){ - if(vf->ready_state>STREAMSET)return 0; - if(vf->ready_state<STREAMSET)return OV_EFAULT; - if(vf->seekable){ - if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link)) - return OV_EBADLINK; - }else{ - if(vorbis_synthesis_init(&vf->vd,vf->vi)) - return OV_EBADLINK; +static int _make_decode_ready(TremorOggVorbis_File *vf){ + int i; + switch(vf->ready_state){ + case OPENED: + case STREAMSET: + for(i=0;i<vf->links;i++) + if(vf->offsets[i+1]>=vf->offset)break; + if(i==vf->links)return -1; + i=_set_link_number_preserve_pos(vf,i); + if(i)return i; + /* fall through */ + case LINKSET: + vf->vd=vorbis_dsp_create(&vf->vi); + vf->ready_state=INITSET; + vf->bittrack=0; + vf->samptrack=0; + case INITSET: + return 0; + default: + return -1; } - vorbis_block_init(&vf->vd,&vf->vb); - vf->ready_state=INITSET; - vf->bittrack=0; - vf->samptrack=0; - return 0; + } -static int _open_seekable2(OggVorbis_File *vf){ - ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1; - int endserial=vf->os.serialno; - int serialno=vf->os.serialno; +static int _open_seekable2(TremorOggVorbis_File *vf){ + tremor_ogg_uint32_t serialno=vf->current_serialno; + tremor_ogg_uint32_t tempserialno; + tremor_ogg_int64_t dataoffset=vf->offset, end; + tremor_ogg_page og={0,0,0,0}; /* we're partially open and have a first link header state in storage in vf */ - - /* fetch initial PCM offset */ - ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi); - /* we can seek, so set out learning all about this file */ - if(vf->callbacks.seek_func && vf->callbacks.tell_func){ - (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); - vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); - }else{ - vf->offset=vf->end=-1; - } + (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); + vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); + + /* We get the offset for the last page of the physical bitstream. + Most TremorOggVorbis files will contain a single logical bitstream */ + end=_get_prev_page(vf,&og); + if(end<0)return end; - /* If seek_func is implemented, tell_func must also be implemented */ - if(vf->end==-1) return(OV_EINVAL); + /* more than one logical bitstream? */ + tempserialno=tremor_ogg_page_serialno(&og); + tremor_ogg_page_release(&og); - /* Get the offset of the last page of the physical bitstream, or, if - we're lucky the last vorbis page of this link as most OggVorbis - files will contain a single logical bitstream */ - end=_get_prev_page_serial(vf,vf->end,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); - if(end<0)return(end); + if(tempserialno!=serialno){ - /* now determine bitstream structure recursively */ - if(_bisect_forward_serialno(vf,0,dataoffset,end,endgran,endserial, - vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD); + /* 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)return OV_EREAD; - vf->offsets[0]=0; - vf->serialnos[0]=serialno; - vf->dataoffsets[0]=dataoffset; - vf->pcmlengths[0]=pcmoffset; - vf->pcmlengths[1]-=pcmoffset; - if(vf->pcmlengths[1]<0)vf->pcmlengths[1]=0; + }else{ - return(ov_raw_seek(vf,dataoffset)); -} + /* Only one logical bitstream */ + if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return OV_EREAD; -/* clear out the current logical bitstream decoder */ -static void _decode_clear(OggVorbis_File *vf){ - vorbis_dsp_clear(&vf->vd); - vorbis_block_clear(&vf->vb); - vf->ready_state=OPENED; + } + + /* the initial header memory is referenced by vf after; don't free it */ + _prefetch_all_offsets(vf,dataoffset); + return ov_raw_seek(vf,0); } /* fetch and process a packet. Handles the case where we're at a bitstream boundary and dumps the decoding machine. If the decoding machine is unloaded, it loads it. It also keeps pcm_offset up to date (seek and read both use this. seek uses a special hack with - readp). + readp). return: <0) error, OV_HOLE (lost packet) or OV_EOF 0) need more data (only if readp==0) - 1) got a packet + 1) got a packet */ -static int _fetch_and_process_packet(OggVorbis_File *vf, - ogg_packet *op_in, - int readp, - int spanp){ - ogg_page og; +static int _fetch_and_process_packet(TremorOggVorbis_File *vf, + int readp, + int spanp){ + tremor_ogg_page og={0,0,0,0}; + tremor_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 */ while(1){ - - if(vf->ready_state==STREAMSET){ - int ret=_make_decode_ready(vf); - if(ret<0)return ret; - } - + /* process a packet if we can. If the machine isn't loaded, neither is a page */ if(vf->ready_state==INITSET){ while(1) { - ogg_packet op; - ogg_packet *op_ptr=(op_in?op_in:&op); - int result=ogg_stream_packetout(&vf->os,op_ptr); - ogg_int64_t granulepos; - - op_in=NULL; - if(result==-1)return(OV_HOLE); /* hole in the data. */ - if(result>0){ - /* got a packet. process it */ - granulepos=op_ptr->granulepos; - if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy - header handling. The - header packets aren't - audio, so if/when we - submit them, - vorbis_synthesis will - reject them */ - - /* suck in the synthesis data and track bitrate */ - { - int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); - /* for proper use of libvorbis within libvorbisfile, - oldsamples will always be zero. */ - if(oldsamples)return(OV_EFAULT); - - vorbis_synthesis_blockin(&vf->vd,&vf->vb); - vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL); - vf->bittrack+=op_ptr->bytes*8; - } - - /* update the pcm offset. */ - if(granulepos!=-1 && !op_ptr->e_o_s){ - int link=(vf->seekable?vf->current_link:0); - int i,samples; - - /* this packet has a pcm_offset on it (the last packet - completed on a page carries the offset) After processing - (above), we know the pcm position of the *last* sample - ready to be returned. Find the offset of the *first* - - As an aside, this trick is inaccurate if we begin - reading anew right at the last page; the end-of-stream - granulepos declares the last frame in the stream, and the - last packet of the last page may be a partial frame. - So, we need a previous granulepos from an in-sequence page - to have a reference point. Thus the !op_ptr->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*2+1]; - vf->pcm_offset=granulepos; - } - return(1); - } - } - else - break; + int result=tremor_ogg_stream_packetout(vf->os,&op); + tremor_ogg_int64_t granulepos; + + 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_dsp_synthesis(vf->vd,&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 */ + + vf->samptrack+=vorbis_dsp_pcmout(vf->vd,NULL,0); + vf->bittrack+=op.bytes*8; + + /* update the pcm offset. */ + if(granulepos!=-1 && !op.e_o_s){ + int link=(vf->seekable?vf->current_link:0); + int i,samples; + + /* this packet has a pcm_offset on it (the last packet + completed on a page carries the offset) After processing + (above), we know the pcm position of the *last* sample + ready to be returned. Find the offset of the *first* + + As an aside, this trick is inaccurate if we begin + reading anew right at the last page; the end-of-stream + granulepos declares the last frame in the stream, and the + last packet of the last page may be a partial frame. + So, we need a previous granulepos from an in-sequence page + to have a reference point. Thus the !op.e_o_s clause + 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_dsp_pcmout(vf->vd,NULL,0); + + granulepos-=samples; + for(i=0;i<link;i++) + granulepos+=vf->pcmlengths[i*2+1]; + vf->pcm_offset=granulepos; + } + ret=1; + goto cleanup; + } + } + else + break; } } if(vf->ready_state>=OPENED){ - ogg_int64_t ret; + 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; + } - while(1){ - /* the loop is not strictly necessary, but there's no sense in - doing the extra checks of the larger loop for the common - case in a multiplexed bistream where the page is simply - part of a different logical bitstream; keep reading until - we get one with the correct serialno */ - - if(!readp)return(0); - if((ret=_get_next_page(vf,&og,-1))<0){ - return(OV_EOF); /* eof. leave unitialized */ - } - - /* bitrate tracking; add the header's bytes here, the body bytes - are done by packet above */ - vf->bittrack+=og.header_len*8; - - if(vf->ready_state==INITSET){ - if(vf->current_serialno!=ogg_page_serialno(&og)){ - - /* two possibilities: - 1) our decoding just traversed a bitstream boundary - 2) another stream is multiplexed into this logical section */ - - if(ogg_page_bos(&og)){ - /* boundary case */ - if(!spanp) - return(OV_EOF); - - _decode_clear(vf); - - if(!vf->seekable){ - vorbis_info_clear(vf->vi); - vorbis_comment_clear(vf->vc); - } - break; - - }else - continue; /* possibility #2 */ - } - } - - break; + /* 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!=tremor_ogg_page_serialno(&og)){ + if(!spanp){ + ret=OV_EOF; + goto cleanup; + } + + _decode_clear(vf); + } } } /* Do we need to load a new machine before submitting the page? */ - /* This is different in the seekable and non-seekable cases. + /* This is different in the seekable and non-seekable cases. In the seekable case, we already have all the header information loaded and cached; we just initialize the machine @@ -814,79 +645,80 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, we're now nominally at the header of the next bitstream */ - if(vf->ready_state!=INITSET){ - int link; + if(vf->ready_state!=INITSET){ + int link,ret; if(vf->ready_state<STREAMSET){ - if(vf->seekable){ - ogg_uint32_t serialno = ogg_page_serialno(&og); - - /* match the serialno to bitstream section. We use this rather than - offset positions to avoid problems near logical bitstream - boundaries */ - - for(link=0;link<vf->links;link++) - if(vf->serialnos[link]==serialno)break; - - if(link==vf->links) continue; /* not the desired Vorbis - bitstream section; keep - trying */ - - vf->current_serialno=serialno; - vf->current_link=link; - - ogg_stream_reset_serialno(&vf->os,vf->current_serialno); - vf->ready_state=STREAMSET; - - }else{ - /* we're streaming */ - /* fetch the three header packets, build the info struct */ - - int ret=_fetch_headers(vf,vf->vi,vf->vc,NULL,NULL,&og); - if(ret)return(ret); - vf->current_serialno=vf->os.serialno; - vf->current_link++; - link=0; - } + if(vf->seekable){ + vf->current_serialno=tremor_ogg_page_serialno(&og); + + /* match the serialno to bitstream section. We use this rather than + offset positions to avoid problems near logical bitstream + boundaries */ + for(link=0;link<vf->links;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links){ + ret=OV_EBADLINK; /* sign of a bogus stream. error out, + leave machine uninitialized */ + goto cleanup; + } + + vf->current_link=link; + ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og); + if(ret) goto cleanup; + + }else{ + /* we're streaming */ + /* fetch the three header packets, build the info struct */ + + int ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og); + if(ret) goto cleanup; + vf->current_link++; + } } + + if(_make_decode_ready(vf)) return OV_EBADLINK; } - - /* the buffered page is the data we want, and we're ready for it; - add it to the stream state */ - ogg_stream_pagein(&vf->os,&og); - + tremor_ogg_stream_pagein(vf->os,&og); } + cleanup: + tremor_ogg_packet_release(&op); + tremor_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); +static int _fseek64_wrap(FILE *f,tremor_ogg_int64_t off,int whence){ + if(f==NULL)return -1; return fseek(f,off,whence); } -static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial, - long ibytes, ov_callbacks callbacks){ - int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1); - ogg_uint32_t *serialno_list=NULL; - int serialno_list_size=0; +static int _ov_open1(void *f,TremorOggVorbis_File *vf,char *initial, + long ibytes, ov_callbacks callbacks){ + int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1); int ret; memset(vf,0,sizeof(*vf)); + + /* Tremor assumes in multiple places that right shift of a signed + integer is an arithmetic shift */ + if( (-1>>1) != -1) return OV_EIMPL; + vf->datasource=f; vf->callbacks = callbacks; /* init the framing state */ - ogg_sync_init(&vf->oy); + vf->oy=tremor_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 (especially as we may be reading from a - non-seekable stream) */ + previously read data (as we may be reading from a non-seekable + stream) */ if(initial){ - char *buffer=ogg_sync_buffer(&vf->oy,ibytes); + unsigned char *buffer=tremor_ogg_sync_bufferin(vf->oy,ibytes); memcpy(buffer,initial,ibytes); - ogg_sync_wrote(&vf->oy,ibytes); + tremor_ogg_sync_wrote(vf->oy,ibytes); } /* can we seek? Stevens suggests the seek test was portable */ @@ -895,154 +727,115 @@ static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial, /* No seeking yet; Set up a 'single' (current) logical bitstream entry for partial open */ vf->links=1; - vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); - vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); - ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ + vf->os=tremor_ogg_stream_create(-1); /* fill in the serialno later */ - /* Fetch all BOS pages, store the vorbis header and all seen serial - numbers, load subsequent vorbis setup headers */ - if((ret=_fetch_headers(vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){ + /* Try to fetch the headers, maintaining all the storage */ + if((ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL))<0){ vf->datasource=NULL; ov_clear(vf); - }else{ - /* serial number list for first link needs to be held somewhere - for second stage of seekable stream open; this saves having to - seek/reread first link's serialnumber data then. */ - vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos)); - vf->serialnos[0]=vf->current_serialno=vf->os.serialno; - vf->serialnos[1]=serialno_list_size; - memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos)); - - vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets)); - vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets)); - vf->offsets[0]=0; - vf->dataoffsets[0]=vf->offset; - + }else if(vf->ready_state < PARTOPEN) vf->ready_state=PARTOPEN; - } - if(serialno_list)_ogg_free(serialno_list); - return(ret); + return ret; } -static int _ov_open2(OggVorbis_File *vf){ - if(vf->ready_state != PARTOPEN) return OV_EINVAL; - vf->ready_state=OPENED; +static int _ov_open2(TremorOggVorbis_File *vf){ + if(vf->ready_state < OPENED) + vf->ready_state=OPENED; if(vf->seekable){ int ret=_open_seekable2(vf); if(ret){ vf->datasource=NULL; ov_clear(vf); } - return(ret); - }else - vf->ready_state=STREAMSET; - + return ret; + } return 0; } -/* clear out the OggVorbis_File struct */ -int ov_clear(OggVorbis_File *vf){ +/* clear out the TremorOggVorbis_File struct */ +int ov_clear(TremorOggVorbis_File *vf){ if(vf){ - vorbis_block_clear(&vf->vb); - vorbis_dsp_clear(&vf->vd); - ogg_stream_clear(&vf->os); - - if(vf->vi && vf->links){ - int i; - for(i=0;i<vf->links;i++){ - vorbis_info_clear(vf->vi+i); - vorbis_comment_clear(vf->vc+i); - } - _ogg_free(vf->vi); - _ogg_free(vf->vc); - } - if(vf->dataoffsets)_ogg_free(vf->dataoffsets); - 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); - if(vf->datasource && vf->callbacks.close_func) - (vf->callbacks.close_func)(vf->datasource); + vorbis_dsp_destroy(vf->vd); + vf->vd=0; + tremor_ogg_stream_destroy(vf->os); + vorbis_info_clear(&vf->vi); + vorbis_comment_clear(&vf->vc); + if(vf->dataoffsets)_tremor_ogg_free(vf->dataoffsets); + if(vf->pcmlengths)_tremor_ogg_free(vf->pcmlengths); + if(vf->serialnos)_tremor_ogg_free(vf->serialnos); + if(vf->offsets)_tremor_ogg_free(vf->offsets); + tremor_ogg_sync_destroy(vf->oy); + + if(vf->datasource)(vf->callbacks.close_func)(vf->datasource); memset(vf,0,sizeof(*vf)); } #ifdef DEBUG_LEAKS _VDBG_dump(); #endif - return(0); + return 0; } -/* inspects the OggVorbis file and finds/documents all the logical +/* inspects the TremorOggVorbis file and finds/documents all the logical bitstreams contained in it. Tries to be tolerant of logical - bitstream sections that are truncated/woogie. + bitstream sections that are truncated/woogie. return: -1) error 0) OK */ -int ov_open_callbacks(void *f,OggVorbis_File *vf, - const char *initial,long ibytes,ov_callbacks callbacks){ +int ov_open_callbacks(void *f,TremorOggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks){ int ret=_ov_open1(f,vf,initial,ibytes,callbacks); if(ret)return ret; return _ov_open2(vf); } -int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){ +int ov_open(FILE *f,TremorOggVorbis_File *vf,char *initial,long ibytes){ ov_callbacks callbacks = { (size_t (*)(void *, size_t, size_t, void *)) fread, - (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *, tremor_ogg_int64_t, int)) _fseek64_wrap, (int (*)(void *)) fclose, (long (*)(void *)) ftell }; return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); } - -int ov_fopen(const char *path,OggVorbis_File *vf){ - int ret; - FILE *f = fopen(path,"rb"); - if(!f) return -1; - - ret = ov_open(f,vf,NULL,0); - if(ret) fclose(f); - return ret; -} - - + /* Only partially open the vorbis file; test for Vorbisness, and load the headers for the first chain. Do not seek (although test for seekability). Use ov_test_open to finish opening the file, else ov_clear to close/free it. Same return codes as open. */ -int ov_test_callbacks(void *f,OggVorbis_File *vf, - const char *initial,long ibytes,ov_callbacks callbacks) +int ov_test_callbacks(void *f,TremorOggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks) { return _ov_open1(f,vf,initial,ibytes,callbacks); } -int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){ +int ov_test(FILE *f,TremorOggVorbis_File *vf,char *initial,long ibytes){ ov_callbacks callbacks = { (size_t (*)(void *, size_t, size_t, void *)) fread, - (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *, tremor_ogg_int64_t, int)) _fseek64_wrap, (int (*)(void *)) fclose, (long (*)(void *)) ftell }; return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); } - -int ov_test_open(OggVorbis_File *vf){ - if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); + +int ov_test_open(TremorOggVorbis_File *vf){ + if(vf->ready_state!=PARTOPEN)return OV_EINVAL; return _ov_open2(vf); } /* How many logical bitstreams in this physical bitstream? */ -long ov_streams(OggVorbis_File *vf){ +long ov_streams(TremorOggVorbis_File *vf){ return vf->links; } /* Is the FILE * associated with vf seekable? */ -long ov_seekable(OggVorbis_File *vf){ +long ov_seekable(TremorOggVorbis_File *vf){ return vf->seekable; } @@ -1055,12 +848,12 @@ long ov_seekable(OggVorbis_File *vf){ If you want the actual bitrate field settings, get them from the vorbis_info structs */ -long ov_bitrate(OggVorbis_File *vf,int i){ - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(i>=vf->links)return(OV_EINVAL); - if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); +long ov_bitrate(TremorOggVorbis_File *vf,int i){ + if(vf->ready_state<OPENED)return OV_EINVAL; + if(i>=vf->links)return OV_EINVAL; + if(!vf->seekable && i!=0)return ov_bitrate(vf,0); if(i<0){ - ogg_int64_t bits=0; + tremor_ogg_int64_t bits=0; int i; for(i=0;i<vf->links;i++) bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; @@ -1068,24 +861,24 @@ long ov_bitrate(OggVorbis_File *vf,int i){ * 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)); + return bits*1000/ov_time_total(vf,-1); }else{ if(vf->seekable){ /* return the actual bitrate */ - return((vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i)); + return (vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i); }else{ /* return nominal if set */ - if(vf->vi[i].bitrate_nominal>0){ - return vf->vi[i].bitrate_nominal; + if(vf->vi.bitrate_nominal>0){ + return vf->vi.bitrate_nominal; }else{ - if(vf->vi[i].bitrate_upper>0){ - if(vf->vi[i].bitrate_lower>0){ - return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; - }else{ - return vf->vi[i].bitrate_upper; - } - } - return(OV_FALSE); + if(vf->vi.bitrate_upper>0){ + if(vf->vi.bitrate_lower>0){ + return (vf->vi.bitrate_upper+vf->vi.bitrate_lower)/2; + }else{ + return vf->vi.bitrate_upper; + } + } + return OV_FALSE; } } } @@ -1093,84 +886,83 @@ long ov_bitrate(OggVorbis_File *vf,int i){ /* returns the actual bitrate since last call. returns -1 if no additional data to offer since last call (or at beginning of stream), - EINVAL if stream is only partially open + EINVAL if stream is only partially open */ -long ov_bitrate_instant(OggVorbis_File *vf){ - int link=(vf->seekable?vf->current_link:0); +long ov_bitrate_instant(TremorOggVorbis_File *vf){ long ret; - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(vf->samptrack==0)return(OV_FALSE); - ret=vf->bittrack/vf->samptrack*vf->vi[link].rate; + if(vf->ready_state<OPENED)return OV_EINVAL; + if(vf->samptrack==0)return OV_FALSE; + ret=vf->bittrack/vf->samptrack*vf->vi.rate; vf->bittrack=0; vf->samptrack=0; - return(ret); + return ret; } /* Guess */ -long ov_serialnumber(OggVorbis_File *vf,int i){ - if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); - if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); +long ov_serialnumber(TremorOggVorbis_File *vf,int i){ + if(i>=vf->links)return ov_serialnumber(vf,vf->links-1); + if(!vf->seekable && i>=0)return ov_serialnumber(vf,-1); if(i<0){ - return(vf->current_serialno); + return vf->current_serialno; }else{ - return(vf->serialnos[i]); + return vf->serialnos[i]; } } /* returns: total raw (compressed) length of content if i==-1 raw (compressed) length of that logical bitstream for i==0 to n - OV_EINVAL if the stream is not seekable (we can't know the length) - or if stream is only partially open + OV_EINVAL if the stream is not seekable (we can't know the length) + or if stream is only partially open */ -ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(!vf->seekable || i>=vf->links)return(OV_EINVAL); +tremor_ogg_int64_t ov_raw_total(TremorOggVorbis_File *vf,int i){ + if(vf->ready_state<OPENED)return OV_EINVAL; + if(!vf->seekable || i>=vf->links)return OV_EINVAL; if(i<0){ - ogg_int64_t acc=0; + tremor_ogg_int64_t acc=0; int i; for(i=0;i<vf->links;i++) acc+=ov_raw_total(vf,i); - return(acc); + return acc; }else{ - return(vf->offsets[i+1]-vf->offsets[i]); + return vf->offsets[i+1]-vf->offsets[i]; } } /* returns: total PCM length (samples) of content if i==-1 PCM length - (samples) of that logical bitstream for i==0 to n - OV_EINVAL if the stream is not seekable (we can't know the - length) or only partially open + (samples) of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open */ -ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(!vf->seekable || i>=vf->links)return(OV_EINVAL); +tremor_ogg_int64_t ov_pcm_total(TremorOggVorbis_File *vf,int i){ + if(vf->ready_state<OPENED)return OV_EINVAL; + if(!vf->seekable || i>=vf->links)return OV_EINVAL; if(i<0){ - ogg_int64_t acc=0; + tremor_ogg_int64_t acc=0; int i; for(i=0;i<vf->links;i++) acc+=ov_pcm_total(vf,i); - return(acc); + return acc; }else{ - return(vf->pcmlengths[i*2+1]); + return vf->pcmlengths[i*2+1]; } } /* returns: total milliseconds of content if i==-1 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 + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open */ -ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){ - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(!vf->seekable || i>=vf->links)return(OV_EINVAL); +tremor_ogg_int64_t ov_time_total(TremorOggVorbis_File *vf,int i){ + if(vf->ready_state<OPENED)return OV_EINVAL; + if(!vf->seekable || i>=vf->links)return OV_EINVAL; if(i<0){ - ogg_int64_t acc=0; + tremor_ogg_int64_t acc=0; int i; for(i=0;i<vf->links;i++) acc+=ov_time_total(vf,i); - return(acc); + return acc; }else{ - return(((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi[i].rate); + return ((tremor_ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi.rate; } } @@ -1181,33 +973,27 @@ 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,ogg_int64_t pos){ - ogg_stream_state work_os; - int ret; - - if(vf->ready_state<OPENED)return(OV_EINVAL); +int ov_raw_seek(TremorOggVorbis_File *vf,tremor_ogg_int64_t pos){ + tremor_ogg_stream_state *work_os=NULL; + tremor_ogg_page og={0,0,0,0}; + tremor_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->end)return(OV_EINVAL); + return OV_ENOSEEK; /* don't dump machine if we can't seek */ - /* is the seek position outside our current link [if any]? */ - if(vf->ready_state>=STREAMSET){ - if(pos<vf->offsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1]) - _decode_clear(vf); /* clear out stream 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; - ogg_stream_reset_serialno(&vf->os, - vf->current_serialno); /* must set serialno */ - vorbis_synthesis_restart(&vf->vd); - - ret=_seek_helper(vf,pos); - if(ret)goto seek_error; + tremor_ogg_stream_reset_serialno(vf->os, + vf->current_serialno); /* must set serialno */ + vorbis_dsp_restart(vf->vd); + + _seek_helper(vf,pos); /* we need to make sure the pcm_offset is set, but we don't want to advance the raw cursor past good packets just to get to the first @@ -1216,428 +1002,281 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ So, a hack. We use two stream states; a local scratch state and the shared vf->os stream state. We use the local state to - scan, and the shared state as a buffer for later decode. + scan, and the shared state as a buffer for later decode. Unfortuantely, on the last page we still advance to last packet because the granulepos on the last page is not necessarily on a packet boundary, and we need to make sure the granpos is - correct. + correct. */ { - ogg_page og; - ogg_packet op; int lastblock=0; int accblock=0; - int thisblock=0; - int lastflag=0; - int firstflag=0; - ogg_int64_t pagepos=-1; - - ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */ - ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE - return from not necessarily - starting from the beginning */ + int thisblock; + int eosflag; + work_os=tremor_ogg_stream_create(vf->current_serialno); /* get the memory ready */ while(1){ if(vf->ready_state>=STREAMSET){ - /* snarf/scan a packet if we can */ - int result=ogg_stream_packetout(&work_os,&op); - - if(result>0){ - - if(vf->vi[vf->current_link].codec_setup){ - thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); - if(thisblock<0){ - ogg_stream_packetout(&vf->os,NULL); - thisblock=0; - }else{ - - /* We can't get a guaranteed correct pcm position out of the - last page in a stream because it might have a 'short' - granpos, which can only be detected in the presence of a - preceding page. However, if the last page is also the first - page, the granpos rules of a first page take precedence. Not - only that, but for first==last, the EOS page must be treated - as if its a normal first page for the stream to open/play. */ - if(lastflag && !firstflag) - 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; - if(vf->pcm_offset<0)vf->pcm_offset=0; - break; - } - lastblock=thisblock; - continue; - }else - ogg_stream_packetout(&vf->os,NULL); - } + /* snarf/scan a packet if we can */ + int result=tremor_ogg_stream_packetout(work_os,&op); + + if(result>0){ + + if(vf->vi.codec_setup){ + thisblock=vorbis_packet_blocksize(&vf->vi,&op); + if(thisblock<0){ + tremor_ogg_stream_packetout(vf->os,NULL); + thisblock=0; + }else{ + + if(eosflag) + tremor_ogg_stream_packetout(vf->os,NULL); + else + if(lastblock)accblock+=(lastblock+thisblock)>>2; + } + + if(op.granulepos!=-1){ + int i,link=vf->current_link; + tremor_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 + tremor_ogg_stream_packetout(vf->os,NULL); + } } - + if(!lastblock){ - pagepos=_get_next_page(vf,&og,-1); - if(pagepos<0){ - vf->pcm_offset=ov_pcm_total(vf,-1); - break; - } + if(_get_next_page(vf,&og,-1)<0){ + vf->pcm_offset=ov_pcm_total(vf,-1); + break; + } }else{ - /* huh? Bogus stream with packets but no granulepos */ - vf->pcm_offset=-1; - break; - } - - /* has our decoding just traversed a bitstream boundary? */ - if(vf->ready_state>=STREAMSET){ - if(vf->current_serialno!=ogg_page_serialno(&og)){ - - /* two possibilities: - 1) our decoding just traversed a bitstream boundary - 2) another stream is multiplexed into this logical section? */ - - if(ogg_page_bos(&og)){ - /* we traversed */ - _decode_clear(vf); /* clear out stream state */ - ogg_stream_clear(&work_os); - } /* else, do nothing; next loop will scoop another page */ - } + /* huh? Bogus stream with packets but no granulepos */ + vf->pcm_offset=-1; + break; } + + /* did we just grab a page from other than current link? */ + if(vf->ready_state>=STREAMSET) + if(vf->current_serialno!=tremor_ogg_page_serialno(&og)){ + _decode_clear(vf); /* clear out stream state */ + tremor_ogg_stream_destroy(work_os); + } if(vf->ready_state<STREAMSET){ - int link; - ogg_uint32_t serialno = ogg_page_serialno(&og); - - for(link=0;link<vf->links;link++) - if(vf->serialnos[link]==serialno)break; - - if(link==vf->links) continue; /* not the desired Vorbis - bitstream section; keep - trying */ - vf->current_link=link; - vf->current_serialno=serialno; - ogg_stream_reset_serialno(&vf->os,serialno); - ogg_stream_reset_serialno(&work_os,serialno); - vf->ready_state=STREAMSET; - firstflag=(pagepos<=vf->dataoffsets[link]); + int link; + + vf->current_serialno=tremor_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 */ + + /* need to initialize machine to this link */ + { + int ret=_set_link_number_preserve_pos(vf,link); + if(ret) goto seek_error; + } + tremor_ogg_stream_reset_serialno(vf->os,vf->current_serialno); + tremor_ogg_stream_reset_serialno(work_os,vf->current_serialno); + + + } + + { + tremor_ogg_page dup; + tremor_ogg_page_dup(&dup,&og); + eosflag=tremor_ogg_page_eos(&og); + tremor_ogg_stream_pagein(vf->os,&og); + tremor_ogg_stream_pagein(work_os,&dup); } - - ogg_stream_pagein(&vf->os,&og); - ogg_stream_pagein(&work_os,&og); - lastflag=ogg_page_eos(&og); - } } - ogg_stream_clear(&work_os); + tremor_ogg_packet_release(&op); + tremor_ogg_page_release(&og); + tremor_ogg_stream_destroy(work_os); vf->bittrack=0; vf->samptrack=0; - return(0); + return 0; seek_error: + tremor_ogg_packet_release(&op); + tremor_ogg_page_release(&og); + /* dump the machine so we're in a known state */ vf->pcm_offset=-1; - ogg_stream_clear(&work_os); + tremor_ogg_stream_destroy(work_os); _decode_clear(vf); return OV_EBADLINK; } -/* rescales the number x from the range of [0,from] to [0,to] - x is in the range [0,from] - from, to are in the range [1, 1<<62-1] */ -ogg_int64_t rescale64(ogg_int64_t x, ogg_int64_t from, ogg_int64_t to){ - ogg_int64_t frac=0; - ogg_int64_t ret=0; - int i; - if(x >= from) return to; - if(x <= 0) return 0; - - for(i=0;i<64;i++){ - if(x>=from){ - frac|=1; - x-=from; - } - x<<=1; - frac<<=1; - } - - for(i=0;i<64;i++){ - if(frac & 1){ - ret+=to; - } - frac>>=1; - ret>>=1; - } - - return ret; -} - /* Page granularity seek (faster than sample granularity because we don't do the last bit of decode to find a specific sample). - Seek to the last [granule marked] page preceding the specified pos + Seek to the last [granule marked] page preceeding the specified pos location, such that decoding past the returned point will quickly arrive at the requested position. */ -int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ +int ov_pcm_seek_page(TremorOggVorbis_File *vf,tremor_ogg_int64_t pos){ int link=-1; - ogg_int64_t result=0; - ogg_int64_t total=ov_pcm_total(vf,-1); - - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(!vf->seekable)return(OV_ENOSEEK); - - if(pos<0 || pos>total)return(OV_EINVAL); - + tremor_ogg_int64_t result=0; + tremor_ogg_int64_t total=ov_pcm_total(vf,-1); + tremor_ogg_page og={0,0,0,0}; + tremor_ogg_packet op={0,0,0,0,0,0}; + + if(vf->ready_state<OPENED)return OV_EINVAL; + if(!vf->seekable)return OV_ENOSEEK; + if(pos<0 || pos>total)return OV_EINVAL; + /* which bitstream section does this pcm offset occur in? */ for(link=vf->links-1;link>=0;link--){ total-=vf->pcmlengths[link*2+1]; if(pos>=total)break; } - /* Search within the logical bitstream for the page with the highest - pcm_pos preceding pos. If we're looking for a position on the - first page, bisection will halt without finding our position as - it's before the first explicit granulepos fencepost. That case is - handled separately below. - - There is a danger here; missing pages or incorrect frame number - information in the bitstream could make our task impossible. - Account for that (it would be an error condition) */ - /* new search algorithm originally by HB (Nicholas Vinen) */ - - { - ogg_int64_t end=vf->offsets[link+1]; - ogg_int64_t begin=vf->dataoffsets[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=-1; - int got_page=0; - - ogg_page og; - - /* if we have only one page, there will be no bisection. Grab the page here */ - if(begin==end){ - result=_seek_helper(vf,begin); - if(result) goto seek_error; + if(link!=vf->current_link){ + int ret=_set_link_number(vf,link); + if(ret) goto seek_error; + }else{ + vorbis_dsp_restart(vf->vd); + } - result=_get_next_page(vf,&og,1); - if(result<0) goto seek_error; + tremor_ogg_stream_reset_serialno(vf->os,vf->serialnos[link]); - got_page=1; - } + /* search within the logical bitstream for the page with the highest + pcm_pos preceeding (or equal to) pos. There is a danger here; + missing pages or incorrect frame number information in the + bitstream could make our task impossible. Account for that (it + would be an error condition) */ - /* bisection loop */ + /* new search algorithm by HB (Nicholas Vinen) */ + { + tremor_ogg_int64_t end=vf->offsets[link+1]; + tremor_ogg_int64_t begin=vf->offsets[link]; + tremor_ogg_int64_t begintime = vf->pcmlengths[link*2]; + tremor_ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; + tremor_ogg_int64_t target=pos-total+begintime; + tremor_ogg_int64_t best=begin; + while(begin<end){ - ogg_int64_t bisect; - + tremor_ogg_int64_t bisect; + if(end-begin<CHUNKSIZE){ - bisect=begin; + bisect=begin; }else{ - /* take a (pretty decent) guess. */ - bisect=begin + rescale64(target-begintime, - endtime-begintime, - end-begin) - CHUNKSIZE; - if(bisect<begin+CHUNKSIZE) - bisect=begin; + /* take a (pretty decent) guess. */ + bisect=begin + + (target-begintime)*(end-begin)/(endtime-begintime) - CHUNKSIZE; + if(bisect<=begin) + bisect=begin+1; } - - result=_seek_helper(vf,bisect); - if(result) goto seek_error; - - /* read loop within the bisection loop */ + + _seek_helper(vf,bisect); + while(begin<end){ - result=_get_next_page(vf,&og,end-vf->offset); - if(result==OV_EREAD) goto seek_error; - if(result<0){ - /* there is no next page! */ - if(bisect<=begin+1) - /* No bisection left to perform. We've either found the - best candidate already or failed. Exit loop. */ - end=begin; - else{ - /* We tried to load a fraction of the last page; back up a - bit and try to get the whole last page */ - if(bisect==0) goto seek_error; - bisect-=CHUNKSIZE; - - /* don't repeat/loop on a read we've already performed */ - if(bisect<=begin)bisect=begin+1; - - /* seek and continue bisection */ - result=_seek_helper(vf,bisect); - if(result) goto seek_error; - } - }else{ - ogg_int64_t granulepos; - got_page=1; - - /* got a page. analyze it */ - /* only consider pages from primary vorbis stream */ - if(ogg_page_serialno(&og)!=vf->serialnos[link]) - continue; - - /* only consider pages with the granulepos set */ - granulepos=ogg_page_granulepos(&og); - if(granulepos==-1)continue; - - if(granulepos<target){ - /* this page is a successful candidate! Set state */ - - best=result; /* raw offset of packet with granulepos */ - begin=vf->offset; /* raw offset of next page */ - begintime=granulepos; - - /* if we're before our target but within a short distance, - don't bisect; read forward */ - if(target-begintime>44100)break; - - bisect=begin; /* *not* begin + 1 as above */ - }else{ - - /* This is one of our pages, but the granpos is - post-target; it is not a bisection return - candidate. (The only way we'd use it is if it's the - first page in the stream; we handle that case later - outside the bisection) */ - if(bisect<=begin+1){ - /* No bisection left to perform. We've either found the - best candidate already or failed. Exit loop. */ - end=begin; - }else{ - if(end==vf->offset){ - /* bisection read to the end; use the known page - boundary (result) to update bisection, back up a - little bit, and try again */ - end=result; - bisect-=CHUNKSIZE; - if(bisect<=begin)bisect=begin+1; - result=_seek_helper(vf,bisect); - if(result) goto seek_error; - }else{ - /* Normal bisection */ - end=bisect; - endtime=granulepos; - break; - } - } - } - } + 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; + bisect-=CHUNKSIZE; + if(bisect<=begin)bisect=begin+1; + _seek_helper(vf,bisect); + } + }else{ + tremor_ogg_int64_t granulepos=tremor_ogg_page_granulepos(&og); + if(granulepos==-1)continue; + if(granulepos<target){ + best=result; /* raw offset of packet with granulepos */ + begin=vf->offset; /* raw offset of next page */ + begintime=granulepos; + + 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=result; + bisect-=CHUNKSIZE; /* an endless loop otherwise. */ + if(bisect<=begin)bisect=begin+1; + _seek_helper(vf,bisect); + }else{ + end=result; + endtime=granulepos; + break; + } + } + } + } } } - /* Out of bisection: did it 'fail?' */ - if(best == -1){ - - /* Check the 'looking for data in first page' special case; - bisection would 'fail' because our search target was before the - first PCM granule position fencepost. */ - - if(got_page && - begin == vf->dataoffsets[link] && - ogg_page_serialno(&og)==vf->serialnos[link]){ - - /* Yes, this is the beginning-of-stream case. We already have - our page, right at the beginning of PCM data. Set state - and return. */ - - vf->pcm_offset=total; - - if(link!=vf->current_link){ - /* Different link; dump entire decode machine */ - _decode_clear(vf); - - vf->current_link=link; - vf->current_serialno=vf->serialnos[link]; - 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); - - }else - goto seek_error; - - }else{ - - /* Bisection found our page. seek to it, update pcm offset. Easier case than - raw_seek, don't keep packets preceding granulepos. */ - - ogg_page og; - ogg_packet op; - + /* found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceeding granulepos. */ + { + /* seek */ - result=_seek_helper(vf,best); + _seek_helper(vf,best); vf->pcm_offset=-1; - if(result) goto seek_error; - result=_get_next_page(vf,&og,-1); - if(result<0) goto seek_error; - - if(link!=vf->current_link){ - /* Different link; dump entire decode machine */ - _decode_clear(vf); - - vf->current_link=link; - vf->current_serialno=vf->serialnos[link]; - vf->ready_state=STREAMSET; - - }else{ - vorbis_synthesis_restart(&vf->vd); + + if(_get_next_page(vf,&og,-1)<0){ + tremor_ogg_page_release(&og); + return OV_EOF; /* shouldn't happen */ } - ogg_stream_reset_serialno(&vf->os,vf->current_serialno); - ogg_stream_pagein(&vf->os,&og); + tremor_ogg_stream_pagein(vf->os,&og); /* pull out all but last packet; the one with granulepos */ while(1){ - result=ogg_stream_packetpeek(&vf->os,&op); - if(result==0){ - /* No packet returned; we exited the bisection with 'best' - pointing to a page with a granule position, so the packet - finishing this page ('best') originated on a preceding - 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. */ - /* Do not rewind past the beginning of link data; if we do, - it's either a bug or a broken stream */ - result=best; - while(result>vf->dataoffsets[link]){ - result=_get_prev_page(vf,result,&og); - if(result<0) goto seek_error; - if(ogg_page_serialno(&og)==vf->current_serialno && - (ogg_page_granulepos(&og)>-1 || - !ogg_page_continued(&og))){ - return ov_raw_seek(vf,result); - } - } - } - if(result<0){ - result = OV_EBADPACKET; - goto seek_error; - } - if(op.granulepos!=-1){ - 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 - result=ogg_stream_packetout(&vf->os,NULL); + result=tremor_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){ + result=_get_prev_page(vf,&og); + if(result<0) goto seek_error; + if(tremor_ogg_page_granulepos(&og)>-1 || + !tremor_ogg_page_continued(&og)){ + return ov_raw_seek(vf,result); + } + vf->offset=result; + } + } + if(result<0){ + result = OV_EBADPACKET; + goto seek_error; + } + if(op.granulepos!=-1){ + 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 + result=tremor_ogg_stream_packetout(vf->os,NULL); } } } - + /* verify result */ if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ result=OV_EFAULT; @@ -1645,89 +1284,98 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ } vf->bittrack=0; vf->samptrack=0; - return(0); + tremor_ogg_page_release(&og); + tremor_ogg_packet_release(&op); + return 0; + seek_error: + + tremor_ogg_page_release(&og); + tremor_ogg_packet_release(&op); + /* dump machine so we're in a known state */ vf->pcm_offset=-1; _decode_clear(vf); return (int)result; } -/* seek to a sample offset relative to the decompressed pcm stream +/* 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){ +int ov_pcm_seek(TremorOggVorbis_File *vf,tremor_ogg_int64_t pos){ + tremor_ogg_packet op={0,0,0,0,0,0}; + tremor_ogg_page og={0,0,0,0}; int thisblock,lastblock=0; int ret=ov_pcm_seek_page(vf,pos); - if(ret<0)return(ret); - if((ret=_make_decode_ready(vf)))return ret; + if(ret<0)return ret; + if(_make_decode_ready(vf))return OV_EBADLINK; /* 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=tremor_ogg_stream_packetpeek(vf->os,&op); if(ret>0){ - thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + thisblock=vorbis_packet_blocksize(&vf->vi,&op); if(thisblock<0){ - ogg_stream_packetout(&vf->os,NULL); - continue; /* non audio packet */ + tremor_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; - + vorbis_info_blocksize(&vf->vi,1))>>2)>=pos)break; + /* remove the packet from packet queue and track its granulepos */ - ogg_stream_packetout(&vf->os,NULL); - vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with - only tracking, no - pcm_decode */ - vorbis_synthesis_blockin(&vf->vd,&vf->vb); - + tremor_ogg_stream_packetout(vf->os,NULL); + vorbis_dsp_synthesis(vf->vd,&op,0); /* set up a vb with + only tracking, no + pcm_decode */ + /* 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->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*2+1]; + int i; + /* always believe the stream markers */ + 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*2+1]; } - + lastblock=thisblock; - + }else{ if(ret<0 && ret!=OV_HOLE)break; - + /* suck in a new page */ if(_get_next_page(vf,&og,-1)<0)break; - if(ogg_page_bos(&og))_decode_clear(vf); - + if(vf->current_serialno!=tremor_ogg_page_serialno(&og))_decode_clear(vf); + if(vf->ready_state<STREAMSET){ - ogg_uint32_t serialno=ogg_page_serialno(&og); - int link; - - for(link=0;link<vf->links;link++) - if(vf->serialnos[link]==serialno)break; - if(link==vf->links) continue; - vf->current_link=link; - - vf->ready_state=STREAMSET; - vf->current_serialno=ogg_page_serialno(&og); - ogg_stream_reset_serialno(&vf->os,serialno); - ret=_make_decode_ready(vf); - if(ret)return ret; - lastblock=0; + int link,ret; + + vf->current_serialno=tremor_ogg_page_serialno(&og); + for(link=0;link<vf->links;link++) + if(vf->serialnos[link]==vf->current_serialno)break; + if(link==vf->links){ + tremor_ogg_page_release(&og); + tremor_ogg_packet_release(&op); + return OV_EBADLINK; + } + + + vf->current_link=link; + ret=_fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,&og); + if(ret) return ret; + if(_make_decode_ready(vf))return OV_EBADLINK; + lastblock=0; } - ogg_stream_pagein(&vf->os,&og); + tremor_ogg_stream_pagein(vf->os,&og); } } @@ -1736,104 +1384,107 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ /* discard samples until we reach the desired position. Crossing a logical bitstream boundary with abandon is OK. */ while(vf->pcm_offset<pos){ - ogg_int64_t target=pos-vf->pcm_offset; - long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + tremor_ogg_int64_t target=pos-vf->pcm_offset; + long samples=vorbis_dsp_pcmout(vf->vd,NULL,0); if(samples>target)samples=target; - vorbis_synthesis_read(&vf->vd,samples); + vorbis_dsp_read(vf->vd,samples); vf->pcm_offset+=samples; - + if(samples<target) - if(_fetch_and_process_packet(vf,NULL,1,1)<=0) - vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */ + if(_fetch_and_process_packet(vf,1,1)<=0) + vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */ } + + tremor_ogg_page_release(&og); + tremor_ogg_packet_release(&op); return 0; } -/* seek to a playback time relative to the decompressed pcm stream +/* seek to a playback time relative to the decompressed pcm stream returns zero on success, nonzero on failure */ -int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){ +int ov_time_seek(TremorOggVorbis_File *vf,tremor_ogg_int64_t milliseconds){ /* translate time to PCM position and call ov_pcm_seek */ int link=-1; - ogg_int64_t pcm_total=0; - ogg_int64_t time_total=0; - - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(!vf->seekable)return(OV_ENOSEEK); - if(milliseconds<0)return(OV_EINVAL); + tremor_ogg_int64_t pcm_total=ov_pcm_total(vf,-1); + tremor_ogg_int64_t time_total=ov_time_total(vf,-1); + if(vf->ready_state<OPENED)return OV_EINVAL; + if(!vf->seekable)return OV_ENOSEEK; + if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL; + /* which bitstream section does this time offset occur in? */ - for(link=0;link<vf->links;link++){ - ogg_int64_t addsec = ov_time_total(vf,link); - if(milliseconds<time_total+addsec)break; - time_total+=addsec; - pcm_total+=vf->pcmlengths[link*2+1]; + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(milliseconds>=time_total)break; } - if(link==vf->links)return(OV_EINVAL); - /* enough information to convert time offset to pcm offset */ { - ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; - return(ov_pcm_seek(vf,target)); + int ret=_set_link_number(vf,link); + if(ret)return ret; + return + ov_pcm_seek(vf,pcm_total+(milliseconds-time_total)* + vf->vi.rate/1000); } } /* page-granularity version of ov_time_seek returns zero on success, nonzero on failure */ -int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t milliseconds){ +int ov_time_seek_page(TremorOggVorbis_File *vf,tremor_ogg_int64_t milliseconds){ /* translate time to PCM position and call ov_pcm_seek */ int link=-1; - ogg_int64_t pcm_total=0; - ogg_int64_t time_total=0; - - if(vf->ready_state<OPENED)return(OV_EINVAL); - if(!vf->seekable)return(OV_ENOSEEK); - if(milliseconds<0)return(OV_EINVAL); + tremor_ogg_int64_t pcm_total=ov_pcm_total(vf,-1); + tremor_ogg_int64_t time_total=ov_time_total(vf,-1); + if(vf->ready_state<OPENED)return OV_EINVAL; + if(!vf->seekable)return OV_ENOSEEK; + if(milliseconds<0 || milliseconds>time_total)return OV_EINVAL; + /* which bitstream section does this time offset occur in? */ - for(link=0;link<vf->links;link++){ - ogg_int64_t addsec = ov_time_total(vf,link); - if(milliseconds<time_total+addsec)break; - time_total+=addsec; - pcm_total+=vf->pcmlengths[link*2+1]; + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(milliseconds>=time_total)break; } - if(link==vf->links)return(OV_EINVAL); - /* enough information to convert time offset to pcm offset */ { - ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; - return(ov_pcm_seek_page(vf,target)); + int ret=_set_link_number(vf,link); + if(ret)return ret; + return + ov_pcm_seek_page(vf,pcm_total+(milliseconds-time_total)* + vf->vi.rate/1000); } } /* tell the current stream offset cursor. Note that seek followed by tell will likely not give the set offset due to caching */ -ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ - if(vf->ready_state<OPENED)return(OV_EINVAL); - return(vf->offset); +tremor_ogg_int64_t ov_raw_tell(TremorOggVorbis_File *vf){ + if(vf->ready_state<OPENED)return OV_EINVAL; + return vf->offset; } /* return PCM offset (sample) of next PCM sample to be read */ -ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ - if(vf->ready_state<OPENED)return(OV_EINVAL); - return(vf->pcm_offset); +tremor_ogg_int64_t ov_pcm_tell(TremorOggVorbis_File *vf){ + if(vf->ready_state<OPENED)return OV_EINVAL; + return vf->pcm_offset; } /* return time offset (milliseconds) of next PCM sample to be read */ -ogg_int64_t ov_time_tell(OggVorbis_File *vf){ +tremor_ogg_int64_t ov_time_tell(TremorOggVorbis_File *vf){ int link=0; - ogg_int64_t pcm_total=0; - ogg_int64_t time_total=0; - - if(vf->ready_state<OPENED)return(OV_EINVAL); + tremor_ogg_int64_t pcm_total=0; + tremor_ogg_int64_t time_total=0; + + if(vf->ready_state<OPENED)return OV_EINVAL; if(vf->seekable){ pcm_total=ov_pcm_total(vf,-1); time_total=ov_time_total(vf,-1); - + /* which bitstream section does this time offset occur in? */ for(link=vf->links-1;link>=0;link--){ pcm_total-=vf->pcmlengths[link*2+1]; @@ -1842,50 +1493,38 @@ ogg_int64_t ov_time_tell(OggVorbis_File *vf){ } } - return(time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi[link].rate); + return time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi.rate; } /* link: -1) return the vorbis_info struct for the bitstream section currently being decoded 0-n) to request information for a specific bitstream section - + In the case of a non-seekable bitstream, any call returns the current bitstream. NULL in the case that the machine is not initialized */ -vorbis_info *ov_info(OggVorbis_File *vf,int link){ +vorbis_info *ov_info(TremorOggVorbis_File *vf,int link){ if(vf->seekable){ - if(link<0) - if(vf->ready_state>=STREAMSET) - return vf->vi+vf->current_link; - else - return vf->vi; - else - if(link>=vf->links) - return NULL; - else - return vf->vi+link; - }else{ - return vf->vi; + if(link>=vf->links)return NULL; + if(link>=0){ + int ret=_set_link_number_preserve_pos(vf,link); + if(ret)return NULL; + } } + return &vf->vi; } /* grr, strong typing, grr, no templates/inheritence, grr */ -vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ +vorbis_comment *ov_comment(TremorOggVorbis_File *vf,int link){ if(vf->seekable){ - if(link<0) - if(vf->ready_state>=STREAMSET) - return vf->vc+vf->current_link; - else - return vf->vc; - else - if(link>=vf->links) - return NULL; - else - return vf->vc+link; - }else{ - return vf->vc; + if(link>=vf->links)return NULL; + if(link>=0){ + int ret=_set_link_number_preserve_pos(vf,link); + if(ret)return NULL; + } } + return &vf->vc; } /* up to this point, everything could more or less hide the multiple @@ -1904,65 +1543,47 @@ vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ functions above are aware of this dichotomy). input values: buffer) a buffer to hold packed PCM data for return - bytes_req) the byte length requested to be placed into buffer + length) the byte length requested to be placed into buffer return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) 0) EOF - n) number of bytes of PCM actually returned. The - below works on a packet-by-packet basis, so the - return length is not related to the 'length' passed - in, just guaranteed to fit. + n) number of bytes of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. - *section) set to the logical bitstream number */ + *section) set to the logical bitstream number */ -long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){ - int i,j; +long ov_read(TremorOggVorbis_File *vf,void *buffer,int bytes_req,int *bitstream){ - ogg_int32_t **pcm; long samples; + long channels; - if(vf->ready_state<OPENED)return(OV_EINVAL); + if(vf->ready_state<OPENED)return OV_EINVAL; while(1){ if(vf->ready_state==INITSET){ - samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); - if(samples)break; + channels=vf->vi.channels; + samples=vorbis_dsp_pcmout(vf->vd,buffer,(bytes_req>>1)/channels); + if(samples){ + if(samples>0){ + vorbis_dsp_read(vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return samples*2*channels; + } + return samples; + } } /* suck in another packet */ { - int ret=_fetch_and_process_packet(vf,NULL,1,1); + int ret=_fetch_and_process_packet(vf,1,1); if(ret==OV_EOF) - return(0); + return 0; if(ret<=0) - return(ret); + return ret; } } - - if(samples>0){ - - /* yay! proceed to pack data into the byte buffer */ - - long channels=ov_info(vf,-1)->channels; - - if(samples>(bytes_req/(2*channels))) - samples=bytes_req/(2*channels); - - 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; - } - } - - vorbis_synthesis_read(&vf->vd,samples); - vf->pcm_offset+=samples; - if(bitstream)*bitstream=vf->current_link; - return(samples*2*channels); - }else{ - return(samples); - } } |
