diff options
Diffstat (limited to 'apps/codecs/Tremor/vorbisfile.c')
-rw-r--r-- | apps/codecs/Tremor/vorbisfile.c | 1592 |
1 files changed, 1592 insertions, 0 deletions
diff --git a/apps/codecs/Tremor/vorbisfile.c b/apps/codecs/Tremor/vorbisfile.c new file mode 100644 index 0000000000..7448b1ed1d --- /dev/null +++ b/apps/codecs/Tremor/vorbisfile.c | |||
@@ -0,0 +1,1592 @@ | |||
1 | /******************************************************************** | ||
2 | * * | ||
3 | * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * | ||
4 | * * | ||
5 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * | ||
6 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * | ||
7 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * | ||
8 | * * | ||
9 | * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003 * | ||
10 | * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * | ||
11 | * * | ||
12 | ******************************************************************** | ||
13 | |||
14 | function: stdio-based convenience library for opening/seeking/decoding | ||
15 | last mod: $Id$ | ||
16 | |||
17 | ********************************************************************/ | ||
18 | |||
19 | #include <stdlib.h> | ||
20 | #include <stdio.h> | ||
21 | #include <errno.h> | ||
22 | #include <string.h> | ||
23 | #include <math.h> | ||
24 | |||
25 | #include "ivorbiscodec.h" | ||
26 | #include "ivorbisfile.h" | ||
27 | |||
28 | #include "os.h" | ||
29 | #include "misc.h" | ||
30 | |||
31 | /* A 'chained bitstream' is a Vorbis bitstream that contains more than | ||
32 | one logical bitstream arranged end to end (the only form of Ogg | ||
33 | multiplexing allowed in a Vorbis bitstream; grouping [parallel | ||
34 | multiplexing] is not allowed in Vorbis) */ | ||
35 | |||
36 | /* A Vorbis file can be played beginning to end (streamed) without | ||
37 | worrying ahead of time about chaining (see decoder_example.c). If | ||
38 | we have the whole file, however, and want random access | ||
39 | (seeking/scrubbing) or desire to know the total length/time of a | ||
40 | file, we need to account for the possibility of chaining. */ | ||
41 | |||
42 | /* We can handle things a number of ways; we can determine the entire | ||
43 | bitstream structure right off the bat, or find pieces on demand. | ||
44 | This example determines and caches structure for the entire | ||
45 | bitstream, but builds a virtual decoder on the fly when moving | ||
46 | between links in the chain. */ | ||
47 | |||
48 | /* There are also different ways to implement seeking. Enough | ||
49 | information exists in an Ogg bitstream to seek to | ||
50 | sample-granularity positions in the output. Or, one can seek by | ||
51 | picking some portion of the stream roughly in the desired area if | ||
52 | we only want coarse navigation through the stream. */ | ||
53 | |||
54 | /************************************************************************* | ||
55 | * Many, many internal helpers. The intention is not to be confusing; | ||
56 | * rampant duplication and monolithic function implementation would be | ||
57 | * harder to understand anyway. The high level functions are last. Begin | ||
58 | * grokking near the end of the file */ | ||
59 | |||
60 | |||
61 | /* read a little more data from the file/pipe into the ogg_sync framer */ | ||
62 | static long _get_data(OggVorbis_File *vf){ | ||
63 | errno=0; | ||
64 | if(vf->datasource){ | ||
65 | char *buffer=ogg_sync_bufferin(vf->oy,CHUNKSIZE); | ||
66 | long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); | ||
67 | if(bytes>0)ogg_sync_wrote(vf->oy,bytes); | ||
68 | if(bytes==0 && errno)return(-1); | ||
69 | return(bytes); | ||
70 | }else | ||
71 | return(0); | ||
72 | } | ||
73 | |||
74 | /* save a tiny smidge of verbosity to make the code more readable */ | ||
75 | static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ | ||
76 | if(vf->datasource){ | ||
77 | (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET); | ||
78 | vf->offset=offset; | ||
79 | ogg_sync_reset(vf->oy); | ||
80 | }else{ | ||
81 | /* shouldn't happen unless someone writes a broken callback */ | ||
82 | return; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | /* The read/seek functions track absolute position within the stream */ | ||
87 | |||
88 | /* from the head of the stream, get the next page. boundary specifies | ||
89 | if the function is allowed to fetch more data from the stream (and | ||
90 | how much) or only use internally buffered data. | ||
91 | |||
92 | boundary: -1) unbounded search | ||
93 | 0) read no additional data; use cached only | ||
94 | n) search for a new page beginning for n bytes | ||
95 | |||
96 | return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) | ||
97 | n) found a page at absolute offset n | ||
98 | |||
99 | produces a refcounted page */ | ||
100 | |||
101 | static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, | ||
102 | ogg_int64_t boundary){ | ||
103 | if(boundary>0)boundary+=vf->offset; | ||
104 | while(1){ | ||
105 | long more; | ||
106 | |||
107 | if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); | ||
108 | more=ogg_sync_pageseek(vf->oy,og); | ||
109 | |||
110 | if(more<0){ | ||
111 | /* skipped n bytes */ | ||
112 | vf->offset-=more; | ||
113 | }else{ | ||
114 | if(more==0){ | ||
115 | /* send more paramedics */ | ||
116 | if(!boundary)return(OV_FALSE); | ||
117 | { | ||
118 | long ret=_get_data(vf); | ||
119 | if(ret==0)return(OV_EOF); | ||
120 | if(ret<0)return(OV_EREAD); | ||
121 | } | ||
122 | }else{ | ||
123 | /* got a page. Return the offset at the page beginning, | ||
124 | advance the internal offset past the page end */ | ||
125 | ogg_int64_t ret=vf->offset; | ||
126 | vf->offset+=more; | ||
127 | return(ret); | ||
128 | |||
129 | } | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | /* find the latest page beginning before the current stream cursor | ||
135 | position. Much dirtier than the above as Ogg doesn't have any | ||
136 | backward search linkage. no 'readp' as it will certainly have to | ||
137 | read. */ | ||
138 | /* returns offset or OV_EREAD, OV_FAULT and produces a refcounted page */ | ||
139 | |||
140 | static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ | ||
141 | ogg_int64_t begin=vf->offset; | ||
142 | ogg_int64_t end=begin; | ||
143 | ogg_int64_t ret; | ||
144 | ogg_int64_t offset=-1; | ||
145 | |||
146 | while(offset==-1){ | ||
147 | begin-=CHUNKSIZE; | ||
148 | if(begin<0) | ||
149 | begin=0; | ||
150 | _seek_helper(vf,begin); | ||
151 | while(vf->offset<end){ | ||
152 | ret=_get_next_page(vf,og,end-vf->offset); | ||
153 | if(ret==OV_EREAD)return(OV_EREAD); | ||
154 | if(ret<0){ | ||
155 | break; | ||
156 | }else{ | ||
157 | offset=ret; | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | /* we have the offset. Actually snork and hold the page now */ | ||
163 | _seek_helper(vf,offset); | ||
164 | ret=_get_next_page(vf,og,CHUNKSIZE); | ||
165 | if(ret<0) | ||
166 | /* this shouldn't be possible */ | ||
167 | return(OV_EFAULT); | ||
168 | |||
169 | return(offset); | ||
170 | } | ||
171 | |||
172 | /* finds each bitstream link one at a time using a bisection search | ||
173 | (has to begin by knowing the offset of the lb's initial page). | ||
174 | Recurses for each link so it can alloc the link storage after | ||
175 | finding them all, then unroll and fill the cache at the same time */ | ||
176 | static int _bisect_forward_serialno(OggVorbis_File *vf, | ||
177 | ogg_int64_t begin, | ||
178 | ogg_int64_t searched, | ||
179 | ogg_int64_t end, | ||
180 | ogg_uint32_t currentno, | ||
181 | long m){ | ||
182 | ogg_int64_t endsearched=end; | ||
183 | ogg_int64_t next=end; | ||
184 | ogg_page og={0,0,0,0}; | ||
185 | ogg_int64_t ret; | ||
186 | |||
187 | /* the below guards against garbage seperating the last and | ||
188 | first pages of two links. */ | ||
189 | while(searched<endsearched){ | ||
190 | ogg_int64_t bisect; | ||
191 | |||
192 | if(endsearched-searched<CHUNKSIZE){ | ||
193 | bisect=searched; | ||
194 | }else{ | ||
195 | bisect=(searched+endsearched)/2; | ||
196 | } | ||
197 | |||
198 | _seek_helper(vf,bisect); | ||
199 | ret=_get_next_page(vf,&og,-1); | ||
200 | if(ret==OV_EREAD)return(OV_EREAD); | ||
201 | if(ret<0 || ogg_page_serialno(&og)!=currentno){ | ||
202 | endsearched=bisect; | ||
203 | if(ret>=0)next=ret; | ||
204 | }else{ | ||
205 | searched=ret+og.header_len+og.body_len; | ||
206 | } | ||
207 | ogg_page_release(&og); | ||
208 | } | ||
209 | |||
210 | _seek_helper(vf,next); | ||
211 | ret=_get_next_page(vf,&og,-1); | ||
212 | if(ret==OV_EREAD)return(OV_EREAD); | ||
213 | |||
214 | if(searched>=end || ret<0){ | ||
215 | ogg_page_release(&og); | ||
216 | vf->links=m+1; | ||
217 | vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); | ||
218 | vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); | ||
219 | vf->offsets[m+1]=searched; | ||
220 | }else{ | ||
221 | ret=_bisect_forward_serialno(vf,next,vf->offset, | ||
222 | end,ogg_page_serialno(&og),m+1); | ||
223 | ogg_page_release(&og); | ||
224 | if(ret==OV_EREAD)return(OV_EREAD); | ||
225 | } | ||
226 | |||
227 | vf->offsets[m]=begin; | ||
228 | vf->serialnos[m]=currentno; | ||
229 | return(0); | ||
230 | } | ||
231 | |||
232 | /* uses the local ogg_stream storage in vf; this is important for | ||
233 | non-streaming input sources */ | ||
234 | /* consumes the page that's passed in (if any) */ | ||
235 | |||
236 | static int _fetch_headers(OggVorbis_File *vf, | ||
237 | vorbis_info *vi, | ||
238 | vorbis_comment *vc, | ||
239 | ogg_uint32_t *serialno, | ||
240 | ogg_page *og_ptr){ | ||
241 | ogg_page og={0,0,0,0}; | ||
242 | ogg_packet op={0,0,0,0,0,0}; | ||
243 | int i,ret; | ||
244 | |||
245 | if(!og_ptr){ | ||
246 | ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); | ||
247 | if(llret==OV_EREAD)return(OV_EREAD); | ||
248 | if(llret<0)return OV_ENOTVORBIS; | ||
249 | og_ptr=&og; | ||
250 | } | ||
251 | |||
252 | ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr)); | ||
253 | if(serialno)*serialno=vf->os->serialno; | ||
254 | vf->ready_state=STREAMSET; | ||
255 | |||
256 | /* extract the initial header from the first page and verify that the | ||
257 | Ogg bitstream is in fact Vorbis data */ | ||
258 | |||
259 | vorbis_info_init(vi); | ||
260 | vorbis_comment_init(vc); | ||
261 | |||
262 | i=0; | ||
263 | while(i<3){ | ||
264 | ogg_stream_pagein(vf->os,og_ptr); | ||
265 | while(i<3){ | ||
266 | int result=ogg_stream_packetout(vf->os,&op); | ||
267 | if(result==0)break; | ||
268 | if(result==-1){ | ||
269 | ret=OV_EBADHEADER; | ||
270 | goto bail_header; | ||
271 | } | ||
272 | if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ | ||
273 | goto bail_header; | ||
274 | } | ||
275 | i++; | ||
276 | } | ||
277 | if(i<3) | ||
278 | if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ | ||
279 | ret=OV_EBADHEADER; | ||
280 | goto bail_header; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | ogg_packet_release(&op); | ||
285 | ogg_page_release(&og); | ||
286 | return 0; | ||
287 | |||
288 | bail_header: | ||
289 | ogg_packet_release(&op); | ||
290 | ogg_page_release(&og); | ||
291 | vorbis_info_clear(vi); | ||
292 | vorbis_comment_clear(vc); | ||
293 | vf->ready_state=OPENED; | ||
294 | |||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | /* last step of the OggVorbis_File initialization; get all the | ||
299 | vorbis_info structs and PCM positions. Only called by the seekable | ||
300 | initialization (local stream storage is hacked slightly; pay | ||
301 | attention to how that's done) */ | ||
302 | |||
303 | /* this is void and does not propogate errors up because we want to be | ||
304 | able to open and use damaged bitstreams as well as we can. Just | ||
305 | watch out for missing information for links in the OggVorbis_File | ||
306 | struct */ | ||
307 | static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ | ||
308 | ogg_page og={0,0,0,0}; | ||
309 | int i; | ||
310 | ogg_int64_t ret; | ||
311 | |||
312 | vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); | ||
313 | vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); | ||
314 | vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); | ||
315 | vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); | ||
316 | |||
317 | for(i=0;i<vf->links;i++){ | ||
318 | if(i==0){ | ||
319 | /* we already grabbed the initial header earlier. Just set the offset */ | ||
320 | vf->dataoffsets[i]=dataoffset; | ||
321 | _seek_helper(vf,dataoffset); | ||
322 | |||
323 | }else{ | ||
324 | |||
325 | /* seek to the location of the initial header */ | ||
326 | |||
327 | _seek_helper(vf,vf->offsets[i]); | ||
328 | if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL,NULL)<0){ | ||
329 | vf->dataoffsets[i]=-1; | ||
330 | }else{ | ||
331 | vf->dataoffsets[i]=vf->offset; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | /* fetch beginning PCM offset */ | ||
336 | |||
337 | if(vf->dataoffsets[i]!=-1){ | ||
338 | ogg_int64_t accumulated=0,pos; | ||
339 | long lastblock=-1; | ||
340 | int result; | ||
341 | |||
342 | ogg_stream_reset_serialno(vf->os,vf->serialnos[i]); | ||
343 | |||
344 | while(1){ | ||
345 | ogg_packet op={0,0,0,0,0,0}; | ||
346 | |||
347 | ret=_get_next_page(vf,&og,-1); | ||
348 | if(ret<0) | ||
349 | /* this should not be possible unless the file is | ||
350 | truncated/mangled */ | ||
351 | break; | ||
352 | |||
353 | if(ogg_page_serialno(&og)!=vf->serialnos[i]) | ||
354 | break; | ||
355 | |||
356 | pos=ogg_page_granulepos(&og); | ||
357 | |||
358 | /* count blocksizes of all frames in the page */ | ||
359 | ogg_stream_pagein(vf->os,&og); | ||
360 | while((result=ogg_stream_packetout(vf->os,&op))){ | ||
361 | if(result>0){ /* ignore holes */ | ||
362 | long thisblock=vorbis_packet_blocksize(vf->vi+i,&op); | ||
363 | if(lastblock!=-1) | ||
364 | accumulated+=(lastblock+thisblock)>>2; | ||
365 | lastblock=thisblock; | ||
366 | } | ||
367 | } | ||
368 | ogg_packet_release(&op); | ||
369 | |||
370 | if(pos!=-1){ | ||
371 | /* pcm offset of last packet on the first audio page */ | ||
372 | accumulated= pos-accumulated; | ||
373 | break; | ||
374 | } | ||
375 | } | ||
376 | |||
377 | /* less than zero? This is a stream with samples trimmed off | ||
378 | the beginning, a normal occurrence; set the offset to zero */ | ||
379 | if(accumulated<0)accumulated=0; | ||
380 | |||
381 | vf->pcmlengths[i*2]=accumulated; | ||
382 | } | ||
383 | |||
384 | /* get the PCM length of this link. To do this, | ||
385 | get the last page of the stream */ | ||
386 | { | ||
387 | ogg_int64_t end=vf->offsets[i+1]; | ||
388 | _seek_helper(vf,end); | ||
389 | |||
390 | while(1){ | ||
391 | ret=_get_prev_page(vf,&og); | ||
392 | if(ret<0){ | ||
393 | /* this should not be possible */ | ||
394 | vorbis_info_clear(vf->vi+i); | ||
395 | vorbis_comment_clear(vf->vc+i); | ||
396 | break; | ||
397 | } | ||
398 | if(ogg_page_granulepos(&og)!=-1){ | ||
399 | vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2]; | ||
400 | break; | ||
401 | } | ||
402 | vf->offset=ret; | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | ogg_page_release(&og); | ||
407 | } | ||
408 | |||
409 | static void _make_decode_ready(OggVorbis_File *vf){ | ||
410 | if(vf->ready_state!=STREAMSET)return; | ||
411 | if(vf->seekable){ | ||
412 | vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link); | ||
413 | }else{ | ||
414 | vorbis_synthesis_init(&vf->vd,vf->vi); | ||
415 | } | ||
416 | vorbis_block_init(&vf->vd,&vf->vb); | ||
417 | vf->ready_state=INITSET; | ||
418 | vf->bittrack=0; | ||
419 | vf->samptrack=0; | ||
420 | return; | ||
421 | } | ||
422 | |||
423 | static int _open_seekable2(OggVorbis_File *vf){ | ||
424 | ogg_uint32_t serialno=vf->current_serialno; | ||
425 | ogg_uint32_t tempserialno; | ||
426 | ogg_int64_t dataoffset=vf->offset, end; | ||
427 | ogg_page og={0,0,0,0}; | ||
428 | |||
429 | /* we're partially open and have a first link header state in | ||
430 | storage in vf */ | ||
431 | /* we can seek, so set out learning all about this file */ | ||
432 | (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); | ||
433 | vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); | ||
434 | |||
435 | /* We get the offset for the last page of the physical bitstream. | ||
436 | Most OggVorbis files will contain a single logical bitstream */ | ||
437 | end=_get_prev_page(vf,&og); | ||
438 | if(end<0)return(end); | ||
439 | |||
440 | /* more than one logical bitstream? */ | ||
441 | tempserialno=ogg_page_serialno(&og); | ||
442 | ogg_page_release(&og); | ||
443 | |||
444 | if(tempserialno!=serialno){ | ||
445 | |||
446 | /* Chained bitstream. Bisect-search each logical bitstream | ||
447 | section. Do so based on serial number only */ | ||
448 | if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return(OV_EREAD); | ||
449 | |||
450 | }else{ | ||
451 | |||
452 | /* Only one logical bitstream */ | ||
453 | if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return(OV_EREAD); | ||
454 | |||
455 | } | ||
456 | |||
457 | /* the initial header memory is referenced by vf after; don't free it */ | ||
458 | _prefetch_all_headers(vf,dataoffset); | ||
459 | return(ov_raw_seek(vf,0)); | ||
460 | } | ||
461 | |||
462 | /* clear out the current logical bitstream decoder */ | ||
463 | static void _decode_clear(OggVorbis_File *vf){ | ||
464 | vorbis_dsp_clear(&vf->vd); | ||
465 | vorbis_block_clear(&vf->vb); | ||
466 | vf->ready_state=OPENED; | ||
467 | } | ||
468 | |||
469 | /* fetch and process a packet. Handles the case where we're at a | ||
470 | bitstream boundary and dumps the decoding machine. If the decoding | ||
471 | machine is unloaded, it loads it. It also keeps pcm_offset up to | ||
472 | date (seek and read both use this. seek uses a special hack with | ||
473 | readp). | ||
474 | |||
475 | return: <0) error, OV_HOLE (lost packet) or OV_EOF | ||
476 | 0) need more data (only if readp==0) | ||
477 | 1) got a packet | ||
478 | */ | ||
479 | |||
480 | static int _fetch_and_process_packet(OggVorbis_File *vf, | ||
481 | int readp, | ||
482 | int spanp){ | ||
483 | ogg_page og={0,0,0,0}; | ||
484 | ogg_packet op={0,0,0,0,0,0}; | ||
485 | int ret=0; | ||
486 | |||
487 | /* handle one packet. Try to fetch it from current stream state */ | ||
488 | /* extract packets from page */ | ||
489 | while(1){ | ||
490 | |||
491 | /* process a packet if we can. If the machine isn't loaded, | ||
492 | neither is a page */ | ||
493 | if(vf->ready_state==INITSET){ | ||
494 | while(1) { | ||
495 | int result=ogg_stream_packetout(vf->os,&op); | ||
496 | ogg_int64_t granulepos; | ||
497 | |||
498 | if(result<0){ | ||
499 | ret=OV_HOLE; /* hole in the data. */ | ||
500 | goto cleanup; | ||
501 | } | ||
502 | if(result>0){ | ||
503 | /* got a packet. process it */ | ||
504 | granulepos=op.granulepos; | ||
505 | if(!vorbis_synthesis(&vf->vb,&op,1)){ /* lazy check for lazy | ||
506 | header handling. The | ||
507 | header packets aren't | ||
508 | audio, so if/when we | ||
509 | submit them, | ||
510 | vorbis_synthesis will | ||
511 | reject them */ | ||
512 | |||
513 | /* suck in the synthesis data and track bitrate */ | ||
514 | { | ||
515 | int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); | ||
516 | /* for proper use of libvorbis within libvorbisfile, | ||
517 | oldsamples will always be zero. */ | ||
518 | if(oldsamples){ | ||
519 | ret=OV_EFAULT; | ||
520 | goto cleanup; | ||
521 | } | ||
522 | |||
523 | vorbis_synthesis_blockin(&vf->vd,&vf->vb); | ||
524 | vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; | ||
525 | vf->bittrack+=op.bytes*8; | ||
526 | } | ||
527 | |||
528 | /* update the pcm offset. */ | ||
529 | if(granulepos!=-1 && !op.e_o_s){ | ||
530 | int link=(vf->seekable?vf->current_link:0); | ||
531 | int i,samples; | ||
532 | |||
533 | /* this packet has a pcm_offset on it (the last packet | ||
534 | completed on a page carries the offset) After processing | ||
535 | (above), we know the pcm position of the *last* sample | ||
536 | ready to be returned. Find the offset of the *first* | ||
537 | |||
538 | As an aside, this trick is inaccurate if we begin | ||
539 | reading anew right at the last page; the end-of-stream | ||
540 | granulepos declares the last frame in the stream, and the | ||
541 | last packet of the last page may be a partial frame. | ||
542 | So, we need a previous granulepos from an in-sequence page | ||
543 | to have a reference point. Thus the !op.e_o_s clause | ||
544 | above */ | ||
545 | |||
546 | if(vf->seekable && link>0) | ||
547 | granulepos-=vf->pcmlengths[link*2]; | ||
548 | if(granulepos<0)granulepos=0; /* actually, this | ||
549 | shouldn't be possible | ||
550 | here unless the stream | ||
551 | is very broken */ | ||
552 | |||
553 | samples=vorbis_synthesis_pcmout(&vf->vd,NULL); | ||
554 | |||
555 | granulepos-=samples; | ||
556 | for(i=0;i<link;i++) | ||
557 | granulepos+=vf->pcmlengths[i*2+1]; | ||
558 | vf->pcm_offset=granulepos; | ||
559 | } | ||
560 | ret=1; | ||
561 | goto cleanup; | ||
562 | } | ||
563 | } | ||
564 | else | ||
565 | break; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | if(vf->ready_state>=OPENED){ | ||
570 | int ret; | ||
571 | if(!readp){ | ||
572 | ret=0; | ||
573 | goto cleanup; | ||
574 | } | ||
575 | if((ret=_get_next_page(vf,&og,-1))<0){ | ||
576 | ret=OV_EOF; /* eof. leave unitialized */ | ||
577 | goto cleanup; | ||
578 | } | ||
579 | |||
580 | /* bitrate tracking; add the header's bytes here, the body bytes | ||
581 | are done by packet above */ | ||
582 | vf->bittrack+=og.header_len*8; | ||
583 | |||
584 | /* has our decoding just traversed a bitstream boundary? */ | ||
585 | if(vf->ready_state==INITSET){ | ||
586 | if(vf->current_serialno!=ogg_page_serialno(&og)){ | ||
587 | if(!spanp){ | ||
588 | ret=OV_EOF; | ||
589 | goto cleanup; | ||
590 | } | ||
591 | |||
592 | _decode_clear(vf); | ||
593 | |||
594 | if(!vf->seekable){ | ||
595 | vorbis_info_clear(vf->vi); | ||
596 | vorbis_comment_clear(vf->vc); | ||
597 | } | ||
598 | } | ||
599 | } | ||
600 | } | ||
601 | |||
602 | /* Do we need to load a new machine before submitting the page? */ | ||
603 | /* This is different in the seekable and non-seekable cases. | ||
604 | |||
605 | In the seekable case, we already have all the header | ||
606 | information loaded and cached; we just initialize the machine | ||
607 | with it and continue on our merry way. | ||
608 | |||
609 | In the non-seekable (streaming) case, we'll only be at a | ||
610 | boundary if we just left the previous logical bitstream and | ||
611 | we're now nominally at the header of the next bitstream | ||
612 | */ | ||
613 | |||
614 | if(vf->ready_state!=INITSET){ | ||
615 | int link; | ||
616 | |||
617 | if(vf->ready_state<STREAMSET){ | ||
618 | if(vf->seekable){ | ||
619 | vf->current_serialno=ogg_page_serialno(&og); | ||
620 | |||
621 | /* match the serialno to bitstream section. We use this rather than | ||
622 | offset positions to avoid problems near logical bitstream | ||
623 | boundaries */ | ||
624 | for(link=0;link<vf->links;link++) | ||
625 | if(vf->serialnos[link]==vf->current_serialno)break; | ||
626 | if(link==vf->links){ | ||
627 | ret=OV_EBADLINK; /* sign of a bogus stream. error out, | ||
628 | leave machine uninitialized */ | ||
629 | goto cleanup; | ||
630 | } | ||
631 | |||
632 | vf->current_link=link; | ||
633 | |||
634 | ogg_stream_reset_serialno(vf->os,vf->current_serialno); | ||
635 | vf->ready_state=STREAMSET; | ||
636 | |||
637 | }else{ | ||
638 | /* we're streaming */ | ||
639 | /* fetch the three header packets, build the info struct */ | ||
640 | |||
641 | int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og); | ||
642 | if(ret) goto cleanup; | ||
643 | vf->current_link++; | ||
644 | link=0; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | _make_decode_ready(vf); | ||
649 | } | ||
650 | ogg_stream_pagein(vf->os,&og); | ||
651 | } | ||
652 | cleanup: | ||
653 | ogg_packet_release(&op); | ||
654 | ogg_page_release(&og); | ||
655 | return ret; | ||
656 | } | ||
657 | |||
658 | /* if, eg, 64 bit stdio is configured by default, this will build with | ||
659 | fseek64 */ | ||
660 | static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ | ||
661 | if(f==NULL)return(-1); | ||
662 | return fseek(f,off,whence); | ||
663 | } | ||
664 | |||
665 | static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, | ||
666 | long ibytes, ov_callbacks callbacks){ | ||
667 | int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1); | ||
668 | int ret; | ||
669 | |||
670 | memset(vf,0,sizeof(*vf)); | ||
671 | vf->datasource=f; | ||
672 | vf->callbacks = callbacks; | ||
673 | |||
674 | /* init the framing state */ | ||
675 | vf->oy=ogg_sync_create(); | ||
676 | |||
677 | /* perhaps some data was previously read into a buffer for testing | ||
678 | against other stream types. Allow initialization from this | ||
679 | previously read data (as we may be reading from a non-seekable | ||
680 | stream) */ | ||
681 | if(initial){ | ||
682 | char *buffer=ogg_sync_bufferin(vf->oy,ibytes); | ||
683 | memcpy(buffer,initial,ibytes); | ||
684 | ogg_sync_wrote(vf->oy,ibytes); | ||
685 | } | ||
686 | |||
687 | /* can we seek? Stevens suggests the seek test was portable */ | ||
688 | if(offsettest!=-1)vf->seekable=1; | ||
689 | |||
690 | /* No seeking yet; Set up a 'single' (current) logical bitstream | ||
691 | entry for partial open */ | ||
692 | vf->links=1; | ||
693 | vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); | ||
694 | vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); | ||
695 | vf->os=ogg_stream_create(-1); /* fill in the serialno later */ | ||
696 | |||
697 | /* Try to fetch the headers, maintaining all the storage */ | ||
698 | if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){ | ||
699 | vf->datasource=NULL; | ||
700 | ov_clear(vf); | ||
701 | }else if(vf->ready_state < PARTOPEN) | ||
702 | vf->ready_state=PARTOPEN; | ||
703 | return(ret); | ||
704 | } | ||
705 | |||
706 | static int _ov_open2(OggVorbis_File *vf){ | ||
707 | if(vf->ready_state < OPENED) | ||
708 | vf->ready_state=OPENED; | ||
709 | if(vf->seekable){ | ||
710 | int ret=_open_seekable2(vf); | ||
711 | if(ret){ | ||
712 | vf->datasource=NULL; | ||
713 | ov_clear(vf); | ||
714 | } | ||
715 | return(ret); | ||
716 | } | ||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | |||
721 | /* clear out the OggVorbis_File struct */ | ||
722 | int ov_clear(OggVorbis_File *vf){ | ||
723 | if(vf){ | ||
724 | vorbis_block_clear(&vf->vb); | ||
725 | vorbis_dsp_clear(&vf->vd); | ||
726 | ogg_stream_destroy(vf->os); | ||
727 | |||
728 | if(vf->vi && vf->links){ | ||
729 | int i; | ||
730 | for(i=0;i<vf->links;i++){ | ||
731 | vorbis_info_clear(vf->vi+i); | ||
732 | vorbis_comment_clear(vf->vc+i); | ||
733 | } | ||
734 | _ogg_free(vf->vi); | ||
735 | _ogg_free(vf->vc); | ||
736 | } | ||
737 | if(vf->dataoffsets)_ogg_free(vf->dataoffsets); | ||
738 | if(vf->pcmlengths)_ogg_free(vf->pcmlengths); | ||
739 | if(vf->serialnos)_ogg_free(vf->serialnos); | ||
740 | if(vf->offsets)_ogg_free(vf->offsets); | ||
741 | ogg_sync_destroy(vf->oy); | ||
742 | |||
743 | if(vf->datasource)(vf->callbacks.close_func)(vf->datasource); | ||
744 | memset(vf,0,sizeof(*vf)); | ||
745 | } | ||
746 | #ifdef DEBUG_LEAKS | ||
747 | _VDBG_dump(); | ||
748 | #endif | ||
749 | return(0); | ||
750 | } | ||
751 | |||
752 | /* inspects the OggVorbis file and finds/documents all the logical | ||
753 | bitstreams contained in it. Tries to be tolerant of logical | ||
754 | bitstream sections that are truncated/woogie. | ||
755 | |||
756 | return: -1) error | ||
757 | 0) OK | ||
758 | */ | ||
759 | |||
760 | int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, | ||
761 | ov_callbacks callbacks){ | ||
762 | int ret=_ov_open1(f,vf,initial,ibytes,callbacks); | ||
763 | if(ret)return ret; | ||
764 | return _ov_open2(vf); | ||
765 | } | ||
766 | |||
767 | int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ | ||
768 | ov_callbacks callbacks = { | ||
769 | (size_t (*)(void *, size_t, size_t, void *)) fread, | ||
770 | (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, | ||
771 | (int (*)(void *)) fclose, | ||
772 | (long (*)(void *)) ftell | ||
773 | }; | ||
774 | |||
775 | return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); | ||
776 | } | ||
777 | |||
778 | /* Only partially open the vorbis file; test for Vorbisness, and load | ||
779 | the headers for the first chain. Do not seek (although test for | ||
780 | seekability). Use ov_test_open to finish opening the file, else | ||
781 | ov_clear to close/free it. Same return codes as open. */ | ||
782 | |||
783 | int ov_test_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, | ||
784 | ov_callbacks callbacks) | ||
785 | { | ||
786 | return _ov_open1(f,vf,initial,ibytes,callbacks); | ||
787 | } | ||
788 | |||
789 | int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ | ||
790 | ov_callbacks callbacks = { | ||
791 | (size_t (*)(void *, size_t, size_t, void *)) fread, | ||
792 | (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, | ||
793 | (int (*)(void *)) fclose, | ||
794 | (long (*)(void *)) ftell | ||
795 | }; | ||
796 | |||
797 | return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); | ||
798 | } | ||
799 | |||
800 | int ov_test_open(OggVorbis_File *vf){ | ||
801 | if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); | ||
802 | return _ov_open2(vf); | ||
803 | } | ||
804 | |||
805 | /* How many logical bitstreams in this physical bitstream? */ | ||
806 | long ov_streams(OggVorbis_File *vf){ | ||
807 | return vf->links; | ||
808 | } | ||
809 | |||
810 | /* Is the FILE * associated with vf seekable? */ | ||
811 | long ov_seekable(OggVorbis_File *vf){ | ||
812 | return vf->seekable; | ||
813 | } | ||
814 | |||
815 | /* returns the bitrate for a given logical bitstream or the entire | ||
816 | physical bitstream. If the file is open for random access, it will | ||
817 | find the *actual* average bitrate. If the file is streaming, it | ||
818 | returns the nominal bitrate (if set) else the average of the | ||
819 | upper/lower bounds (if set) else -1 (unset). | ||
820 | |||
821 | If you want the actual bitrate field settings, get them from the | ||
822 | vorbis_info structs */ | ||
823 | |||
824 | long ov_bitrate(OggVorbis_File *vf,int i){ | ||
825 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
826 | if(i>=vf->links)return(OV_EINVAL); | ||
827 | if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); | ||
828 | if(i<0){ | ||
829 | ogg_int64_t bits=0; | ||
830 | int i; | ||
831 | for(i=0;i<vf->links;i++) | ||
832 | bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; | ||
833 | /* This once read: return(rint(bits/ov_time_total(vf,-1))); | ||
834 | * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, | ||
835 | * so this is slightly transformed to make it work. | ||
836 | */ | ||
837 | return(bits*1000/ov_time_total(vf,-1)); | ||
838 | }else{ | ||
839 | if(vf->seekable){ | ||
840 | /* return the actual bitrate */ | ||
841 | return((vf->offsets[i+1]-vf->dataoffsets[i])*8000/ov_time_total(vf,i)); | ||
842 | }else{ | ||
843 | /* return nominal if set */ | ||
844 | if(vf->vi[i].bitrate_nominal>0){ | ||
845 | return vf->vi[i].bitrate_nominal; | ||
846 | }else{ | ||
847 | if(vf->vi[i].bitrate_upper>0){ | ||
848 | if(vf->vi[i].bitrate_lower>0){ | ||
849 | return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; | ||
850 | }else{ | ||
851 | return vf->vi[i].bitrate_upper; | ||
852 | } | ||
853 | } | ||
854 | return(OV_FALSE); | ||
855 | } | ||
856 | } | ||
857 | } | ||
858 | } | ||
859 | |||
860 | /* returns the actual bitrate since last call. returns -1 if no | ||
861 | additional data to offer since last call (or at beginning of stream), | ||
862 | EINVAL if stream is only partially open | ||
863 | */ | ||
864 | long ov_bitrate_instant(OggVorbis_File *vf){ | ||
865 | int link=(vf->seekable?vf->current_link:0); | ||
866 | long ret; | ||
867 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
868 | if(vf->samptrack==0)return(OV_FALSE); | ||
869 | ret=vf->bittrack/vf->samptrack*vf->vi[link].rate; | ||
870 | vf->bittrack=0; | ||
871 | vf->samptrack=0; | ||
872 | return(ret); | ||
873 | } | ||
874 | |||
875 | /* Guess */ | ||
876 | long ov_serialnumber(OggVorbis_File *vf,int i){ | ||
877 | if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); | ||
878 | if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); | ||
879 | if(i<0){ | ||
880 | return(vf->current_serialno); | ||
881 | }else{ | ||
882 | return(vf->serialnos[i]); | ||
883 | } | ||
884 | } | ||
885 | |||
886 | /* returns: total raw (compressed) length of content if i==-1 | ||
887 | raw (compressed) length of that logical bitstream for i==0 to n | ||
888 | OV_EINVAL if the stream is not seekable (we can't know the length) | ||
889 | or if stream is only partially open | ||
890 | */ | ||
891 | ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ | ||
892 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
893 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); | ||
894 | if(i<0){ | ||
895 | ogg_int64_t acc=0; | ||
896 | int i; | ||
897 | for(i=0;i<vf->links;i++) | ||
898 | acc+=ov_raw_total(vf,i); | ||
899 | return(acc); | ||
900 | }else{ | ||
901 | return(vf->offsets[i+1]-vf->offsets[i]); | ||
902 | } | ||
903 | } | ||
904 | |||
905 | /* returns: total PCM length (samples) of content if i==-1 PCM length | ||
906 | (samples) of that logical bitstream for i==0 to n | ||
907 | OV_EINVAL if the stream is not seekable (we can't know the | ||
908 | length) or only partially open | ||
909 | */ | ||
910 | ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ | ||
911 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
912 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); | ||
913 | if(i<0){ | ||
914 | ogg_int64_t acc=0; | ||
915 | int i; | ||
916 | for(i=0;i<vf->links;i++) | ||
917 | acc+=ov_pcm_total(vf,i); | ||
918 | return(acc); | ||
919 | }else{ | ||
920 | return(vf->pcmlengths[i*2+1]); | ||
921 | } | ||
922 | } | ||
923 | |||
924 | /* returns: total milliseconds of content if i==-1 | ||
925 | milliseconds in that logical bitstream for i==0 to n | ||
926 | OV_EINVAL if the stream is not seekable (we can't know the | ||
927 | length) or only partially open | ||
928 | */ | ||
929 | ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){ | ||
930 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
931 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); | ||
932 | if(i<0){ | ||
933 | ogg_int64_t acc=0; | ||
934 | int i; | ||
935 | for(i=0;i<vf->links;i++) | ||
936 | acc+=ov_time_total(vf,i); | ||
937 | return(acc); | ||
938 | }else{ | ||
939 | return(((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi[i].rate); | ||
940 | } | ||
941 | } | ||
942 | |||
943 | /* seek to an offset relative to the *compressed* data. This also | ||
944 | scans packets to update the PCM cursor. It will cross a logical | ||
945 | bitstream boundary, but only if it can't get any packets out of the | ||
946 | tail of the bitstream we seek to (so no surprises). | ||
947 | |||
948 | returns zero on success, nonzero on failure */ | ||
949 | |||
950 | int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ | ||
951 | ogg_stream_state *work_os=NULL; | ||
952 | ogg_page og={0,0,0,0}; | ||
953 | ogg_packet op={0,0,0,0,0,0}; | ||
954 | |||
955 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
956 | if(!vf->seekable) | ||
957 | return(OV_ENOSEEK); /* don't dump machine if we can't seek */ | ||
958 | |||
959 | if(pos<0 || pos>vf->end)return(OV_EINVAL); | ||
960 | |||
961 | /* don't yet clear out decoding machine (if it's initialized), in | ||
962 | the case we're in the same link. Restart the decode lapping, and | ||
963 | let _fetch_and_process_packet deal with a potential bitstream | ||
964 | boundary */ | ||
965 | vf->pcm_offset=-1; | ||
966 | ogg_stream_reset_serialno(vf->os, | ||
967 | vf->current_serialno); /* must set serialno */ | ||
968 | vorbis_synthesis_restart(&vf->vd); | ||
969 | |||
970 | _seek_helper(vf,pos); | ||
971 | |||
972 | /* we need to make sure the pcm_offset is set, but we don't want to | ||
973 | advance the raw cursor past good packets just to get to the first | ||
974 | with a granulepos. That's not equivalent behavior to beginning | ||
975 | decoding as immediately after the seek position as possible. | ||
976 | |||
977 | So, a hack. We use two stream states; a local scratch state and | ||
978 | the shared vf->os stream state. We use the local state to | ||
979 | scan, and the shared state as a buffer for later decode. | ||
980 | |||
981 | Unfortuantely, on the last page we still advance to last packet | ||
982 | because the granulepos on the last page is not necessarily on a | ||
983 | packet boundary, and we need to make sure the granpos is | ||
984 | correct. | ||
985 | */ | ||
986 | |||
987 | { | ||
988 | int lastblock=0; | ||
989 | int accblock=0; | ||
990 | int thisblock; | ||
991 | int eosflag; | ||
992 | |||
993 | work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */ | ||
994 | while(1){ | ||
995 | if(vf->ready_state>=STREAMSET){ | ||
996 | /* snarf/scan a packet if we can */ | ||
997 | int result=ogg_stream_packetout(work_os,&op); | ||
998 | |||
999 | if(result>0){ | ||
1000 | |||
1001 | if(vf->vi[vf->current_link].codec_setup){ | ||
1002 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); | ||
1003 | if(thisblock<0){ | ||
1004 | ogg_stream_packetout(vf->os,NULL); | ||
1005 | thisblock=0; | ||
1006 | }else{ | ||
1007 | |||
1008 | if(eosflag) | ||
1009 | ogg_stream_packetout(vf->os,NULL); | ||
1010 | else | ||
1011 | if(lastblock)accblock+=(lastblock+thisblock)>>2; | ||
1012 | } | ||
1013 | |||
1014 | if(op.granulepos!=-1){ | ||
1015 | int i,link=vf->current_link; | ||
1016 | ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; | ||
1017 | if(granulepos<0)granulepos=0; | ||
1018 | |||
1019 | for(i=0;i<link;i++) | ||
1020 | granulepos+=vf->pcmlengths[i*2+1]; | ||
1021 | vf->pcm_offset=granulepos-accblock; | ||
1022 | break; | ||
1023 | } | ||
1024 | lastblock=thisblock; | ||
1025 | continue; | ||
1026 | }else | ||
1027 | ogg_stream_packetout(vf->os,NULL); | ||
1028 | } | ||
1029 | } | ||
1030 | |||
1031 | if(!lastblock){ | ||
1032 | if(_get_next_page(vf,&og,-1)<0){ | ||
1033 | vf->pcm_offset=ov_pcm_total(vf,-1); | ||
1034 | break; | ||
1035 | } | ||
1036 | }else{ | ||
1037 | /* huh? Bogus stream with packets but no granulepos */ | ||
1038 | vf->pcm_offset=-1; | ||
1039 | break; | ||
1040 | } | ||
1041 | |||
1042 | /* has our decoding just traversed a bitstream boundary? */ | ||
1043 | if(vf->ready_state>=STREAMSET) | ||
1044 | if(vf->current_serialno!=ogg_page_serialno(&og)){ | ||
1045 | _decode_clear(vf); /* clear out stream state */ | ||
1046 | ogg_stream_destroy(work_os); | ||
1047 | } | ||
1048 | |||
1049 | if(vf->ready_state<STREAMSET){ | ||
1050 | int link; | ||
1051 | |||
1052 | vf->current_serialno=ogg_page_serialno(&og); | ||
1053 | for(link=0;link<vf->links;link++) | ||
1054 | if(vf->serialnos[link]==vf->current_serialno)break; | ||
1055 | if(link==vf->links) | ||
1056 | goto seek_error; /* sign of a bogus stream. error out, | ||
1057 | leave machine uninitialized */ | ||
1058 | |||
1059 | vf->current_link=link; | ||
1060 | |||
1061 | ogg_stream_reset_serialno(vf->os,vf->current_serialno); | ||
1062 | ogg_stream_reset_serialno(work_os,vf->current_serialno); | ||
1063 | vf->ready_state=STREAMSET; | ||
1064 | |||
1065 | } | ||
1066 | |||
1067 | { | ||
1068 | ogg_page dup; | ||
1069 | ogg_page_dup(&dup,&og); | ||
1070 | eosflag=ogg_page_eos(&og); | ||
1071 | ogg_stream_pagein(vf->os,&og); | ||
1072 | ogg_stream_pagein(work_os,&dup); | ||
1073 | } | ||
1074 | } | ||
1075 | } | ||
1076 | |||
1077 | ogg_packet_release(&op); | ||
1078 | ogg_page_release(&og); | ||
1079 | ogg_stream_destroy(work_os); | ||
1080 | vf->bittrack=0; | ||
1081 | vf->samptrack=0; | ||
1082 | return(0); | ||
1083 | |||
1084 | seek_error: | ||
1085 | ogg_packet_release(&op); | ||
1086 | ogg_page_release(&og); | ||
1087 | |||
1088 | /* dump the machine so we're in a known state */ | ||
1089 | vf->pcm_offset=-1; | ||
1090 | ogg_stream_destroy(work_os); | ||
1091 | _decode_clear(vf); | ||
1092 | return OV_EBADLINK; | ||
1093 | } | ||
1094 | |||
1095 | /* Page granularity seek (faster than sample granularity because we | ||
1096 | don't do the last bit of decode to find a specific sample). | ||
1097 | |||
1098 | Seek to the last [granule marked] page preceeding the specified pos | ||
1099 | location, such that decoding past the returned point will quickly | ||
1100 | arrive at the requested position. */ | ||
1101 | int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ | ||
1102 | int link=-1; | ||
1103 | ogg_int64_t result=0; | ||
1104 | ogg_int64_t total=ov_pcm_total(vf,-1); | ||
1105 | ogg_page og={0,0,0,0}; | ||
1106 | ogg_packet op={0,0,0,0,0,0}; | ||
1107 | |||
1108 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1109 | if(!vf->seekable)return(OV_ENOSEEK); | ||
1110 | if(pos<0 || pos>total)return(OV_EINVAL); | ||
1111 | |||
1112 | /* which bitstream section does this pcm offset occur in? */ | ||
1113 | for(link=vf->links-1;link>=0;link--){ | ||
1114 | total-=vf->pcmlengths[link*2+1]; | ||
1115 | if(pos>=total)break; | ||
1116 | } | ||
1117 | |||
1118 | /* search within the logical bitstream for the page with the highest | ||
1119 | pcm_pos preceeding (or equal to) pos. There is a danger here; | ||
1120 | missing pages or incorrect frame number information in the | ||
1121 | bitstream could make our task impossible. Account for that (it | ||
1122 | would be an error condition) */ | ||
1123 | |||
1124 | /* new search algorithm by HB (Nicholas Vinen) */ | ||
1125 | { | ||
1126 | ogg_int64_t end=vf->offsets[link+1]; | ||
1127 | ogg_int64_t begin=vf->offsets[link]; | ||
1128 | ogg_int64_t begintime = vf->pcmlengths[link*2]; | ||
1129 | ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; | ||
1130 | ogg_int64_t target=pos-total+begintime; | ||
1131 | ogg_int64_t best=begin; | ||
1132 | |||
1133 | while(begin<end){ | ||
1134 | ogg_int64_t bisect; | ||
1135 | |||
1136 | if(end-begin<CHUNKSIZE){ | ||
1137 | bisect=begin; | ||
1138 | }else{ | ||
1139 | /* take a (pretty decent) guess. */ | ||
1140 | bisect=begin + | ||
1141 | (target-begintime)*(end-begin)/(endtime-begintime) - CHUNKSIZE; | ||
1142 | if(bisect<=begin) | ||
1143 | bisect=begin+1; | ||
1144 | } | ||
1145 | |||
1146 | _seek_helper(vf,bisect); | ||
1147 | |||
1148 | while(begin<end){ | ||
1149 | result=_get_next_page(vf,&og,end-vf->offset); | ||
1150 | if(result==OV_EREAD) goto seek_error; | ||
1151 | if(result<0){ | ||
1152 | if(bisect<=begin+1) | ||
1153 | end=begin; /* found it */ | ||
1154 | else{ | ||
1155 | if(bisect==0) goto seek_error; | ||
1156 | bisect-=CHUNKSIZE; | ||
1157 | if(bisect<=begin)bisect=begin+1; | ||
1158 | _seek_helper(vf,bisect); | ||
1159 | } | ||
1160 | }else{ | ||
1161 | ogg_int64_t granulepos=ogg_page_granulepos(&og); | ||
1162 | if(granulepos==-1)continue; | ||
1163 | if(granulepos<target){ | ||
1164 | best=result; /* raw offset of packet with granulepos */ | ||
1165 | begin=vf->offset; /* raw offset of next page */ | ||
1166 | begintime=granulepos; | ||
1167 | |||
1168 | if(target-begintime>44100)break; | ||
1169 | bisect=begin; /* *not* begin + 1 */ | ||
1170 | }else{ | ||
1171 | if(bisect<=begin+1) | ||
1172 | end=begin; /* found it */ | ||
1173 | else{ | ||
1174 | if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ | ||
1175 | end=result; | ||
1176 | bisect-=CHUNKSIZE; /* an endless loop otherwise. */ | ||
1177 | if(bisect<=begin)bisect=begin+1; | ||
1178 | _seek_helper(vf,bisect); | ||
1179 | }else{ | ||
1180 | end=result; | ||
1181 | endtime=granulepos; | ||
1182 | break; | ||
1183 | } | ||
1184 | } | ||
1185 | } | ||
1186 | } | ||
1187 | } | ||
1188 | } | ||
1189 | |||
1190 | /* found our page. seek to it, update pcm offset. Easier case than | ||
1191 | raw_seek, don't keep packets preceeding granulepos. */ | ||
1192 | { | ||
1193 | |||
1194 | /* seek */ | ||
1195 | _seek_helper(vf,best); | ||
1196 | vf->pcm_offset=-1; | ||
1197 | |||
1198 | if(_get_next_page(vf,&og,-1)<0){ | ||
1199 | ogg_page_release(&og); | ||
1200 | return(OV_EOF); /* shouldn't happen */ | ||
1201 | } | ||
1202 | |||
1203 | if(link!=vf->current_link){ | ||
1204 | /* Different link; dump entire decode machine */ | ||
1205 | _decode_clear(vf); | ||
1206 | |||
1207 | vf->current_link=link; | ||
1208 | vf->current_serialno=ogg_page_serialno(&og); | ||
1209 | vf->ready_state=STREAMSET; | ||
1210 | |||
1211 | }else{ | ||
1212 | vorbis_synthesis_restart(&vf->vd); | ||
1213 | } | ||
1214 | |||
1215 | ogg_stream_reset_serialno(vf->os,vf->current_serialno); | ||
1216 | ogg_stream_pagein(vf->os,&og); | ||
1217 | |||
1218 | /* pull out all but last packet; the one with granulepos */ | ||
1219 | while(1){ | ||
1220 | result=ogg_stream_packetpeek(vf->os,&op); | ||
1221 | if(result==0){ | ||
1222 | /* !!! the packet finishing this page originated on a | ||
1223 | preceeding page. Keep fetching previous pages until we | ||
1224 | get one with a granulepos or without the 'continued' flag | ||
1225 | set. Then just use raw_seek for simplicity. */ | ||
1226 | |||
1227 | _seek_helper(vf,best); | ||
1228 | |||
1229 | while(1){ | ||
1230 | result=_get_prev_page(vf,&og); | ||
1231 | if(result<0) goto seek_error; | ||
1232 | if(ogg_page_granulepos(&og)>-1 || | ||
1233 | !ogg_page_continued(&og)){ | ||
1234 | return ov_raw_seek(vf,result); | ||
1235 | } | ||
1236 | vf->offset=result; | ||
1237 | } | ||
1238 | } | ||
1239 | if(result<0){ | ||
1240 | result = OV_EBADPACKET; | ||
1241 | goto seek_error; | ||
1242 | } | ||
1243 | if(op.granulepos!=-1){ | ||
1244 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; | ||
1245 | if(vf->pcm_offset<0)vf->pcm_offset=0; | ||
1246 | vf->pcm_offset+=total; | ||
1247 | break; | ||
1248 | }else | ||
1249 | result=ogg_stream_packetout(vf->os,NULL); | ||
1250 | } | ||
1251 | } | ||
1252 | } | ||
1253 | |||
1254 | /* verify result */ | ||
1255 | if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ | ||
1256 | result=OV_EFAULT; | ||
1257 | goto seek_error; | ||
1258 | } | ||
1259 | vf->bittrack=0; | ||
1260 | vf->samptrack=0; | ||
1261 | |||
1262 | ogg_page_release(&og); | ||
1263 | ogg_packet_release(&op); | ||
1264 | return(0); | ||
1265 | |||
1266 | seek_error: | ||
1267 | |||
1268 | ogg_page_release(&og); | ||
1269 | ogg_packet_release(&op); | ||
1270 | |||
1271 | /* dump machine so we're in a known state */ | ||
1272 | vf->pcm_offset=-1; | ||
1273 | _decode_clear(vf); | ||
1274 | return (int)result; | ||
1275 | } | ||
1276 | |||
1277 | /* seek to a sample offset relative to the decompressed pcm stream | ||
1278 | returns zero on success, nonzero on failure */ | ||
1279 | |||
1280 | int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ | ||
1281 | ogg_packet op={0,0,0,0,0,0}; | ||
1282 | ogg_page og={0,0,0,0}; | ||
1283 | int thisblock,lastblock=0; | ||
1284 | int ret=ov_pcm_seek_page(vf,pos); | ||
1285 | if(ret<0)return(ret); | ||
1286 | _make_decode_ready(vf); | ||
1287 | |||
1288 | /* discard leading packets we don't need for the lapping of the | ||
1289 | position we want; don't decode them */ | ||
1290 | |||
1291 | while(1){ | ||
1292 | |||
1293 | int ret=ogg_stream_packetpeek(vf->os,&op); | ||
1294 | if(ret>0){ | ||
1295 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); | ||
1296 | if(thisblock<0){ | ||
1297 | ogg_stream_packetout(vf->os,NULL); | ||
1298 | continue; /* non audio packet */ | ||
1299 | } | ||
1300 | if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; | ||
1301 | |||
1302 | if(vf->pcm_offset+((thisblock+ | ||
1303 | vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; | ||
1304 | |||
1305 | /* remove the packet from packet queue and track its granulepos */ | ||
1306 | ogg_stream_packetout(vf->os,NULL); | ||
1307 | vorbis_synthesis(&vf->vb,&op,0); /* set up a vb with | ||
1308 | only tracking, no | ||
1309 | pcm_decode */ | ||
1310 | vorbis_synthesis_blockin(&vf->vd,&vf->vb); | ||
1311 | |||
1312 | /* end of logical stream case is hard, especially with exact | ||
1313 | length positioning. */ | ||
1314 | |||
1315 | if(op.granulepos>-1){ | ||
1316 | int i; | ||
1317 | /* always believe the stream markers */ | ||
1318 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; | ||
1319 | if(vf->pcm_offset<0)vf->pcm_offset=0; | ||
1320 | for(i=0;i<vf->current_link;i++) | ||
1321 | vf->pcm_offset+=vf->pcmlengths[i*2+1]; | ||
1322 | } | ||
1323 | |||
1324 | lastblock=thisblock; | ||
1325 | |||
1326 | }else{ | ||
1327 | if(ret<0 && ret!=OV_HOLE)break; | ||
1328 | |||
1329 | /* suck in a new page */ | ||
1330 | if(_get_next_page(vf,&og,-1)<0)break; | ||
1331 | if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf); | ||
1332 | |||
1333 | if(vf->ready_state<STREAMSET){ | ||
1334 | int link; | ||
1335 | |||
1336 | vf->current_serialno=ogg_page_serialno(&og); | ||
1337 | for(link=0;link<vf->links;link++) | ||
1338 | if(vf->serialnos[link]==vf->current_serialno)break; | ||
1339 | if(link==vf->links){ | ||
1340 | ogg_page_release(&og); | ||
1341 | ogg_packet_release(&op); | ||
1342 | return(OV_EBADLINK); | ||
1343 | } | ||
1344 | vf->current_link=link; | ||
1345 | |||
1346 | ogg_stream_reset_serialno(vf->os,vf->current_serialno); | ||
1347 | vf->ready_state=STREAMSET; | ||
1348 | _make_decode_ready(vf); | ||
1349 | lastblock=0; | ||
1350 | } | ||
1351 | |||
1352 | ogg_stream_pagein(vf->os,&og); | ||
1353 | } | ||
1354 | } | ||
1355 | |||
1356 | vf->bittrack=0; | ||
1357 | vf->samptrack=0; | ||
1358 | /* discard samples until we reach the desired position. Crossing a | ||
1359 | logical bitstream boundary with abandon is OK. */ | ||
1360 | while(vf->pcm_offset<pos){ | ||
1361 | ogg_int64_t target=pos-vf->pcm_offset; | ||
1362 | long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); | ||
1363 | |||
1364 | if(samples>target)samples=target; | ||
1365 | vorbis_synthesis_read(&vf->vd,samples); | ||
1366 | vf->pcm_offset+=samples; | ||
1367 | |||
1368 | if(samples<target) | ||
1369 | if(_fetch_and_process_packet(vf,1,1)<=0) | ||
1370 | vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */ | ||
1371 | } | ||
1372 | |||
1373 | ogg_page_release(&og); | ||
1374 | ogg_packet_release(&op); | ||
1375 | return 0; | ||
1376 | } | ||
1377 | |||
1378 | /* seek to a playback time relative to the decompressed pcm stream | ||
1379 | returns zero on success, nonzero on failure */ | ||
1380 | int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){ | ||
1381 | /* translate time to PCM position and call ov_pcm_seek */ | ||
1382 | |||
1383 | int link=-1; | ||
1384 | ogg_int64_t pcm_total=ov_pcm_total(vf,-1); | ||
1385 | ogg_int64_t time_total=ov_time_total(vf,-1); | ||
1386 | |||
1387 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1388 | if(!vf->seekable)return(OV_ENOSEEK); | ||
1389 | if(milliseconds<0 || milliseconds>time_total)return(OV_EINVAL); | ||
1390 | |||
1391 | /* which bitstream section does this time offset occur in? */ | ||
1392 | for(link=vf->links-1;link>=0;link--){ | ||
1393 | pcm_total-=vf->pcmlengths[link*2+1]; | ||
1394 | time_total-=ov_time_total(vf,link); | ||
1395 | if(milliseconds>=time_total)break; | ||
1396 | } | ||
1397 | |||
1398 | /* enough information to convert time offset to pcm offset */ | ||
1399 | { | ||
1400 | ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; | ||
1401 | return(ov_pcm_seek(vf,target)); | ||
1402 | } | ||
1403 | } | ||
1404 | |||
1405 | /* page-granularity version of ov_time_seek | ||
1406 | returns zero on success, nonzero on failure */ | ||
1407 | int ov_time_seek_page(OggVorbis_File *vf,ogg_int64_t milliseconds){ | ||
1408 | /* translate time to PCM position and call ov_pcm_seek */ | ||
1409 | |||
1410 | int link=-1; | ||
1411 | ogg_int64_t pcm_total=ov_pcm_total(vf,-1); | ||
1412 | ogg_int64_t time_total=ov_time_total(vf,-1); | ||
1413 | |||
1414 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1415 | if(!vf->seekable)return(OV_ENOSEEK); | ||
1416 | if(milliseconds<0 || milliseconds>time_total)return(OV_EINVAL); | ||
1417 | |||
1418 | /* which bitstream section does this time offset occur in? */ | ||
1419 | for(link=vf->links-1;link>=0;link--){ | ||
1420 | pcm_total-=vf->pcmlengths[link*2+1]; | ||
1421 | time_total-=ov_time_total(vf,link); | ||
1422 | if(milliseconds>=time_total)break; | ||
1423 | } | ||
1424 | |||
1425 | /* enough information to convert time offset to pcm offset */ | ||
1426 | { | ||
1427 | ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; | ||
1428 | return(ov_pcm_seek_page(vf,target)); | ||
1429 | } | ||
1430 | } | ||
1431 | |||
1432 | /* tell the current stream offset cursor. Note that seek followed by | ||
1433 | tell will likely not give the set offset due to caching */ | ||
1434 | ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ | ||
1435 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1436 | return(vf->offset); | ||
1437 | } | ||
1438 | |||
1439 | /* return PCM offset (sample) of next PCM sample to be read */ | ||
1440 | ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ | ||
1441 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1442 | return(vf->pcm_offset); | ||
1443 | } | ||
1444 | |||
1445 | /* return time offset (milliseconds) of next PCM sample to be read */ | ||
1446 | ogg_int64_t ov_time_tell(OggVorbis_File *vf){ | ||
1447 | int link=0; | ||
1448 | ogg_int64_t pcm_total=0; | ||
1449 | ogg_int64_t time_total=0; | ||
1450 | |||
1451 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1452 | if(vf->seekable){ | ||
1453 | pcm_total=ov_pcm_total(vf,-1); | ||
1454 | time_total=ov_time_total(vf,-1); | ||
1455 | |||
1456 | /* which bitstream section does this time offset occur in? */ | ||
1457 | for(link=vf->links-1;link>=0;link--){ | ||
1458 | pcm_total-=vf->pcmlengths[link*2+1]; | ||
1459 | time_total-=ov_time_total(vf,link); | ||
1460 | if(vf->pcm_offset>=pcm_total)break; | ||
1461 | } | ||
1462 | } | ||
1463 | |||
1464 | return(time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi[link].rate); | ||
1465 | } | ||
1466 | |||
1467 | /* link: -1) return the vorbis_info struct for the bitstream section | ||
1468 | currently being decoded | ||
1469 | 0-n) to request information for a specific bitstream section | ||
1470 | |||
1471 | In the case of a non-seekable bitstream, any call returns the | ||
1472 | current bitstream. NULL in the case that the machine is not | ||
1473 | initialized */ | ||
1474 | |||
1475 | vorbis_info *ov_info(OggVorbis_File *vf,int link){ | ||
1476 | if(vf->seekable){ | ||
1477 | if(link<0) | ||
1478 | if(vf->ready_state>=STREAMSET) | ||
1479 | return vf->vi+vf->current_link; | ||
1480 | else | ||
1481 | return vf->vi; | ||
1482 | else | ||
1483 | if(link>=vf->links) | ||
1484 | return NULL; | ||
1485 | else | ||
1486 | return vf->vi+link; | ||
1487 | }else{ | ||
1488 | return vf->vi; | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | /* grr, strong typing, grr, no templates/inheritence, grr */ | ||
1493 | vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ | ||
1494 | if(vf->seekable){ | ||
1495 | if(link<0) | ||
1496 | if(vf->ready_state>=STREAMSET) | ||
1497 | return vf->vc+vf->current_link; | ||
1498 | else | ||
1499 | return vf->vc; | ||
1500 | else | ||
1501 | if(link>=vf->links) | ||
1502 | return NULL; | ||
1503 | else | ||
1504 | return vf->vc+link; | ||
1505 | }else{ | ||
1506 | return vf->vc; | ||
1507 | } | ||
1508 | } | ||
1509 | |||
1510 | /* up to this point, everything could more or less hide the multiple | ||
1511 | logical bitstream nature of chaining from the toplevel application | ||
1512 | if the toplevel application didn't particularly care. However, at | ||
1513 | the point that we actually read audio back, the multiple-section | ||
1514 | nature must surface: Multiple bitstream sections do not necessarily | ||
1515 | have to have the same number of channels or sampling rate. | ||
1516 | |||
1517 | ov_read returns the sequential logical bitstream number currently | ||
1518 | being decoded along with the PCM data in order that the toplevel | ||
1519 | application can take action on channel/sample rate changes. This | ||
1520 | number will be incremented even for streamed (non-seekable) streams | ||
1521 | (for seekable streams, it represents the actual logical bitstream | ||
1522 | index within the physical bitstream. Note that the accessor | ||
1523 | functions above are aware of this dichotomy). | ||
1524 | |||
1525 | input values: buffer) a buffer to hold packed PCM data for return | ||
1526 | length) the byte length requested to be placed into buffer | ||
1527 | |||
1528 | return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) | ||
1529 | 0) EOF | ||
1530 | n) number of bytes of PCM actually returned. The | ||
1531 | below works on a packet-by-packet basis, so the | ||
1532 | return length is not related to the 'length' passed | ||
1533 | in, just guaranteed to fit. | ||
1534 | |||
1535 | *section) set to the logical bitstream number */ | ||
1536 | |||
1537 | long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){ | ||
1538 | int i,j; | ||
1539 | |||
1540 | ogg_int32_t **pcm; | ||
1541 | long samples; | ||
1542 | |||
1543 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1544 | |||
1545 | while(1){ | ||
1546 | if(vf->ready_state==INITSET){ | ||
1547 | samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); | ||
1548 | if(samples)break; | ||
1549 | } | ||
1550 | |||
1551 | /* suck in another packet */ | ||
1552 | { | ||
1553 | int ret=_fetch_and_process_packet(vf,1,1); | ||
1554 | if(ret==OV_EOF) | ||
1555 | return(0); | ||
1556 | if(ret<=0) | ||
1557 | return(ret); | ||
1558 | } | ||
1559 | |||
1560 | } | ||
1561 | |||
1562 | if(samples>0){ | ||
1563 | |||
1564 | /* yay! proceed to pack data into the byte buffer */ | ||
1565 | |||
1566 | long channels=ov_info(vf,-1)->channels; | ||
1567 | |||
1568 | if(channels==1){ | ||
1569 | if(samples>(bytes_req/2)) | ||
1570 | samples=bytes_req/2; | ||
1571 | }else{ | ||
1572 | if(samples>(bytes_req/4)) | ||
1573 | samples=bytes_req/4; | ||
1574 | } | ||
1575 | |||
1576 | for(i=0;i<channels;i++) { /* It's faster in this order */ | ||
1577 | ogg_int32_t *src=pcm[i]; | ||
1578 | short *dest=((short *)buffer)+i; | ||
1579 | for(j=0;j<samples;j++) { | ||
1580 | *dest=CLIP_TO_15(src[j]>>9); | ||
1581 | dest+=channels; | ||
1582 | } | ||
1583 | } | ||
1584 | |||
1585 | vorbis_synthesis_read(&vf->vd,samples); | ||
1586 | vf->pcm_offset+=samples; | ||
1587 | if(bitstream)*bitstream=vf->current_link; | ||
1588 | return(samples*2*channels); | ||
1589 | }else{ | ||
1590 | return(samples); | ||
1591 | } | ||
1592 | } | ||