1 /********************************************************************
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. *
8 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
9 * by the Xiph.Org Foundation http://www.xiph.org/ *
11 ********************************************************************
13 function: code raw [Vorbis] packets into framed OggSquish stream and
14 decode Ogg streams back into raw packets
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
21 ********************************************************************/
23 //#include "config-tremor.h"
25 #include "speex/ogg.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);
59 int spx_ogg_page_serialno(spx_ogg_page
*og
){
60 return(og
->header
[14] |
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] |
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
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;
95 if(og
->header
[27+i
]<255)count
++;
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
){
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 */
117 return (r
& 0xffffffffUL
);
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
){
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
;
206 /* _clear does not free os, only the non-flat storage within */
207 int spx_ogg_stream_clear(spx_ogg_stream_state
*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
));
218 int spx_ogg_stream_destroy(spx_ogg_stream_state
*os
){
220 spx_ogg_stream_clear(os
);
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
){
250 spx_ogg_uint32_t crc_reg
=0;
253 /* safety; needed for API behavior, but not framing code */
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
280 os
->body_fill
-=os
->body_returned
;
282 memmove(os
->body_data
,os
->body_data
+os
->body_returned
,
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
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 */
315 if(op
->e_o_s
)os
->e_o_s
=1;
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
){
337 int maxvals
=(os
->lacing_fill
>255?255:os
->lacing_fill
);
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 */
351 for(vals
=0;vals
<maxvals
;vals
++){
352 if((os
->lacing_vals
[vals
]&0x0ff)<255){
358 for(vals
=0;vals
<maxvals
;vals
++){
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 */
372 /* continued packet flag? */
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;
381 /* 64 bits of PCM position */
383 os
->header
[i
]=(unsigned char)(granule_pos
&0xff);
387 /* 32 bits of stream serial number */
389 long serialno
=os
->serialno
;
391 os
->header
[i
]=(unsigned char)(serialno
&0xff);
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
404 long pageno
=os
->pageno
++;
406 os
->header
[i
]=(unsigned char)(pageno
&0xff);
411 /* zero for computation; filled in later */
418 os
->header
[26]=(unsigned char)(vals
&0xff);
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
;
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
);
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 */
462 int spx_ogg_stream_eos(spx_ogg_stream_state
*os
){
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
){
484 memset(oy
,0,sizeof(*oy
));
489 /* clear non-flat storage within */
490 int spx_ogg_sync_clear(spx_ogg_sync_state
*oy
){
492 if(oy
->data
)_spx_ogg_free(oy
->data
);
493 spx_ogg_sync_init(oy
);
498 int spx_ogg_sync_destroy(spx_ogg_sync_state
*oy
){
500 spx_ogg_sync_clear(oy
);
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 */
508 oy
->data
=_spx_ogg_realloc(oy
->data
,newsize
);
510 oy
->data
=_spx_ogg_malloc(newsize
);
515 char *spx_ogg_sync_buffer(spx_ogg_sync_state
*oy
, long size
){
517 /* first, clear out any space that has been previously returned */
519 oy
->fill
-=oy
->returned
;
521 memmove(oy
->data
,oy
->data
+oy
->returned
,oy
->fill
);
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 */
529 oy
->data
=_spx_ogg_realloc(oy
->data
,newsize
);
531 oy
->data
=_spx_ogg_malloc(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);
545 /* sync the stream. This is meant to be useful for finding page
548 return values for this:
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
;
558 long bytes
=oy
->fill
-oy
->returned
;
560 if(oy
->headerbytes
==0){
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 */
585 memcpy(chksum
,page
+22,4);
588 /* set up a temp page struct and recompute the checksum */
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
);
596 if(memcmp(chksum
,page
+22,4)){
597 /* D'oh. Mismatch! Corrupt page (or miscapture and not a page
599 /* replace the computed checksum with the one actually read in */
600 memcpy(page
+22,chksum
,4);
602 /* Bad checksum. Lose sync */
607 /* yes, have a whole page all ready to go */
609 unsigned char *page
=oy
->data
+oy
->returned
;
614 og
->header_len
=oy
->headerbytes
;
615 og
->body
=page
+oy
->headerbytes
;
616 og
->body_len
=oy
->bodybytes
;
620 oy
->returned
+=(bytes
=oy
->headerbytes
+oy
->bodybytes
);
631 /* search for possible capture */
632 next
=memchr(page
+1,'O',bytes
-1);
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.
644 -1) recapture (hole in data)
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
658 long ret
=spx_ogg_sync_pageseek(oy
,og
);
668 /* head did not start a synced page... skipped some bytes */
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
;
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
;
706 memmove(os
->body_data
,os
->body_data
+br
,os
->body_fill
);
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
));
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
){
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 */
741 os
->lacing_vals
[os
->lacing_fill
++]=0x400;
746 /* are we a 'continued packet' page? If so, we may need to skip
749 if(os
->lacing_fill
<1 ||
750 os
->lacing_vals
[os
->lacing_fill
-1]==0x400){
752 for(;segptr
<segments
;segptr
++){
753 int val
=header
[27+segptr
];
765 _os_body_expand(os
,bodysize
);
766 memcpy(os
->body_data
+os
->body_fill
,body
,bodysize
);
767 os
->body_fill
+=bodysize
;
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;
778 os
->lacing_vals
[os
->lacing_fill
]|=0x100;
782 if(val
<255)saved
=os
->lacing_fill
;
787 if(val
<255)os
->lacing_packet
=os
->lacing_fill
;
790 /* set the granulepos on the last granuleval of the last full packet */
792 os
->granule_vals
[saved
]=granulepos
;
799 if(os
->lacing_fill
>0)
800 os
->lacing_vals
[os
->lacing_fill
-1]|=0x200;
808 /* clear things to an initial state. Good to call, eg, before seeking */
809 int spx_ogg_sync_reset(spx_ogg_sync_state
*oy
){
818 int spx_ogg_stream_reset(spx_ogg_stream_state
*os
){
824 os
->lacing_returned
=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
;
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
++;
861 if(!op
&& !adv
)return(1); /* just using peek as an inexpensive way
862 to ask if there's a whole packet
865 /* Gather the whole packet. We'll have no holes or a partial packet */
867 int size
=os
->lacing_vals
[ptr
]&0xff;
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? */
873 int val
=os
->lacing_vals
[++ptr
];
875 if(val
&0x200)eos
=0x200;
882 op
->packet
=os
->body_data
+os
->body_returned
;
883 op
->packetno
=os
->packetno
;
884 op
->granulepos
=os
->granule_vals
[ptr
];
889 os
->body_returned
+=bytes
;
890 os
->lacing_returned
=ptr
+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
));