summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libspeex/oggframing.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libspeex/oggframing.c')
-rw-r--r--lib/rbcodec/codecs/libspeex/oggframing.c909
1 files changed, 909 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libspeex/oggframing.c b/lib/rbcodec/codecs/libspeex/oggframing.c
new file mode 100644
index 0000000000..42d29502a8
--- /dev/null
+++ b/lib/rbcodec/codecs/libspeex/oggframing.c
@@ -0,0 +1,909 @@
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 ********************************************************************
12
13 function: code raw [Vorbis] packets into framed OggSquish stream and
14 decode Ogg streams back into raw packets
15 last mod: $Id$
16
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.
20
21 ********************************************************************/
22
23//#include "config-tremor.h"
24#include <string.h>
25#include "speex/ogg.h"
26#include "arch.h"
27
28/* A complete description of Ogg framing exists in docs/framing.html */
29
30int spx_ogg_page_version(spx_ogg_page *og){
31 return((int)(og->header[4]));
32}
33
34int spx_ogg_page_continued(spx_ogg_page *og){
35 return((int)(og->header[5]&0x01));
36}
37
38int spx_ogg_page_bos(spx_ogg_page *og){
39 return((int)(og->header[5]&0x02));
40}
41
42int spx_ogg_page_eos(spx_ogg_page *og){
43 return((int)(og->header[5]&0x04));
44}
45
46spx_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);
57}
58
59int 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));
64}
65
66long 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));
71}
72
73
74
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 */
78
79/* NOTE:
80If a page consists of a packet begun on a previous page, and a new
81packet 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
84
85If a page happens to be a single packet that was begun on a
86previous page, and spans to the next page (in the case of a three or
87more page packet), the return will be:
88 spx_ogg_page_packets(page) ==0,
89 spx_ogg_page_continued(page) !=0
90*/
91
92int 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);
97}
98
99
100#if 0
101/* helper to initialize lookup for direct-table CRC (illustrative; we
102 use the static init below) */
103
104static spx_ogg_uint32_t _spx_ogg_crc_entry(unsigned long index){
105 int i;
106 unsigned long r;
107
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);
118}
119#endif
120
121static 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};
186
187/* init the encode/decode logical stream state */
188
189int 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));
194
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));
198
199 os->serialno=serialno;
200
201 return(0);
202 }
203 return(-1);
204}
205
206/* _clear does not free os, only the non-flat storage within */
207int 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);
212
213 memset(os,0,sizeof(*os));
214 }
215 return(0);
216}
217
218int spx_ogg_stream_destroy(spx_ogg_stream_state *os){
219 if(os){
220 spx_ogg_stream_clear(os);
221 _spx_ogg_free(os);
222 }
223 return(0);
224}
225
226/* Helpers for spx_ogg_stream_encode; this keeps the structure and
227 what's happening fairly clear */
228
229static 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));
233 }
234}
235
236static 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));
241 }
242}
243
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 */
247
248void spx_ogg_page_checksum_set(spx_ogg_page *og){
249 if(og){
250 spx_ogg_uint32_t crc_reg=0;
251 int i;
252
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;
258
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]];
263
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);
268 }
269}
270
271/* submit data to the internal buffer of the framing engine */
272int spx_ogg_stream_packetin(spx_ogg_stream_state *os,spx_ogg_packet *op){
273 int lacing_vals=op->bytes/255+1,i;
274
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 */
279
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;
285 }
286
287 /* make sure we have the buffer storage */
288 _os_body_expand(os,op->bytes);
289 _os_lacing_expand(os,lacing_vals);
290
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 */
295
296 memcpy(os->body_data+os->body_fill,op->packet,op->bytes);
297 os->body_fill+=op->bytes;
298
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;
303 }
304 os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255;
305 os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos;
306
307 /* flag the first segment as the beginning of the packet */
308 os->lacing_vals[os->lacing_fill]|= 0x100;
309
310 os->lacing_fill+=lacing_vals;
311
312 /* for the sake of completeness */
313 os->packetno++;
314
315 if(op->e_o_s)os->e_o_s=1;
316
317 return(0);
318}
319
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.
328
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. */
333
334int 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;
341
342 if(maxvals==0)return(0);
343
344 /* construct a page */
345 /* decide how many segments to include */
346
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;
355 }
356 }
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];
363 }
364 }
365
366 /* construct the header in temp storage */
367 memcpy(os->header,"OggS",4);
368
369 /* stream structure version */
370 os->header[4]=0x00;
371
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;
380
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;
385 }
386
387 /* 32 bits of stream serial number */
388 {
389 long serialno=os->serialno;
390 for(i=14;i<18;i++){
391 os->header[i]=(unsigned char)(serialno&0xff);
392 serialno>>=8;
393 }
394 }
395
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 */
403 {
404 long pageno=os->pageno++;
405 for(i=18;i<22;i++){
406 os->header[i]=(unsigned char)(pageno&0xff);
407 pageno>>=8;
408 }
409 }
410
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;
416
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);
421
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;
427
428 /* advance the lacing data and set the body_returned pointer */
429
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;
434
435 /* calculate the checksum */
436
437 spx_ogg_page_checksum_set(og);
438
439 /* done */
440 return(1);
441}
442
443
444/* This constructs pages from buffered packet segments. The pointers
445returned are to static buffers; do not free. The returned buffers are
446good only until the next call (using the same spx_ogg_stream_state) */
447
448int spx_ogg_stream_pageout(spx_ogg_stream_state *os, spx_ogg_page *og){
449
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 */
454
455 return(spx_ogg_stream_flush(os,og));
456 }
457
458 /* not enough data to construct a page and not end of stream */
459 return(0);
460}
461
462int spx_ogg_stream_eos(spx_ogg_stream_state *os){
463 return os->e_o_s;
464}
465
466/* DECODING PRIMITIVES: packet streaming layer **********************/
467
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.
473
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. */
480
481/* initialize the struct to a known state */
482int spx_ogg_sync_init(spx_ogg_sync_state *oy){
483 if(oy){
484 memset(oy,0,sizeof(*oy));
485 }
486 return(0);
487}
488
489/* clear non-flat storage within */
490int 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);
494 }
495 return(0);
496}
497
498int spx_ogg_sync_destroy(spx_ogg_sync_state *oy){
499 if(oy){
500 spx_ogg_sync_clear(oy);
501 _spx_ogg_free(oy);
502 }
503 return(0);
504}
505void 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;
513}
514
515char *spx_ogg_sync_buffer(spx_ogg_sync_state *oy, long size){
516
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;
523 }
524
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;
533 }
534
535 /* expose a segment at least as large as requested at the fill mark */
536 return((char *)oy->data+oy->fill);
537}
538
539int 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);
543}
544
545/* sync the stream. This is meant to be useful for finding page
546 boundaries.
547
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
552
553*/
554
555long 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;
559
560 if(oy->headerbytes==0){
561 int headerbytes,i;
562 if(bytes<27)return(0); /* not enough for a header */
563
564 /* verify capture pattern */
565 if(memcmp(page,"OggS",4))goto sync_fail;
566
567 headerbytes=page[26]+27;
568 if(bytes<headerbytes)return(0); /* not enough for header + seg table */
569
570 /* count up body length in the segment table */
571
572 for(i=0;i<page[26];i++)
573 oy->bodybytes+=page[27+i];
574 oy->headerbytes=headerbytes;
575 }
576
577 if(oy->bodybytes+oy->headerbytes>bytes)return(0);
578
579 /* The whole test page is buffered. Verify the checksum */
580 {
581 /* Grab the checksum bytes, set the header field to zero */
582 char chksum[4];
583 spx_ogg_page log;
584
585 memcpy(chksum,page+22,4);
586 memset(page+22,0,4);
587
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);
594
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);
601
602 /* Bad checksum. Lose sync */
603 goto sync_fail;
604 }
605 }
606
607 /* yes, have a whole page all ready to go */
608 {
609 unsigned char *page=oy->data+oy->returned;
610 long bytes;
611
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;
617 }
618
619 oy->unsynced=0;
620 oy->returned+=(bytes=oy->headerbytes+oy->bodybytes);
621 oy->headerbytes=0;
622 oy->bodybytes=0;
623 return(bytes);
624 }
625
626 sync_fail:
627
628 oy->headerbytes=0;
629 oy->bodybytes=0;
630
631 /* search for possible capture */
632 next=memchr(page+1,'O',bytes-1);
633 if(!next)
634 next=oy->data+oy->fill;
635
636 oy->returned=next-oy->data;
637 return(-(next-page));
638}
639
640/* sync the stream and get a page. Keep trying until we find a page.
641 Supress 'sync errors' after reporting the first.
642
643 return values:
644 -1) recapture (hole in data)
645 0) need more data
646 1) page returned
647
648 Returns pointers into buffered data; invalidated by next call to
649 _stream, _clear, _init, or _buffer */
650
651int spx_ogg_sync_pageout(spx_ogg_sync_state *oy, spx_ogg_page *og){
652
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 */
656
657 for(;;){
658 long ret=spx_ogg_sync_pageseek(oy,og);
659 if(ret>0){
660 /* have a page */
661 return(1);
662 }
663 if(ret==0){
664 /* need more data */
665 return(0);
666 }
667
668 /* head did not start a synced page... skipped some bytes */
669 if(!oy->unsynced){
670 oy->unsynced=1;
671 return(-1);
672 }
673
674 /* loop. keep looking */
675
676 }
677}
678
679/* add the incoming page to the stream state; we decompose the page
680 into packet segments here as well. */
681
682int 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;
687
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];
696
697 /* clean up 'returned data' */
698 {
699 long lr=os->lacing_returned;
700 long br=os->body_returned;
701
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;
708 }
709
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));
717 }
718 os->lacing_fill-=lr;
719 os->lacing_packet-=lr;
720 os->lacing_returned=0;
721 }
722 }
723
724 /* check the serial number */
725 if(serialno!=os->serialno)return(-1);
726 if(version>0)return(-1);
727
728 _os_lacing_expand(os,segments+1);
729
730 /* are we in sequence? */
731 if(pageno!=os->pageno){
732 int i;
733
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;
738
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++;
743 }
744 }
745
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;
759 }
760 }
761 }
762 }
763
764 if(bodysize){
765 _os_body_expand(os,bodysize);
766 memcpy(os->body_data+os->body_fill,body,bodysize);
767 os->body_fill+=bodysize;
768 }
769
770 {
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;
776
777 if(bos){
778 os->lacing_vals[os->lacing_fill]|=0x100;
779 bos=0;
780 }
781
782 if(val<255)saved=os->lacing_fill;
783
784 os->lacing_fill++;
785 segptr++;
786
787 if(val<255)os->lacing_packet=os->lacing_fill;
788 }
789
790 /* set the granulepos on the last granuleval of the last full packet */
791 if(saved!=-1){
792 os->granule_vals[saved]=granulepos;
793 }
794
795 }
796
797 if(eos){
798 os->e_o_s=1;
799 if(os->lacing_fill>0)
800 os->lacing_vals[os->lacing_fill-1]|=0x200;
801 }
802
803 os->pageno=pageno+1;
804
805 return(0);
806}
807
808/* clear things to an initial state. Good to call, eg, before seeking */
809int 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);
816}
817
818int spx_ogg_stream_reset(spx_ogg_stream_state *os){
819 os->body_fill=0;
820 os->body_returned=0;
821
822 os->lacing_fill=0;
823 os->lacing_packet=0;
824 os->lacing_returned=0;
825
826 os->header_fill=0;
827
828 os->e_o_s=0;
829 os->b_o_s=0;
830 os->pageno=-1;
831 os->packetno=0;
832 os->granulepos=0;
833
834 return(0);
835}
836
837int 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);
841}
842
843static int _packetout(spx_ogg_stream_state *os,spx_ogg_packet *op,int adv){
844
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) */
848
849 int ptr=os->lacing_returned;
850
851 if(os->lacing_packet<=ptr)return(0);
852
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);
859 }
860
861 if(!op && !adv)return(1); /* just using peek as an inexpensive way
862 to ask if there's a whole packet
863 waiting */
864
865 /* Gather the whole packet. We'll have no holes or a partial packet */
866 {
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? */
871
872 while(size==255){
873 int val=os->lacing_vals[++ptr];
874 size=val&0xff;
875 if(val&0x200)eos=0x200;
876 bytes+=size;
877 }
878
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;
886 }
887
888 if(adv){
889 os->body_returned+=bytes;
890 os->lacing_returned=ptr+1;
891 os->packetno++;
892 }
893 }
894 return(1);
895}
896
897int spx_ogg_stream_packetout(spx_ogg_stream_state *os,spx_ogg_packet *op){
898 return _packetout(os,op,1);
899}
900
901int spx_ogg_stream_packetpeek(spx_ogg_stream_state *os,spx_ogg_packet *op){
902 return _packetout(os,op,0);
903}
904
905void spx_ogg_packet_clear(spx_ogg_packet *op) {
906 _spx_ogg_free(op->packet);
907 memset(op, 0, sizeof(*op));
908}
909