FS#11195, plus. Simplified hotkey struct, thanks alle!
[kugel-rb.git] / apps / codecs / libspeex / oggframing.c
blob42d29502a8529456c1096b991155f75e05ff4096
1 /********************************************************************
2 * *
3 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
4 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
5 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
6 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
7 * *
8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
10 * *
11 ********************************************************************
13 function: code raw [Vorbis] packets into framed OggSquish stream and
14 decode Ogg streams back into raw packets
15 last mod: $Id$
17 note: The CRC code is directly derived from public domain code by
18 Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html
19 for details.
21 ********************************************************************/
23 //#include "config-tremor.h"
24 #include <string.h>
25 #include "speex/ogg.h"
26 #include "arch.h"
28 /* A complete description of Ogg framing exists in docs/framing.html */
30 int spx_ogg_page_version(spx_ogg_page *og){
31 return((int)(og->header[4]));
34 int spx_ogg_page_continued(spx_ogg_page *og){
35 return((int)(og->header[5]&0x01));
38 int spx_ogg_page_bos(spx_ogg_page *og){
39 return((int)(og->header[5]&0x02));
42 int spx_ogg_page_eos(spx_ogg_page *og){
43 return((int)(og->header[5]&0x04));
46 spx_ogg_int64_t spx_ogg_page_granulepos(spx_ogg_page *og){
47 unsigned char *page=og->header;
48 spx_ogg_int64_t granulepos=page[13]&(0xff);
49 granulepos= (granulepos<<8)|(page[12]&0xff);
50 granulepos= (granulepos<<8)|(page[11]&0xff);
51 granulepos= (granulepos<<8)|(page[10]&0xff);
52 granulepos= (granulepos<<8)|(page[9]&0xff);
53 granulepos= (granulepos<<8)|(page[8]&0xff);
54 granulepos= (granulepos<<8)|(page[7]&0xff);
55 granulepos= (granulepos<<8)|(page[6]&0xff);
56 return(granulepos);
59 int spx_ogg_page_serialno(spx_ogg_page *og){
60 return(og->header[14] |
61 (og->header[15]<<8) |
62 (og->header[16]<<16) |
63 (og->header[17]<<24));
66 long spx_ogg_page_pageno(spx_ogg_page *og){
67 return(og->header[18] |
68 (og->header[19]<<8) |
69 (og->header[20]<<16) |
70 (og->header[21]<<24));
75 /* returns the number of packets that are completed on this page (if
76 the leading packet is begun on a previous page, but ends on this
77 page, it's counted */
79 /* NOTE:
80 If a page consists of a packet begun on a previous page, and a new
81 packet begun (but not completed) on this page, the return will be:
82 spx_ogg_page_packets(page) ==1,
83 spx_ogg_page_continued(page) !=0
85 If a page happens to be a single packet that was begun on a
86 previous page, and spans to the next page (in the case of a three or
87 more page packet), the return will be:
88 spx_ogg_page_packets(page) ==0,
89 spx_ogg_page_continued(page) !=0
92 int spx_ogg_page_packets(spx_ogg_page *og){
93 int i,n=og->header[26],count=0;
94 for(i=0;i<n;i++)
95 if(og->header[27+i]<255)count++;
96 return(count);
100 #if 0
101 /* helper to initialize lookup for direct-table CRC (illustrative; we
102 use the static init below) */
104 static spx_ogg_uint32_t _spx_ogg_crc_entry(unsigned long index){
105 int i;
106 unsigned long r;
108 r = index << 24;
109 for (i=0; i<8; i++)
110 if (r & 0x80000000UL)
111 r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator
112 polynomial, although we use an
113 unreflected alg and an init/final
114 of 0, not 0xffffffff */
115 else
116 r<<=1;
117 return (r & 0xffffffffUL);
119 #endif
121 static const spx_ogg_uint32_t crc_lookup[256]={
122 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9,
123 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005,
124 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61,
125 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd,
126 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9,
127 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75,
128 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011,
129 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd,
130 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039,
131 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5,
132 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81,
133 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d,
134 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49,
135 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95,
136 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1,
137 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d,
138 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae,
139 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072,
140 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16,
141 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca,
142 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde,
143 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02,
144 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066,
145 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba,
146 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e,
147 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692,
148 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6,
149 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a,
150 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e,
151 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2,
152 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686,
153 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a,
154 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637,
155 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb,
156 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f,
157 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53,
158 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47,
159 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b,
160 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff,
161 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623,
162 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7,
163 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b,
164 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f,
165 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3,
166 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7,
167 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b,
168 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f,
169 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3,
170 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640,
171 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c,
172 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8,
173 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24,
174 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30,
175 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec,
176 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088,
177 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654,
178 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0,
179 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c,
180 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18,
181 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4,
182 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0,
183 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c,
184 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668,
185 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4};
187 /* init the encode/decode logical stream state */
189 int spx_ogg_stream_init(spx_ogg_stream_state *os,int serialno){
190 if(os){
191 memset(os,0,sizeof(*os));
192 os->body_storage=16*1024;
193 os->body_data=_spx_ogg_malloc(os->body_storage*sizeof(*os->body_data));
195 os->lacing_storage=1024;
196 os->lacing_vals=_spx_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals));
197 os->granule_vals=_spx_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals));
199 os->serialno=serialno;
201 return(0);
203 return(-1);
206 /* _clear does not free os, only the non-flat storage within */
207 int spx_ogg_stream_clear(spx_ogg_stream_state *os){
208 if(os){
209 if(os->body_data)_spx_ogg_free(os->body_data);
210 if(os->lacing_vals)_spx_ogg_free(os->lacing_vals);
211 if(os->granule_vals)_spx_ogg_free(os->granule_vals);
213 memset(os,0,sizeof(*os));
215 return(0);
218 int spx_ogg_stream_destroy(spx_ogg_stream_state *os){
219 if(os){
220 spx_ogg_stream_clear(os);
221 _spx_ogg_free(os);
223 return(0);
226 /* Helpers for spx_ogg_stream_encode; this keeps the structure and
227 what's happening fairly clear */
229 static void _os_body_expand(spx_ogg_stream_state *os,int needed){
230 if(os->body_storage<=os->body_fill+needed){
231 os->body_storage+=(needed+1024);
232 os->body_data=_spx_ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data));
236 static void _os_lacing_expand(spx_ogg_stream_state *os,int needed){
237 if(os->lacing_storage<=os->lacing_fill+needed){
238 os->lacing_storage+=(needed+32);
239 os->lacing_vals=_spx_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals));
240 os->granule_vals=_spx_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals));
244 /* checksum the page */
245 /* Direct table CRC; note that this will be faster in the future if we
246 perform the checksum silmultaneously with other copies */
248 void spx_ogg_page_checksum_set(spx_ogg_page *og){
249 if(og){
250 spx_ogg_uint32_t crc_reg=0;
251 int i;
253 /* safety; needed for API behavior, but not framing code */
254 og->header[22]=0;
255 og->header[23]=0;
256 og->header[24]=0;
257 og->header[25]=0;
259 for(i=0;i<og->header_len;i++)
260 crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]];
261 for(i=0;i<og->body_len;i++)
262 crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]];
264 og->header[22]=(unsigned char)(crc_reg&0xff);
265 og->header[23]=(unsigned char)((crc_reg>>8)&0xff);
266 og->header[24]=(unsigned char)((crc_reg>>16)&0xff);
267 og->header[25]=(unsigned char)((crc_reg>>24)&0xff);
271 /* submit data to the internal buffer of the framing engine */
272 int spx_ogg_stream_packetin(spx_ogg_stream_state *os,spx_ogg_packet *op){
273 int lacing_vals=op->bytes/255+1,i;
275 if(os->body_returned){
276 /* advance packet data according to the body_returned pointer. We
277 had to keep it around to return a pointer into the buffer last
278 call */
280 os->body_fill-=os->body_returned;
281 if(os->body_fill)
282 memmove(os->body_data,os->body_data+os->body_returned,
283 os->body_fill);
284 os->body_returned=0;
287 /* make sure we have the buffer storage */
288 _os_body_expand(os,op->bytes);
289 _os_lacing_expand(os,lacing_vals);
291 /* Copy in the submitted packet. Yes, the copy is a waste; this is
292 the liability of overly clean abstraction for the time being. It
293 will actually be fairly easy to eliminate the extra copy in the
294 future */
296 memcpy(os->body_data+os->body_fill,op->packet,op->bytes);
297 os->body_fill+=op->bytes;
299 /* Store lacing vals for this packet */
300 for(i=0;i<lacing_vals-1;i++){
301 os->lacing_vals[os->lacing_fill+i]=255;
302 os->granule_vals[os->lacing_fill+i]=os->granulepos;
304 os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255;
305 os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos;
307 /* flag the first segment as the beginning of the packet */
308 os->lacing_vals[os->lacing_fill]|= 0x100;
310 os->lacing_fill+=lacing_vals;
312 /* for the sake of completeness */
313 os->packetno++;
315 if(op->e_o_s)os->e_o_s=1;
317 return(0);
320 /* This will flush remaining packets into a page (returning nonzero),
321 even if there is not enough data to trigger a flush normally
322 (undersized page). If there are no packets or partial packets to
323 flush, spx_ogg_stream_flush returns 0. Note that spx_ogg_stream_flush will
324 try to flush a normal sized page like spx_ogg_stream_pageout; a call to
325 spx_ogg_stream_flush does not guarantee that all packets have flushed.
326 Only a return value of 0 from spx_ogg_stream_flush indicates all packet
327 data is flushed into pages.
329 since spx_ogg_stream_flush will flush the last page in a stream even if
330 it's undersized, you almost certainly want to use spx_ogg_stream_pageout
331 (and *not* spx_ogg_stream_flush) unless you specifically need to flush
332 an page regardless of size in the middle of a stream. */
334 int spx_ogg_stream_flush(spx_ogg_stream_state *os,spx_ogg_page *og){
335 int i;
336 int vals=0;
337 int maxvals=(os->lacing_fill>255?255:os->lacing_fill);
338 int bytes=0;
339 long acc=0;
340 spx_ogg_int64_t granule_pos=-1;
342 if(maxvals==0)return(0);
344 /* construct a page */
345 /* decide how many segments to include */
347 /* If this is the initial header case, the first page must only include
348 the initial header packet */
349 if(os->b_o_s==0){ /* 'initial header page' case */
350 granule_pos=0;
351 for(vals=0;vals<maxvals;vals++){
352 if((os->lacing_vals[vals]&0x0ff)<255){
353 vals++;
354 break;
357 }else{
358 for(vals=0;vals<maxvals;vals++){
359 if(acc>4096)break;
360 acc+=os->lacing_vals[vals]&0x0ff;
361 if((os->lacing_vals[vals]&0xff)<255)
362 granule_pos=os->granule_vals[vals];
366 /* construct the header in temp storage */
367 memcpy(os->header,"OggS",4);
369 /* stream structure version */
370 os->header[4]=0x00;
372 /* continued packet flag? */
373 os->header[5]=0x00;
374 if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01;
375 /* first page flag? */
376 if(os->b_o_s==0)os->header[5]|=0x02;
377 /* last page flag? */
378 if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04;
379 os->b_o_s=1;
381 /* 64 bits of PCM position */
382 for(i=6;i<14;i++){
383 os->header[i]=(unsigned char)(granule_pos&0xff);
384 granule_pos>>=8;
387 /* 32 bits of stream serial number */
389 long serialno=os->serialno;
390 for(i=14;i<18;i++){
391 os->header[i]=(unsigned char)(serialno&0xff);
392 serialno>>=8;
396 /* 32 bits of page counter (we have both counter and page header
397 because this val can roll over) */
398 if(os->pageno==-1)os->pageno=0; /* because someone called
399 stream_reset; this would be a
400 strange thing to do in an
401 encode stream, but it has
402 plausible uses */
404 long pageno=os->pageno++;
405 for(i=18;i<22;i++){
406 os->header[i]=(unsigned char)(pageno&0xff);
407 pageno>>=8;
411 /* zero for computation; filled in later */
412 os->header[22]=0;
413 os->header[23]=0;
414 os->header[24]=0;
415 os->header[25]=0;
417 /* segment table */
418 os->header[26]=(unsigned char)(vals&0xff);
419 for(i=0;i<vals;i++)
420 bytes+=os->header[i+27]=(unsigned char)(os->lacing_vals[i]&0xff);
422 /* set pointers in the spx_ogg_page struct */
423 og->header=os->header;
424 og->header_len=os->header_fill=vals+27;
425 og->body=os->body_data+os->body_returned;
426 og->body_len=bytes;
428 /* advance the lacing data and set the body_returned pointer */
430 os->lacing_fill-=vals;
431 memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals));
432 memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals));
433 os->body_returned+=bytes;
435 /* calculate the checksum */
437 spx_ogg_page_checksum_set(og);
439 /* done */
440 return(1);
444 /* This constructs pages from buffered packet segments. The pointers
445 returned are to static buffers; do not free. The returned buffers are
446 good only until the next call (using the same spx_ogg_stream_state) */
448 int spx_ogg_stream_pageout(spx_ogg_stream_state *os, spx_ogg_page *og){
450 if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */
451 os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */
452 os->lacing_fill>=255 || /* 'segment table full' case */
453 (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */
455 return(spx_ogg_stream_flush(os,og));
458 /* not enough data to construct a page and not end of stream */
459 return(0);
462 int spx_ogg_stream_eos(spx_ogg_stream_state *os){
463 return os->e_o_s;
466 /* DECODING PRIMITIVES: packet streaming layer **********************/
468 /* This has two layers to place more of the multi-serialno and paging
469 control in the application's hands. First, we expose a data buffer
470 using spx_ogg_sync_buffer(). The app either copies into the
471 buffer, or passes it directly to read(), etc. We then call
472 spx_ogg_sync_wrote() to tell how many bytes we just added.
474 Pages are returned (pointers into the buffer in spx_ogg_sync_state)
475 by spx_ogg_sync_pageout(). The page is then submitted to
476 spx_ogg_stream_pagein() along with the appropriate
477 spx_ogg_stream_state* (ie, matching serialno). We then get raw
478 packets out calling spx_ogg_stream_packetout() with a
479 spx_ogg_stream_state. */
481 /* initialize the struct to a known state */
482 int spx_ogg_sync_init(spx_ogg_sync_state *oy){
483 if(oy){
484 memset(oy,0,sizeof(*oy));
486 return(0);
489 /* clear non-flat storage within */
490 int spx_ogg_sync_clear(spx_ogg_sync_state *oy){
491 if(oy){
492 if(oy->data)_spx_ogg_free(oy->data);
493 spx_ogg_sync_init(oy);
495 return(0);
498 int spx_ogg_sync_destroy(spx_ogg_sync_state *oy){
499 if(oy){
500 spx_ogg_sync_clear(oy);
501 _spx_ogg_free(oy);
503 return(0);
505 void spx_ogg_alloc_buffer(spx_ogg_sync_state *oy, long size){
506 long newsize=size+oy->fill+size; /* an extra page to be nice */
507 if(oy->data){
508 oy->data=_spx_ogg_realloc(oy->data,newsize);
509 }else
510 oy->data=_spx_ogg_malloc(newsize);
511 oy->storage=newsize;
512 return;
515 char *spx_ogg_sync_buffer(spx_ogg_sync_state *oy, long size){
517 /* first, clear out any space that has been previously returned */
518 if(oy->returned){
519 oy->fill-=oy->returned;
520 if(oy->fill>0)
521 memmove(oy->data,oy->data+oy->returned,oy->fill);
522 oy->returned=0;
525 if(size>oy->storage-oy->fill){
526 /* We need to extend the internal buffer */
527 long newsize=size+oy->fill+4096; /* an extra page to be nice */
528 if(oy->data){
529 oy->data=_spx_ogg_realloc(oy->data,newsize);
530 }else
531 oy->data=_spx_ogg_malloc(newsize);
532 oy->storage=newsize;
535 /* expose a segment at least as large as requested at the fill mark */
536 return((char *)oy->data+oy->fill);
539 int spx_ogg_sync_wrote(spx_ogg_sync_state *oy, long bytes){
540 if(oy->fill+bytes>oy->storage)return(-1);
541 oy->fill+=bytes;
542 return(0);
545 /* sync the stream. This is meant to be useful for finding page
546 boundaries.
548 return values for this:
549 -n) skipped n bytes
550 0) page not ready; more data (no bytes skipped)
551 n) page synced at current location; page length n bytes
555 long spx_ogg_sync_pageseek(spx_ogg_sync_state *oy,spx_ogg_page *og){
556 unsigned char *page=oy->data+oy->returned;
557 unsigned char *next;
558 long bytes=oy->fill-oy->returned;
560 if(oy->headerbytes==0){
561 int headerbytes,i;
562 if(bytes<27)return(0); /* not enough for a header */
564 /* verify capture pattern */
565 if(memcmp(page,"OggS",4))goto sync_fail;
567 headerbytes=page[26]+27;
568 if(bytes<headerbytes)return(0); /* not enough for header + seg table */
570 /* count up body length in the segment table */
572 for(i=0;i<page[26];i++)
573 oy->bodybytes+=page[27+i];
574 oy->headerbytes=headerbytes;
577 if(oy->bodybytes+oy->headerbytes>bytes)return(0);
579 /* The whole test page is buffered. Verify the checksum */
581 /* Grab the checksum bytes, set the header field to zero */
582 char chksum[4];
583 spx_ogg_page log;
585 memcpy(chksum,page+22,4);
586 memset(page+22,0,4);
588 /* set up a temp page struct and recompute the checksum */
589 log.header=page;
590 log.header_len=oy->headerbytes;
591 log.body=page+oy->headerbytes;
592 log.body_len=oy->bodybytes;
593 spx_ogg_page_checksum_set(&log);
595 /* Compare */
596 if(memcmp(chksum,page+22,4)){
597 /* D'oh. Mismatch! Corrupt page (or miscapture and not a page
598 at all) */
599 /* replace the computed checksum with the one actually read in */
600 memcpy(page+22,chksum,4);
602 /* Bad checksum. Lose sync */
603 goto sync_fail;
607 /* yes, have a whole page all ready to go */
609 unsigned char *page=oy->data+oy->returned;
610 long bytes;
612 if(og){
613 og->header=page;
614 og->header_len=oy->headerbytes;
615 og->body=page+oy->headerbytes;
616 og->body_len=oy->bodybytes;
619 oy->unsynced=0;
620 oy->returned+=(bytes=oy->headerbytes+oy->bodybytes);
621 oy->headerbytes=0;
622 oy->bodybytes=0;
623 return(bytes);
626 sync_fail:
628 oy->headerbytes=0;
629 oy->bodybytes=0;
631 /* search for possible capture */
632 next=memchr(page+1,'O',bytes-1);
633 if(!next)
634 next=oy->data+oy->fill;
636 oy->returned=next-oy->data;
637 return(-(next-page));
640 /* sync the stream and get a page. Keep trying until we find a page.
641 Supress 'sync errors' after reporting the first.
643 return values:
644 -1) recapture (hole in data)
645 0) need more data
646 1) page returned
648 Returns pointers into buffered data; invalidated by next call to
649 _stream, _clear, _init, or _buffer */
651 int spx_ogg_sync_pageout(spx_ogg_sync_state *oy, spx_ogg_page *og){
653 /* all we need to do is verify a page at the head of the stream
654 buffer. If it doesn't verify, we look for the next potential
655 frame */
657 for(;;){
658 long ret=spx_ogg_sync_pageseek(oy,og);
659 if(ret>0){
660 /* have a page */
661 return(1);
663 if(ret==0){
664 /* need more data */
665 return(0);
668 /* head did not start a synced page... skipped some bytes */
669 if(!oy->unsynced){
670 oy->unsynced=1;
671 return(-1);
674 /* loop. keep looking */
679 /* add the incoming page to the stream state; we decompose the page
680 into packet segments here as well. */
682 int spx_ogg_stream_pagein(spx_ogg_stream_state *os, spx_ogg_page *og){
683 unsigned char *header=og->header;
684 unsigned char *body=og->body;
685 long bodysize=og->body_len;
686 int segptr=0;
688 int version=spx_ogg_page_version(og);
689 int continued=spx_ogg_page_continued(og);
690 int bos=spx_ogg_page_bos(og);
691 int eos=spx_ogg_page_eos(og);
692 spx_ogg_int64_t granulepos=spx_ogg_page_granulepos(og);
693 int serialno=spx_ogg_page_serialno(og);
694 long pageno=spx_ogg_page_pageno(og);
695 int segments=header[26];
697 /* clean up 'returned data' */
699 long lr=os->lacing_returned;
700 long br=os->body_returned;
702 /* body data */
703 if(br){
704 os->body_fill-=br;
705 if(os->body_fill)
706 memmove(os->body_data,os->body_data+br,os->body_fill);
707 os->body_returned=0;
710 if(lr){
711 /* segment table */
712 if(os->lacing_fill-lr){
713 memmove(os->lacing_vals,os->lacing_vals+lr,
714 (os->lacing_fill-lr)*sizeof(*os->lacing_vals));
715 memmove(os->granule_vals,os->granule_vals+lr,
716 (os->lacing_fill-lr)*sizeof(*os->granule_vals));
718 os->lacing_fill-=lr;
719 os->lacing_packet-=lr;
720 os->lacing_returned=0;
724 /* check the serial number */
725 if(serialno!=os->serialno)return(-1);
726 if(version>0)return(-1);
728 _os_lacing_expand(os,segments+1);
730 /* are we in sequence? */
731 if(pageno!=os->pageno){
732 int i;
734 /* unroll previous partial packet (if any) */
735 for(i=os->lacing_packet;i<os->lacing_fill;i++)
736 os->body_fill-=os->lacing_vals[i]&0xff;
737 os->lacing_fill=os->lacing_packet;
739 /* make a note of dropped data in segment table */
740 if(os->pageno!=-1){
741 os->lacing_vals[os->lacing_fill++]=0x400;
742 os->lacing_packet++;
746 /* are we a 'continued packet' page? If so, we may need to skip
747 some segments */
748 if(continued){
749 if(os->lacing_fill<1 ||
750 os->lacing_vals[os->lacing_fill-1]==0x400){
751 bos=0;
752 for(;segptr<segments;segptr++){
753 int val=header[27+segptr];
754 body+=val;
755 bodysize-=val;
756 if(val<255){
757 segptr++;
758 break;
764 if(bodysize){
765 _os_body_expand(os,bodysize);
766 memcpy(os->body_data+os->body_fill,body,bodysize);
767 os->body_fill+=bodysize;
771 int saved=-1;
772 while(segptr<segments){
773 int val=header[27+segptr];
774 os->lacing_vals[os->lacing_fill]=val;
775 os->granule_vals[os->lacing_fill]=-1;
777 if(bos){
778 os->lacing_vals[os->lacing_fill]|=0x100;
779 bos=0;
782 if(val<255)saved=os->lacing_fill;
784 os->lacing_fill++;
785 segptr++;
787 if(val<255)os->lacing_packet=os->lacing_fill;
790 /* set the granulepos on the last granuleval of the last full packet */
791 if(saved!=-1){
792 os->granule_vals[saved]=granulepos;
797 if(eos){
798 os->e_o_s=1;
799 if(os->lacing_fill>0)
800 os->lacing_vals[os->lacing_fill-1]|=0x200;
803 os->pageno=pageno+1;
805 return(0);
808 /* clear things to an initial state. Good to call, eg, before seeking */
809 int spx_ogg_sync_reset(spx_ogg_sync_state *oy){
810 oy->fill=0;
811 oy->returned=0;
812 oy->unsynced=0;
813 oy->headerbytes=0;
814 oy->bodybytes=0;
815 return(0);
818 int spx_ogg_stream_reset(spx_ogg_stream_state *os){
819 os->body_fill=0;
820 os->body_returned=0;
822 os->lacing_fill=0;
823 os->lacing_packet=0;
824 os->lacing_returned=0;
826 os->header_fill=0;
828 os->e_o_s=0;
829 os->b_o_s=0;
830 os->pageno=-1;
831 os->packetno=0;
832 os->granulepos=0;
834 return(0);
837 int spx_ogg_stream_reset_serialno(spx_ogg_stream_state *os,int serialno){
838 spx_ogg_stream_reset(os);
839 os->serialno=serialno;
840 return(0);
843 static int _packetout(spx_ogg_stream_state *os,spx_ogg_packet *op,int adv){
845 /* The last part of decode. We have the stream broken into packet
846 segments. Now we need to group them into packets (or return the
847 out of sync markers) */
849 int ptr=os->lacing_returned;
851 if(os->lacing_packet<=ptr)return(0);
853 if(os->lacing_vals[ptr]&0x400){
854 /* we need to tell the codec there's a gap; it might need to
855 handle previous packet dependencies. */
856 os->lacing_returned++;
857 os->packetno++;
858 return(-1);
861 if(!op && !adv)return(1); /* just using peek as an inexpensive way
862 to ask if there's a whole packet
863 waiting */
865 /* Gather the whole packet. We'll have no holes or a partial packet */
867 int size=os->lacing_vals[ptr]&0xff;
868 int bytes=size;
869 int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */
870 int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */
872 while(size==255){
873 int val=os->lacing_vals[++ptr];
874 size=val&0xff;
875 if(val&0x200)eos=0x200;
876 bytes+=size;
879 if(op){
880 op->e_o_s=eos;
881 op->b_o_s=bos;
882 op->packet=os->body_data+os->body_returned;
883 op->packetno=os->packetno;
884 op->granulepos=os->granule_vals[ptr];
885 op->bytes=bytes;
888 if(adv){
889 os->body_returned+=bytes;
890 os->lacing_returned=ptr+1;
891 os->packetno++;
894 return(1);
897 int spx_ogg_stream_packetout(spx_ogg_stream_state *os,spx_ogg_packet *op){
898 return _packetout(os,op,1);
901 int spx_ogg_stream_packetpeek(spx_ogg_stream_state *os,spx_ogg_packet *op){
902 return _packetout(os,op,0);
905 void spx_ogg_packet_clear(spx_ogg_packet *op) {
906 _spx_ogg_free(op->packet);
907 memset(op, 0, sizeof(*op));