From 67efbc13870ee87ce3df442f7c396c13481921ec Mon Sep 17 00:00:00 2001 From: Nils Wallménius Date: Mon, 6 Dec 2010 14:36:52 +0000 Subject: libtremor: Merge in upstream revision 17375. This removes tremor's internal ogg code and now uses libogg instead so a bunch of changes are just adjusting to the new api. Also brings in improvements to vorbisfile which fixes FS#10484. Disabled a lot of unused code in the libogg files and moved some small functions into the ogg.h header so they can be inlined. Some small tweaks to fix warnings. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28742 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/libtremor/bitwise.c | 949 ++++++++++-- apps/codecs/libtremor/codebook.c | 18 +- apps/codecs/libtremor/framing.c | 2730 ++++++++++++++++++++++++----------- apps/codecs/libtremor/info.c | 4 +- apps/codecs/libtremor/ivorbisfile.h | 10 +- apps/codecs/libtremor/ogg.h | 387 +++-- apps/codecs/libtremor/synthesis.c | 6 +- apps/codecs/libtremor/vorbisfile.c | 509 +++---- 8 files changed, 3131 insertions(+), 1482 deletions(-) diff --git a/apps/codecs/libtremor/bitwise.c b/apps/codecs/libtremor/bitwise.c index dabba468b9..050fb67c60 100644 --- a/apps/codecs/libtremor/bitwise.c +++ b/apps/codecs/libtremor/bitwise.c @@ -1,28 +1,31 @@ /******************************************************************** * * - * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * - * * + * THIS FILE IS PART OF THE Ogg CONTAINER 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-2002 * - * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * + * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** function: packing variable sized words into an octet stream + last mod: $Id$ ********************************************************************/ /* We're 'LSb' endian; if we write a word but read individual bits, then we'll read the lsb first */ -#include "config-tremor.h" #include +#include +#include #include "ogg.h" -const unsigned long oggpack_mask[] ICONST_ATTR = +#define BUFFER_INCREMENT 256 + +const unsigned long mask[] ICONST_ATTR = {0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, @@ -31,184 +34,832 @@ const unsigned long oggpack_mask[] ICONST_ATTR = 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, 0x3fffffff,0x7fffffff,0xffffffff }; -void oggpack_readinit(oggpack_buffer *b,ogg_reference *r){ +#if 0 +static const unsigned int mask8B[]= +{0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void oggpack_writeinit(oggpack_buffer *b){ memset(b,0,sizeof(*b)); + b->ptr=b->buffer=_ogg_malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpackB_writeinit(oggpack_buffer *b){ + oggpack_writeinit(b); +} - b->tail=b->head=r; - b->count=0; - b->headptr=b->head->buffer->data+b->head->begin; - b->headend=b->head->length; - _span(b); +int oggpack_writecheck(oggpack_buffer *b){ + if(!b->ptr || !b->storage)return -1; + return 0; } -#define _lookspan() while(!end){\ - head=head->next;\ - if(!head) return -1;\ - ptr=head->buffer->data + head->begin;\ - end=head->length;\ - } +int oggpackB_writecheck(oggpack_buffer *b){ + return oggpack_writecheck(b); +} -/* Read in bits without advancing the bitptr; bits <= 32 */ -long oggpack_look_full(oggpack_buffer *b,int bits) ICODE_ATTR_TREMOR_NOT_MDCT; -long oggpack_look_full(oggpack_buffer *b,int bits){ - unsigned long m=oggpack_mask[bits]; - unsigned long ret=-1; - - bits+=b->headbit; - - 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); - } - } +void oggpack_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + if(b->ptr){ + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; + } +} + +void oggpackB_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + if(b->ptr){ + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; + } +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ + if(bits<0 || bits>32) goto err; + if(b->endbyte>=b->storage-4){ + void *ret; + if(!b->ptr)return; + if(b->storage>LONG_MAX-BUFFER_INCREMENT) goto err; + ret=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + if(!ret) goto err; + b->buffer=ret; + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(8-b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(16-b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(24-b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value>>(32-b->endbit)); + else + b->ptr[4]=0; } } } + } - }else{ + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; + return; + err: + oggpack_writeclear(b); +} + +/* Takes only up to 32 bits. */ +void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ + if(bits<0 || bits>32) goto err; + if(b->endbyte>=b->storage-4){ + void *ret; + if(!b->ptr)return; + if(b->storage>LONG_MAX-BUFFER_INCREMENT) goto err; + ret=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + if(!ret) goto err; + b->buffer=ret; + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value=(value&mask[bits])<<(32-bits); + bits+=b->endbit; - /* 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); + b->ptr[0]|=value>>(24+b->endbit); + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(16+b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(8+b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value<<(8-b->endbit)); + else + b->ptr[4]=0; } } } } - ret&=m; - return ret; + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; + return; + err: + oggpack_writeclear(b); +} + +void oggpack_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpackB_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpackB_write(b,0,bits); +} + +static void oggpack_writecopy_helper(oggpack_buffer *b, + void *source, + long bits, + void (*w)(oggpack_buffer *, + unsigned long, + int), + int msb){ + unsigned char *ptr=(unsigned char *)source; + + long bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + int i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + void *ret; + if(!b->ptr) goto err; + if(b->endbyte+bytes+BUFFER_INCREMENT>b->storage) goto err; + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + ret=_ogg_realloc(b->buffer,b->storage); + if(!ret) goto err; + b->buffer=ret; + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->endbyte+=bytes; + *b->ptr=0; + + } + if(bits){ + if(msb) + w(b,(unsigned long)(ptr[bytes]>>(8-bits)),bits); + else + w(b,(unsigned long)(ptr[bytes]),bits); + } + return; + err: + oggpack_writeclear(b); +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpack_write,0); +} + +void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpackB_write,1); +} + +void oggpack_reset(oggpack_buffer *b){ + if(!b->ptr)return; + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpackB_reset(oggpack_buffer *b){ + oggpack_reset(b); +} + +void oggpack_writeclear(oggpack_buffer *b){ + if(b->buffer)_ogg_free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpackB_writeclear(oggpack_buffer *b){ + oggpack_writeclear(b); } +#endif +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} +#if 0 +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + oggpack_readinit(b,buf,bytes); +} +#endif +/* Read in bits without advancing the bitptr; bits <= 32 */ +/* moved to ogg.h for inlining */ +#if 0 +long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m; + + if(bits<0 || bits>32) return -1; + m=mask[bits]; + bits+=b->endbit; + + if(b->endbyte >= b->storage-4){ + /* not the main path */ + if(b->endbyte > b->storage-((bits+7)>>3)) return -1; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); + } -/* 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; + 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); + } + } } + return(m&ret); } +#endif + +#if 0 +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpackB_look(oggpack_buffer *b,int bits){ + unsigned long ret; + int m=32-bits; + + if(m<0 || m>32) return -1; + bits+=b->endbit; -static int _halt_one(oggpack_buffer *b){ - if(b->headend<1){ - _adv_halt(b); - return -1; + if(b->endbyte >= b->storage-4){ + /* not the main path */ + if(b->endbyte > b->storage-((bits+7)>>3)) return -1; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); } - return 0; + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return ((ret&0xffffffff)>>(m>>1))>>((m+1)>>1); +} +#endif +long oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); } +#if 0 +long oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} +#endif +/* moved to ogg.h for inlining +void oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->endbit; + + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return; + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; +} +*/ +#if 0 +void oggpackB_adv(oggpack_buffer *b,int bits){ + oggpack_adv(b,bits); +} +#endif +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} +#if 0 +void oggpackB_adv1(oggpack_buffer *b){ + oggpack_adv1(b); +} +#endif /* bits <= 32 */ -long oggpack_read(oggpack_buffer *b,register int bits) ICODE_ATTR_TREMOR_NOT_MDCT; -long oggpack_read(oggpack_buffer *b,register int bits){ - unsigned long m=oggpack_mask[bits]; - ogg_uint32_t ret=-1; +long oggpack_read(oggpack_buffer *b,int bits) ICODE_ATTR_TREMOR_NOT_MDCT; +long oggpack_read(oggpack_buffer *b,int bits){ + long ret; + unsigned long m; - bits+=b->headbit; + if(bits<0 || bits>32) goto err; + m=mask[bits]; + bits+=b->endbit; - if(bits >= b->headend<<3){ + if(b->endbyte >= b->storage-4){ + /* not the main path */ + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); + } - 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); - - } - } - } - } - } - } + 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); } } } - }else{ + } + ret&=m; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return ret; - 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); - } - } + overflow: + err: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; +} +#if 0 +/* bits <= 32 */ +long oggpackB_read(oggpack_buffer *b,int bits){ + long ret; + long m=32-bits; + + if(m<0 || m>32) goto err; + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); } } - - b->headptr+=((unsigned)bits)/8; - b->headend-=((unsigned)bits)/8; } + ret=((ret&0xffffffffUL)>>(m>>1))>>((m+1)>>1); - ret&=m; - b->headbit=bits&7; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; return ret; + + overflow: + err: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; } +#endif +long oggpack_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte >= b->storage) goto overflow; + ret=(b->ptr[0]>>b->endbit)&1; + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return ret; + + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; +} +#if 0 +long oggpackB_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte >= b->storage) goto overflow; + ret=(b->ptr[0]>>(7-b->endbit))&1; + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return ret; + + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; + return -1L; +} + +long oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +long oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +long oggpackB_bytes(oggpack_buffer *b){ + return oggpack_bytes(b); +} + +long oggpackB_bits(oggpack_buffer *b){ + return oggpack_bits(b); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *oggpackB_get_buffer(oggpack_buffer *b){ + return oggpack_get_buffer(b); +} +#endif +/* Self test of the bitwise routines; everything else is based on + them, so they damned well better be solid. */ + +#ifdef _V_SELFTEST +#include + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +oggpack_buffer o; +oggpack_buffer r; + +void report(char *in){ + fprintf(stderr,"%s",in); + exit(1); +} + +void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ + long bytes,i; + unsigned char *buffer; + + oggpack_reset(&o); + for(i=0;iheadend > 8) { + if(b->endbyte < b->storage - 8) { ogg_uint32_t *ptr; unsigned long bit, bitend; unsigned long adr; @@ -292,10 +292,10 @@ static long decode_packed_block(codebook *book, oggpack_buffer *b, const ogg_uint32_t *book_codelist = book->codelist; const char *book_dec_codelengths = book->dec_codelengths; - adr = (unsigned long)b->headptr; - bit = (adr&3)*8+b->headbit; + adr = (unsigned long)b->ptr; + bit = (adr&3)*8+b->endbit; ptr = (ogg_uint32_t*)(adr&~3); - bitend = ((adr&3)+b->headend)*8; + bitend = ((adr&3)+(b->storage-b->endbyte))*8; while (bufptr=bitend) @@ -323,11 +323,11 @@ static long decode_packed_block(codebook *book, oggpack_buffer *b, cache >>= l; } - adr=(unsigned long)b->headptr; + adr=(unsigned long)b->ptr; bit-=(adr&3)*8+cachesize; - b->headend-=(bit/8); - b->headptr+=bit/8; - b->headbit=bit%8; + b->endbyte+=bit/8; + b->ptr+=bit/8; + b->endbit=bit&7; } else { long r = decode_packed_entry_number(book, b); if (r == -1) return bufptr-buf; @@ -337,7 +337,6 @@ static long decode_packed_block(codebook *book, oggpack_buffer *b, return n; } - /* Decode side is specced and easier, because we don't need to find matches using different criteria; we simply read and map. There are two things we need to do 'depending': @@ -570,3 +569,4 @@ long vorbis_book_decodevv_add(codebook *book,ogg_int32_t **a, } return(0); } + diff --git a/apps/codecs/libtremor/framing.c b/apps/codecs/libtremor/framing.c index ac95831d5d..a40e8dec65 100644 --- a/apps/codecs/libtremor/framing.c +++ b/apps/codecs/libtremor/framing.c @@ -1,17 +1,18 @@ /******************************************************************** * * - * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * - * * + * THIS FILE IS PART OF THE Ogg CONTAINER 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-2003 * - * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * + * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** - function: decode Ogg streams back into raw packets + function: code raw packets into framed OggSquish stream and + decode Ogg streams back into raw packets + last mod: $Id$ note: The CRC code is directly derived from public domain code by Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html @@ -19,458 +20,102 @@ ********************************************************************/ -#include "config-tremor.h" +#include #include #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; - - 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; - - if(!bs->outstanding) - _ogg_free(bs); - - } -} - -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->sizedata=_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; -} - -STATICIRAM_NOT_MDCT ogg_reference *_fetch_ref(ogg_buffer_state *bs) - ICODE_ATTR_TREMOR_NOT_MDCT; -STATICIRAM_NOT_MDCT 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->sizedata=_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; -} - -static 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); +int ogg_page_version(const ogg_page *og){ + return((int)(og->header[4])); } -/* *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); +int ogg_page_continued(const ogg_page *og){ + return((int)(og->header[5]&0x01)); } -static void _positionB(oggbyte_buffer *b,int pos){ - if(pospos){ - /* 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; - } +int ogg_page_bos(const ogg_page *og){ + return((int)(og->header[5]&0x02)); } -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; - } +int ogg_page_eos(const ogg_page *og){ + return((int)(og->header[5]&0x04)); } -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; +ogg_int64_t ogg_page_granulepos(const 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); } -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; - } +ogg_uint32_t ogg_page_serialno(const ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); } -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; +long ogg_page_pageno(const ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); } -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; -} +/* 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 */ -/* Now we get to the actual framing code */ +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 -int ogg_page_version(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read1(&ob,4); -} +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ -int ogg_page_continued(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read1(&ob,5)&0x01; +int ogg_page_packets(const ogg_page *og){ + int i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); } -int ogg_page_bos(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read1(&ob,5)&0x02; -} -int ogg_page_eos(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read1(&ob,5)&0x04; -} +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ -ogg_int64_t ogg_page_granulepos(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read8(&ob,6); -} +static ogg_uint32_t _ogg_crc_entry(unsigned long index){ + int i; + unsigned long r; -ogg_uint32_t ogg_page_serialno(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read4(&ob,14); -} - -ogg_uint32_t ogg_page_pageno(ogg_page *og){ - oggbyte_buffer ob; - oggbyte_init(&ob,og->header); - return oggbyte_read4(&ob,18); + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); } - -/* Static CRC calculation table. See older code in CVS for dead - run-time initialization code. */ +#endif static const ogg_uint32_t crc_lookup[256] ICONST_ATTR = { 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, @@ -538,530 +183,1915 @@ static const ogg_uint32_t crc_lookup[256] ICONST_ATTR = { 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; -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; -} +/* init the encode/decode logical stream state */ -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); +int ogg_stream_init(ogg_stream_state *os,int serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->lacing_storage=1024; + + os->body_data=_ogg_malloc(os->body_storage*sizeof(*os->body_data)); + os->lacing_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + if(!os->body_data || !os->lacing_vals || !os->granule_vals){ + ogg_stream_clear(os); + return -1; + } + + os->serialno=serialno; + + return(0); } - return OGG_SUCCESS; + return(-1); +} + +/* async/delayed error detection for the ogg_stream_state */ +int ogg_stream_check(ogg_stream_state *os){ + if(!os || !os->body_data) return -1; + return 0; } -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; - } - - /* 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; +/* _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)); } - - /* 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); +} + +int ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + _ogg_free(os); } - return oy->fifo_head->buffer->data; -} + return(0); +} -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; +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static int _os_body_expand(ogg_stream_state *os,int needed){ + if(os->body_storage<=os->body_fill+needed){ + void *ret; + ret=_ogg_realloc(os->body_data,(os->body_storage+needed+1024)* + sizeof(*os->body_data)); + if(!ret){ + ogg_stream_clear(os); + return -1; + } + os->body_storage+=(needed+1024); + os->body_data=ret; + } + return 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=(byteslength?bytes:or->length); - for(j=0;j> 24)&0xff)^data[j]]; - bytes-=j; - or=or->next; +static int _os_lacing_expand(ogg_stream_state *os,int needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + void *ret; + ret=_ogg_realloc(os->lacing_vals,(os->lacing_storage+needed+32)* + sizeof(*os->lacing_vals)); + if(!ret){ + ogg_stream_clear(os); + return -1; + } + os->lacing_vals=ret; + ret=_ogg_realloc(os->granule_vals,(os->lacing_storage+needed+32)* + sizeof(*os->granule_vals)); + if(!ret){ + ogg_stream_clear(os); + return -1; + } + os->granule_vals=ret; + os->lacing_storage+=(needed+32); } - - return crc_reg; + return 0; } +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum simultaneously with other copies */ -/* sync the stream. This is meant to be useful for finding page - boundaries. +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + ogg_uint32_t crc_reg=0; + int i; - return values for this: - -n) skipped n bytes - 0) page not ready; more data (no bytes skipped) - n) page synced at current location; page length n bytes - -*/ + /* 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;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=(unsigned char)(crc_reg&0xff); + og->header[23]=(unsigned char)((crc_reg>>8)&0xff); + og->header[24]=(unsigned char)((crc_reg>>16)&0xff); + og->header[25]=(unsigned char)((crc_reg>>24)&0xff); + } +} -long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ - oggbyte_buffer page; - long bytes,ret=0; +#if 0 +/* submit data to the internal buffer of the framing engine */ +int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, int count, + long e_o_s, ogg_int64_t granulepos){ - ogg_page_release(og); + int bytes = 0, lacing_vals, i; - bytes=oy->fifo_fill; - oggbyte_init(&page,oy->fifo_tail); + if(ogg_stream_check(os)) return -1; + if(!iov) return 0; + + for (i = 0; i < count; ++i) bytes += (int)iov[i].iov_len; + lacing_vals=bytes/255+1; - if(oy->headerbytes==0){ - if(bytes<27)goto sync_out; /* not enough for even a minimal header */ + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ - /* verify capture pattern */ - 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; + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; } - if(bytesheaderbytes)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;iheaderbytes-27;i++) - oy->bodybytes+=oggbyte_read1(&page,27+i); - } - - if(oy->bodybytes+oy->headerbytes>bytes)goto sync_out; + + /* make sure we have the buffer storage */ + if(_os_body_expand(os,bytes) || _os_lacing_expand(os,lacing_vals)) + return -1; - /* we have what appears to be a complete page; last test: verify - checksum */ - { - ogg_uint32_t chksum=oggbyte_read4(&page,22); - oggbyte_set4(&page,0,22); + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ - /* 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; remember all the memory is common access */ - - oggbyte_set4(&page,chksum,22); - goto sync_fail; - } - oggbyte_set4(&page,chksum,22); + for (i = 0; i < count; ++i) { + memcpy(os->body_data+os->body_fill, iov[i].iov_base, iov[i].iov_len); + os->body_fill += (int)iov[i].iov_len; } - /* 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; + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; } - - ret=oy->headerbytes+oy->bodybytes; - oy->unsynced=0; - oy->headerbytes=0; - oy->bodybytes=0; - oy->fifo_fill-=ret; - - return ret; - - sync_fail: + os->lacing_vals[os->lacing_fill+i]=bytes%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=granulepos; - oy->headerbytes=0; - oy->bodybytes=0; - oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,1); - ret--; - - /* 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; + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; - sync_out: - return ret; -} + os->lacing_fill+=lacing_vals; -/* clear things to an initial state. Good to call, eg, before seeking */ -int ogg_sync_reset(ogg_sync_state *oy){ + /* for the sake of completeness */ + os->packetno++; - ogg_buffer_release(oy->fifo_tail); - oy->fifo_tail=0; - oy->fifo_head=0; - oy->fifo_fill=0; + if(e_o_s)os->e_o_s=1; - oy->unsynced=0; - oy->headerbytes=0; - oy->bodybytes=0; - return OGG_SUCCESS; + return(0); } -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; -} - -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)); - _ogg_free(os); - } - return OGG_SUCCESS; -} +int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + ogg_iovec_t iov; + iov.iov_base = op->packet; + iov.iov_len = op->bytes; + return ogg_stream_iovecin(os, &iov, 1, op->e_o_s, op->granulepos); +} -#define FINFLAG 0x80000000UL -#define FINMASK 0x7fffffffUL +/* Conditionally flush a page; force==0 will only flush nominal-size + pages, force==1 forces us to flush a page regardless of page size + so long as there's any data available at all. */ +static int ogg_stream_flush_i(ogg_stream_state *os,ogg_page *og, int force, int nfill){ + int i; + int vals=0; + int maxvals=(os->lacing_fill>255?255:os->lacing_fill); + int bytes=0; + long acc=0; + ogg_int64_t granule_pos=-1; + + if(ogg_stream_check(os)) return(0); + if(maxvals==0) return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ -static void _next_lace(oggbyte_buffer *ob,ogg_stream_state *os){ - /* search ahead one lace */ - os->body_fill_next=0; - while(os->laceptrlacing_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; + /* The extra packets_done, packet_just_done logic here attempts to do two things: + 1) Don't unneccessarily span pages. + 2) Unless necessary, don't flush pages if there are less than four packets on + them; this expands page size to reduce unneccessary overhead if incoming packets + are large. + These are not necessary behaviors, just 'always better than naive flushing' + without requiring an application to explicitly request a specific optimized + behavior. We'll want an explicit behavior setup pathway eventually as well. */ + + int packets_done=0; + int packet_just_done=0; + for(vals=0;valsnfill && packet_just_done>=4){ + force=1; + break; + } + acc+=os->lacing_vals[vals]&0x0ff; + if((os->lacing_vals[vals]&0xff)<255){ + granule_pos=os->granule_vals[vals]; + packet_just_done=++packets_done; + }else + packet_just_done=0; } + if(vals==255)force=1; } -} -STATICIRAM_NOT_MDCT void _span_queued_page(ogg_stream_state *os) - ICODE_ATTR_TREMOR_NOT_MDCT; -STATICIRAM_NOT_MDCT 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{ - - /* process/prepare next page, if any */ + if(!force) return(0); - long pageno; - oggbyte_buffer ob; - ogg_page og; /* only for parsing header values */ - og.header=os->header_tail; /* only for parsing header values */ - pageno=ogg_page_pageno(&og); + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); - oggbyte_init(&ob,os->header_tail); - os->lacing_fill=oggbyte_read1(&ob,26); - - /* 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; + /* stream structure version */ + os->header[4]=0x00; - } - - 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; - } - } + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; - if(os->laceptrlacing_fill){ - os->granulepos=ogg_page_granulepos(&og); + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(unsigned char)(granule_pos&0xff); + granule_pos>>=8; + } - /* 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); + /* 32 bits of stream serial number */ + { + long serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(unsigned char)(serialno&0xff); + serialno>>=8; + } + } - } - - os->pageno=pageno+1; - os->e_o_s=ogg_page_eos(&og); - os->b_o_s=ogg_page_bos(&og); - + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + long pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(unsigned char)(pageno&0xff); + pageno>>=8; } } -} + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=(unsigned char)(vals&0xff); + for(i=0;iheader[i+27]=(unsigned char)(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); -/* add the incoming page to the stream state; we decompose the page - into packet segments here as well. */ + /* done */ + return(1); +} -int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + an page regardless of size in the middle of a stream. */ + +int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + return ogg_stream_flush_i(os,og,1,4096); +} - int serialno=ogg_page_serialno(og); - int version=ogg_page_version(og); +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ - /* 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; - } +int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + int force=0; + if(ogg_stream_check(os)) return 0; - /* 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); - } + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + (os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */ + force=1; - memset(og,0,sizeof(*og)); - return OGG_SUCCESS; + return(ogg_stream_flush_i(os,og,force,4096)); } -int ogg_stream_reset(ogg_stream_state *os){ +/* Like the above, but an argument is provided to adjust the nominal +page size for applications which are smart enough to provide their +own delay based flushing */ + +int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill){ + int force=0; + if(ogg_stream_check(os)) return 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; + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + (os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */ + force=1; - os->e_o_s=0; - os->b_o_s=0; - os->pageno=-1; - os->packetno=0; - os->granulepos=0; + return(ogg_stream_flush_i(os,og,force,nfill)); +} - os->body_fill=0; - os->lacing_fill=0; +int ogg_stream_eos(ogg_stream_state *os){ + if(ogg_stream_check(os)) return 1; + return os->e_o_s; +} +#endif +/* 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. */ + +/* initialize the struct to a known state */ +int ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + oy->storage = -1; /* used as a readiness flag */ + memset(oy,0,sizeof(*oy)); + } + return(0); +} - os->holeflag=0; - os->spanflag=0; - os->clearflag=0; - os->laceptr=0; - os->body_fill_next=0; +/* clear non-flat storage within */ +int ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)_ogg_free(oy->data); + memset(oy,0,sizeof(*oy)); + } + return(0); +} - return OGG_SUCCESS; +int ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + _ogg_free(oy); + } + return(0); } -int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ - ogg_stream_reset(os); - os->serialno=serialno; - return OGG_SUCCESS; +int ogg_sync_check(ogg_sync_state *oy){ + if(oy->storage<0) return -1; + return 0; } -STATICIRAM_NOT_MDCT int _packetout(ogg_stream_state *os,ogg_packet *op,int adv) - ICODE_ATTR_TREMOR_NOT_MDCT; -STATICIRAM_NOT_MDCT int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ +char *ogg_sync_buffer(ogg_sync_state *oy, long size){ + if(ogg_sync_check(oy)) return NULL; + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } - ogg_packet_release(op); - _span_queued_page(os); + 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 */ + void *ret; - if(os->holeflag){ - int temp=os->holeflag; - if(os->clearflag) - os->holeflag=0; + if(oy->data) + ret=_ogg_realloc(oy->data,newsize); else - os->holeflag=1; - if(temp==2){ - os->packetno++; - return OGG_HOLE; + ret=_ogg_malloc(newsize); + if(!ret){ + ogg_sync_clear(oy); + return NULL; } + oy->data=ret; + oy->storage=newsize; } - 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; + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ + if(ogg_sync_check(oy))return -1; + 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. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + long bytes=oy->fill-oy->returned; + + if(ogg_sync_check(oy))return 0; + + if(oy->headerbytes==0){ + int headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,page+22,4); + memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; } } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + long bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } - 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; + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next=memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; - if(adv){ - oggbyte_buffer ob; - oggbyte_init(&ob,os->header_tail); + oy->returned=(int)(next-oy->data); + return((long)-(next-page)); +} - /* split the body contents off */ - if(op){ - 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; +/* sync the stream and get a page. Keep trying until we find a page. + Suppress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + if(ogg_sync_check(oy))return 0; + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + for(;;){ + long ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); } - /* 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; + /* loop. keep looking */ + + } +} + +/* 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; + + 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); + ogg_uint32_t serialno=ogg_page_serialno(og); + long pageno=ogg_page_pageno(og); + int segments=header[26]; + + if(ogg_stream_check(os)) return -1; + + /* clean up 'returned data' */ + { + long lr=os->lacing_returned; + long br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* 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; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + if(_os_lacing_expand(os,segments+1)) return -1; + + /* are we in sequence? */ + if(pageno!=os->pageno){ + int i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* 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 may need to skip + some segments */ + if(continued){ + if(os->lacing_fill<1 || + os->lacing_vals[os->lacing_fill-1]==0x400){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + int saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + 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; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int ogg_sync_reset(ogg_sync_state *oy){ + if(ogg_sync_check(oy))return -1; + + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +int ogg_stream_reset(ogg_stream_state *os){ + if(ogg_stream_check(os)) return -1; + + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + +/* os->header_fill=0; */ + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + if(ogg_stream_check(os)) return -1; + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv) + ICODE_ATTR_TREMOR_NOT_MDCT; +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + /* 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) */ + + int ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + 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++; - os->b_o_s=0; + return(-1); } - return 1; + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=os->lacing_vals[ptr]&0xff; + long 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(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; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); } int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op) ICODE_ATTR_TREMOR_NOT_MDCT; int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + if(ogg_stream_check(os)) return 0; return _packetout(os,op,1); } int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op) ICODE_ATTR_TREMOR_NOT_MDCT; int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + if(ogg_stream_check(os)) return 0; return _packetout(os,op,0); } -int ogg_packet_release(ogg_packet *op) { - if(op){ - ogg_buffer_release(op->packet); - memset(op, 0, sizeof(*op)); +void ogg_packet_clear(ogg_packet *op) { + _ogg_free(op->packet); + memset(op, 0, sizeof(*op)); +} + +#ifdef _V_SELFTEST +#include + +ogg_stream_state os_en, os_de; +ogg_sync_state oy; + +void checkpacket(ogg_packet *op,long len, int no, long pos){ + long j; + static int sequence=0; + static int lastno=0; + + if(op->bytes!=len){ + fprintf(stderr,"incorrect packet length (%ld != %ld)!\n",op->bytes,len); + exit(1); } - return OGG_SUCCESS; + if(op->granulepos!=pos){ + fprintf(stderr,"incorrect packet granpos (%ld != %ld)!\n",(long)op->granulepos,pos); + exit(1); + } + + /* packet number just follows sequence/gap; adjust the input number + for that */ + if(no==0){ + sequence=0; + }else{ + sequence++; + if(no>lastno+1) + sequence++; + } + lastno=no; + if(op->packetno!=sequence){ + fprintf(stderr,"incorrect packet sequence %ld != %d\n", + (long)(op->packetno),sequence); + exit(1); + } + + /* Test data */ + for(j=0;jbytes;j++) + if(op->packet[j]!=((j+no)&0xff)){ + fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", + j,op->packet[j],(j+no)&0xff); + exit(1); + } } -int ogg_page_release(ogg_page *og) { - if(og){ - ogg_buffer_release(og->header); - ogg_buffer_release(og->body); - memset(og, 0, sizeof(*og)); +void check_page(unsigned char *data,const int *header,ogg_page *og){ + long j; + /* Test data */ + for(j=0;jbody_len;j++) + if(og->body[j]!=data[j]){ + fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", + j,data[j],og->body[j]); + exit(1); + } + + /* Test header */ + for(j=0;jheader_len;j++){ + if(og->header[j]!=header[j]){ + fprintf(stderr,"header content mismatch at pos %ld:\n",j); + for(j=0;jheader[j]); + fprintf(stderr,"\n"); + exit(1); + } + } + if(og->header_len!=header[26]+27){ + fprintf(stderr,"header length incorrect! (%ld!=%d)\n", + og->header_len,header[26]+27); + exit(1); } - 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); +void print_header(ogg_page *og){ + int j; + fprintf(stderr,"\nHEADER:\n"); + fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", + og->header[0],og->header[1],og->header[2],og->header[3], + (int)og->header[4],(int)og->header[5]); + + fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", + (og->header[9]<<24)|(og->header[8]<<16)| + (og->header[7]<<8)|og->header[6], + (og->header[17]<<24)|(og->header[16]<<16)| + (og->header[15]<<8)|og->header[14], + ((long)(og->header[21])<<24)|(og->header[20]<<16)| + (og->header[19]<<8)|og->header[18]); + + fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", + (int)og->header[22],(int)og->header[23], + (int)og->header[24],(int)og->header[25], + (int)og->header[26]); + + for(j=27;jheader_len;j++) + fprintf(stderr,"%d ",(int)og->header[j]); + fprintf(stderr,")\n\n"); +} + +void copy_page(ogg_page *og){ + unsigned char *temp=_ogg_malloc(og->header_len); + memcpy(temp,og->header,og->header_len); + og->header=temp; + + temp=_ogg_malloc(og->body_len); + memcpy(temp,og->body,og->body_len); + og->body=temp; +} + +void free_page(ogg_page *og){ + _ogg_free (og->header); + _ogg_free (og->body); +} + +void error(void){ + fprintf(stderr,"error!\n"); + exit(1); +} + +/* 17 only */ +const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; + +/* 17, 254, 255, 256, 500, 510, 600 byte, pad */ +const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; +const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; + +/* nil packets; beginning,middle,end */ +const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; +const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; + +/* large initial packet */ +const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; + +const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; + + +/* continuing packet test */ +const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xf8,0x3c,0x19,0x79, + 255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255}; + +const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x38,0xe6,0xb6,0x28, + 6, + 255,220,255,4,255,0}; + + +/* spill expansion test */ +const int head1_4b[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4b[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xce,0x8f,0x17,0x1a, + 23, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10,255,4,255,0,0}; + + +const int head3_4b[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x9b,0xb2,0x50,0xa1, + 1, + 0}; + +/* page with the 255 segment limit */ +const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; + +const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; + + +/* packet that overspans over an entire page */ +const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x68,0x22,0x7c,0x3d, + 255, + 100, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255}; + +const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xf4,0x87,0xba,0xf3, + 255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255}; + +const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xf7,0x2f,0x6c,0x60, + 5, + 254,255,4,255,0}; + +/* packet that overspans over an entire page */ +const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x68,0x22,0x7c,0x3d, + 255, + 100, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255}; + +const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1, + 0}; + +void test_pack(const int *pl, const int **headers, int byteskip, + int pageskip, int packetskip){ + unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ + long inptr=0; + long outptr=0; + long deptr=0; + long depacket=0; + long granule_pos=7,pageno=0; + int i,j,packets,pageout=pageskip; + int eosflag=0; + int bosflag=0; + + int byteskipcount=0; + + ogg_stream_reset(&os_en); + ogg_stream_reset(&os_de); + ogg_sync_reset(&oy); + + for(packets=0;packetsbyteskip){ + memcpy(next,og.header,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + byteskipcount+=og.body_len; + if(byteskipcount>byteskip){ + memcpy(next,og.body,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + ogg_sync_wrote(&oy,next-buf); + + while(1){ + int ret=ogg_sync_pageout(&oy,&og_de); + if(ret==0)break; + if(ret<0)continue; + /* got a page. Happy happy. Verify that it's good. */ + + fprintf(stderr,"(%d), ",pageout); + + check_page(data+deptr,headers[pageout],&og_de); + deptr+=og_de.body_len; + pageout++; + + /* submit it to deconstitution */ + ogg_stream_pagein(&os_de,&og_de); + + /* packets out? */ + while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ + ogg_stream_packetpeek(&os_de,NULL); + ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ + + /* verify peek and out match */ + if(memcmp(&op_de,&op_de2,sizeof(op_de))){ + fprintf(stderr,"packetout != packetpeek! pos=%ld\n", + depacket); + exit(1); + } + + /* verify the packet! */ + /* check data */ + if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ + fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", + depacket); + exit(1); + } + /* check bos flag */ + if(bosflag==0 && op_de.b_o_s==0){ + fprintf(stderr,"b_o_s flag not set on packet!\n"); + exit(1); + } + if(bosflag && op_de.b_o_s){ + fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); + exit(1); + } + bosflag=1; + depacket+=op_de.bytes; + + /* check eos flag */ + if(eosflag){ + fprintf(stderr,"Multiple decoded packets with eos flag!\n"); + exit(1); + } + + if(op_de.e_o_s)eosflag=1; + + /* check granulepos flag */ + if(op_de.granulepos!=-1){ + fprintf(stderr," granule:%ld ",(long)op_de.granulepos); + } + } + } + } + } + } + } + _ogg_free(data); + if(headers[pageno]!=NULL){ + fprintf(stderr,"did not write last page!\n"); + exit(1); + } + if(headers[pageout]!=NULL){ + fprintf(stderr,"did not decode last page!\n"); + exit(1); + } + if(inptr!=outptr){ + fprintf(stderr,"encoded page data incomplete!\n"); + exit(1); + } + if(inptr!=deptr){ + fprintf(stderr,"decoded page data incomplete!\n"); + exit(1); + } + if(inptr!=depacket){ + fprintf(stderr,"decoded packet data incomplete!\n"); + exit(1); + } + if(!eosflag){ + fprintf(stderr,"Never got a packet with EOS set!\n"); + exit(1); + } + fprintf(stderr,"ok.\n"); +} + +int main(void){ + + ogg_stream_init(&os_en,0x04030201); + ogg_stream_init(&os_de,0x04030201); + ogg_sync_init(&oy); + + /* Exercise each code path in the framing code. Also verify that + the checksums are working. */ + + { + /* 17 only */ + const int packets[]={17, -1}; + const int *headret[]={head1_0,NULL}; + + fprintf(stderr,"testing single page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ + const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; + const int *headret[]={head1_1,head2_1,NULL}; + + fprintf(stderr,"testing basic page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* nil packets; beginning,middle,end */ + const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; + const int *headret[]={head1_2,head2_2,NULL}; + + fprintf(stderr,"testing basic nil packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* large initial packet */ + const int packets[]={4345,259,255,-1}; + const int *headret[]={head1_3,head2_3,NULL}; + + fprintf(stderr,"testing initial-packet lacing > 4k... "); + test_pack(packets,headret,0,0,0); + } + + { + /* continuing packet test; with page spill expansion, we have to + overflow the lacing table. */ + const int packets[]={0,65500,259,255,-1}; + const int *headret[]={head1_4,head2_4,head3_4,NULL}; + + fprintf(stderr,"testing single packet page span... "); + test_pack(packets,headret,0,0,0); + } + + { + /* spill expand packet test */ + const int packets[]={0,4345,259,255,0,0,-1}; + const int *headret[]={head1_4b,head2_4b,head3_4b,NULL}; + + fprintf(stderr,"testing page spill expansion... "); + test_pack(packets,headret,0,0,0); + } + + /* page with the 255 segment limit */ + { + + const int packets[]={0,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; + const int *headret[]={head1_5,head2_5,head3_5,NULL}; + + fprintf(stderr,"testing max packet segments... "); + test_pack(packets,headret,0,0,0); + } + + { + /* packet that overspans over an entire page */ + const int packets[]={0,100,130049,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing very large packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* test for the libogg 1.1.1 resync in large continuation bug + found by Josh Coalson) */ + const int packets[]={0,100,130049,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing continuation resync in very large packets... "); + test_pack(packets,headret,100,2,3); + } + + { + /* term only page. why not? */ + const int packets[]={0,100,64770,-1}; + const int *headret[]={head1_7,head2_7,head3_7,NULL}; + + fprintf(stderr,"testing zero data page (1 nil packet)... "); + test_pack(packets,headret,0,0,0); + } + + + + { + /* build a bunch of pages for testing */ + unsigned char *data=_ogg_malloc(1024*1024); + int pl[]={0, 1,1,98,4079, 1,1,2954,2057, 76,34,912,0,234,1000,1000, 1000,300,-1}; + int inptr=0,i,j; + ogg_page og[5]; + + ogg_stream_reset(&os_en); + + for(i=0;pl[i]!=-1;i++){ + ogg_packet op; + int len=pl[i]; + + op.packet=data+inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=(i+1)*1000; + + for(j=0;j0)error(); + + /* Test fractional page inputs: incomplete fixed header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, + 5); + ogg_sync_wrote(&oy,5); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete body */ + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, + og[1].header_len-28); + ogg_sync_wrote(&oy,og[1].header_len-28); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); + ogg_sync_wrote(&oy,1000); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, + og[1].body_len-1000); + ogg_sync_wrote(&oy,og[1].body_len-1000); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test fractional page inputs: page + incomplete capture */ + { + ogg_page og_de; + fprintf(stderr,"Testing sync on 1+partial inputs... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, + og[1].header_len-20); + ogg_sync_wrote(&oy,og[1].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing search for capture... "); + ogg_sync_reset(&oy); + + /* 'garbage' */ + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, + og[2].header_len-20); + ogg_sync_wrote(&oy,og[2].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len); + ogg_sync_wrote(&oy,og[2].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: page + garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing recapture... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len-5); + ogg_sync_wrote(&oy,og[2].body_len-5); + + memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, + og[3].header_len); + ogg_sync_wrote(&oy,og[3].header_len); + + memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, + og[3].body_len); + ogg_sync_wrote(&oy,og[3].body_len); + + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Free page data that was previously copied */ + { + for(i=0;i<5;i++){ + free_page(&og[i]); + } + } + } + + return(0); } +#endif + + + + diff --git a/apps/codecs/libtremor/info.c b/apps/codecs/libtremor/info.c index 9bfa7d03b0..f3ac5f87f6 100644 --- a/apps/codecs/libtremor/info.c +++ b/apps/codecs/libtremor/info.c @@ -240,7 +240,7 @@ int vorbis_synthesis_idheader(ogg_packet *op){ char buffer[6]; if(op){ - oggpack_readinit(&opb,op->packet); + oggpack_readinit(&opb,op->packet,op->bytes); if(!op->b_o_s) return(0); /* Not the initial packet */ @@ -268,7 +268,7 @@ int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op) oggpack_buffer opb; if(op){ - oggpack_readinit(&opb,op->packet); + oggpack_readinit(&opb,op->packet,op->bytes); /* Which of the three types of header is this? */ /* Also verify header-ness, vorbis */ diff --git a/apps/codecs/libtremor/ivorbisfile.h b/apps/codecs/libtremor/ivorbisfile.h index 39a648961a..1aeb0ca84b 100644 --- a/apps/codecs/libtremor/ivorbisfile.h +++ b/apps/codecs/libtremor/ivorbisfile.h @@ -56,7 +56,7 @@ 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 */ @@ -77,7 +77,7 @@ typedef struct OggVorbis_File { 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 */ @@ -87,13 +87,11 @@ typedef struct OggVorbis_File { } OggVorbis_File; extern int ov_clear(OggVorbis_File *vf); - //extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, - char *initial, long ibytes, ov_callbacks callbacks); + const char *initial, long ibytes, ov_callbacks callbacks); - //extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, - char *initial, long ibytes, ov_callbacks callbacks); + const char *initial, long ibytes, ov_callbacks callbacks); extern int ov_test_open(OggVorbis_File *vf); extern long ov_bitrate(OggVorbis_File *vf,int i); diff --git a/apps/codecs/libtremor/ogg.h b/apps/codecs/libtremor/ogg.h index 15ca46b3cd..da072184b4 100644 --- a/apps/codecs/libtremor/ogg.h +++ b/apps/codecs/libtremor/ogg.h @@ -1,17 +1,17 @@ /******************************************************************** * * - * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * - * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE 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-2003 * - * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** - function: subsumed libogg includes + function: toplevel libogg include + last mod: $Id$ ********************************************************************/ #ifndef _OGG_H @@ -21,241 +21,238 @@ extern "C" { #endif +#include #include "os_types.h" -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; +extern const unsigned long mask[] ICONST_ATTR; -typedef struct oggbyte_buffer { - ogg_reference *baseref; +typedef struct { + void *iov_base; + size_t iov_len; +} ogg_iovec_t; + +typedef struct { + long endbyte; + int endbit; - ogg_reference *ref; + unsigned char *buffer; unsigned char *ptr; - long pos; - long end; -} oggbyte_buffer; + long storage; +} oggpack_buffer; -typedef struct ogg_sync_state { - /* decode memory management pool */ - ogg_buffer_state *bufferpool; +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ - /* stream buffers */ - ogg_reference *fifo_head; - ogg_reference *fifo_tail; - long fifo_fill; +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; - /* stream sync management */ - int unsynced; - int headerbytes; - int bodybytes; +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ -} ogg_sync_state; +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; + +#if 0 + unsigned char header[282]; /* working space for header encode */ + int header_fill; +#endif + 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 */ + ogg_uint32_t 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 separate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; -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 { - 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 */ + 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 separate abstraction + layer) also knows about the gap */ } ogg_packet; typedef struct { - ogg_reference *header; - int header_len; - ogg_reference *body; - long body_len; -} ogg_page; + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; /* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ +/* +extern void oggpack_writeinit(oggpack_buffer *b); +extern int oggpack_writecheck(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); */ +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +/* extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); */ + +//extern long oggpack_look(oggpack_buffer *b,int bits); +static inline long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m; + + if(bits<0 || bits>32) return -1; + m=mask[bits]; + bits+=b->endbit; + + if(b->endbyte >= b->storage-4){ + /* not the main path */ + if(b->endbyte > b->storage-((bits+7)>>3)) return -1; + /* special case to avoid reading b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting */ + else if(!bits)return(0L); + } -extern void oggpack_readinit(oggpack_buffer *b,ogg_reference *r); -extern long oggpack_look_full(oggpack_buffer *b,int bits); -extern long oggpack_read(oggpack_buffer *b,register int bits); - -/* Inline a few, often called functions */ - -/* mark read process as having run off the end */ -static inline 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 inline 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; + 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); + } } } + return(m&ret); } -/* limited to 32 at a time */ +extern long oggpack_look1(oggpack_buffer *b); + +//extern void oggpack_adv(oggpack_buffer *b,int bits); static inline void oggpack_adv(oggpack_buffer *b,int bits){ - bits+=b->headbit; - b->headbit=bits&7; - b->headptr+=bits/8; - if((b->headend-=((unsigned)bits)/8)<1)_span(b); -} + bits+=b->endbit; -static inline long oggpack_look(oggpack_buffer *b, int bits){ - if(bits+b->headbit < b->headend<<3){ - extern const unsigned long oggpack_mask[]; - unsigned long m=oggpack_mask[bits]; - unsigned long ret=-1; - - bits+=b->headbit; - 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 ret&m; - }else{ - return oggpack_look_full(b, bits); - } + if(b->endbyte > b->storage-((bits+7)>>3)) goto overflow; + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return; + + overflow: + b->ptr=NULL; + b->endbyte=b->storage; + b->endbit=1; } +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +#if 0 +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +extern void oggpackB_writeinit(oggpack_buffer *b); +extern int oggpackB_writecheck(oggpack_buffer *b); +extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); +extern void oggpackB_writealign(oggpack_buffer *b); +extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpackB_reset(oggpack_buffer *b); +extern void oggpackB_writeclear(oggpack_buffer *b); +extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpackB_look(oggpack_buffer *b,int bits); +extern long oggpackB_look1(oggpack_buffer *b); +extern void oggpackB_adv(oggpack_buffer *b,int bits); +extern void oggpackB_adv1(oggpack_buffer *b); +extern long oggpackB_read(oggpack_buffer *b,int bits); +extern long oggpackB_read1(oggpack_buffer *b); +extern long oggpackB_bytes(oggpack_buffer *b); +extern long oggpackB_bits(oggpack_buffer *b); +extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); +# endif +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ +#if 0 +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, + int count, long e_o_s, ogg_int64_t granulepos); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); +#endif /* Ogg BITSTREAM PRIMITIVES: decoding **************************/ -extern ogg_sync_state *ogg_sync_create(void); -extern int ogg_sync_destroy(ogg_sync_state *oy); +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); +extern int ogg_sync_check(ogg_sync_state *oy); -extern unsigned char *ogg_sync_bufferin(ogg_sync_state *oy, long size); +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); /* Ogg BITSTREAM PRIMITIVES: general ***************************/ -extern ogg_stream_state *ogg_stream_create(int serialno); -extern int ogg_stream_destroy(ogg_stream_state *os); +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 int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_check(ogg_stream_state *os); extern int ogg_stream_eos(ogg_stream_state *os); -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 ogg_uint32_t ogg_page_serialno(ogg_page *og); -extern ogg_uint32_t ogg_page_pageno(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 ***************************/ +extern void ogg_page_checksum_set(ogg_page *og); -#define OGG_SUCCESS 0 +extern int ogg_page_version(const ogg_page *og); +extern int ogg_page_continued(const ogg_page *og); +extern int ogg_page_bos(const ogg_page *og); +extern int ogg_page_eos(const ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(const ogg_page *og); +extern ogg_uint32_t ogg_page_serialno(const ogg_page *og); +extern long ogg_page_pageno(const ogg_page *og); +extern int ogg_page_packets(const ogg_page *og); -#define OGG_HOLE -10 -#define OGG_SPAN -11 -#define OGG_EVERSION -12 -#define OGG_ESERIAL -13 -#define OGG_EINVAL -14 -#define OGG_EEOS -15 +extern void ogg_packet_clear(ogg_packet *op); #ifdef __cplusplus diff --git a/apps/codecs/libtremor/synthesis.c b/apps/codecs/libtremor/synthesis.c index 69180b7acf..c47f381592 100644 --- a/apps/codecs/libtremor/synthesis.c +++ b/apps/codecs/libtremor/synthesis.c @@ -37,7 +37,7 @@ static inline int _vorbis_synthesis1(vorbis_block *vb,ogg_packet *op,int decodep /* first things first. Make sure decode is ready */ _vorbis_block_ripcord(vb); - oggpack_readinit(opb,op->packet); + oggpack_readinit(opb,op->packet,op->bytes); /* Check the packet type */ if(oggpack_read(opb,1)!=0){ @@ -102,6 +102,8 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ return _vorbis_synthesis1(vb,op,1); } +/* used to track pcm position without actually performing decode. + Useful for sequential 'fast forward' */ int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){ return _vorbis_synthesis1(vb,op,0); } @@ -111,7 +113,7 @@ long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ oggpack_buffer opb; int mode; - oggpack_readinit(&opb,op->packet); + oggpack_readinit(&opb,op->packet,op->bytes); /* Check the packet type */ if(oggpack_read(&opb,1)!=0){ diff --git a/apps/codecs/libtremor/vorbisfile.c b/apps/codecs/libtremor/vorbisfile.c index 50a57affbd..9365ba344b 100644 --- a/apps/codecs/libtremor/vorbisfile.c +++ b/apps/codecs/libtremor/vorbisfile.c @@ -53,18 +53,19 @@ 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){ + if(!(vf->callbacks.read_func))return(-1); if(vf->datasource){ - char *buffer=(char *)ogg_sync_bufferin(vf->oy,CHUNKSIZE); + char *buffer=ogg_sync_buffer(&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); return(bytes); }else return(0); @@ -77,7 +78,7 @@ static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) return OV_EREAD; vf->offset=offset; - ogg_sync_reset(vf->oy); + ogg_sync_reset(&vf->oy); }else{ /* shouldn't happen unless someone writes a broken callback */ return OV_EFAULT; @@ -96,9 +97,7 @@ static int _seek_helper(OggVorbis_File *vf,ogg_int64_t 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 - - produces a refcounted page */ + n) found a page at absolute offset n */ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, ogg_int64_t boundary){ @@ -107,8 +106,8 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, 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 */ vf->offset-=more; @@ -127,7 +126,7 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, ogg_int64_t ret=vf->offset; vf->offset+=more; return(ret); - + } } } @@ -137,8 +136,7 @@ static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, 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 */ - +/* returns offset or OV_EREAD, OV_FAULT */ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ ogg_int64_t begin=vf->offset; ogg_int64_t end=begin; @@ -151,9 +149,10 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ begin=0; ret=_seek_helper(vf,begin); - if(ret)return(ret); + if(ret)return(ret); while(vf->offsetoffset); if(ret==OV_EREAD)return(OV_EREAD); if(ret<0){ @@ -168,7 +167,6 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ 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){ - ogg_page_release(og); ret=_seek_helper(vf,offset); if(ret)return(ret); @@ -219,14 +217,14 @@ static int _lookup_page_serialno(ogg_page *og, ogg_uint32_t *serialno_list, int static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ogg_uint32_t *serial_list, int serial_n, int *serialno, ogg_int64_t *granpos){ - ogg_page og={0,0,0,0}; + ogg_page og; ogg_int64_t begin=vf->offset; ogg_int64_t end=begin; ogg_int64_t ret; ogg_int64_t prefoffset=-1; ogg_int64_t offset=-1; - ogg_uint32_t ret_serialno=-1; + ogg_int64_t ret_serialno=-1; ogg_int64_t ret_gran=-1; while(offset==-1){ @@ -241,13 +239,11 @@ static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ret=_get_next_page(vf,&og,end-vf->offset); if(ret==OV_EREAD)return(OV_EREAD); if(ret<0){ - ogg_page_release(&og); break; }else{ ret_serialno=ogg_page_serialno(&og); ret_gran=ogg_page_granulepos(&og); offset=ret; - ogg_page_release(&og); if(ret_serialno == (ogg_uint32_t) *serialno){ prefoffset=ret; @@ -276,23 +272,18 @@ static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, /* uses the local ogg_stream storage in vf; this is important for non-streaming input sources */ -/* 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_list, - int *serialno_n, +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={0,0,0,0}; - ogg_packet op={0,0,0,0,0,0}; + ogg_page og; + ogg_packet op; int i,ret; int allbos=0; - + 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; + if(llret<0)return(OV_ENOTVORBIS); og_ptr=&og; } @@ -320,10 +311,10 @@ static int _fetch_headers(OggVorbis_File *vf, if(vf->ready_stateos,ogg_page_serialno(og_ptr)); - ogg_stream_pagein(vf->os,og_ptr); + 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 && + if(ogg_stream_packetout(&vf->os,&op) > 0 && vorbis_synthesis_idheader(&op)){ /* vorbis header; continue setup */ vf->ready_state=STREAMSET; @@ -348,8 +339,8 @@ static int _fetch_headers(OggVorbis_File *vf, /* if this page also belongs to our vorbis stream, submit it and break */ if(vf->ready_state==STREAMSET && - (ogg_uint32_t) vf->os->serialno == ogg_page_serialno(og_ptr)){ - ogg_stream_pagein(vf->os,og_ptr); + vf->os.serialno == ogg_page_serialno(og_ptr)){ + ogg_stream_pagein(&vf->os,og_ptr); break; } } @@ -367,7 +358,7 @@ static int _fetch_headers(OggVorbis_File *vf, while(i<2){ /* get a packet loop */ - 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; @@ -387,8 +378,8 @@ static int _fetch_headers(OggVorbis_File *vf, } /* if this page belongs to the correct stream, go parse it */ - if((ogg_uint32_t) vf->os->serialno == ogg_page_serialno(og_ptr)){ - ogg_stream_pagein(vf->os,og_ptr); + if(vf->os.serialno == ogg_page_serialno(og_ptr)){ + ogg_stream_pagein(&vf->os,og_ptr); break; } @@ -406,15 +397,10 @@ static int _fetch_headers(OggVorbis_File *vf, } } - 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); vf->ready_state=OPENED; @@ -427,25 +413,23 @@ static int _fetch_headers(OggVorbis_File *vf, 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={0,0,0,0}; - ogg_int64_t accumulated=0,pos; + ogg_page og; + ogg_int64_t accumulated=0; long lastblock=-1; int result; - int serialno = vf->os->serialno; + ogg_uint32_t serialno = vf->os.serialno; while(1){ - ogg_packet op={0,0,0,0,0,0}; - + 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)!=(ogg_uint32_t) serialno) continue; - pos=ogg_page_granulepos(&og); + 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))){ + 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) @@ -453,11 +437,10 @@ static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ lastblock=thisblock; } } - ogg_packet_release(&op); - if(pos!=-1){ + if(ogg_page_granulepos(&og)!=-1){ /* pcm offset of last packet on the first audio page */ - accumulated= pos-accumulated; + accumulated= ogg_page_granulepos(&og)-accumulated; break; } } @@ -466,11 +449,9 @@ static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ the beginning, a normal occurrence; set the offset to zero */ if(accumulated<0)accumulated=0; - ogg_page_release(&og); return accumulated; } - /* 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 @@ -484,14 +465,14 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, 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; + int serialno = vf->os.serialno; /* invariants: we have the headers and serialnos for the link beginning at 'begin' @@ -538,7 +519,6 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, /* the below guards against garbage seperating the last and first pages of two links. */ while(searchedoffset){ + ret=_seek_helper(vf,bisect); + if(ret)return(ret); + } last=_get_next_page(vf,&og,-1); if(last==OV_EREAD)return(OV_EREAD); @@ -556,9 +538,8 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, endsearched=bisect; if(last>=0)next=last; }else{ - searched=last+og.header_len+og.body_len; + searched=vf->offset; } - ogg_page_release(&og); } /* Bisection point found */ @@ -580,7 +561,7 @@ static int _bisect_forward_serialno(OggVorbis_File *vf, ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL); if(ret)return(ret); - serialno = vf->os->serialno; + serialno = vf->os.serialno; dataoffset = vf->offset; /* this will consume a page, however the next bistection always @@ -627,8 +608,8 @@ static int _make_decode_ready(OggVorbis_File *vf){ 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; + int endserial=vf->os.serialno; + int serialno=vf->os.serialno; /* we're partially open and have a first link header state in storage in vf */ @@ -666,7 +647,7 @@ static int _open_seekable2(OggVorbis_File *vf){ return(ov_raw_seek(vf,dataoffset)); } -/* clear out the current logical bitstream decoder */ +/* clear out the current logical bitstream decoder */ static void _decode_clear(OggVorbis_File *vf){ vorbis_dsp_clear(&vf->vd); vorbis_block_clear(&vf->vb); @@ -677,74 +658,67 @@ static void _decode_clear(OggVorbis_File *vf){ 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 */ -STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, - int readp, - int spanp) ICODE_ATTR_TREMOR_NOT_MDCT; -STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, +static int _fetch_and_process_packet(OggVorbis_File *vf, + ogg_packet *op_in, int readp, int spanp){ - ogg_page og={0,0,0,0}; - ogg_packet op={0,0,0,0,0,0}; - int ret=0; + ogg_page og; /* handle one packet. Try to fetch it from current stream state */ /* extract packets from page */ while(1){ - + if(vf->ready_state==STREAMSET){ - ret=_make_decode_ready(vf); - if(ret<0) goto cleanup; + 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) { - int result=ogg_stream_packetout(vf->os,&op); + 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; - if(result==-1){ - ret=OV_HOLE; /* hole in the data. */ - goto cleanup; - } + op_in=NULL; + if(result==-1)return(OV_HOLE); /* hole in the data. */ if(result>0){ /* got a packet. process it */ - granulepos=op.granulepos; - if(!vorbis_synthesis(&vf->vb,&op)){ /* lazy check for lazy - header handling. The - header packets aren't - audio, so if/when we - submit them, - vorbis_synthesis will - reject them */ + granulepos=op_ptr->granulepos; + if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy + header handling. The + header packets aren't + audio, so if/when we + submit them, + vorbis_synthesis will + reject them */ /* suck in the synthesis data and track bitrate */ { int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); /* for proper use of libvorbis within libvorbisfile, oldsamples will always be zero. */ - if(oldsamples){ - ret=OV_EFAULT; - goto cleanup; - } + if(oldsamples)return(OV_EFAULT); vorbis_synthesis_blockin(&vf->vd,&vf->vb); vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; - vf->bittrack+=op.bytes*8; + vf->bittrack+=op_ptr->bytes*8; } - + /* update the pcm offset. */ - if(granulepos!=-1 && !op.e_o_s){ + if(granulepos!=-1 && !op_ptr->e_o_s){ int link=(vf->seekable?vf->current_link:0); int i,samples; - + /* 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 @@ -755,7 +729,7 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, granulepos declares the last frame in the stream, and the last packet of the last page may be a partial frame. So, we need a previous granulepos from an in-sequence page - to have a reference point. Thus the !op.e_o_s clause + to have a reference point. Thus the !op_ptr->e_o_s clause above */ if(vf->seekable && link>0) @@ -766,23 +740,22 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, is very broken */ samples=vorbis_synthesis_pcmout(&vf->vd,NULL); - + granulepos-=samples; for(i=0;ipcmlengths[i*2+1]; vf->pcm_offset=granulepos; } - ret=1; - goto cleanup; + return(1); } } - else + else break; } } if(vf->ready_state>=OPENED){ - ogg_int64_t lret; + ogg_int64_t ret; while(1){ /* the loop is not strictly necessary, but there's no sense in @@ -791,13 +764,9 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, part of a different logical bitstream; keep reading until we get one with the correct serialno */ - if(!readp){ - ret=0; - goto cleanup; - } - if((lret=_get_next_page(vf,&og,-1))<0){ - ret=OV_EOF; /* eof. leave unitialized */ - goto cleanup; + 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 @@ -813,10 +782,8 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, if(ogg_page_bos(&og)){ /* boundary case */ - if(!spanp){ - ret=OV_EOF; - goto cleanup; - } + if(!spanp) + return(OV_EOF); _decode_clear(vf); @@ -830,12 +797,13 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, continue; /* possibility #2 */ } } + break; } } /* 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 @@ -846,37 +814,37 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, we're now nominally at the header of the next bitstream */ - if(vf->ready_state!=INITSET){ + if(vf->ready_state!=INITSET){ int link; if(vf->ready_stateseekable){ - long serialno=ogg_page_serialno(&og); + 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;linklinks;link++) - if(vf->serialnos[link]==(ogg_uint32_t) serialno)break; + if(vf->serialnos[link]==serialno)break; - if(link==vf->links) continue; /* not the desired Vorbis - bitstream section; keep - trying */ + 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); + 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) goto cleanup; - vf->current_serialno=vf->os->serialno; + if(ret)return(ret); + vf->current_serialno=vf->os.serialno; vf->current_link++; link=0; } @@ -885,17 +853,14 @@ STATICIRAM_NOT_MDCT int _fetch_and_process_packet(OggVorbis_File *vf, /* 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); + ogg_stream_pagein(&vf->os,&og); + } - cleanup: - ogg_packet_release(&op); - ogg_page_release(&og); - return ret; } -static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, +static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial, long ibytes, ov_callbacks callbacks){ - int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1); + 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; int ret; @@ -905,16 +870,16 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, vf->callbacks = callbacks; /* init the framing state */ - vf->oy=ogg_sync_create(); + ogg_sync_init(&vf->oy); /* perhaps some data was previously read into a buffer for testing against other stream types. Allow initialization from this previously read data (especially as we may be reading from a non-seekable stream) */ if(initial){ - unsigned char *buffer=ogg_sync_bufferin(vf->oy,ibytes); + char *buffer=ogg_sync_buffer(&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 */ @@ -925,7 +890,7 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, vf->links=1; 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 */ + ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ /* Fetch all BOS pages, store the vorbis header and all seen serial numbers, load subsequent vorbis setup headers */ @@ -945,7 +910,7 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets)); vf->offsets[0]=0; vf->dataoffsets[0]=vf->offset; - vf->current_serialno=vf->os->serialno; + vf->current_serialno=vf->os.serialno; vf->ready_state=PARTOPEN; } @@ -954,8 +919,8 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, } static int _ov_open2(OggVorbis_File *vf){ - if(vf->ready_state < OPENED) - vf->ready_state=OPENED; + if(vf->ready_state != PARTOPEN) return OV_EINVAL; + vf->ready_state=OPENED; if(vf->seekable){ int ret=_open_seekable2(vf); if(ret){ @@ -963,7 +928,9 @@ static int _ov_open2(OggVorbis_File *vf){ ov_clear(vf); } return(ret); - } + }else + vf->ready_state=STREAMSET; + return 0; } @@ -973,8 +940,8 @@ int ov_clear(OggVorbis_File *vf){ if(vf){ vorbis_block_clear(&vf->vb); vorbis_dsp_clear(&vf->vd); - ogg_stream_destroy(vf->os); - + ogg_stream_clear(&vf->os); + if(vf->vi && vf->links){ int i; for(i=0;ilinks;i++){ @@ -988,8 +955,7 @@ 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_destroy(vf->oy); - + ogg_sync_clear(&vf->oy); if(vf->datasource && vf->callbacks.close_func) (vf->callbacks.close_func)(vf->datasource); memset(vf,0,sizeof(*vf)); @@ -1002,14 +968,14 @@ int ov_clear(OggVorbis_File *vf){ /* inspects the OggVorbis file and finds/documents all the logical bitstreams contained in it. Tries to be tolerant of logical - bitstream sections that are truncated/woogie. + bitstream sections that are truncated/woogie. return: -1) error 0) OK */ -int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, - ov_callbacks callbacks){ +int ov_open_callbacks(void *f,OggVorbis_File *vf, + const char *initial,long ibytes,ov_callbacks callbacks){ #if defined(CPU_COLDFIRE) /* this seems to be the closest we get to an init function, let's init emac here. rounding is disabled because of MULT31_SHIFT15, which will be @@ -1067,9 +1033,7 @@ 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=NULL; - ogg_page og={0,0,0,0}; - ogg_packet op={0,0,0,0,0,0}; + ogg_stream_state work_os; int ret; if(vf->ready_statevf->end)return(OV_EINVAL); + /* is the seek position outside our current link [if any]? */ + if(vf->ready_state>=STREAMSET){ + if(posoffsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1]) + _decode_clear(vf); /* clear out stream state */ + } + /* 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, + 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; @@ -1097,55 +1067,61 @@ 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; + int thisblock=0; int lastflag=0; int firstflag=0; ogg_int64_t pagepos=-1; - work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */ + 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 */ + while(1){ 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){ thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); if(thisblock<0){ - ogg_stream_packetout(vf->os,NULL); + 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 - preceeding page. However, if the last page is also the first + 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); + 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;ipcmlengths[i*2+1]; vf->pcm_offset=granulepos-accblock; @@ -1155,10 +1131,10 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ lastblock=thisblock; continue; }else - ogg_stream_packetout(vf->os,NULL); + ogg_stream_packetout(&vf->os,NULL); } } - + if(!lastblock){ pagepos=_get_next_page(vf,&og,-1); if(pagepos<0){ @@ -1170,10 +1146,11 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ 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? */ @@ -1181,53 +1158,45 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ if(ogg_page_bos(&og)){ /* we traversed */ _decode_clear(vf); /* clear out stream state */ - ogg_stream_destroy(work_os); + ogg_stream_clear(&work_os); } /* else, do nothing; next loop will scoop another page */ } } if(vf->ready_statelinks;link++) - if(vf->serialnos[link]==vf->current_serialno)break; + 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,vf->current_serialno); - ogg_stream_reset_serialno(work_os,vf->current_serialno); + ogg_stream_reset_serialno(&vf->os,serialno); + ogg_stream_reset_serialno(&work_os,serialno); vf->ready_state=STREAMSET; firstflag=(pagepos<=vf->dataoffsets[link]); } - - { - ogg_page dup; - ogg_page_dup(&dup,&og); - lastflag=ogg_page_eos(&og); - ogg_stream_pagein(vf->os,&og); - ogg_stream_pagein(work_os,&dup); - } + + ogg_stream_pagein(&vf->os,&og); + ogg_stream_pagein(&work_os,&og); + lastflag=ogg_page_eos(&og); + } } - ogg_packet_release(&op); - ogg_page_release(&og); - ogg_stream_destroy(work_os); + ogg_stream_clear(&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_destroy(work_os); + ogg_stream_clear(&work_os); _decode_clear(vf); return OV_EBADLINK; } @@ -1235,20 +1204,19 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ /* 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 preceeding the specified pos + Seek to the last [granule marked] page preceding 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 link=-1; 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_stateseekable)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]; @@ -1256,7 +1224,7 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ } /* search within the logical bitstream for the page with the highest - pcm_pos preceeding (or equal to) pos. There is a danger here; + pcm_pos preceding (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) */ @@ -1269,22 +1237,26 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ 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(beginoffset){ + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + } + while(beginoffset); if(result==OV_EREAD) goto seek_error; @@ -1295,16 +1267,23 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ if(bisect==0) goto seek_error; bisect-=CHUNKSIZE; if(bisect<=begin)bisect=begin+1; - _seek_helper(vf,bisect); + result=_seek_helper(vf,bisect); + if(result) goto seek_error; } }else{ - ogg_int64_t granulepos=ogg_page_granulepos(&og); + ogg_int64_t granulepos; + + if(ogg_page_serialno(&og)!=vf->serialnos[link]) + continue; + + granulepos=ogg_page_granulepos(&og); if(granulepos==-1)continue; + if(granuleposoffset; /* raw offset of next page */ begintime=granulepos; - + if(target-begintime>44100)break; bisect=begin; /* *not* begin + 1 */ }else{ @@ -1315,9 +1294,10 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ end=result; bisect-=CHUNKSIZE; /* an endless loop otherwise. */ if(bisect<=begin)bisect=begin+1; - _seek_helper(vf,bisect); + result=_seek_helper(vf,bisect); + if(result) goto seek_error; }else{ - end=result; + end=bisect; endtime=granulepos; break; } @@ -1328,9 +1308,11 @@ 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. */ + raw_seek, don't keep packets preceding granulepos. */ { - + ogg_page og; + ogg_packet op; + /* seek */ result=_seek_helper(vf,best); vf->pcm_offset=-1; @@ -1340,28 +1322,28 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ if(link!=vf->current_link){ /* Different link; dump entire decode machine */ - _decode_clear(vf); - + _decode_clear(vf); + vf->current_link=link; - vf->current_serialno=ogg_page_serialno(&og); + 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); + 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){ - result=ogg_stream_packetpeek(vf->os,&op); + 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 + 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. */ - + result=_seek_helper(vf,best); if(result<0) goto seek_error; @@ -1377,7 +1359,7 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ } } if(result<0){ - result = OV_EBADPACKET; + result = OV_EBADPACKET; goto seek_error; } if(op.granulepos!=-1){ @@ -1386,11 +1368,11 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ vf->pcm_offset+=total; break; }else - result=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)){ result=OV_EFAULT; @@ -1398,60 +1380,53 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ } 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); + seek_error: /* 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){ - 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); + if((ret=_make_decode_ready(vf)))return ret; /* 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); + 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; - + /* remove the packet from packet queue and track its granulepos */ - ogg_stream_packetout(vf->os,NULL); + 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); - + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + /* end of logical stream case is hard, especially with exact length positioning. */ - + if(op.granulepos>-1){ int i; /* always believe the stream markers */ @@ -1460,20 +1435,20 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ for(i=0;icurrent_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->ready_statelinks;link++) if(vf->serialnos[link]==(ogg_uint32_t) serialno)break; if(link==vf->links) continue; @@ -1481,17 +1456,13 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ vf->ready_state=STREAMSET; vf->current_serialno=ogg_page_serialno(&og); - ogg_stream_reset_serialno(vf->os,serialno); + ogg_stream_reset_serialno(&vf->os,serialno); ret=_make_decode_ready(vf); - if(ret){ - ogg_page_release(&og); - ogg_packet_release(&op); - return ret; - } + if(ret)return ret; lastblock=0; } - ogg_stream_pagein(vf->os,&og); + ogg_stream_pagein(&vf->os,&og); } } @@ -1506,18 +1477,15 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ if(samples>target)samples=target; vorbis_synthesis_read(&vf->vd,samples); vf->pcm_offset+=samples; - + if(samplespcm_offset=ov_pcm_total(vf,-1); /* eof */ } - - ogg_page_release(&og); - 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){ /* translate time to PCM position and call ov_pcm_seek */ @@ -1529,7 +1497,7 @@ int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){ if(vf->ready_stateseekable)return(OV_ENOSEEK); if(milliseconds<0)return(OV_EINVAL); - + /* which bitstream section does this time offset occur in? */ for(link=0;linklinks;link++){ ogg_int64_t addsec = ov_time_total(vf,link); @@ -1559,7 +1527,7 @@ ogg_int64_t ov_time_tell(OggVorbis_File *vf){ int link=0; ogg_int64_t pcm_total=0; ogg_int64_t time_total=0; - + if(vf->ready_stateseekable){ pcm_total=ov_pcm_total(vf,-1); @@ -1579,7 +1547,7 @@ ogg_int64_t ov_time_tell(OggVorbis_File *vf){ /* 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 */ @@ -1634,9 +1602,12 @@ long ov_read_fixed(OggVorbis_File *vf,ogg_int32_t ***pcm_channels,int length, /* suck in another packet */ { - int ret=_fetch_and_process_packet(vf,1,1); - if(ret==OV_EOF)return(0); - if(ret<=0)return(ret); + int ret=_fetch_and_process_packet(vf,NULL,1,1); + if(ret==OV_EOF) + return(0); + if(ret<=0) + return(ret); } } } + -- cgit