diff options
author | Nils Wallménius <nils@rockbox.org> | 2010-12-06 14:36:52 +0000 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2010-12-06 14:36:52 +0000 |
commit | 67efbc13870ee87ce3df442f7c396c13481921ec (patch) | |
tree | eaf63d36c5bf2d41a6b3bb2addecb4d3c05f7eef /apps/codecs/libtremor/framing.c | |
parent | 1f64b7fb1fa5058e3b7871078055156eac0c511d (diff) | |
download | rockbox-67efbc13870ee87ce3df442f7c396c13481921ec.tar.gz rockbox-67efbc13870ee87ce3df442f7c396c13481921ec.zip |
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
Diffstat (limited to 'apps/codecs/libtremor/framing.c')
-rw-r--r-- | apps/codecs/libtremor/framing.c | 2672 |
1 files changed, 1851 insertions, 821 deletions
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 @@ | |||
1 | /******************************************************************** | 1 | /******************************************************************** |
2 | * * | 2 | * * |
3 | * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * | 3 | * THIS FILE IS PART OF THE Ogg CONTAINER SOURCE CODE. * |
4 | * * | ||
5 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * | 4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
6 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * | 5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
7 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * | 6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
8 | * * | 7 | * * |
9 | * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * | 8 | * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2010 * |
10 | * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * | 9 | * by the Xiph.Org Foundation http://www.xiph.org/ * |
11 | * * | 10 | * * |
12 | ******************************************************************** | 11 | ******************************************************************** |
13 | 12 | ||
14 | function: decode Ogg streams back into raw packets | 13 | function: code raw packets into framed OggSquish stream and |
14 | decode Ogg streams back into raw packets | ||
15 | last mod: $Id$ | ||
15 | 16 | ||
16 | note: The CRC code is directly derived from public domain code by | 17 | note: The CRC code is directly derived from public domain code by |
17 | Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html | 18 | Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html |
@@ -19,458 +20,102 @@ | |||
19 | 20 | ||
20 | ********************************************************************/ | 21 | ********************************************************************/ |
21 | 22 | ||
22 | #include "config-tremor.h" | 23 | #include <stdlib.h> |
23 | #include <string.h> | 24 | #include <string.h> |
24 | #include "ogg.h" | 25 | #include "ogg.h" |
25 | #include "misc.h" | ||
26 | |||
27 | 26 | ||
28 | /* A complete description of Ogg framing exists in docs/framing.html */ | 27 | /* A complete description of Ogg framing exists in docs/framing.html */ |
29 | 28 | ||
30 | /* basic, centralized Ogg memory management based on linked lists of | 29 | int ogg_page_version(const ogg_page *og){ |
31 | references to refcounted memory buffers. References and buffers | 30 | return((int)(og->header[4])); |
32 | are both recycled. Buffers are passed around and consumed in | ||
33 | reference form. */ | ||
34 | |||
35 | static ogg_buffer_state *ogg_buffer_create(void){ | ||
36 | ogg_buffer_state *bs=_ogg_calloc(1,sizeof(*bs)); | ||
37 | return bs; | ||
38 | } | ||
39 | |||
40 | /* destruction is 'lazy'; there may be memory references outstanding, | ||
41 | and yanking the buffer state out from underneath would be | ||
42 | antisocial. Dealloc what is currently unused and have | ||
43 | _release_one watch for the stragglers to come in. When they do, | ||
44 | finish destruction. */ | ||
45 | |||
46 | /* call the helper while holding lock */ | ||
47 | static void _ogg_buffer_destroy(ogg_buffer_state *bs){ | ||
48 | ogg_buffer *bt; | ||
49 | ogg_reference *rt; | ||
50 | |||
51 | if(bs->shutdown){ | ||
52 | |||
53 | bt=bs->unused_buffers; | ||
54 | rt=bs->unused_references; | ||
55 | |||
56 | while(bt){ | ||
57 | ogg_buffer *b=bt; | ||
58 | bt=b->ptr.next; | ||
59 | if(b->data)_ogg_free(b->data); | ||
60 | _ogg_free(b); | ||
61 | } | ||
62 | bs->unused_buffers=0; | ||
63 | while(rt){ | ||
64 | ogg_reference *r=rt; | ||
65 | rt=r->next; | ||
66 | _ogg_free(r); | ||
67 | } | ||
68 | bs->unused_references=0; | ||
69 | |||
70 | if(!bs->outstanding) | ||
71 | _ogg_free(bs); | ||
72 | |||
73 | } | ||
74 | } | ||
75 | |||
76 | static void ogg_buffer_destroy(ogg_buffer_state *bs){ | ||
77 | bs->shutdown=1; | ||
78 | _ogg_buffer_destroy(bs); | ||
79 | } | ||
80 | |||
81 | static ogg_buffer *_fetch_buffer(ogg_buffer_state *bs,long bytes){ | ||
82 | ogg_buffer *ob; | ||
83 | bs->outstanding++; | ||
84 | |||
85 | /* do we have an unused buffer sitting in the pool? */ | ||
86 | if(bs->unused_buffers){ | ||
87 | ob=bs->unused_buffers; | ||
88 | bs->unused_buffers=ob->ptr.next; | ||
89 | |||
90 | /* if the unused buffer is too small, grow it */ | ||
91 | if(ob->size<bytes){ | ||
92 | ob->data=_ogg_realloc(ob->data,bytes); | ||
93 | ob->size=bytes; | ||
94 | } | ||
95 | }else{ | ||
96 | /* allocate a new buffer */ | ||
97 | ob=_ogg_malloc(sizeof(*ob)); | ||
98 | ob->data=_ogg_malloc(bytes<16?16:bytes); | ||
99 | ob->size=bytes; | ||
100 | } | ||
101 | |||
102 | ob->refcount=1; | ||
103 | ob->ptr.owner=bs; | ||
104 | return ob; | ||
105 | } | ||
106 | |||
107 | STATICIRAM_NOT_MDCT ogg_reference *_fetch_ref(ogg_buffer_state *bs) | ||
108 | ICODE_ATTR_TREMOR_NOT_MDCT; | ||
109 | STATICIRAM_NOT_MDCT ogg_reference *_fetch_ref(ogg_buffer_state *bs){ | ||
110 | ogg_reference *or; | ||
111 | bs->outstanding++; | ||
112 | |||
113 | /* do we have an unused reference sitting in the pool? */ | ||
114 | if(bs->unused_references){ | ||
115 | or=bs->unused_references; | ||
116 | bs->unused_references=or->next; | ||
117 | }else{ | ||
118 | /* allocate a new reference */ | ||
119 | or=_ogg_malloc(sizeof(*or)); | ||
120 | } | ||
121 | |||
122 | or->begin=0; | ||
123 | or->length=0; | ||
124 | or->next=0; | ||
125 | return or; | ||
126 | } | ||
127 | |||
128 | /* fetch a reference pointing to a fresh, initially continguous buffer | ||
129 | of at least [bytes] length */ | ||
130 | static ogg_reference *ogg_buffer_alloc(ogg_buffer_state *bs,long bytes){ | ||
131 | ogg_buffer *ob=_fetch_buffer(bs,bytes); | ||
132 | ogg_reference *or=_fetch_ref(bs); | ||
133 | or->buffer=ob; | ||
134 | return or; | ||
135 | } | ||
136 | |||
137 | /* enlarge the data buffer in the current link */ | ||
138 | static void ogg_buffer_realloc(ogg_reference *or,long bytes){ | ||
139 | ogg_buffer *ob=or->buffer; | ||
140 | |||
141 | /* if the unused buffer is too small, grow it */ | ||
142 | if(ob->size<bytes){ | ||
143 | ob->data=_ogg_realloc(ob->data,bytes); | ||
144 | ob->size=bytes; | ||
145 | } | ||
146 | } | 31 | } |
147 | 32 | ||
148 | static void _ogg_buffer_mark_one(ogg_reference *or){ | 33 | int ogg_page_continued(const ogg_page *og){ |
149 | or->buffer->refcount++; | 34 | return((int)(og->header[5]&0x01)); |
150 | } | 35 | } |
151 | 36 | ||
152 | /* increase the refcount of the buffers to which the reference points */ | 37 | int ogg_page_bos(const ogg_page *og){ |
153 | static void ogg_buffer_mark(ogg_reference *or){ | 38 | return((int)(og->header[5]&0x02)); |
154 | while(or){ | ||
155 | _ogg_buffer_mark_one(or); | ||
156 | or=or->next; | ||
157 | } | ||
158 | } | 39 | } |
159 | 40 | ||
160 | /* duplicate a reference (pointing to the same actual buffer memory) | 41 | int ogg_page_eos(const ogg_page *og){ |
161 | and increment buffer refcount. If the desired segment begins out | 42 | return((int)(og->header[5]&0x04)); |
162 | of range, NULL is returned; if the desired segment is simply zero | ||
163 | length, a zero length ref is returned. Partial range overlap | ||
164 | returns the overlap of the ranges */ | ||
165 | static ogg_reference *ogg_buffer_sub(ogg_reference *or,long begin,long length){ | ||
166 | ogg_reference *ret=0,*head=0; | ||
167 | |||
168 | /* walk past any preceeding fragments we don't want */ | ||
169 | while(or && begin>=or->length){ | ||
170 | begin-=or->length; | ||
171 | or=or->next; | ||
172 | } | ||
173 | |||
174 | /* duplicate the reference chain; increment refcounts */ | ||
175 | while(or && length){ | ||
176 | ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner); | ||
177 | if(head) | ||
178 | head->next=temp; | ||
179 | else | ||
180 | ret=temp; | ||
181 | head=temp; | ||
182 | head->buffer=or->buffer; | ||
183 | head->begin=or->begin+begin; | ||
184 | head->length=length; | ||
185 | if(head->length>or->length-begin) | ||
186 | head->length=or->length-begin; | ||
187 | |||
188 | begin=0; | ||
189 | length-=head->length; | ||
190 | or=or->next; | ||
191 | } | ||
192 | |||
193 | ogg_buffer_mark(ret); | ||
194 | return ret; | ||
195 | } | 43 | } |
196 | 44 | ||
197 | static ogg_reference *ogg_buffer_dup(ogg_reference *or){ | 45 | ogg_int64_t ogg_page_granulepos(const ogg_page *og){ |
198 | ogg_reference *ret=0,*head=0; | 46 | unsigned char *page=og->header; |
199 | /* duplicate the reference chain; increment refcounts */ | 47 | ogg_int64_t granulepos=page[13]&(0xff); |
200 | while(or){ | 48 | granulepos= (granulepos<<8)|(page[12]&0xff); |
201 | ogg_reference *temp=_fetch_ref(or->buffer->ptr.owner); | 49 | granulepos= (granulepos<<8)|(page[11]&0xff); |
202 | if(head) | 50 | granulepos= (granulepos<<8)|(page[10]&0xff); |
203 | head->next=temp; | 51 | granulepos= (granulepos<<8)|(page[9]&0xff); |
204 | else | 52 | granulepos= (granulepos<<8)|(page[8]&0xff); |
205 | ret=temp; | 53 | granulepos= (granulepos<<8)|(page[7]&0xff); |
206 | head=temp; | 54 | granulepos= (granulepos<<8)|(page[6]&0xff); |
207 | head->buffer=or->buffer; | 55 | return(granulepos); |
208 | head->begin=or->begin; | ||
209 | head->length=or->length; | ||
210 | or=or->next; | ||
211 | } | ||
212 | |||
213 | ogg_buffer_mark(ret); | ||
214 | return ret; | ||
215 | } | 56 | } |
216 | 57 | ||
217 | /* split a reference into two references; 'return' is a reference to | 58 | ogg_uint32_t ogg_page_serialno(const ogg_page *og){ |
218 | the buffer preceeding pos and 'head'/'tail' are the buffer past the | 59 | return(og->header[14] | |
219 | split. If pos is at or past the end of the passed in segment, | 60 | (og->header[15]<<8) | |
220 | 'head/tail' are NULL */ | 61 | (og->header[16]<<16) | |
221 | static ogg_reference *ogg_buffer_split(ogg_reference **tail, | 62 | (og->header[17]<<24)); |
222 | ogg_reference **head,long pos){ | ||
223 | |||
224 | /* walk past any preceeding fragments to one of: | ||
225 | a) the exact boundary that seps two fragments | ||
226 | b) the fragment that needs split somewhere in the middle */ | ||
227 | ogg_reference *ret=*tail; | ||
228 | ogg_reference *or=*tail; | ||
229 | |||
230 | while(or && pos>or->length){ | ||
231 | pos-=or->length; | ||
232 | or=or->next; | ||
233 | } | ||
234 | |||
235 | if(!or || pos==0){ | ||
236 | |||
237 | return 0; | ||
238 | |||
239 | }else{ | ||
240 | |||
241 | if(pos>=or->length){ | ||
242 | /* exact split, or off the end? */ | ||
243 | if(or->next){ | ||
244 | |||
245 | /* a split */ | ||
246 | *tail=or->next; | ||
247 | or->next=0; | ||
248 | |||
249 | }else{ | ||
250 | |||
251 | /* off or at the end */ | ||
252 | *tail=*head=0; | ||
253 | |||
254 | } | ||
255 | }else{ | ||
256 | |||
257 | /* split within a fragment */ | ||
258 | long lengthA=pos; | ||
259 | long beginB=or->begin+pos; | ||
260 | long lengthB=or->length-pos; | ||
261 | |||
262 | /* make a new reference to tail the second piece */ | ||
263 | *tail=_fetch_ref(or->buffer->ptr.owner); | ||
264 | |||
265 | (*tail)->buffer=or->buffer; | ||
266 | (*tail)->begin=beginB; | ||
267 | (*tail)->length=lengthB; | ||
268 | (*tail)->next=or->next; | ||
269 | _ogg_buffer_mark_one(*tail); | ||
270 | if(head && or==*head)*head=*tail; | ||
271 | |||
272 | /* update the first piece */ | ||
273 | or->next=0; | ||
274 | or->length=lengthA; | ||
275 | |||
276 | } | ||
277 | } | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | static void ogg_buffer_release_one(ogg_reference *or){ | ||
282 | ogg_buffer *ob=or->buffer; | ||
283 | ogg_buffer_state *bs=ob->ptr.owner; | ||
284 | |||
285 | ob->refcount--; | ||
286 | if(ob->refcount==0){ | ||
287 | bs->outstanding--; /* for the returned buffer */ | ||
288 | ob->ptr.next=bs->unused_buffers; | ||
289 | bs->unused_buffers=ob; | ||
290 | } | ||
291 | |||
292 | bs->outstanding--; /* for the returned reference */ | ||
293 | or->next=bs->unused_references; | ||
294 | bs->unused_references=or; | ||
295 | |||
296 | _ogg_buffer_destroy(bs); /* lazy cleanup (if needed) */ | ||
297 | |||
298 | } | ||
299 | |||
300 | /* release the references, decrease the refcounts of buffers to which | ||
301 | they point, release any buffers with a refcount that drops to zero */ | ||
302 | static void ogg_buffer_release(ogg_reference *or){ | ||
303 | while(or){ | ||
304 | ogg_reference *next=or->next; | ||
305 | ogg_buffer_release_one(or); | ||
306 | or=next; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | static ogg_reference *ogg_buffer_pretruncate(ogg_reference *or,long pos){ | ||
311 | /* release preceeding fragments we don't want */ | ||
312 | while(or && pos>=or->length){ | ||
313 | ogg_reference *next=or->next; | ||
314 | pos-=or->length; | ||
315 | ogg_buffer_release_one(or); | ||
316 | or=next; | ||
317 | } | ||
318 | if (or) { | ||
319 | or->begin+=pos; | ||
320 | or->length-=pos; | ||
321 | } | ||
322 | return or; | ||
323 | } | ||
324 | |||
325 | static ogg_reference *ogg_buffer_walk(ogg_reference *or){ | ||
326 | if(!or)return NULL; | ||
327 | while(or->next){ | ||
328 | or=or->next; | ||
329 | } | ||
330 | return(or); | ||
331 | } | ||
332 | |||
333 | /* *head is appended to the front end (head) of *tail; both continue to | ||
334 | be valid pointers, with *tail at the tail and *head at the head */ | ||
335 | static ogg_reference *ogg_buffer_cat(ogg_reference *tail, ogg_reference *head){ | ||
336 | if(!tail)return head; | ||
337 | |||
338 | while(tail->next){ | ||
339 | tail=tail->next; | ||
340 | } | ||
341 | tail->next=head; | ||
342 | return ogg_buffer_walk(head); | ||
343 | } | ||
344 | |||
345 | static void _positionB(oggbyte_buffer *b,int pos){ | ||
346 | if(pos<b->pos){ | ||
347 | /* start at beginning, scan forward */ | ||
348 | b->ref=b->baseref; | ||
349 | b->pos=0; | ||
350 | b->end=b->pos+b->ref->length; | ||
351 | b->ptr=b->ref->buffer->data+b->ref->begin; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | static void _positionF(oggbyte_buffer *b,int pos){ | ||
356 | /* scan forward for position */ | ||
357 | while(pos>=b->end){ | ||
358 | /* just seek forward */ | ||
359 | b->pos+=b->ref->length; | ||
360 | b->ref=b->ref->next; | ||
361 | b->end=b->ref->length+b->pos; | ||
362 | b->ptr=b->ref->buffer->data+b->ref->begin; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | static int oggbyte_init(oggbyte_buffer *b,ogg_reference *or){ | ||
367 | memset(b,0,sizeof(*b)); | ||
368 | if(or){ | ||
369 | b->ref=b->baseref=or; | ||
370 | b->pos=0; | ||
371 | b->end=b->ref->length; | ||
372 | b->ptr=b->ref->buffer->data+b->ref->begin; | ||
373 | return 0; | ||
374 | }else | ||
375 | return -1; | ||
376 | } | ||
377 | |||
378 | static void oggbyte_set4(oggbyte_buffer *b,ogg_uint32_t val,int pos){ | ||
379 | int i; | ||
380 | _positionB(b,pos); | ||
381 | for(i=0;i<4;i++){ | ||
382 | _positionF(b,pos); | ||
383 | b->ptr[pos-b->pos]=val; | ||
384 | val>>=8; | ||
385 | ++pos; | ||
386 | } | ||
387 | } | 63 | } |
388 | 64 | ||
389 | static unsigned char oggbyte_read1(oggbyte_buffer *b,int pos){ | 65 | long ogg_page_pageno(const ogg_page *og){ |
390 | _positionB(b,pos); | 66 | return(og->header[18] | |
391 | _positionF(b,pos); | 67 | (og->header[19]<<8) | |
392 | return b->ptr[pos-b->pos]; | 68 | (og->header[20]<<16) | |
393 | } | 69 | (og->header[21]<<24)); |
394 | |||
395 | static ogg_uint32_t oggbyte_read4(oggbyte_buffer *b,int pos){ | ||
396 | ogg_uint32_t ret; | ||
397 | _positionB(b,pos); | ||
398 | _positionF(b,pos); | ||
399 | ret=b->ptr[pos-b->pos]; | ||
400 | _positionF(b,++pos); | ||
401 | ret|=b->ptr[pos-b->pos]<<8; | ||
402 | _positionF(b,++pos); | ||
403 | ret|=b->ptr[pos-b->pos]<<16; | ||
404 | _positionF(b,++pos); | ||
405 | ret|=b->ptr[pos-b->pos]<<24; | ||
406 | return ret; | ||
407 | } | 70 | } |
408 | 71 | ||
409 | static ogg_int64_t oggbyte_read8(oggbyte_buffer *b,int pos){ | ||
410 | ogg_int64_t ret; | ||
411 | unsigned char t[7]; | ||
412 | int i; | ||
413 | _positionB(b,pos); | ||
414 | for(i=0;i<7;i++){ | ||
415 | _positionF(b,pos); | ||
416 | t[i]=b->ptr[pos++ -b->pos]; | ||
417 | } | ||
418 | 72 | ||
419 | _positionF(b,pos); | ||
420 | ret=b->ptr[pos-b->pos]; | ||
421 | 73 | ||
422 | for(i=6;i>=0;--i) | 74 | /* returns the number of packets that are completed on this page (if |
423 | ret= ret<<8 | t[i]; | 75 | the leading packet is begun on a previous page, but ends on this |
76 | page, it's counted */ | ||
424 | 77 | ||
425 | return ret; | 78 | /* NOTE: |
426 | } | 79 | If a page consists of a packet begun on a previous page, and a new |
80 | packet begun (but not completed) on this page, the return will be: | ||
81 | ogg_page_packets(page) ==1, | ||
82 | ogg_page_continued(page) !=0 | ||
427 | 83 | ||
428 | /* Now we get to the actual framing code */ | 84 | If a page happens to be a single packet that was begun on a |
429 | 85 | previous page, and spans to the next page (in the case of a three or | |
430 | int ogg_page_version(ogg_page *og){ | 86 | more page packet), the return will be: |
431 | oggbyte_buffer ob; | 87 | ogg_page_packets(page) ==0, |
432 | oggbyte_init(&ob,og->header); | 88 | ogg_page_continued(page) !=0 |
433 | return oggbyte_read1(&ob,4); | 89 | */ |
434 | } | ||
435 | 90 | ||
436 | int ogg_page_continued(ogg_page *og){ | 91 | int ogg_page_packets(const ogg_page *og){ |
437 | oggbyte_buffer ob; | 92 | int i,n=og->header[26],count=0; |
438 | oggbyte_init(&ob,og->header); | 93 | for(i=0;i<n;i++) |
439 | return oggbyte_read1(&ob,5)&0x01; | 94 | if(og->header[27+i]<255)count++; |
95 | return(count); | ||
440 | } | 96 | } |
441 | 97 | ||
442 | int ogg_page_bos(ogg_page *og){ | ||
443 | oggbyte_buffer ob; | ||
444 | oggbyte_init(&ob,og->header); | ||
445 | return oggbyte_read1(&ob,5)&0x02; | ||
446 | } | ||
447 | 98 | ||
448 | int ogg_page_eos(ogg_page *og){ | 99 | #if 0 |
449 | oggbyte_buffer ob; | 100 | /* helper to initialize lookup for direct-table CRC (illustrative; we |
450 | oggbyte_init(&ob,og->header); | 101 | use the static init below) */ |
451 | return oggbyte_read1(&ob,5)&0x04; | ||
452 | } | ||
453 | 102 | ||
454 | ogg_int64_t ogg_page_granulepos(ogg_page *og){ | 103 | static ogg_uint32_t _ogg_crc_entry(unsigned long index){ |
455 | oggbyte_buffer ob; | 104 | int i; |
456 | oggbyte_init(&ob,og->header); | 105 | unsigned long r; |
457 | return oggbyte_read8(&ob,6); | ||
458 | } | ||
459 | 106 | ||
460 | ogg_uint32_t ogg_page_serialno(ogg_page *og){ | 107 | r = index << 24; |
461 | oggbyte_buffer ob; | 108 | for (i=0; i<8; i++) |
462 | oggbyte_init(&ob,og->header); | 109 | if (r & 0x80000000UL) |
463 | return oggbyte_read4(&ob,14); | 110 | r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator |
464 | } | 111 | polynomial, although we use an |
465 | 112 | unreflected alg and an init/final | |
466 | ogg_uint32_t ogg_page_pageno(ogg_page *og){ | 113 | of 0, not 0xffffffff */ |
467 | oggbyte_buffer ob; | 114 | else |
468 | oggbyte_init(&ob,og->header); | 115 | r<<=1; |
469 | return oggbyte_read4(&ob,18); | 116 | return (r & 0xffffffffUL); |
470 | } | 117 | } |
471 | 118 | #endif | |
472 | /* Static CRC calculation table. See older code in CVS for dead | ||
473 | run-time initialization code. */ | ||
474 | 119 | ||
475 | static const ogg_uint32_t crc_lookup[256] ICONST_ATTR = { | 120 | static const ogg_uint32_t crc_lookup[256] ICONST_ATTR = { |
476 | 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, | 121 | 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, |
@@ -538,92 +183,462 @@ static const ogg_uint32_t crc_lookup[256] ICONST_ATTR = { | |||
538 | 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, | 183 | 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, |
539 | 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; | 184 | 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; |
540 | 185 | ||
541 | ogg_sync_state *ogg_sync_create(void){ | 186 | /* init the encode/decode logical stream state */ |
542 | ogg_sync_state *oy=_ogg_calloc(1,sizeof(*oy)); | 187 | |
543 | memset(oy,0,sizeof(*oy)); | 188 | int ogg_stream_init(ogg_stream_state *os,int serialno){ |
544 | oy->bufferpool=ogg_buffer_create(); | 189 | if(os){ |
545 | return oy; | 190 | memset(os,0,sizeof(*os)); |
191 | os->body_storage=16*1024; | ||
192 | os->lacing_storage=1024; | ||
193 | |||
194 | os->body_data=_ogg_malloc(os->body_storage*sizeof(*os->body_data)); | ||
195 | os->lacing_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); | ||
196 | os->granule_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); | ||
197 | |||
198 | if(!os->body_data || !os->lacing_vals || !os->granule_vals){ | ||
199 | ogg_stream_clear(os); | ||
200 | return -1; | ||
201 | } | ||
202 | |||
203 | os->serialno=serialno; | ||
204 | |||
205 | return(0); | ||
206 | } | ||
207 | return(-1); | ||
208 | } | ||
209 | |||
210 | /* async/delayed error detection for the ogg_stream_state */ | ||
211 | int ogg_stream_check(ogg_stream_state *os){ | ||
212 | if(!os || !os->body_data) return -1; | ||
213 | return 0; | ||
546 | } | 214 | } |
547 | 215 | ||
548 | int ogg_sync_destroy(ogg_sync_state *oy){ | 216 | /* _clear does not free os, only the non-flat storage within */ |
549 | if(oy){ | 217 | int ogg_stream_clear(ogg_stream_state *os){ |
550 | ogg_sync_reset(oy); | 218 | if(os){ |
551 | ogg_buffer_destroy(oy->bufferpool); | 219 | if(os->body_data)_ogg_free(os->body_data); |
552 | memset(oy,0,sizeof(*oy)); | 220 | if(os->lacing_vals)_ogg_free(os->lacing_vals); |
553 | _ogg_free(oy); | 221 | if(os->granule_vals)_ogg_free(os->granule_vals); |
222 | |||
223 | memset(os,0,sizeof(*os)); | ||
224 | } | ||
225 | return(0); | ||
226 | } | ||
227 | |||
228 | int ogg_stream_destroy(ogg_stream_state *os){ | ||
229 | if(os){ | ||
230 | ogg_stream_clear(os); | ||
231 | _ogg_free(os); | ||
232 | } | ||
233 | return(0); | ||
234 | } | ||
235 | |||
236 | /* Helpers for ogg_stream_encode; this keeps the structure and | ||
237 | what's happening fairly clear */ | ||
238 | |||
239 | static int _os_body_expand(ogg_stream_state *os,int needed){ | ||
240 | if(os->body_storage<=os->body_fill+needed){ | ||
241 | void *ret; | ||
242 | ret=_ogg_realloc(os->body_data,(os->body_storage+needed+1024)* | ||
243 | sizeof(*os->body_data)); | ||
244 | if(!ret){ | ||
245 | ogg_stream_clear(os); | ||
246 | return -1; | ||
247 | } | ||
248 | os->body_storage+=(needed+1024); | ||
249 | os->body_data=ret; | ||
554 | } | 250 | } |
555 | return OGG_SUCCESS; | 251 | return 0; |
556 | } | 252 | } |
557 | 253 | ||
558 | unsigned char *ogg_sync_bufferin(ogg_sync_state *oy, long bytes){ | 254 | static int _os_lacing_expand(ogg_stream_state *os,int needed){ |
559 | 255 | if(os->lacing_storage<=os->lacing_fill+needed){ | |
560 | /* [allocate and] expose a buffer for data submission. | 256 | void *ret; |
561 | 257 | ret=_ogg_realloc(os->lacing_vals,(os->lacing_storage+needed+32)* | |
562 | If there is no head fragment | 258 | sizeof(*os->lacing_vals)); |
563 | allocate one and expose it | 259 | if(!ret){ |
564 | else | 260 | ogg_stream_clear(os); |
565 | if the current head fragment has sufficient unused space | 261 | return -1; |
566 | expose it | 262 | } |
567 | else | 263 | os->lacing_vals=ret; |
568 | if the current head fragment is unused | 264 | ret=_ogg_realloc(os->granule_vals,(os->lacing_storage+needed+32)* |
569 | resize and expose it | 265 | sizeof(*os->granule_vals)); |
570 | else | 266 | if(!ret){ |
571 | allocate new fragment and expose it | 267 | ogg_stream_clear(os); |
572 | */ | 268 | return -1; |
573 | 269 | } | |
574 | /* base case; fifo uninitialized */ | 270 | os->granule_vals=ret; |
575 | if(!oy->fifo_head){ | 271 | os->lacing_storage+=(needed+32); |
576 | oy->fifo_head=oy->fifo_tail=ogg_buffer_alloc(oy->bufferpool,bytes); | ||
577 | return oy->fifo_head->buffer->data; | ||
578 | } | 272 | } |
579 | 273 | return 0; | |
580 | /* space left in current fragment case */ | 274 | } |
581 | if(oy->fifo_head->buffer->size- | 275 | |
582 | oy->fifo_head->length- | 276 | /* checksum the page */ |
583 | oy->fifo_head->begin >= bytes) | 277 | /* Direct table CRC; note that this will be faster in the future if we |
584 | return oy->fifo_head->buffer->data+ | 278 | perform the checksum simultaneously with other copies */ |
585 | oy->fifo_head->length+oy->fifo_head->begin; | 279 | |
586 | 280 | void ogg_page_checksum_set(ogg_page *og){ | |
587 | /* current fragment is unused, but too small */ | 281 | if(og){ |
588 | if(!oy->fifo_head->length){ | 282 | ogg_uint32_t crc_reg=0; |
589 | ogg_buffer_realloc(oy->fifo_head,bytes); | 283 | int i; |
590 | return oy->fifo_head->buffer->data+oy->fifo_head->begin; | 284 | |
285 | /* safety; needed for API behavior, but not framing code */ | ||
286 | og->header[22]=0; | ||
287 | og->header[23]=0; | ||
288 | og->header[24]=0; | ||
289 | og->header[25]=0; | ||
290 | |||
291 | for(i=0;i<og->header_len;i++) | ||
292 | crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; | ||
293 | for(i=0;i<og->body_len;i++) | ||
294 | crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; | ||
295 | |||
296 | og->header[22]=(unsigned char)(crc_reg&0xff); | ||
297 | og->header[23]=(unsigned char)((crc_reg>>8)&0xff); | ||
298 | og->header[24]=(unsigned char)((crc_reg>>16)&0xff); | ||
299 | og->header[25]=(unsigned char)((crc_reg>>24)&0xff); | ||
591 | } | 300 | } |
592 | 301 | } | |
593 | /* current fragment used/full; get new fragment */ | 302 | |
303 | #if 0 | ||
304 | /* submit data to the internal buffer of the framing engine */ | ||
305 | int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, int count, | ||
306 | long e_o_s, ogg_int64_t granulepos){ | ||
307 | |||
308 | int bytes = 0, lacing_vals, i; | ||
309 | |||
310 | if(ogg_stream_check(os)) return -1; | ||
311 | if(!iov) return 0; | ||
312 | |||
313 | for (i = 0; i < count; ++i) bytes += (int)iov[i].iov_len; | ||
314 | lacing_vals=bytes/255+1; | ||
315 | |||
316 | if(os->body_returned){ | ||
317 | /* advance packet data according to the body_returned pointer. We | ||
318 | had to keep it around to return a pointer into the buffer last | ||
319 | call */ | ||
320 | |||
321 | os->body_fill-=os->body_returned; | ||
322 | if(os->body_fill) | ||
323 | memmove(os->body_data,os->body_data+os->body_returned, | ||
324 | os->body_fill); | ||
325 | os->body_returned=0; | ||
326 | } | ||
327 | |||
328 | /* make sure we have the buffer storage */ | ||
329 | if(_os_body_expand(os,bytes) || _os_lacing_expand(os,lacing_vals)) | ||
330 | return -1; | ||
331 | |||
332 | /* Copy in the submitted packet. Yes, the copy is a waste; this is | ||
333 | the liability of overly clean abstraction for the time being. It | ||
334 | will actually be fairly easy to eliminate the extra copy in the | ||
335 | future */ | ||
336 | |||
337 | for (i = 0; i < count; ++i) { | ||
338 | memcpy(os->body_data+os->body_fill, iov[i].iov_base, iov[i].iov_len); | ||
339 | os->body_fill += (int)iov[i].iov_len; | ||
340 | } | ||
341 | |||
342 | /* Store lacing vals for this packet */ | ||
343 | for(i=0;i<lacing_vals-1;i++){ | ||
344 | os->lacing_vals[os->lacing_fill+i]=255; | ||
345 | os->granule_vals[os->lacing_fill+i]=os->granulepos; | ||
346 | } | ||
347 | os->lacing_vals[os->lacing_fill+i]=bytes%255; | ||
348 | os->granulepos=os->granule_vals[os->lacing_fill+i]=granulepos; | ||
349 | |||
350 | /* flag the first segment as the beginning of the packet */ | ||
351 | os->lacing_vals[os->lacing_fill]|= 0x100; | ||
352 | |||
353 | os->lacing_fill+=lacing_vals; | ||
354 | |||
355 | /* for the sake of completeness */ | ||
356 | os->packetno++; | ||
357 | |||
358 | if(e_o_s)os->e_o_s=1; | ||
359 | |||
360 | return(0); | ||
361 | } | ||
362 | |||
363 | int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ | ||
364 | ogg_iovec_t iov; | ||
365 | iov.iov_base = op->packet; | ||
366 | iov.iov_len = op->bytes; | ||
367 | return ogg_stream_iovecin(os, &iov, 1, op->e_o_s, op->granulepos); | ||
368 | } | ||
369 | |||
370 | |||
371 | /* Conditionally flush a page; force==0 will only flush nominal-size | ||
372 | pages, force==1 forces us to flush a page regardless of page size | ||
373 | so long as there's any data available at all. */ | ||
374 | static int ogg_stream_flush_i(ogg_stream_state *os,ogg_page *og, int force, int nfill){ | ||
375 | int i; | ||
376 | int vals=0; | ||
377 | int maxvals=(os->lacing_fill>255?255:os->lacing_fill); | ||
378 | int bytes=0; | ||
379 | long acc=0; | ||
380 | ogg_int64_t granule_pos=-1; | ||
381 | |||
382 | if(ogg_stream_check(os)) return(0); | ||
383 | if(maxvals==0) return(0); | ||
384 | |||
385 | /* construct a page */ | ||
386 | /* decide how many segments to include */ | ||
387 | |||
388 | /* If this is the initial header case, the first page must only include | ||
389 | the initial header packet */ | ||
390 | if(os->b_o_s==0){ /* 'initial header page' case */ | ||
391 | granule_pos=0; | ||
392 | for(vals=0;vals<maxvals;vals++){ | ||
393 | if((os->lacing_vals[vals]&0x0ff)<255){ | ||
394 | vals++; | ||
395 | break; | ||
396 | } | ||
397 | } | ||
398 | }else{ | ||
399 | |||
400 | /* The extra packets_done, packet_just_done logic here attempts to do two things: | ||
401 | 1) Don't unneccessarily span pages. | ||
402 | 2) Unless necessary, don't flush pages if there are less than four packets on | ||
403 | them; this expands page size to reduce unneccessary overhead if incoming packets | ||
404 | are large. | ||
405 | These are not necessary behaviors, just 'always better than naive flushing' | ||
406 | without requiring an application to explicitly request a specific optimized | ||
407 | behavior. We'll want an explicit behavior setup pathway eventually as well. */ | ||
408 | |||
409 | int packets_done=0; | ||
410 | int packet_just_done=0; | ||
411 | for(vals=0;vals<maxvals;vals++){ | ||
412 | if(acc>nfill && packet_just_done>=4){ | ||
413 | force=1; | ||
414 | break; | ||
415 | } | ||
416 | acc+=os->lacing_vals[vals]&0x0ff; | ||
417 | if((os->lacing_vals[vals]&0xff)<255){ | ||
418 | granule_pos=os->granule_vals[vals]; | ||
419 | packet_just_done=++packets_done; | ||
420 | }else | ||
421 | packet_just_done=0; | ||
422 | } | ||
423 | if(vals==255)force=1; | ||
424 | } | ||
425 | |||
426 | if(!force) return(0); | ||
427 | |||
428 | /* construct the header in temp storage */ | ||
429 | memcpy(os->header,"OggS",4); | ||
430 | |||
431 | /* stream structure version */ | ||
432 | os->header[4]=0x00; | ||
433 | |||
434 | /* continued packet flag? */ | ||
435 | os->header[5]=0x00; | ||
436 | if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; | ||
437 | /* first page flag? */ | ||
438 | if(os->b_o_s==0)os->header[5]|=0x02; | ||
439 | /* last page flag? */ | ||
440 | if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; | ||
441 | os->b_o_s=1; | ||
442 | |||
443 | /* 64 bits of PCM position */ | ||
444 | for(i=6;i<14;i++){ | ||
445 | os->header[i]=(unsigned char)(granule_pos&0xff); | ||
446 | granule_pos>>=8; | ||
447 | } | ||
448 | |||
449 | /* 32 bits of stream serial number */ | ||
450 | { | ||
451 | long serialno=os->serialno; | ||
452 | for(i=14;i<18;i++){ | ||
453 | os->header[i]=(unsigned char)(serialno&0xff); | ||
454 | serialno>>=8; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | /* 32 bits of page counter (we have both counter and page header | ||
459 | because this val can roll over) */ | ||
460 | if(os->pageno==-1)os->pageno=0; /* because someone called | ||
461 | stream_reset; this would be a | ||
462 | strange thing to do in an | ||
463 | encode stream, but it has | ||
464 | plausible uses */ | ||
594 | { | 465 | { |
595 | ogg_reference *new=ogg_buffer_alloc(oy->bufferpool,bytes); | 466 | long pageno=os->pageno++; |
596 | oy->fifo_head->next=new; | 467 | for(i=18;i<22;i++){ |
597 | oy->fifo_head=new; | 468 | os->header[i]=(unsigned char)(pageno&0xff); |
469 | pageno>>=8; | ||
470 | } | ||
598 | } | 471 | } |
599 | return oy->fifo_head->buffer->data; | 472 | |
473 | /* zero for computation; filled in later */ | ||
474 | os->header[22]=0; | ||
475 | os->header[23]=0; | ||
476 | os->header[24]=0; | ||
477 | os->header[25]=0; | ||
478 | |||
479 | /* segment table */ | ||
480 | os->header[26]=(unsigned char)(vals&0xff); | ||
481 | for(i=0;i<vals;i++) | ||
482 | bytes+=os->header[i+27]=(unsigned char)(os->lacing_vals[i]&0xff); | ||
483 | |||
484 | /* set pointers in the ogg_page struct */ | ||
485 | og->header=os->header; | ||
486 | og->header_len=os->header_fill=vals+27; | ||
487 | og->body=os->body_data+os->body_returned; | ||
488 | og->body_len=bytes; | ||
489 | |||
490 | /* advance the lacing data and set the body_returned pointer */ | ||
491 | |||
492 | os->lacing_fill-=vals; | ||
493 | memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); | ||
494 | memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); | ||
495 | os->body_returned+=bytes; | ||
496 | |||
497 | /* calculate the checksum */ | ||
498 | |||
499 | ogg_page_checksum_set(og); | ||
500 | |||
501 | /* done */ | ||
502 | return(1); | ||
600 | } | 503 | } |
601 | 504 | ||
602 | int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ | 505 | /* This will flush remaining packets into a page (returning nonzero), |
603 | if(!oy->fifo_head)return OGG_EINVAL; | 506 | even if there is not enough data to trigger a flush normally |
604 | if(oy->fifo_head->buffer->size-oy->fifo_head->length-oy->fifo_head->begin < | 507 | (undersized page). If there are no packets or partial packets to |
605 | bytes)return OGG_EINVAL; | 508 | flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will |
606 | oy->fifo_head->length+=bytes; | 509 | try to flush a normal sized page like ogg_stream_pageout; a call to |
607 | oy->fifo_fill+=bytes; | 510 | ogg_stream_flush does not guarantee that all packets have flushed. |
608 | return OGG_SUCCESS; | 511 | Only a return value of 0 from ogg_stream_flush indicates all packet |
512 | data is flushed into pages. | ||
513 | |||
514 | since ogg_stream_flush will flush the last page in a stream even if | ||
515 | it's undersized, you almost certainly want to use ogg_stream_pageout | ||
516 | (and *not* ogg_stream_flush) unless you specifically need to flush | ||
517 | an page regardless of size in the middle of a stream. */ | ||
518 | |||
519 | int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ | ||
520 | return ogg_stream_flush_i(os,og,1,4096); | ||
521 | } | ||
522 | |||
523 | /* This constructs pages from buffered packet segments. The pointers | ||
524 | returned are to static buffers; do not free. The returned buffers are | ||
525 | good only until the next call (using the same ogg_stream_state) */ | ||
526 | |||
527 | int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ | ||
528 | int force=0; | ||
529 | if(ogg_stream_check(os)) return 0; | ||
530 | |||
531 | if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ | ||
532 | (os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */ | ||
533 | force=1; | ||
534 | |||
535 | return(ogg_stream_flush_i(os,og,force,4096)); | ||
609 | } | 536 | } |
610 | 537 | ||
611 | static ogg_uint32_t _checksum(ogg_reference *or, int bytes){ | 538 | /* Like the above, but an argument is provided to adjust the nominal |
612 | ogg_uint32_t crc_reg=0; | 539 | page size for applications which are smart enough to provide their |
613 | int j,post; | 540 | own delay based flushing */ |
614 | 541 | ||
615 | while(or){ | 542 | int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill){ |
616 | unsigned char *data=or->buffer->data+or->begin; | 543 | int force=0; |
617 | post=(bytes<or->length?bytes:or->length); | 544 | if(ogg_stream_check(os)) return 0; |
618 | for(j=0;j<post;++j) | 545 | |
619 | crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^data[j]]; | 546 | if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ |
620 | bytes-=j; | 547 | (os->lacing_fill&&!os->b_o_s)) /* 'initial header page' case */ |
621 | or=or->next; | 548 | force=1; |
549 | |||
550 | return(ogg_stream_flush_i(os,og,force,nfill)); | ||
551 | } | ||
552 | |||
553 | int ogg_stream_eos(ogg_stream_state *os){ | ||
554 | if(ogg_stream_check(os)) return 1; | ||
555 | return os->e_o_s; | ||
556 | } | ||
557 | #endif | ||
558 | /* DECODING PRIMITIVES: packet streaming layer **********************/ | ||
559 | |||
560 | /* This has two layers to place more of the multi-serialno and paging | ||
561 | control in the application's hands. First, we expose a data buffer | ||
562 | using ogg_sync_buffer(). The app either copies into the | ||
563 | buffer, or passes it directly to read(), etc. We then call | ||
564 | ogg_sync_wrote() to tell how many bytes we just added. | ||
565 | |||
566 | Pages are returned (pointers into the buffer in ogg_sync_state) | ||
567 | by ogg_sync_pageout(). The page is then submitted to | ||
568 | ogg_stream_pagein() along with the appropriate | ||
569 | ogg_stream_state* (ie, matching serialno). We then get raw | ||
570 | packets out calling ogg_stream_packetout() with a | ||
571 | ogg_stream_state. */ | ||
572 | |||
573 | /* initialize the struct to a known state */ | ||
574 | int ogg_sync_init(ogg_sync_state *oy){ | ||
575 | if(oy){ | ||
576 | oy->storage = -1; /* used as a readiness flag */ | ||
577 | memset(oy,0,sizeof(*oy)); | ||
622 | } | 578 | } |
579 | return(0); | ||
580 | } | ||
581 | |||
582 | /* clear non-flat storage within */ | ||
583 | int ogg_sync_clear(ogg_sync_state *oy){ | ||
584 | if(oy){ | ||
585 | if(oy->data)_ogg_free(oy->data); | ||
586 | memset(oy,0,sizeof(*oy)); | ||
587 | } | ||
588 | return(0); | ||
589 | } | ||
590 | |||
591 | int ogg_sync_destroy(ogg_sync_state *oy){ | ||
592 | if(oy){ | ||
593 | ogg_sync_clear(oy); | ||
594 | _ogg_free(oy); | ||
595 | } | ||
596 | return(0); | ||
597 | } | ||
623 | 598 | ||
624 | return crc_reg; | 599 | int ogg_sync_check(ogg_sync_state *oy){ |
600 | if(oy->storage<0) return -1; | ||
601 | return 0; | ||
625 | } | 602 | } |
626 | 603 | ||
604 | char *ogg_sync_buffer(ogg_sync_state *oy, long size){ | ||
605 | if(ogg_sync_check(oy)) return NULL; | ||
606 | |||
607 | /* first, clear out any space that has been previously returned */ | ||
608 | if(oy->returned){ | ||
609 | oy->fill-=oy->returned; | ||
610 | if(oy->fill>0) | ||
611 | memmove(oy->data,oy->data+oy->returned,oy->fill); | ||
612 | oy->returned=0; | ||
613 | } | ||
614 | |||
615 | if(size>oy->storage-oy->fill){ | ||
616 | /* We need to extend the internal buffer */ | ||
617 | long newsize=size+oy->fill+4096; /* an extra page to be nice */ | ||
618 | void *ret; | ||
619 | |||
620 | if(oy->data) | ||
621 | ret=_ogg_realloc(oy->data,newsize); | ||
622 | else | ||
623 | ret=_ogg_malloc(newsize); | ||
624 | if(!ret){ | ||
625 | ogg_sync_clear(oy); | ||
626 | return NULL; | ||
627 | } | ||
628 | oy->data=ret; | ||
629 | oy->storage=newsize; | ||
630 | } | ||
631 | |||
632 | /* expose a segment at least as large as requested at the fill mark */ | ||
633 | return((char *)oy->data+oy->fill); | ||
634 | } | ||
635 | |||
636 | int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ | ||
637 | if(ogg_sync_check(oy))return -1; | ||
638 | if(oy->fill+bytes>oy->storage)return -1; | ||
639 | oy->fill+=bytes; | ||
640 | return(0); | ||
641 | } | ||
627 | 642 | ||
628 | /* sync the stream. This is meant to be useful for finding page | 643 | /* sync the stream. This is meant to be useful for finding page |
629 | boundaries. | 644 | boundaries. |
@@ -636,248 +651,130 @@ static ogg_uint32_t _checksum(ogg_reference *or, int bytes){ | |||
636 | */ | 651 | */ |
637 | 652 | ||
638 | long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ | 653 | long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ |
639 | oggbyte_buffer page; | 654 | unsigned char *page=oy->data+oy->returned; |
640 | long bytes,ret=0; | 655 | unsigned char *next; |
641 | 656 | long bytes=oy->fill-oy->returned; | |
642 | ogg_page_release(og); | ||
643 | |||
644 | bytes=oy->fifo_fill; | ||
645 | oggbyte_init(&page,oy->fifo_tail); | ||
646 | 657 | ||
658 | if(ogg_sync_check(oy))return 0; | ||
659 | |||
647 | if(oy->headerbytes==0){ | 660 | if(oy->headerbytes==0){ |
648 | if(bytes<27)goto sync_out; /* not enough for even a minimal header */ | 661 | int headerbytes,i; |
662 | if(bytes<27)return(0); /* not enough for a header */ | ||
649 | 663 | ||
650 | /* verify capture pattern */ | 664 | /* verify capture pattern */ |
651 | if(oggbyte_read1(&page,0)!=(int)'O' || | 665 | if(memcmp(page,"OggS",4))goto sync_fail; |
652 | oggbyte_read1(&page,1)!=(int)'g' || | 666 | |
653 | oggbyte_read1(&page,2)!=(int)'g' || | 667 | headerbytes=page[26]+27; |
654 | oggbyte_read1(&page,3)!=(int)'S' ) goto sync_fail; | 668 | if(bytes<headerbytes)return(0); /* not enough for header + seg table */ |
655 | 669 | ||
656 | oy->headerbytes=oggbyte_read1(&page,26)+27; | ||
657 | } | ||
658 | if(bytes<oy->headerbytes)goto sync_out; /* not enough for header + | ||
659 | seg table */ | ||
660 | if(oy->bodybytes==0){ | ||
661 | int i; | ||
662 | /* count up body length in the segment table */ | 670 | /* count up body length in the segment table */ |
663 | for(i=0;i<oy->headerbytes-27;i++) | 671 | |
664 | oy->bodybytes+=oggbyte_read1(&page,27+i); | 672 | for(i=0;i<page[26];i++) |
673 | oy->bodybytes+=page[27+i]; | ||
674 | oy->headerbytes=headerbytes; | ||
665 | } | 675 | } |
666 | 676 | ||
667 | if(oy->bodybytes+oy->headerbytes>bytes)goto sync_out; | 677 | if(oy->bodybytes+oy->headerbytes>bytes)return(0); |
668 | 678 | ||
669 | /* we have what appears to be a complete page; last test: verify | 679 | /* The whole test page is buffered. Verify the checksum */ |
670 | checksum */ | ||
671 | { | 680 | { |
672 | ogg_uint32_t chksum=oggbyte_read4(&page,22); | 681 | /* Grab the checksum bytes, set the header field to zero */ |
673 | oggbyte_set4(&page,0,22); | 682 | char chksum[4]; |
674 | 683 | ogg_page log; | |
675 | /* Compare checksums; memory continues to be common access */ | 684 | |
676 | if(chksum!=_checksum(oy->fifo_tail,oy->bodybytes+oy->headerbytes)){ | 685 | memcpy(chksum,page+22,4); |
677 | 686 | memset(page+22,0,4); | |
687 | |||
688 | /* set up a temp page struct and recompute the checksum */ | ||
689 | log.header=page; | ||
690 | log.header_len=oy->headerbytes; | ||
691 | log.body=page+oy->headerbytes; | ||
692 | log.body_len=oy->bodybytes; | ||
693 | ogg_page_checksum_set(&log); | ||
694 | |||
695 | /* Compare */ | ||
696 | if(memcmp(chksum,page+22,4)){ | ||
678 | /* D'oh. Mismatch! Corrupt page (or miscapture and not a page | 697 | /* D'oh. Mismatch! Corrupt page (or miscapture and not a page |
679 | at all). replace the computed checksum with the one actually | 698 | at all) */ |
680 | read in; remember all the memory is common access */ | 699 | /* replace the computed checksum with the one actually read in */ |
700 | memcpy(page+22,chksum,4); | ||
681 | 701 | ||
682 | oggbyte_set4(&page,chksum,22); | 702 | /* Bad checksum. Lose sync */ |
683 | goto sync_fail; | 703 | goto sync_fail; |
684 | } | 704 | } |
685 | oggbyte_set4(&page,chksum,22); | ||
686 | } | ||
687 | |||
688 | /* We have a page. Set up page return. */ | ||
689 | if(og){ | ||
690 | /* set up page output */ | ||
691 | og->header=ogg_buffer_split(&oy->fifo_tail,&oy->fifo_head,oy->headerbytes); | ||
692 | og->header_len=oy->headerbytes; | ||
693 | og->body=ogg_buffer_split(&oy->fifo_tail,&oy->fifo_head,oy->bodybytes); | ||
694 | og->body_len=oy->bodybytes; | ||
695 | }else{ | ||
696 | /* simply advance */ | ||
697 | oy->fifo_tail= | ||
698 | ogg_buffer_pretruncate(oy->fifo_tail,oy->headerbytes+oy->bodybytes); | ||
699 | if(!oy->fifo_tail)oy->fifo_head=0; | ||
700 | } | 705 | } |
701 | 706 | ||
702 | ret=oy->headerbytes+oy->bodybytes; | 707 | /* yes, have a whole page all ready to go */ |
703 | oy->unsynced=0; | 708 | { |
704 | oy->headerbytes=0; | 709 | unsigned char *page=oy->data+oy->returned; |
705 | oy->bodybytes=0; | 710 | long bytes; |
706 | oy->fifo_fill-=ret; | 711 | |
712 | if(og){ | ||
713 | og->header=page; | ||
714 | og->header_len=oy->headerbytes; | ||
715 | og->body=page+oy->headerbytes; | ||
716 | og->body_len=oy->bodybytes; | ||
717 | } | ||
707 | 718 | ||
708 | return ret; | 719 | oy->unsynced=0; |
720 | oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); | ||
721 | oy->headerbytes=0; | ||
722 | oy->bodybytes=0; | ||
723 | return(bytes); | ||
724 | } | ||
709 | 725 | ||
710 | sync_fail: | 726 | sync_fail: |
711 | 727 | ||
712 | oy->headerbytes=0; | 728 | oy->headerbytes=0; |
713 | oy->bodybytes=0; | 729 | oy->bodybytes=0; |
714 | oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,1); | ||
715 | ret--; | ||
716 | 730 | ||
717 | /* search forward through fragments for possible capture */ | 731 | /* search for possible capture */ |
718 | while(oy->fifo_tail){ | 732 | next=memchr(page+1,'O',bytes-1); |
719 | /* invariant: fifo_cursor points to a position in fifo_tail */ | 733 | if(!next) |
720 | unsigned char *now=oy->fifo_tail->buffer->data+oy->fifo_tail->begin; | 734 | next=oy->data+oy->fill; |
721 | unsigned char *next=memchr(now, 'O', oy->fifo_tail->length); | ||
722 | |||
723 | if(next){ | ||
724 | /* possible capture in this segment */ | ||
725 | long bytes=next-now; | ||
726 | oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,bytes); | ||
727 | ret-=bytes; | ||
728 | break; | ||
729 | }else{ | ||
730 | /* no capture. advance to next segment */ | ||
731 | long bytes=oy->fifo_tail->length; | ||
732 | ret-=bytes; | ||
733 | oy->fifo_tail=ogg_buffer_pretruncate(oy->fifo_tail,bytes); | ||
734 | } | ||
735 | } | ||
736 | if(!oy->fifo_tail)oy->fifo_head=0; | ||
737 | oy->fifo_fill+=ret; | ||
738 | 735 | ||
739 | sync_out: | 736 | oy->returned=(int)(next-oy->data); |
740 | return ret; | 737 | return((long)-(next-page)); |
741 | } | 738 | } |
742 | 739 | ||
743 | /* clear things to an initial state. Good to call, eg, before seeking */ | 740 | /* sync the stream and get a page. Keep trying until we find a page. |
744 | int ogg_sync_reset(ogg_sync_state *oy){ | 741 | Suppress 'sync errors' after reporting the first. |
745 | 742 | ||
746 | ogg_buffer_release(oy->fifo_tail); | 743 | return values: |
747 | oy->fifo_tail=0; | 744 | -1) recapture (hole in data) |
748 | oy->fifo_head=0; | 745 | 0) need more data |
749 | oy->fifo_fill=0; | 746 | 1) page returned |
750 | 747 | ||
751 | oy->unsynced=0; | 748 | Returns pointers into buffered data; invalidated by next call to |
752 | oy->headerbytes=0; | 749 | _stream, _clear, _init, or _buffer */ |
753 | oy->bodybytes=0; | ||
754 | return OGG_SUCCESS; | ||
755 | } | ||
756 | 750 | ||
757 | ogg_stream_state *ogg_stream_create(int serialno){ | 751 | int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ |
758 | ogg_stream_state *os=_ogg_calloc(1,sizeof(*os)); | ||
759 | os->serialno=serialno; | ||
760 | os->pageno=-1; | ||
761 | return os; | ||
762 | } | ||
763 | 752 | ||
764 | int ogg_stream_destroy(ogg_stream_state *os){ | 753 | if(ogg_sync_check(oy))return 0; |
765 | if(os){ | ||
766 | ogg_buffer_release(os->header_tail); | ||
767 | ogg_buffer_release(os->body_tail); | ||
768 | memset(os,0,sizeof(*os)); | ||
769 | _ogg_free(os); | ||
770 | } | ||
771 | return OGG_SUCCESS; | ||
772 | } | ||
773 | 754 | ||
755 | /* all we need to do is verify a page at the head of the stream | ||
756 | buffer. If it doesn't verify, we look for the next potential | ||
757 | frame */ | ||
774 | 758 | ||
775 | #define FINFLAG 0x80000000UL | 759 | for(;;){ |
776 | #define FINMASK 0x7fffffffUL | 760 | long ret=ogg_sync_pageseek(oy,og); |
777 | 761 | if(ret>0){ | |
778 | static void _next_lace(oggbyte_buffer *ob,ogg_stream_state *os){ | 762 | /* have a page */ |
779 | /* search ahead one lace */ | 763 | return(1); |
780 | os->body_fill_next=0; | 764 | } |
781 | while(os->laceptr<os->lacing_fill){ | 765 | if(ret==0){ |
782 | int val=oggbyte_read1(ob,27+os->laceptr++); | 766 | /* need more data */ |
783 | os->body_fill_next+=val; | 767 | return(0); |
784 | if(val<255){ | ||
785 | os->body_fill_next|=FINFLAG; | ||
786 | os->clearflag=1; | ||
787 | break; | ||
788 | } | 768 | } |
789 | } | ||
790 | } | ||
791 | |||
792 | STATICIRAM_NOT_MDCT void _span_queued_page(ogg_stream_state *os) | ||
793 | ICODE_ATTR_TREMOR_NOT_MDCT; | ||
794 | STATICIRAM_NOT_MDCT void _span_queued_page(ogg_stream_state *os){ | ||
795 | while( !(os->body_fill&FINFLAG) ){ | ||
796 | |||
797 | if(!os->header_tail)break; | ||
798 | |||
799 | /* first flush out preceeding page header (if any). Body is | ||
800 | flushed as it's consumed, so that's not done here. */ | ||
801 | |||
802 | if(os->lacing_fill>=0) | ||
803 | os->header_tail=ogg_buffer_pretruncate(os->header_tail, | ||
804 | os->lacing_fill+27); | ||
805 | os->lacing_fill=0; | ||
806 | os->laceptr=0; | ||
807 | os->clearflag=0; | ||
808 | |||
809 | if(!os->header_tail){ | ||
810 | os->header_head=0; | ||
811 | break; | ||
812 | }else{ | ||
813 | |||
814 | /* process/prepare next page, if any */ | ||
815 | |||
816 | long pageno; | ||
817 | oggbyte_buffer ob; | ||
818 | ogg_page og; /* only for parsing header values */ | ||
819 | og.header=os->header_tail; /* only for parsing header values */ | ||
820 | pageno=ogg_page_pageno(&og); | ||
821 | |||
822 | oggbyte_init(&ob,os->header_tail); | ||
823 | os->lacing_fill=oggbyte_read1(&ob,26); | ||
824 | |||
825 | /* are we in sequence? */ | ||
826 | if(pageno!=os->pageno){ | ||
827 | if(os->pageno==-1) /* indicates seek or reset */ | ||
828 | os->holeflag=1; /* set for internal use */ | ||
829 | else | ||
830 | os->holeflag=2; /* set for external reporting */ | ||
831 | |||
832 | os->body_tail=ogg_buffer_pretruncate(os->body_tail, | ||
833 | os->body_fill); | ||
834 | if(os->body_tail==0)os->body_head=0; | ||
835 | os->body_fill=0; | ||
836 | |||
837 | } | ||
838 | 769 | ||
839 | if(ogg_page_continued(&og)){ | 770 | /* head did not start a synced page... skipped some bytes */ |
840 | if(os->body_fill==0){ | 771 | if(!oy->unsynced){ |
841 | /* continued packet, but no preceeding data to continue */ | 772 | oy->unsynced=1; |
842 | /* dump the first partial packet on the page */ | 773 | return(-1); |
843 | _next_lace(&ob,os); | 774 | } |
844 | os->body_tail= | ||
845 | ogg_buffer_pretruncate(os->body_tail,os->body_fill_next&FINMASK); | ||
846 | if(os->body_tail==0)os->body_head=0; | ||
847 | /* set span flag */ | ||
848 | if(!os->spanflag && !os->holeflag)os->spanflag=2; | ||
849 | } | ||
850 | }else{ | ||
851 | if(os->body_fill>0){ | ||
852 | /* preceeding data to continue, but not a continued page */ | ||
853 | /* dump body_fill */ | ||
854 | os->body_tail=ogg_buffer_pretruncate(os->body_tail, | ||
855 | os->body_fill); | ||
856 | if(os->body_tail==0)os->body_head=0; | ||
857 | os->body_fill=0; | ||
858 | |||
859 | /* set espan flag */ | ||
860 | if(!os->spanflag && !os->holeflag)os->spanflag=2; | ||
861 | } | ||
862 | } | ||
863 | |||
864 | if(os->laceptr<os->lacing_fill){ | ||
865 | os->granulepos=ogg_page_granulepos(&og); | ||
866 | 775 | ||
867 | /* get current packet size & flag */ | 776 | /* loop. keep looking */ |
868 | _next_lace(&ob,os); | ||
869 | os->body_fill+=os->body_fill_next; /* addition handles the flag fine; | ||
870 | unsigned on purpose */ | ||
871 | /* ...and next packet size & flag */ | ||
872 | _next_lace(&ob,os); | ||
873 | 777 | ||
874 | } | ||
875 | |||
876 | os->pageno=pageno+1; | ||
877 | os->e_o_s=ogg_page_eos(&og); | ||
878 | os->b_o_s=ogg_page_bos(&og); | ||
879 | |||
880 | } | ||
881 | } | 778 | } |
882 | } | 779 | } |
883 | 780 | ||
@@ -885,45 +782,156 @@ STATICIRAM_NOT_MDCT void _span_queued_page(ogg_stream_state *os){ | |||
885 | into packet segments here as well. */ | 782 | into packet segments here as well. */ |
886 | 783 | ||
887 | int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ | 784 | int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ |
785 | unsigned char *header=og->header; | ||
786 | unsigned char *body=og->body; | ||
787 | long bodysize=og->body_len; | ||
788 | int segptr=0; | ||
888 | 789 | ||
889 | int serialno=ogg_page_serialno(og); | ||
890 | int version=ogg_page_version(og); | 790 | int version=ogg_page_version(og); |
791 | int continued=ogg_page_continued(og); | ||
792 | int bos=ogg_page_bos(og); | ||
793 | int eos=ogg_page_eos(og); | ||
794 | ogg_int64_t granulepos=ogg_page_granulepos(og); | ||
795 | ogg_uint32_t serialno=ogg_page_serialno(og); | ||
796 | long pageno=ogg_page_pageno(og); | ||
797 | int segments=header[26]; | ||
798 | |||
799 | if(ogg_stream_check(os)) return -1; | ||
800 | |||
801 | /* clean up 'returned data' */ | ||
802 | { | ||
803 | long lr=os->lacing_returned; | ||
804 | long br=os->body_returned; | ||
805 | |||
806 | /* body data */ | ||
807 | if(br){ | ||
808 | os->body_fill-=br; | ||
809 | if(os->body_fill) | ||
810 | memmove(os->body_data,os->body_data+br,os->body_fill); | ||
811 | os->body_returned=0; | ||
812 | } | ||
813 | |||
814 | if(lr){ | ||
815 | /* segment table */ | ||
816 | if(os->lacing_fill-lr){ | ||
817 | memmove(os->lacing_vals,os->lacing_vals+lr, | ||
818 | (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); | ||
819 | memmove(os->granule_vals,os->granule_vals+lr, | ||
820 | (os->lacing_fill-lr)*sizeof(*os->granule_vals)); | ||
821 | } | ||
822 | os->lacing_fill-=lr; | ||
823 | os->lacing_packet-=lr; | ||
824 | os->lacing_returned=0; | ||
825 | } | ||
826 | } | ||
891 | 827 | ||
892 | /* check the serial number */ | 828 | /* check the serial number */ |
893 | if(serialno!=os->serialno){ | 829 | if(serialno!=os->serialno)return(-1); |
894 | ogg_page_release(og); | 830 | if(version>0)return(-1); |
895 | return OGG_ESERIAL; | 831 | |
832 | if(_os_lacing_expand(os,segments+1)) return -1; | ||
833 | |||
834 | /* are we in sequence? */ | ||
835 | if(pageno!=os->pageno){ | ||
836 | int i; | ||
837 | |||
838 | /* unroll previous partial packet (if any) */ | ||
839 | for(i=os->lacing_packet;i<os->lacing_fill;i++) | ||
840 | os->body_fill-=os->lacing_vals[i]&0xff; | ||
841 | os->lacing_fill=os->lacing_packet; | ||
842 | |||
843 | /* make a note of dropped data in segment table */ | ||
844 | if(os->pageno!=-1){ | ||
845 | os->lacing_vals[os->lacing_fill++]=0x400; | ||
846 | os->lacing_packet++; | ||
847 | } | ||
848 | } | ||
849 | |||
850 | /* are we a 'continued packet' page? If so, we may need to skip | ||
851 | some segments */ | ||
852 | if(continued){ | ||
853 | if(os->lacing_fill<1 || | ||
854 | os->lacing_vals[os->lacing_fill-1]==0x400){ | ||
855 | bos=0; | ||
856 | for(;segptr<segments;segptr++){ | ||
857 | int val=header[27+segptr]; | ||
858 | body+=val; | ||
859 | bodysize-=val; | ||
860 | if(val<255){ | ||
861 | segptr++; | ||
862 | break; | ||
863 | } | ||
864 | } | ||
865 | } | ||
896 | } | 866 | } |
897 | if(version>0){ | 867 | |
898 | ogg_page_release(og); | 868 | if(bodysize){ |
899 | return OGG_EVERSION; | 869 | if(_os_body_expand(os,bodysize)) return -1; |
870 | memcpy(os->body_data+os->body_fill,body,bodysize); | ||
871 | os->body_fill+=bodysize; | ||
900 | } | 872 | } |
901 | 873 | ||
902 | /* add to fifos */ | 874 | { |
903 | if(!os->body_tail){ | 875 | int saved=-1; |
904 | os->body_tail=og->body; | 876 | while(segptr<segments){ |
905 | os->body_head=ogg_buffer_walk(og->body); | 877 | int val=header[27+segptr]; |
906 | }else{ | 878 | os->lacing_vals[os->lacing_fill]=val; |
907 | os->body_head=ogg_buffer_cat(os->body_head,og->body); | 879 | os->granule_vals[os->lacing_fill]=-1; |
880 | |||
881 | if(bos){ | ||
882 | os->lacing_vals[os->lacing_fill]|=0x100; | ||
883 | bos=0; | ||
884 | } | ||
885 | |||
886 | if(val<255)saved=os->lacing_fill; | ||
887 | |||
888 | os->lacing_fill++; | ||
889 | segptr++; | ||
890 | |||
891 | if(val<255)os->lacing_packet=os->lacing_fill; | ||
892 | } | ||
893 | |||
894 | /* set the granulepos on the last granuleval of the last full packet */ | ||
895 | if(saved!=-1){ | ||
896 | os->granule_vals[saved]=granulepos; | ||
897 | } | ||
898 | |||
908 | } | 899 | } |
909 | if(!os->header_tail){ | 900 | |
910 | os->header_tail=og->header; | 901 | if(eos){ |
911 | os->header_head=ogg_buffer_walk(og->header); | 902 | os->e_o_s=1; |
912 | os->lacing_fill=-27; | 903 | if(os->lacing_fill>0) |
913 | }else{ | 904 | os->lacing_vals[os->lacing_fill-1]|=0x200; |
914 | os->header_head=ogg_buffer_cat(os->header_head,og->header); | ||
915 | } | 905 | } |
916 | 906 | ||
917 | memset(og,0,sizeof(*og)); | 907 | os->pageno=pageno+1; |
918 | return OGG_SUCCESS; | 908 | |
909 | return(0); | ||
910 | } | ||
911 | |||
912 | /* clear things to an initial state. Good to call, eg, before seeking */ | ||
913 | int ogg_sync_reset(ogg_sync_state *oy){ | ||
914 | if(ogg_sync_check(oy))return -1; | ||
915 | |||
916 | oy->fill=0; | ||
917 | oy->returned=0; | ||
918 | oy->unsynced=0; | ||
919 | oy->headerbytes=0; | ||
920 | oy->bodybytes=0; | ||
921 | return(0); | ||
919 | } | 922 | } |
920 | 923 | ||
921 | int ogg_stream_reset(ogg_stream_state *os){ | 924 | int ogg_stream_reset(ogg_stream_state *os){ |
925 | if(ogg_stream_check(os)) return -1; | ||
926 | |||
927 | os->body_fill=0; | ||
928 | os->body_returned=0; | ||
922 | 929 | ||
923 | ogg_buffer_release(os->header_tail); | 930 | os->lacing_fill=0; |
924 | ogg_buffer_release(os->body_tail); | 931 | os->lacing_packet=0; |
925 | os->header_tail=os->header_head=0; | 932 | os->lacing_returned=0; |
926 | os->body_tail=os->body_head=0; | 933 | |
934 | /* os->header_fill=0; */ | ||
927 | 935 | ||
928 | os->e_o_s=0; | 936 | os->e_o_s=0; |
929 | os->b_o_s=0; | 937 | os->b_o_s=0; |
@@ -931,137 +939,1159 @@ int ogg_stream_reset(ogg_stream_state *os){ | |||
931 | os->packetno=0; | 939 | os->packetno=0; |
932 | os->granulepos=0; | 940 | os->granulepos=0; |
933 | 941 | ||
934 | os->body_fill=0; | 942 | return(0); |
935 | os->lacing_fill=0; | ||
936 | |||
937 | os->holeflag=0; | ||
938 | os->spanflag=0; | ||
939 | os->clearflag=0; | ||
940 | os->laceptr=0; | ||
941 | os->body_fill_next=0; | ||
942 | |||
943 | return OGG_SUCCESS; | ||
944 | } | 943 | } |
945 | 944 | ||
946 | int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ | 945 | int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ |
946 | if(ogg_stream_check(os)) return -1; | ||
947 | ogg_stream_reset(os); | 947 | ogg_stream_reset(os); |
948 | os->serialno=serialno; | 948 | os->serialno=serialno; |
949 | return OGG_SUCCESS; | 949 | return(0); |
950 | } | 950 | } |
951 | 951 | ||
952 | STATICIRAM_NOT_MDCT int _packetout(ogg_stream_state *os,ogg_packet *op,int adv) | 952 | static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv) |
953 | ICODE_ATTR_TREMOR_NOT_MDCT; | 953 | ICODE_ATTR_TREMOR_NOT_MDCT; |
954 | STATICIRAM_NOT_MDCT int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ | 954 | static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ |
955 | 955 | ||
956 | ogg_packet_release(op); | 956 | /* The last part of decode. We have the stream broken into packet |
957 | _span_queued_page(os); | 957 | segments. Now we need to group them into packets (or return the |
958 | out of sync markers) */ | ||
958 | 959 | ||
959 | if(os->holeflag){ | 960 | int ptr=os->lacing_returned; |
960 | int temp=os->holeflag; | 961 | |
961 | if(os->clearflag) | 962 | if(os->lacing_packet<=ptr)return(0); |
962 | os->holeflag=0; | 963 | |
963 | else | 964 | if(os->lacing_vals[ptr]&0x400){ |
964 | os->holeflag=1; | 965 | /* we need to tell the codec there's a gap; it might need to |
965 | if(temp==2){ | 966 | handle previous packet dependencies. */ |
966 | os->packetno++; | 967 | os->lacing_returned++; |
967 | return OGG_HOLE; | 968 | os->packetno++; |
968 | } | 969 | return(-1); |
969 | } | ||
970 | if(os->spanflag){ | ||
971 | int temp=os->spanflag; | ||
972 | if(os->clearflag) | ||
973 | os->spanflag=0; | ||
974 | else | ||
975 | os->spanflag=1; | ||
976 | if(temp==2){ | ||
977 | os->packetno++; | ||
978 | return OGG_SPAN; | ||
979 | } | ||
980 | } | 970 | } |
981 | 971 | ||
982 | if(!(os->body_fill&FINFLAG)) return 0; | 972 | if(!op && !adv)return(1); /* just using peek as an inexpensive way |
983 | if(!op && !adv)return 1; /* just using peek as an inexpensive way | ||
984 | to ask if there's a whole packet | 973 | to ask if there's a whole packet |
985 | waiting */ | 974 | waiting */ |
986 | if(op){ | ||
987 | op->b_o_s=os->b_o_s; | ||
988 | if(os->e_o_s && os->body_fill_next==0) | ||
989 | op->e_o_s=os->e_o_s; | ||
990 | else | ||
991 | op->e_o_s=0; | ||
992 | if( (os->body_fill&FINFLAG) && !(os->body_fill_next&FINFLAG) ) | ||
993 | op->granulepos=os->granulepos; | ||
994 | else | ||
995 | op->granulepos=-1; | ||
996 | op->packetno=os->packetno; | ||
997 | } | ||
998 | 975 | ||
999 | if(adv){ | 976 | /* Gather the whole packet. We'll have no holes or a partial packet */ |
1000 | oggbyte_buffer ob; | 977 | { |
1001 | oggbyte_init(&ob,os->header_tail); | 978 | int size=os->lacing_vals[ptr]&0xff; |
979 | long bytes=size; | ||
980 | int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ | ||
981 | int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ | ||
982 | |||
983 | while(size==255){ | ||
984 | int val=os->lacing_vals[++ptr]; | ||
985 | size=val&0xff; | ||
986 | if(val&0x200)eos=0x200; | ||
987 | bytes+=size; | ||
988 | } | ||
1002 | 989 | ||
1003 | /* split the body contents off */ | ||
1004 | if(op){ | 990 | if(op){ |
1005 | op->packet=ogg_buffer_split(&os->body_tail,&os->body_head, | 991 | op->e_o_s=eos; |
1006 | os->body_fill&FINMASK); | 992 | op->b_o_s=bos; |
1007 | op->bytes=os->body_fill&FINMASK; | 993 | op->packet=os->body_data+os->body_returned; |
1008 | }else{ | 994 | op->packetno=os->packetno; |
1009 | os->body_tail=ogg_buffer_pretruncate(os->body_tail, | 995 | op->granulepos=os->granule_vals[ptr]; |
1010 | os->body_fill&FINMASK); | 996 | op->bytes=bytes; |
1011 | if(os->body_tail==0)os->body_head=0; | ||
1012 | } | 997 | } |
1013 | 998 | ||
1014 | /* update lacing pointers */ | 999 | if(adv){ |
1015 | os->body_fill=os->body_fill_next; | 1000 | os->body_returned+=bytes; |
1016 | _next_lace(&ob,os); | 1001 | os->lacing_returned=ptr+1; |
1017 | }else{ | 1002 | os->packetno++; |
1018 | if(op){ | ||
1019 | op->packet=ogg_buffer_sub(os->body_tail,0,os->body_fill&FINMASK); | ||
1020 | op->bytes=os->body_fill&FINMASK; | ||
1021 | } | 1003 | } |
1022 | } | 1004 | } |
1023 | 1005 | return(1); | |
1024 | if(adv){ | ||
1025 | os->packetno++; | ||
1026 | os->b_o_s=0; | ||
1027 | } | ||
1028 | |||
1029 | return 1; | ||
1030 | } | 1006 | } |
1031 | 1007 | ||
1032 | int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op) | 1008 | int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op) |
1033 | ICODE_ATTR_TREMOR_NOT_MDCT; | 1009 | ICODE_ATTR_TREMOR_NOT_MDCT; |
1034 | int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ | 1010 | int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ |
1011 | if(ogg_stream_check(os)) return 0; | ||
1035 | return _packetout(os,op,1); | 1012 | return _packetout(os,op,1); |
1036 | } | 1013 | } |
1037 | 1014 | ||
1038 | int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op) | 1015 | int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op) |
1039 | ICODE_ATTR_TREMOR_NOT_MDCT; | 1016 | ICODE_ATTR_TREMOR_NOT_MDCT; |
1040 | int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ | 1017 | int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ |
1018 | if(ogg_stream_check(os)) return 0; | ||
1041 | return _packetout(os,op,0); | 1019 | return _packetout(os,op,0); |
1042 | } | 1020 | } |
1043 | 1021 | ||
1044 | int ogg_packet_release(ogg_packet *op) { | 1022 | void ogg_packet_clear(ogg_packet *op) { |
1045 | if(op){ | 1023 | _ogg_free(op->packet); |
1046 | ogg_buffer_release(op->packet); | 1024 | memset(op, 0, sizeof(*op)); |
1047 | memset(op, 0, sizeof(*op)); | 1025 | } |
1026 | |||
1027 | #ifdef _V_SELFTEST | ||
1028 | #include <stdio.h> | ||
1029 | |||
1030 | ogg_stream_state os_en, os_de; | ||
1031 | ogg_sync_state oy; | ||
1032 | |||
1033 | void checkpacket(ogg_packet *op,long len, int no, long pos){ | ||
1034 | long j; | ||
1035 | static int sequence=0; | ||
1036 | static int lastno=0; | ||
1037 | |||
1038 | if(op->bytes!=len){ | ||
1039 | fprintf(stderr,"incorrect packet length (%ld != %ld)!\n",op->bytes,len); | ||
1040 | exit(1); | ||
1041 | } | ||
1042 | if(op->granulepos!=pos){ | ||
1043 | fprintf(stderr,"incorrect packet granpos (%ld != %ld)!\n",(long)op->granulepos,pos); | ||
1044 | exit(1); | ||
1045 | } | ||
1046 | |||
1047 | /* packet number just follows sequence/gap; adjust the input number | ||
1048 | for that */ | ||
1049 | if(no==0){ | ||
1050 | sequence=0; | ||
1051 | }else{ | ||
1052 | sequence++; | ||
1053 | if(no>lastno+1) | ||
1054 | sequence++; | ||
1055 | } | ||
1056 | lastno=no; | ||
1057 | if(op->packetno!=sequence){ | ||
1058 | fprintf(stderr,"incorrect packet sequence %ld != %d\n", | ||
1059 | (long)(op->packetno),sequence); | ||
1060 | exit(1); | ||
1048 | } | 1061 | } |
1049 | return OGG_SUCCESS; | 1062 | |
1063 | /* Test data */ | ||
1064 | for(j=0;j<op->bytes;j++) | ||
1065 | if(op->packet[j]!=((j+no)&0xff)){ | ||
1066 | fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", | ||
1067 | j,op->packet[j],(j+no)&0xff); | ||
1068 | exit(1); | ||
1069 | } | ||
1050 | } | 1070 | } |
1051 | 1071 | ||
1052 | int ogg_page_release(ogg_page *og) { | 1072 | void check_page(unsigned char *data,const int *header,ogg_page *og){ |
1053 | if(og){ | 1073 | long j; |
1054 | ogg_buffer_release(og->header); | 1074 | /* Test data */ |
1055 | ogg_buffer_release(og->body); | 1075 | for(j=0;j<og->body_len;j++) |
1056 | memset(og, 0, sizeof(*og)); | 1076 | if(og->body[j]!=data[j]){ |
1077 | fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", | ||
1078 | j,data[j],og->body[j]); | ||
1079 | exit(1); | ||
1080 | } | ||
1081 | |||
1082 | /* Test header */ | ||
1083 | for(j=0;j<og->header_len;j++){ | ||
1084 | if(og->header[j]!=header[j]){ | ||
1085 | fprintf(stderr,"header content mismatch at pos %ld:\n",j); | ||
1086 | for(j=0;j<header[26]+27;j++) | ||
1087 | fprintf(stderr," (%ld)%02x:%02x",j,header[j],og->header[j]); | ||
1088 | fprintf(stderr,"\n"); | ||
1089 | exit(1); | ||
1090 | } | ||
1057 | } | 1091 | } |
1058 | return OGG_SUCCESS; | 1092 | if(og->header_len!=header[26]+27){ |
1093 | fprintf(stderr,"header length incorrect! (%ld!=%d)\n", | ||
1094 | og->header_len,header[26]+27); | ||
1095 | exit(1); | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | void print_header(ogg_page *og){ | ||
1100 | int j; | ||
1101 | fprintf(stderr,"\nHEADER:\n"); | ||
1102 | fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", | ||
1103 | og->header[0],og->header[1],og->header[2],og->header[3], | ||
1104 | (int)og->header[4],(int)og->header[5]); | ||
1105 | |||
1106 | fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", | ||
1107 | (og->header[9]<<24)|(og->header[8]<<16)| | ||
1108 | (og->header[7]<<8)|og->header[6], | ||
1109 | (og->header[17]<<24)|(og->header[16]<<16)| | ||
1110 | (og->header[15]<<8)|og->header[14], | ||
1111 | ((long)(og->header[21])<<24)|(og->header[20]<<16)| | ||
1112 | (og->header[19]<<8)|og->header[18]); | ||
1113 | |||
1114 | fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", | ||
1115 | (int)og->header[22],(int)og->header[23], | ||
1116 | (int)og->header[24],(int)og->header[25], | ||
1117 | (int)og->header[26]); | ||
1118 | |||
1119 | for(j=27;j<og->header_len;j++) | ||
1120 | fprintf(stderr,"%d ",(int)og->header[j]); | ||
1121 | fprintf(stderr,")\n\n"); | ||
1059 | } | 1122 | } |
1060 | 1123 | ||
1061 | void ogg_page_dup(ogg_page *dup,ogg_page *orig){ | 1124 | void copy_page(ogg_page *og){ |
1062 | dup->header_len=orig->header_len; | 1125 | unsigned char *temp=_ogg_malloc(og->header_len); |
1063 | dup->body_len=orig->body_len; | 1126 | memcpy(temp,og->header,og->header_len); |
1064 | dup->header=ogg_buffer_dup(orig->header); | 1127 | og->header=temp; |
1065 | dup->body=ogg_buffer_dup(orig->body); | 1128 | |
1129 | temp=_ogg_malloc(og->body_len); | ||
1130 | memcpy(temp,og->body,og->body_len); | ||
1131 | og->body=temp; | ||
1132 | } | ||
1133 | |||
1134 | void free_page(ogg_page *og){ | ||
1135 | _ogg_free (og->header); | ||
1136 | _ogg_free (og->body); | ||
1137 | } | ||
1138 | |||
1139 | void error(void){ | ||
1140 | fprintf(stderr,"error!\n"); | ||
1141 | exit(1); | ||
1142 | } | ||
1143 | |||
1144 | /* 17 only */ | ||
1145 | const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, | ||
1146 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1147 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1148 | 0x15,0xed,0xec,0x91, | ||
1149 | 1, | ||
1150 | 17}; | ||
1151 | |||
1152 | /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ | ||
1153 | const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1154 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1155 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1156 | 0x59,0x10,0x6c,0x2c, | ||
1157 | 1, | ||
1158 | 17}; | ||
1159 | const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, | ||
1160 | 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1161 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1162 | 0x89,0x33,0x85,0xce, | ||
1163 | 13, | ||
1164 | 254,255,0,255,1,255,245,255,255,0, | ||
1165 | 255,255,90}; | ||
1166 | |||
1167 | /* nil packets; beginning,middle,end */ | ||
1168 | const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1169 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1170 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1171 | 0xff,0x7b,0x23,0x17, | ||
1172 | 1, | ||
1173 | 0}; | ||
1174 | const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, | ||
1175 | 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1176 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1177 | 0x5c,0x3f,0x66,0xcb, | ||
1178 | 17, | ||
1179 | 17,254,255,0,0,255,1,0,255,245,255,255,0, | ||
1180 | 255,255,90,0}; | ||
1181 | |||
1182 | /* large initial packet */ | ||
1183 | const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1184 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1185 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1186 | 0x01,0x27,0x31,0xaa, | ||
1187 | 18, | ||
1188 | 255,255,255,255,255,255,255,255, | ||
1189 | 255,255,255,255,255,255,255,255,255,10}; | ||
1190 | |||
1191 | const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, | ||
1192 | 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1193 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1194 | 0x7f,0x4e,0x8a,0xd2, | ||
1195 | 4, | ||
1196 | 255,4,255,0}; | ||
1197 | |||
1198 | |||
1199 | /* continuing packet test */ | ||
1200 | const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1201 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1202 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1203 | 0xff,0x7b,0x23,0x17, | ||
1204 | 1, | ||
1205 | 0}; | ||
1206 | |||
1207 | const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, | ||
1208 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | ||
1209 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1210 | 0xf8,0x3c,0x19,0x79, | ||
1211 | 255, | ||
1212 | 255,255,255,255,255,255,255,255, | ||
1213 | 255,255,255,255,255,255,255,255, | ||
1214 | 255,255,255,255,255,255,255,255, | ||
1215 | 255,255,255,255,255,255,255,255, | ||
1216 | 255,255,255,255,255,255,255,255, | ||
1217 | 255,255,255,255,255,255,255,255, | ||
1218 | 255,255,255,255,255,255,255,255, | ||
1219 | 255,255,255,255,255,255,255,255, | ||
1220 | 255,255,255,255,255,255,255,255, | ||
1221 | 255,255,255,255,255,255,255,255, | ||
1222 | 255,255,255,255,255,255,255,255, | ||
1223 | 255,255,255,255,255,255,255,255, | ||
1224 | 255,255,255,255,255,255,255,255, | ||
1225 | 255,255,255,255,255,255,255,255, | ||
1226 | 255,255,255,255,255,255,255,255, | ||
1227 | 255,255,255,255,255,255,255,255, | ||
1228 | 255,255,255,255,255,255,255,255, | ||
1229 | 255,255,255,255,255,255,255,255, | ||
1230 | 255,255,255,255,255,255,255,255, | ||
1231 | 255,255,255,255,255,255,255,255, | ||
1232 | 255,255,255,255,255,255,255,255, | ||
1233 | 255,255,255,255,255,255,255,255, | ||
1234 | 255,255,255,255,255,255,255,255, | ||
1235 | 255,255,255,255,255,255,255,255, | ||
1236 | 255,255,255,255,255,255,255,255, | ||
1237 | 255,255,255,255,255,255,255,255, | ||
1238 | 255,255,255,255,255,255,255,255, | ||
1239 | 255,255,255,255,255,255,255,255, | ||
1240 | 255,255,255,255,255,255,255,255, | ||
1241 | 255,255,255,255,255,255,255,255, | ||
1242 | 255,255,255,255,255,255,255,255, | ||
1243 | 255,255,255,255,255,255,255}; | ||
1244 | |||
1245 | const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, | ||
1246 | 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1247 | 0x01,0x02,0x03,0x04,2,0,0,0, | ||
1248 | 0x38,0xe6,0xb6,0x28, | ||
1249 | 6, | ||
1250 | 255,220,255,4,255,0}; | ||
1251 | |||
1252 | |||
1253 | /* spill expansion test */ | ||
1254 | const int head1_4b[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1255 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1256 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1257 | 0xff,0x7b,0x23,0x17, | ||
1258 | 1, | ||
1259 | 0}; | ||
1260 | |||
1261 | const int head2_4b[] = {0x4f,0x67,0x67,0x53,0,0x00, | ||
1262 | 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1263 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1264 | 0xce,0x8f,0x17,0x1a, | ||
1265 | 23, | ||
1266 | 255,255,255,255,255,255,255,255, | ||
1267 | 255,255,255,255,255,255,255,255,255,10,255,4,255,0,0}; | ||
1268 | |||
1269 | |||
1270 | const int head3_4b[] = {0x4f,0x67,0x67,0x53,0,0x04, | ||
1271 | 0x07,0x14,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1272 | 0x01,0x02,0x03,0x04,2,0,0,0, | ||
1273 | 0x9b,0xb2,0x50,0xa1, | ||
1274 | 1, | ||
1275 | 0}; | ||
1276 | |||
1277 | /* page with the 255 segment limit */ | ||
1278 | const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1279 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1280 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1281 | 0xff,0x7b,0x23,0x17, | ||
1282 | 1, | ||
1283 | 0}; | ||
1284 | |||
1285 | const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, | ||
1286 | 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, | ||
1287 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1288 | 0xed,0x2a,0x2e,0xa7, | ||
1289 | 255, | ||
1290 | 10,10,10,10,10,10,10,10, | ||
1291 | 10,10,10,10,10,10,10,10, | ||
1292 | 10,10,10,10,10,10,10,10, | ||
1293 | 10,10,10,10,10,10,10,10, | ||
1294 | 10,10,10,10,10,10,10,10, | ||
1295 | 10,10,10,10,10,10,10,10, | ||
1296 | 10,10,10,10,10,10,10,10, | ||
1297 | 10,10,10,10,10,10,10,10, | ||
1298 | 10,10,10,10,10,10,10,10, | ||
1299 | 10,10,10,10,10,10,10,10, | ||
1300 | 10,10,10,10,10,10,10,10, | ||
1301 | 10,10,10,10,10,10,10,10, | ||
1302 | 10,10,10,10,10,10,10,10, | ||
1303 | 10,10,10,10,10,10,10,10, | ||
1304 | 10,10,10,10,10,10,10,10, | ||
1305 | 10,10,10,10,10,10,10,10, | ||
1306 | 10,10,10,10,10,10,10,10, | ||
1307 | 10,10,10,10,10,10,10,10, | ||
1308 | 10,10,10,10,10,10,10,10, | ||
1309 | 10,10,10,10,10,10,10,10, | ||
1310 | 10,10,10,10,10,10,10,10, | ||
1311 | 10,10,10,10,10,10,10,10, | ||
1312 | 10,10,10,10,10,10,10,10, | ||
1313 | 10,10,10,10,10,10,10,10, | ||
1314 | 10,10,10,10,10,10,10,10, | ||
1315 | 10,10,10,10,10,10,10,10, | ||
1316 | 10,10,10,10,10,10,10,10, | ||
1317 | 10,10,10,10,10,10,10,10, | ||
1318 | 10,10,10,10,10,10,10,10, | ||
1319 | 10,10,10,10,10,10,10,10, | ||
1320 | 10,10,10,10,10,10,10,10, | ||
1321 | 10,10,10,10,10,10,10}; | ||
1322 | |||
1323 | const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, | ||
1324 | 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, | ||
1325 | 0x01,0x02,0x03,0x04,2,0,0,0, | ||
1326 | 0x6c,0x3b,0x82,0x3d, | ||
1327 | 1, | ||
1328 | 50}; | ||
1329 | |||
1330 | |||
1331 | /* packet that overspans over an entire page */ | ||
1332 | const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1333 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1334 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1335 | 0xff,0x7b,0x23,0x17, | ||
1336 | 1, | ||
1337 | 0}; | ||
1338 | |||
1339 | const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, | ||
1340 | 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1341 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1342 | 0x68,0x22,0x7c,0x3d, | ||
1343 | 255, | ||
1344 | 100, | ||
1345 | 255,255,255,255,255,255,255,255, | ||
1346 | 255,255,255,255,255,255,255,255, | ||
1347 | 255,255,255,255,255,255,255,255, | ||
1348 | 255,255,255,255,255,255,255,255, | ||
1349 | 255,255,255,255,255,255,255,255, | ||
1350 | 255,255,255,255,255,255,255,255, | ||
1351 | 255,255,255,255,255,255,255,255, | ||
1352 | 255,255,255,255,255,255,255,255, | ||
1353 | 255,255,255,255,255,255,255,255, | ||
1354 | 255,255,255,255,255,255,255,255, | ||
1355 | 255,255,255,255,255,255,255,255, | ||
1356 | 255,255,255,255,255,255,255,255, | ||
1357 | 255,255,255,255,255,255,255,255, | ||
1358 | 255,255,255,255,255,255,255,255, | ||
1359 | 255,255,255,255,255,255,255,255, | ||
1360 | 255,255,255,255,255,255,255,255, | ||
1361 | 255,255,255,255,255,255,255,255, | ||
1362 | 255,255,255,255,255,255,255,255, | ||
1363 | 255,255,255,255,255,255,255,255, | ||
1364 | 255,255,255,255,255,255,255,255, | ||
1365 | 255,255,255,255,255,255,255,255, | ||
1366 | 255,255,255,255,255,255,255,255, | ||
1367 | 255,255,255,255,255,255,255,255, | ||
1368 | 255,255,255,255,255,255,255,255, | ||
1369 | 255,255,255,255,255,255,255,255, | ||
1370 | 255,255,255,255,255,255,255,255, | ||
1371 | 255,255,255,255,255,255,255,255, | ||
1372 | 255,255,255,255,255,255,255,255, | ||
1373 | 255,255,255,255,255,255,255,255, | ||
1374 | 255,255,255,255,255,255,255,255, | ||
1375 | 255,255,255,255,255,255,255,255, | ||
1376 | 255,255,255,255,255,255}; | ||
1377 | |||
1378 | const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, | ||
1379 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | ||
1380 | 0x01,0x02,0x03,0x04,2,0,0,0, | ||
1381 | 0xf4,0x87,0xba,0xf3, | ||
1382 | 255, | ||
1383 | 255,255,255,255,255,255,255,255, | ||
1384 | 255,255,255,255,255,255,255,255, | ||
1385 | 255,255,255,255,255,255,255,255, | ||
1386 | 255,255,255,255,255,255,255,255, | ||
1387 | 255,255,255,255,255,255,255,255, | ||
1388 | 255,255,255,255,255,255,255,255, | ||
1389 | 255,255,255,255,255,255,255,255, | ||
1390 | 255,255,255,255,255,255,255,255, | ||
1391 | 255,255,255,255,255,255,255,255, | ||
1392 | 255,255,255,255,255,255,255,255, | ||
1393 | 255,255,255,255,255,255,255,255, | ||
1394 | 255,255,255,255,255,255,255,255, | ||
1395 | 255,255,255,255,255,255,255,255, | ||
1396 | 255,255,255,255,255,255,255,255, | ||
1397 | 255,255,255,255,255,255,255,255, | ||
1398 | 255,255,255,255,255,255,255,255, | ||
1399 | 255,255,255,255,255,255,255,255, | ||
1400 | 255,255,255,255,255,255,255,255, | ||
1401 | 255,255,255,255,255,255,255,255, | ||
1402 | 255,255,255,255,255,255,255,255, | ||
1403 | 255,255,255,255,255,255,255,255, | ||
1404 | 255,255,255,255,255,255,255,255, | ||
1405 | 255,255,255,255,255,255,255,255, | ||
1406 | 255,255,255,255,255,255,255,255, | ||
1407 | 255,255,255,255,255,255,255,255, | ||
1408 | 255,255,255,255,255,255,255,255, | ||
1409 | 255,255,255,255,255,255,255,255, | ||
1410 | 255,255,255,255,255,255,255,255, | ||
1411 | 255,255,255,255,255,255,255,255, | ||
1412 | 255,255,255,255,255,255,255,255, | ||
1413 | 255,255,255,255,255,255,255,255, | ||
1414 | 255,255,255,255,255,255,255}; | ||
1415 | |||
1416 | const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, | ||
1417 | 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1418 | 0x01,0x02,0x03,0x04,3,0,0,0, | ||
1419 | 0xf7,0x2f,0x6c,0x60, | ||
1420 | 5, | ||
1421 | 254,255,4,255,0}; | ||
1422 | |||
1423 | /* packet that overspans over an entire page */ | ||
1424 | const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, | ||
1425 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1426 | 0x01,0x02,0x03,0x04,0,0,0,0, | ||
1427 | 0xff,0x7b,0x23,0x17, | ||
1428 | 1, | ||
1429 | 0}; | ||
1430 | |||
1431 | const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, | ||
1432 | 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1433 | 0x01,0x02,0x03,0x04,1,0,0,0, | ||
1434 | 0x68,0x22,0x7c,0x3d, | ||
1435 | 255, | ||
1436 | 100, | ||
1437 | 255,255,255,255,255,255,255,255, | ||
1438 | 255,255,255,255,255,255,255,255, | ||
1439 | 255,255,255,255,255,255,255,255, | ||
1440 | 255,255,255,255,255,255,255,255, | ||
1441 | 255,255,255,255,255,255,255,255, | ||
1442 | 255,255,255,255,255,255,255,255, | ||
1443 | 255,255,255,255,255,255,255,255, | ||
1444 | 255,255,255,255,255,255,255,255, | ||
1445 | 255,255,255,255,255,255,255,255, | ||
1446 | 255,255,255,255,255,255,255,255, | ||
1447 | 255,255,255,255,255,255,255,255, | ||
1448 | 255,255,255,255,255,255,255,255, | ||
1449 | 255,255,255,255,255,255,255,255, | ||
1450 | 255,255,255,255,255,255,255,255, | ||
1451 | 255,255,255,255,255,255,255,255, | ||
1452 | 255,255,255,255,255,255,255,255, | ||
1453 | 255,255,255,255,255,255,255,255, | ||
1454 | 255,255,255,255,255,255,255,255, | ||
1455 | 255,255,255,255,255,255,255,255, | ||
1456 | 255,255,255,255,255,255,255,255, | ||
1457 | 255,255,255,255,255,255,255,255, | ||
1458 | 255,255,255,255,255,255,255,255, | ||
1459 | 255,255,255,255,255,255,255,255, | ||
1460 | 255,255,255,255,255,255,255,255, | ||
1461 | 255,255,255,255,255,255,255,255, | ||
1462 | 255,255,255,255,255,255,255,255, | ||
1463 | 255,255,255,255,255,255,255,255, | ||
1464 | 255,255,255,255,255,255,255,255, | ||
1465 | 255,255,255,255,255,255,255,255, | ||
1466 | 255,255,255,255,255,255,255,255, | ||
1467 | 255,255,255,255,255,255,255,255, | ||
1468 | 255,255,255,255,255,255}; | ||
1469 | |||
1470 | const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, | ||
1471 | 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, | ||
1472 | 0x01,0x02,0x03,0x04,2,0,0,0, | ||
1473 | 0xd4,0xe0,0x60,0xe5, | ||
1474 | 1, | ||
1475 | 0}; | ||
1476 | |||
1477 | void test_pack(const int *pl, const int **headers, int byteskip, | ||
1478 | int pageskip, int packetskip){ | ||
1479 | unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ | ||
1480 | long inptr=0; | ||
1481 | long outptr=0; | ||
1482 | long deptr=0; | ||
1483 | long depacket=0; | ||
1484 | long granule_pos=7,pageno=0; | ||
1485 | int i,j,packets,pageout=pageskip; | ||
1486 | int eosflag=0; | ||
1487 | int bosflag=0; | ||
1488 | |||
1489 | int byteskipcount=0; | ||
1490 | |||
1491 | ogg_stream_reset(&os_en); | ||
1492 | ogg_stream_reset(&os_de); | ||
1493 | ogg_sync_reset(&oy); | ||
1494 | |||
1495 | for(packets=0;packets<packetskip;packets++) | ||
1496 | depacket+=pl[packets]; | ||
1497 | |||
1498 | for(packets=0;;packets++)if(pl[packets]==-1)break; | ||
1499 | |||
1500 | for(i=0;i<packets;i++){ | ||
1501 | /* construct a test packet */ | ||
1502 | ogg_packet op; | ||
1503 | int len=pl[i]; | ||
1504 | |||
1505 | op.packet=data+inptr; | ||
1506 | op.bytes=len; | ||
1507 | op.e_o_s=(pl[i+1]<0?1:0); | ||
1508 | op.granulepos=granule_pos; | ||
1509 | |||
1510 | granule_pos+=1024; | ||
1511 | |||
1512 | for(j=0;j<len;j++)data[inptr++]=i+j; | ||
1513 | |||
1514 | /* submit the test packet */ | ||
1515 | ogg_stream_packetin(&os_en,&op); | ||
1516 | |||
1517 | /* retrieve any finished pages */ | ||
1518 | { | ||
1519 | ogg_page og; | ||
1520 | |||
1521 | while(ogg_stream_pageout(&os_en,&og)){ | ||
1522 | /* We have a page. Check it carefully */ | ||
1523 | |||
1524 | fprintf(stderr,"%ld, ",pageno); | ||
1525 | |||
1526 | if(headers[pageno]==NULL){ | ||
1527 | fprintf(stderr,"coded too many pages!\n"); | ||
1528 | exit(1); | ||
1529 | } | ||
1530 | |||
1531 | check_page(data+outptr,headers[pageno],&og); | ||
1532 | |||
1533 | outptr+=og.body_len; | ||
1534 | pageno++; | ||
1535 | if(pageskip){ | ||
1536 | bosflag=1; | ||
1537 | pageskip--; | ||
1538 | deptr+=og.body_len; | ||
1539 | } | ||
1540 | |||
1541 | /* have a complete page; submit it to sync/decode */ | ||
1542 | |||
1543 | { | ||
1544 | ogg_page og_de; | ||
1545 | ogg_packet op_de,op_de2; | ||
1546 | char *buf=ogg_sync_buffer(&oy,og.header_len+og.body_len); | ||
1547 | char *next=buf; | ||
1548 | byteskipcount+=og.header_len; | ||
1549 | if(byteskipcount>byteskip){ | ||
1550 | memcpy(next,og.header,byteskipcount-byteskip); | ||
1551 | next+=byteskipcount-byteskip; | ||
1552 | byteskipcount=byteskip; | ||
1553 | } | ||
1554 | |||
1555 | byteskipcount+=og.body_len; | ||
1556 | if(byteskipcount>byteskip){ | ||
1557 | memcpy(next,og.body,byteskipcount-byteskip); | ||
1558 | next+=byteskipcount-byteskip; | ||
1559 | byteskipcount=byteskip; | ||
1560 | } | ||
1561 | |||
1562 | ogg_sync_wrote(&oy,next-buf); | ||
1563 | |||
1564 | while(1){ | ||
1565 | int ret=ogg_sync_pageout(&oy,&og_de); | ||
1566 | if(ret==0)break; | ||
1567 | if(ret<0)continue; | ||
1568 | /* got a page. Happy happy. Verify that it's good. */ | ||
1569 | |||
1570 | fprintf(stderr,"(%d), ",pageout); | ||
1571 | |||
1572 | check_page(data+deptr,headers[pageout],&og_de); | ||
1573 | deptr+=og_de.body_len; | ||
1574 | pageout++; | ||
1575 | |||
1576 | /* submit it to deconstitution */ | ||
1577 | ogg_stream_pagein(&os_de,&og_de); | ||
1578 | |||
1579 | /* packets out? */ | ||
1580 | while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ | ||
1581 | ogg_stream_packetpeek(&os_de,NULL); | ||
1582 | ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ | ||
1583 | |||
1584 | /* verify peek and out match */ | ||
1585 | if(memcmp(&op_de,&op_de2,sizeof(op_de))){ | ||
1586 | fprintf(stderr,"packetout != packetpeek! pos=%ld\n", | ||
1587 | depacket); | ||
1588 | exit(1); | ||
1589 | } | ||
1590 | |||
1591 | /* verify the packet! */ | ||
1592 | /* check data */ | ||
1593 | if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ | ||
1594 | fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", | ||
1595 | depacket); | ||
1596 | exit(1); | ||
1597 | } | ||
1598 | /* check bos flag */ | ||
1599 | if(bosflag==0 && op_de.b_o_s==0){ | ||
1600 | fprintf(stderr,"b_o_s flag not set on packet!\n"); | ||
1601 | exit(1); | ||
1602 | } | ||
1603 | if(bosflag && op_de.b_o_s){ | ||
1604 | fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); | ||
1605 | exit(1); | ||
1606 | } | ||
1607 | bosflag=1; | ||
1608 | depacket+=op_de.bytes; | ||
1609 | |||
1610 | /* check eos flag */ | ||
1611 | if(eosflag){ | ||
1612 | fprintf(stderr,"Multiple decoded packets with eos flag!\n"); | ||
1613 | exit(1); | ||
1614 | } | ||
1615 | |||
1616 | if(op_de.e_o_s)eosflag=1; | ||
1617 | |||
1618 | /* check granulepos flag */ | ||
1619 | if(op_de.granulepos!=-1){ | ||
1620 | fprintf(stderr," granule:%ld ",(long)op_de.granulepos); | ||
1621 | } | ||
1622 | } | ||
1623 | } | ||
1624 | } | ||
1625 | } | ||
1626 | } | ||
1627 | } | ||
1628 | _ogg_free(data); | ||
1629 | if(headers[pageno]!=NULL){ | ||
1630 | fprintf(stderr,"did not write last page!\n"); | ||
1631 | exit(1); | ||
1632 | } | ||
1633 | if(headers[pageout]!=NULL){ | ||
1634 | fprintf(stderr,"did not decode last page!\n"); | ||
1635 | exit(1); | ||
1636 | } | ||
1637 | if(inptr!=outptr){ | ||
1638 | fprintf(stderr,"encoded page data incomplete!\n"); | ||
1639 | exit(1); | ||
1640 | } | ||
1641 | if(inptr!=deptr){ | ||
1642 | fprintf(stderr,"decoded page data incomplete!\n"); | ||
1643 | exit(1); | ||
1644 | } | ||
1645 | if(inptr!=depacket){ | ||
1646 | fprintf(stderr,"decoded packet data incomplete!\n"); | ||
1647 | exit(1); | ||
1648 | } | ||
1649 | if(!eosflag){ | ||
1650 | fprintf(stderr,"Never got a packet with EOS set!\n"); | ||
1651 | exit(1); | ||
1652 | } | ||
1653 | fprintf(stderr,"ok.\n"); | ||
1654 | } | ||
1655 | |||
1656 | int main(void){ | ||
1657 | |||
1658 | ogg_stream_init(&os_en,0x04030201); | ||
1659 | ogg_stream_init(&os_de,0x04030201); | ||
1660 | ogg_sync_init(&oy); | ||
1661 | |||
1662 | /* Exercise each code path in the framing code. Also verify that | ||
1663 | the checksums are working. */ | ||
1664 | |||
1665 | { | ||
1666 | /* 17 only */ | ||
1667 | const int packets[]={17, -1}; | ||
1668 | const int *headret[]={head1_0,NULL}; | ||
1669 | |||
1670 | fprintf(stderr,"testing single page encoding... "); | ||
1671 | test_pack(packets,headret,0,0,0); | ||
1672 | } | ||
1673 | |||
1674 | { | ||
1675 | /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ | ||
1676 | const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; | ||
1677 | const int *headret[]={head1_1,head2_1,NULL}; | ||
1678 | |||
1679 | fprintf(stderr,"testing basic page encoding... "); | ||
1680 | test_pack(packets,headret,0,0,0); | ||
1681 | } | ||
1682 | |||
1683 | { | ||
1684 | /* nil packets; beginning,middle,end */ | ||
1685 | const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; | ||
1686 | const int *headret[]={head1_2,head2_2,NULL}; | ||
1687 | |||
1688 | fprintf(stderr,"testing basic nil packets... "); | ||
1689 | test_pack(packets,headret,0,0,0); | ||
1690 | } | ||
1691 | |||
1692 | { | ||
1693 | /* large initial packet */ | ||
1694 | const int packets[]={4345,259,255,-1}; | ||
1695 | const int *headret[]={head1_3,head2_3,NULL}; | ||
1696 | |||
1697 | fprintf(stderr,"testing initial-packet lacing > 4k... "); | ||
1698 | test_pack(packets,headret,0,0,0); | ||
1699 | } | ||
1700 | |||
1701 | { | ||
1702 | /* continuing packet test; with page spill expansion, we have to | ||
1703 | overflow the lacing table. */ | ||
1704 | const int packets[]={0,65500,259,255,-1}; | ||
1705 | const int *headret[]={head1_4,head2_4,head3_4,NULL}; | ||
1706 | |||
1707 | fprintf(stderr,"testing single packet page span... "); | ||
1708 | test_pack(packets,headret,0,0,0); | ||
1709 | } | ||
1710 | |||
1711 | { | ||
1712 | /* spill expand packet test */ | ||
1713 | const int packets[]={0,4345,259,255,0,0,-1}; | ||
1714 | const int *headret[]={head1_4b,head2_4b,head3_4b,NULL}; | ||
1715 | |||
1716 | fprintf(stderr,"testing page spill expansion... "); | ||
1717 | test_pack(packets,headret,0,0,0); | ||
1718 | } | ||
1719 | |||
1720 | /* page with the 255 segment limit */ | ||
1721 | { | ||
1722 | |||
1723 | const int packets[]={0,10,10,10,10,10,10,10,10, | ||
1724 | 10,10,10,10,10,10,10,10, | ||
1725 | 10,10,10,10,10,10,10,10, | ||
1726 | 10,10,10,10,10,10,10,10, | ||
1727 | 10,10,10,10,10,10,10,10, | ||
1728 | 10,10,10,10,10,10,10,10, | ||
1729 | 10,10,10,10,10,10,10,10, | ||
1730 | 10,10,10,10,10,10,10,10, | ||
1731 | 10,10,10,10,10,10,10,10, | ||
1732 | 10,10,10,10,10,10,10,10, | ||
1733 | 10,10,10,10,10,10,10,10, | ||
1734 | 10,10,10,10,10,10,10,10, | ||
1735 | 10,10,10,10,10,10,10,10, | ||
1736 | 10,10,10,10,10,10,10,10, | ||
1737 | 10,10,10,10,10,10,10,10, | ||
1738 | 10,10,10,10,10,10,10,10, | ||
1739 | 10,10,10,10,10,10,10,10, | ||
1740 | 10,10,10,10,10,10,10,10, | ||
1741 | 10,10,10,10,10,10,10,10, | ||
1742 | 10,10,10,10,10,10,10,10, | ||
1743 | 10,10,10,10,10,10,10,10, | ||
1744 | 10,10,10,10,10,10,10,10, | ||
1745 | 10,10,10,10,10,10,10,10, | ||
1746 | 10,10,10,10,10,10,10,10, | ||
1747 | 10,10,10,10,10,10,10,10, | ||
1748 | 10,10,10,10,10,10,10,10, | ||
1749 | 10,10,10,10,10,10,10,10, | ||
1750 | 10,10,10,10,10,10,10,10, | ||
1751 | 10,10,10,10,10,10,10,10, | ||
1752 | 10,10,10,10,10,10,10,10, | ||
1753 | 10,10,10,10,10,10,10,10, | ||
1754 | 10,10,10,10,10,10,10,50,-1}; | ||
1755 | const int *headret[]={head1_5,head2_5,head3_5,NULL}; | ||
1756 | |||
1757 | fprintf(stderr,"testing max packet segments... "); | ||
1758 | test_pack(packets,headret,0,0,0); | ||
1759 | } | ||
1760 | |||
1761 | { | ||
1762 | /* packet that overspans over an entire page */ | ||
1763 | const int packets[]={0,100,130049,259,255,-1}; | ||
1764 | const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; | ||
1765 | |||
1766 | fprintf(stderr,"testing very large packets... "); | ||
1767 | test_pack(packets,headret,0,0,0); | ||
1768 | } | ||
1769 | |||
1770 | { | ||
1771 | /* test for the libogg 1.1.1 resync in large continuation bug | ||
1772 | found by Josh Coalson) */ | ||
1773 | const int packets[]={0,100,130049,259,255,-1}; | ||
1774 | const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; | ||
1775 | |||
1776 | fprintf(stderr,"testing continuation resync in very large packets... "); | ||
1777 | test_pack(packets,headret,100,2,3); | ||
1778 | } | ||
1779 | |||
1780 | { | ||
1781 | /* term only page. why not? */ | ||
1782 | const int packets[]={0,100,64770,-1}; | ||
1783 | const int *headret[]={head1_7,head2_7,head3_7,NULL}; | ||
1784 | |||
1785 | fprintf(stderr,"testing zero data page (1 nil packet)... "); | ||
1786 | test_pack(packets,headret,0,0,0); | ||
1787 | } | ||
1788 | |||
1789 | |||
1790 | |||
1791 | { | ||
1792 | /* build a bunch of pages for testing */ | ||
1793 | unsigned char *data=_ogg_malloc(1024*1024); | ||
1794 | int pl[]={0, 1,1,98,4079, 1,1,2954,2057, 76,34,912,0,234,1000,1000, 1000,300,-1}; | ||
1795 | int inptr=0,i,j; | ||
1796 | ogg_page og[5]; | ||
1797 | |||
1798 | ogg_stream_reset(&os_en); | ||
1799 | |||
1800 | for(i=0;pl[i]!=-1;i++){ | ||
1801 | ogg_packet op; | ||
1802 | int len=pl[i]; | ||
1803 | |||
1804 | op.packet=data+inptr; | ||
1805 | op.bytes=len; | ||
1806 | op.e_o_s=(pl[i+1]<0?1:0); | ||
1807 | op.granulepos=(i+1)*1000; | ||
1808 | |||
1809 | for(j=0;j<len;j++)data[inptr++]=i+j; | ||
1810 | ogg_stream_packetin(&os_en,&op); | ||
1811 | } | ||
1812 | |||
1813 | _ogg_free(data); | ||
1814 | |||
1815 | /* retrieve finished pages */ | ||
1816 | for(i=0;i<5;i++){ | ||
1817 | if(ogg_stream_pageout(&os_en,&og[i])==0){ | ||
1818 | fprintf(stderr,"Too few pages output building sync tests!\n"); | ||
1819 | exit(1); | ||
1820 | } | ||
1821 | copy_page(&og[i]); | ||
1822 | } | ||
1823 | |||
1824 | /* Test lost pages on pagein/packetout: no rollback */ | ||
1825 | { | ||
1826 | ogg_page temp; | ||
1827 | ogg_packet test; | ||
1828 | |||
1829 | fprintf(stderr,"Testing loss of pages... "); | ||
1830 | |||
1831 | ogg_sync_reset(&oy); | ||
1832 | ogg_stream_reset(&os_de); | ||
1833 | for(i=0;i<5;i++){ | ||
1834 | memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, | ||
1835 | og[i].header_len); | ||
1836 | ogg_sync_wrote(&oy,og[i].header_len); | ||
1837 | memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); | ||
1838 | ogg_sync_wrote(&oy,og[i].body_len); | ||
1839 | } | ||
1840 | |||
1841 | ogg_sync_pageout(&oy,&temp); | ||
1842 | ogg_stream_pagein(&os_de,&temp); | ||
1843 | ogg_sync_pageout(&oy,&temp); | ||
1844 | ogg_stream_pagein(&os_de,&temp); | ||
1845 | ogg_sync_pageout(&oy,&temp); | ||
1846 | /* skip */ | ||
1847 | ogg_sync_pageout(&oy,&temp); | ||
1848 | ogg_stream_pagein(&os_de,&temp); | ||
1849 | |||
1850 | /* do we get the expected results/packets? */ | ||
1851 | |||
1852 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1853 | checkpacket(&test,0,0,0); | ||
1854 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1855 | checkpacket(&test,1,1,-1); | ||
1856 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1857 | checkpacket(&test,1,2,-1); | ||
1858 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1859 | checkpacket(&test,98,3,-1); | ||
1860 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1861 | checkpacket(&test,4079,4,5000); | ||
1862 | if(ogg_stream_packetout(&os_de,&test)!=-1){ | ||
1863 | fprintf(stderr,"Error: loss of page did not return error\n"); | ||
1864 | exit(1); | ||
1865 | } | ||
1866 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1867 | checkpacket(&test,76,9,-1); | ||
1868 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1869 | checkpacket(&test,34,10,-1); | ||
1870 | fprintf(stderr,"ok.\n"); | ||
1871 | } | ||
1872 | |||
1873 | /* Test lost pages on pagein/packetout: rollback with continuation */ | ||
1874 | { | ||
1875 | ogg_page temp; | ||
1876 | ogg_packet test; | ||
1877 | |||
1878 | fprintf(stderr,"Testing loss of pages (rollback required)... "); | ||
1879 | |||
1880 | ogg_sync_reset(&oy); | ||
1881 | ogg_stream_reset(&os_de); | ||
1882 | for(i=0;i<5;i++){ | ||
1883 | memcpy(ogg_sync_buffer(&oy,og[i].header_len),og[i].header, | ||
1884 | og[i].header_len); | ||
1885 | ogg_sync_wrote(&oy,og[i].header_len); | ||
1886 | memcpy(ogg_sync_buffer(&oy,og[i].body_len),og[i].body,og[i].body_len); | ||
1887 | ogg_sync_wrote(&oy,og[i].body_len); | ||
1888 | } | ||
1889 | |||
1890 | ogg_sync_pageout(&oy,&temp); | ||
1891 | ogg_stream_pagein(&os_de,&temp); | ||
1892 | ogg_sync_pageout(&oy,&temp); | ||
1893 | ogg_stream_pagein(&os_de,&temp); | ||
1894 | ogg_sync_pageout(&oy,&temp); | ||
1895 | ogg_stream_pagein(&os_de,&temp); | ||
1896 | ogg_sync_pageout(&oy,&temp); | ||
1897 | /* skip */ | ||
1898 | ogg_sync_pageout(&oy,&temp); | ||
1899 | ogg_stream_pagein(&os_de,&temp); | ||
1900 | |||
1901 | /* do we get the expected results/packets? */ | ||
1902 | |||
1903 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1904 | checkpacket(&test,0,0,0); | ||
1905 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1906 | checkpacket(&test,1,1,-1); | ||
1907 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1908 | checkpacket(&test,1,2,-1); | ||
1909 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1910 | checkpacket(&test,98,3,-1); | ||
1911 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1912 | checkpacket(&test,4079,4,5000); | ||
1913 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1914 | checkpacket(&test,1,5,-1); | ||
1915 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1916 | checkpacket(&test,1,6,-1); | ||
1917 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1918 | checkpacket(&test,2954,7,-1); | ||
1919 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1920 | checkpacket(&test,2057,8,9000); | ||
1921 | if(ogg_stream_packetout(&os_de,&test)!=-1){ | ||
1922 | fprintf(stderr,"Error: loss of page did not return error\n"); | ||
1923 | exit(1); | ||
1924 | } | ||
1925 | if(ogg_stream_packetout(&os_de,&test)!=1)error(); | ||
1926 | checkpacket(&test,300,17,18000); | ||
1927 | fprintf(stderr,"ok.\n"); | ||
1928 | } | ||
1929 | |||
1930 | /* the rest only test sync */ | ||
1931 | { | ||
1932 | ogg_page og_de; | ||
1933 | /* Test fractional page inputs: incomplete capture */ | ||
1934 | fprintf(stderr,"Testing sync on partial inputs... "); | ||
1935 | ogg_sync_reset(&oy); | ||
1936 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, | ||
1937 | 3); | ||
1938 | ogg_sync_wrote(&oy,3); | ||
1939 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
1940 | |||
1941 | /* Test fractional page inputs: incomplete fixed header */ | ||
1942 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, | ||
1943 | 20); | ||
1944 | ogg_sync_wrote(&oy,20); | ||
1945 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
1946 | |||
1947 | /* Test fractional page inputs: incomplete header */ | ||
1948 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, | ||
1949 | 5); | ||
1950 | ogg_sync_wrote(&oy,5); | ||
1951 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
1952 | |||
1953 | /* Test fractional page inputs: incomplete body */ | ||
1954 | |||
1955 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, | ||
1956 | og[1].header_len-28); | ||
1957 | ogg_sync_wrote(&oy,og[1].header_len-28); | ||
1958 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
1959 | |||
1960 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); | ||
1961 | ogg_sync_wrote(&oy,1000); | ||
1962 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
1963 | |||
1964 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, | ||
1965 | og[1].body_len-1000); | ||
1966 | ogg_sync_wrote(&oy,og[1].body_len-1000); | ||
1967 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
1968 | |||
1969 | fprintf(stderr,"ok.\n"); | ||
1970 | } | ||
1971 | |||
1972 | /* Test fractional page inputs: page + incomplete capture */ | ||
1973 | { | ||
1974 | ogg_page og_de; | ||
1975 | fprintf(stderr,"Testing sync on 1+partial inputs... "); | ||
1976 | ogg_sync_reset(&oy); | ||
1977 | |||
1978 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, | ||
1979 | og[1].header_len); | ||
1980 | ogg_sync_wrote(&oy,og[1].header_len); | ||
1981 | |||
1982 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, | ||
1983 | og[1].body_len); | ||
1984 | ogg_sync_wrote(&oy,og[1].body_len); | ||
1985 | |||
1986 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, | ||
1987 | 20); | ||
1988 | ogg_sync_wrote(&oy,20); | ||
1989 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
1990 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
1991 | |||
1992 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, | ||
1993 | og[1].header_len-20); | ||
1994 | ogg_sync_wrote(&oy,og[1].header_len-20); | ||
1995 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, | ||
1996 | og[1].body_len); | ||
1997 | ogg_sync_wrote(&oy,og[1].body_len); | ||
1998 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
1999 | |||
2000 | fprintf(stderr,"ok.\n"); | ||
2001 | } | ||
2002 | |||
2003 | /* Test recapture: garbage + page */ | ||
2004 | { | ||
2005 | ogg_page og_de; | ||
2006 | fprintf(stderr,"Testing search for capture... "); | ||
2007 | ogg_sync_reset(&oy); | ||
2008 | |||
2009 | /* 'garbage' */ | ||
2010 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, | ||
2011 | og[1].body_len); | ||
2012 | ogg_sync_wrote(&oy,og[1].body_len); | ||
2013 | |||
2014 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, | ||
2015 | og[1].header_len); | ||
2016 | ogg_sync_wrote(&oy,og[1].header_len); | ||
2017 | |||
2018 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, | ||
2019 | og[1].body_len); | ||
2020 | ogg_sync_wrote(&oy,og[1].body_len); | ||
2021 | |||
2022 | memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, | ||
2023 | 20); | ||
2024 | ogg_sync_wrote(&oy,20); | ||
2025 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
2026 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
2027 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
2028 | |||
2029 | memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, | ||
2030 | og[2].header_len-20); | ||
2031 | ogg_sync_wrote(&oy,og[2].header_len-20); | ||
2032 | memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, | ||
2033 | og[2].body_len); | ||
2034 | ogg_sync_wrote(&oy,og[2].body_len); | ||
2035 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
2036 | |||
2037 | fprintf(stderr,"ok.\n"); | ||
2038 | } | ||
2039 | |||
2040 | /* Test recapture: page + garbage + page */ | ||
2041 | { | ||
2042 | ogg_page og_de; | ||
2043 | fprintf(stderr,"Testing recapture... "); | ||
2044 | ogg_sync_reset(&oy); | ||
2045 | |||
2046 | memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, | ||
2047 | og[1].header_len); | ||
2048 | ogg_sync_wrote(&oy,og[1].header_len); | ||
2049 | |||
2050 | memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, | ||
2051 | og[1].body_len); | ||
2052 | ogg_sync_wrote(&oy,og[1].body_len); | ||
2053 | |||
2054 | memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, | ||
2055 | og[2].header_len); | ||
2056 | ogg_sync_wrote(&oy,og[2].header_len); | ||
2057 | |||
2058 | memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, | ||
2059 | og[2].header_len); | ||
2060 | ogg_sync_wrote(&oy,og[2].header_len); | ||
2061 | |||
2062 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
2063 | |||
2064 | memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, | ||
2065 | og[2].body_len-5); | ||
2066 | ogg_sync_wrote(&oy,og[2].body_len-5); | ||
2067 | |||
2068 | memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, | ||
2069 | og[3].header_len); | ||
2070 | ogg_sync_wrote(&oy,og[3].header_len); | ||
2071 | |||
2072 | memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, | ||
2073 | og[3].body_len); | ||
2074 | ogg_sync_wrote(&oy,og[3].body_len); | ||
2075 | |||
2076 | if(ogg_sync_pageout(&oy,&og_de)>0)error(); | ||
2077 | if(ogg_sync_pageout(&oy,&og_de)<=0)error(); | ||
2078 | |||
2079 | fprintf(stderr,"ok.\n"); | ||
2080 | } | ||
2081 | |||
2082 | /* Free page data that was previously copied */ | ||
2083 | { | ||
2084 | for(i=0;i<5;i++){ | ||
2085 | free_page(&og[i]); | ||
2086 | } | ||
2087 | } | ||
2088 | } | ||
2089 | |||
2090 | return(0); | ||
1066 | } | 2091 | } |
1067 | 2092 | ||
2093 | #endif | ||
2094 | |||
2095 | |||
2096 | |||
2097 | |||