1 | /******************************************************************** |
2 | * * |
3 | * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * |
4 | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
5 | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
6 | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
7 | * * |
8 | * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2015 * |
9 | * by the Xiph.Org Foundation https://xiph.org/ * |
10 | * * |
11 | ******************************************************************** |
12 | |
13 | function: stdio-based convenience library for opening/seeking/decoding |
14 | |
15 | ********************************************************************/ |
16 | |
17 | #include <stdlib.h> |
18 | #include <stdio.h> |
19 | #include <errno.h> |
20 | #include <string.h> |
21 | #include <math.h> |
22 | |
23 | #include "vorbis/codec.h" |
24 | |
25 | /* we don't need or want the static callback symbols here */ |
26 | #define OV_EXCLUDE_STATIC_CALLBACKS |
27 | #include "vorbis/vorbisfile.h" |
28 | |
29 | #include "os.h" |
30 | #include "misc.h" |
31 | |
32 | /* A 'chained bitstream' is a Vorbis bitstream that contains more than |
33 | one logical bitstream arranged end to end (the only form of Ogg |
34 | multiplexing allowed in a Vorbis bitstream; grouping [parallel |
35 | multiplexing] is not allowed in Vorbis) */ |
36 | |
37 | /* A Vorbis file can be played beginning to end (streamed) without |
38 | worrying ahead of time about chaining (see decoder_example.c). If |
39 | we have the whole file, however, and want random access |
40 | (seeking/scrubbing) or desire to know the total length/time of a |
41 | file, we need to account for the possibility of chaining. */ |
42 | |
43 | /* We can handle things a number of ways; we can determine the entire |
44 | bitstream structure right off the bat, or find pieces on demand. |
45 | This example determines and caches structure for the entire |
46 | bitstream, but builds a virtual decoder on the fly when moving |
47 | between links in the chain. */ |
48 | |
49 | /* There are also different ways to implement seeking. Enough |
50 | information exists in an Ogg bitstream to seek to |
51 | sample-granularity positions in the output. Or, one can seek by |
52 | picking some portion of the stream roughly in the desired area if |
53 | we only want coarse navigation through the stream. */ |
54 | |
55 | /************************************************************************* |
56 | * Many, many internal helpers. The intention is not to be confusing; |
57 | * rampant duplication and monolithic function implementation would be |
58 | * harder to understand anyway. The high level functions are last. Begin |
59 | * grokking near the end of the file */ |
60 | |
61 | /* read a little more data from the file/pipe into the ogg_sync framer |
62 | */ |
63 | #define CHUNKSIZE 65536 /* greater-than-page-size granularity seeking */ |
64 | #define READSIZE 2048 /* a smaller read size is needed for low-rate streaming. */ |
65 | |
66 | static long _get_data(OggVorbis_File *vf){ |
67 | errno=0; |
68 | if(!(vf->callbacks.read_func))return(-1); |
69 | if(vf->datasource){ |
70 | char *buffer=ogg_sync_buffer(&vf->oy,READSIZE); |
71 | long bytes=(vf->callbacks.read_func)(buffer,1,READSIZE,vf->datasource); |
72 | if(bytes>0)ogg_sync_wrote(&vf->oy,bytes); |
73 | if(bytes==0 && errno)return(-1); |
74 | return(bytes); |
75 | }else |
76 | return(0); |
77 | } |
78 | |
79 | /* save a tiny smidge of verbosity to make the code more readable */ |
80 | static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ |
81 | if(vf->datasource){ |
82 | /* only seek if the file position isn't already there */ |
83 | if(vf->offset != offset){ |
84 | if(!(vf->callbacks.seek_func)|| |
85 | (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) |
86 | return OV_EREAD; |
87 | vf->offset=offset; |
88 | ogg_sync_reset(&vf->oy); |
89 | } |
90 | }else{ |
91 | /* shouldn't happen unless someone writes a broken callback */ |
92 | return OV_EFAULT; |
93 | } |
94 | return 0; |
95 | } |
96 | |
97 | /* The read/seek functions track absolute position within the stream */ |
98 | |
99 | /* from the head of the stream, get the next page. boundary specifies |
100 | if the function is allowed to fetch more data from the stream (and |
101 | how much) or only use internally buffered data. |
102 | |
103 | boundary: -1) unbounded search |
104 | 0) read no additional data; use cached only |
105 | n) search for a new page beginning for n bytes |
106 | |
107 | return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) |
108 | n) found a page at absolute offset n */ |
109 | |
110 | static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, |
111 | ogg_int64_t boundary){ |
112 | if(boundary>0)boundary+=vf->offset; |
113 | while(1){ |
114 | long more; |
115 | |
116 | if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); |
117 | more=ogg_sync_pageseek(&vf->oy,og); |
118 | |
119 | if(more<0){ |
120 | /* skipped n bytes */ |
121 | vf->offset-=more; |
122 | }else{ |
123 | if(more==0){ |
124 | /* send more paramedics */ |
125 | if(!boundary)return(OV_FALSE); |
126 | { |
127 | long ret=_get_data(vf); |
128 | if(ret==0)return(OV_EOF); |
129 | if(ret<0)return(OV_EREAD); |
130 | } |
131 | }else{ |
132 | /* got a page. Return the offset at the page beginning, |
133 | advance the internal offset past the page end */ |
134 | ogg_int64_t ret=vf->offset; |
135 | vf->offset+=more; |
136 | return(ret); |
137 | |
138 | } |
139 | } |
140 | } |
141 | } |
142 | |
143 | /* find the latest page beginning before the passed in position. Much |
144 | dirtier than the above as Ogg doesn't have any backward search |
145 | linkage. no 'readp' as it will certainly have to read. */ |
146 | /* returns offset or OV_EREAD, OV_FAULT */ |
147 | static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_int64_t begin,ogg_page *og){ |
148 | ogg_int64_t end = begin; |
149 | ogg_int64_t ret; |
150 | ogg_int64_t offset=-1; |
151 | |
152 | while(offset==-1){ |
153 | begin-=CHUNKSIZE; |
154 | if(begin<0) |
155 | begin=0; |
156 | |
157 | ret=_seek_helper(vf,begin); |
158 | if(ret)return(ret); |
159 | |
160 | while(vf->offset<end){ |
161 | memset(og,0,sizeof(*og)); |
162 | ret=_get_next_page(vf,og,end-vf->offset); |
163 | if(ret==OV_EREAD)return(OV_EREAD); |
164 | if(ret<0){ |
165 | break; |
166 | }else{ |
167 | offset=ret; |
168 | } |
169 | } |
170 | } |
171 | |
172 | /* In a fully compliant, non-multiplexed stream, we'll still be |
173 | holding the last page. In multiplexed (or noncompliant streams), |
174 | we will probably have to re-read the last page we saw */ |
175 | if(og->header_len==0){ |
176 | ret=_seek_helper(vf,offset); |
177 | if(ret)return(ret); |
178 | |
179 | ret=_get_next_page(vf,og,CHUNKSIZE); |
180 | if(ret<0) |
181 | /* this shouldn't be possible */ |
182 | return(OV_EFAULT); |
183 | } |
184 | |
185 | return(offset); |
186 | } |
187 | |
188 | static void _add_serialno(ogg_page *og,long **serialno_list, int *n){ |
189 | long s = ogg_page_serialno(og); |
190 | (*n)++; |
191 | |
192 | if(*serialno_list){ |
193 | *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n)); |
194 | }else{ |
195 | *serialno_list = _ogg_malloc(sizeof(**serialno_list)); |
196 | } |
197 | |
198 | (*serialno_list)[(*n)-1] = s; |
199 | } |
200 | |
201 | /* returns nonzero if found */ |
202 | static int _lookup_serialno(long s, long *serialno_list, int n){ |
203 | if(serialno_list){ |
204 | while(n--){ |
205 | if(*serialno_list == s) return 1; |
206 | serialno_list++; |
207 | } |
208 | } |
209 | return 0; |
210 | } |
211 | |
212 | static int _lookup_page_serialno(ogg_page *og, long *serialno_list, int n){ |
213 | long s = ogg_page_serialno(og); |
214 | return _lookup_serialno(s,serialno_list,n); |
215 | } |
216 | |
217 | /* performs the same search as _get_prev_page, but prefers pages of |
218 | the specified serial number. If a page of the specified serialno is |
219 | spotted during the seek-back-and-read-forward, it will return the |
220 | info of last page of the matching serial number instead of the very |
221 | last page. If no page of the specified serialno is seen, it will |
222 | return the info of last page and alter *serialno. */ |
223 | static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, ogg_int64_t begin, |
224 | long *serial_list, int serial_n, |
225 | int *serialno, ogg_int64_t *granpos){ |
226 | ogg_page og; |
227 | ogg_int64_t end=begin; |
228 | ogg_int64_t ret; |
229 | |
230 | ogg_int64_t prefoffset=-1; |
231 | ogg_int64_t offset=-1; |
232 | ogg_int64_t ret_serialno=-1; |
233 | ogg_int64_t ret_gran=-1; |
234 | |
235 | while(offset==-1){ |
236 | begin-=CHUNKSIZE; |
237 | if(begin<0) |
238 | begin=0; |
239 | |
240 | ret=_seek_helper(vf,begin); |
241 | if(ret)return(ret); |
242 | |
243 | while(vf->offset<end){ |
244 | ret=_get_next_page(vf,&og,end-vf->offset); |
245 | if(ret==OV_EREAD)return(OV_EREAD); |
246 | if(ret<0){ |
247 | break; |
248 | }else{ |
249 | ret_serialno=ogg_page_serialno(&og); |
250 | ret_gran=ogg_page_granulepos(&og); |
251 | offset=ret; |
252 | |
253 | if(ret_serialno == *serialno){ |
254 | prefoffset=ret; |
255 | *granpos=ret_gran; |
256 | } |
257 | |
258 | if(!_lookup_serialno(ret_serialno,serial_list,serial_n)){ |
259 | /* we fell off the end of the link, which means we seeked |
260 | back too far and shouldn't have been looking in that link |
261 | to begin with. If we found the preferred serial number, |
262 | forget that we saw it. */ |
263 | prefoffset=-1; |
264 | } |
265 | } |
266 | } |
267 | /*We started from the beginning of the stream and found nothing. |
268 | This should be impossible unless the contents of the stream changed out |
269 | from under us after we read from it.*/ |
270 | if(!begin&&vf->offset<0)return OV_EBADLINK; |
271 | } |
272 | |
273 | /* we're not interested in the page... just the serialno and granpos. */ |
274 | if(prefoffset>=0)return(prefoffset); |
275 | |
276 | *serialno = ret_serialno; |
277 | *granpos = ret_gran; |
278 | return(offset); |
279 | |
280 | } |
281 | |
282 | /* uses the local ogg_stream storage in vf; this is important for |
283 | non-streaming input sources */ |
284 | static int (OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, |
285 | long **serialno_list, int *serialno_n, |
286 | ogg_page *og_ptr){ |
287 | ogg_page og; |
288 | ogg_packet op; |
289 | int i,ret; |
290 | int allbos=0; |
291 | |
292 | if(!og_ptr){ |
293 | ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); |
294 | if(llret==OV_EREAD)return(OV_EREAD); |
295 | if(llret<0)return(OV_ENOTVORBIS); |
296 | og_ptr=&og; |
297 | } |
298 | |
299 | vorbis_info_init(vi); |
300 | vorbis_comment_init(vc); |
301 | vf->ready_state=OPENED; |
302 | |
303 | /* extract the serialnos of all BOS pages + the first set of vorbis |
304 | headers we see in the link */ |
305 | |
306 | while(ogg_page_bos(og_ptr)){ |
307 | if(serialno_list){ |
308 | if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){ |
309 | /* a dupe serialnumber in an initial header packet set == invalid stream */ |
310 | if(*serialno_list)_ogg_free(*serialno_list); |
311 | *serialno_list=0; |
312 | *serialno_n=0; |
313 | ret=OV_EBADHEADER; |
314 | goto bail_header; |
315 | } |
316 | |
317 | _add_serialno(og_ptr,serialno_list,serialno_n); |
318 | } |
319 | |
320 | if(vf->ready_state<STREAMSET){ |
321 | /* we don't have a vorbis stream in this link yet, so begin |
322 | prospective stream setup. We need a stream to get packets */ |
323 | ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr)); |
324 | ogg_stream_pagein(&vf->os,og_ptr); |
325 | |
326 | if(ogg_stream_packetout(&vf->os,&op) > 0 && |
327 | vorbis_synthesis_idheader(&op)){ |
328 | /* vorbis header; continue setup */ |
329 | vf->ready_state=STREAMSET; |
330 | if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ |
331 | ret=OV_EBADHEADER; |
332 | goto bail_header; |
333 | } |
334 | } |
335 | } |
336 | |
337 | /* get next page */ |
338 | { |
339 | ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE); |
340 | if(llret==OV_EREAD){ |
341 | ret=OV_EREAD; |
342 | goto bail_header; |
343 | } |
344 | if(llret<0){ |
345 | ret=OV_ENOTVORBIS; |
346 | goto bail_header; |
347 | } |
348 | |
349 | /* if this page also belongs to our vorbis stream, submit it and break */ |
350 | if(vf->ready_state==STREAMSET && |
351 | vf->os.serialno == ogg_page_serialno(og_ptr)){ |
352 | ogg_stream_pagein(&vf->os,og_ptr); |
353 | break; |
354 | } |
355 | } |
356 | } |
357 | |
358 | if(vf->ready_state!=STREAMSET){ |
359 | ret = OV_ENOTVORBIS; |
360 | goto bail_header; |
361 | } |
362 | |
363 | while(1){ |
364 | |
365 | i=0; |
366 | while(i<2){ /* get a page loop */ |
367 | |
368 | while(i<2){ /* get a packet loop */ |
369 | |
370 | int result=ogg_stream_packetout(&vf->os,&op); |
371 | if(result==0)break; |
372 | if(result==-1){ |
373 | ret=OV_EBADHEADER; |
374 | goto bail_header; |
375 | } |
376 | |
377 | if((ret=vorbis_synthesis_headerin(vi,vc,&op))) |
378 | goto bail_header; |
379 | |
380 | i++; |
381 | } |
382 | |
383 | while(i<2){ |
384 | if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ |
385 | ret=OV_EBADHEADER; |
386 | goto bail_header; |
387 | } |
388 | |
389 | /* if this page belongs to the correct stream, go parse it */ |
390 | if(vf->os.serialno == ogg_page_serialno(og_ptr)){ |
391 | ogg_stream_pagein(&vf->os,og_ptr); |
392 | break; |
393 | } |
394 | |
395 | /* if we never see the final vorbis headers before the link |
396 | ends, abort */ |
397 | if(ogg_page_bos(og_ptr)){ |
398 | if(allbos){ |
399 | ret = OV_EBADHEADER; |
400 | goto bail_header; |
401 | }else |
402 | allbos=1; |
403 | } |
404 | |
405 | /* otherwise, keep looking */ |
406 | } |
407 | } |
408 | |
409 | return 0; |
410 | } |
411 | |
412 | : |
413 | vorbis_info_clear(vi); |
414 | vorbis_comment_clear(vc); |
415 | vf->ready_state=OPENED; |
416 | |
417 | return ret; |
418 | } |
419 | |
420 | /* Starting from current cursor position, get initial PCM offset of |
421 | next page. Consumes the page in the process without decoding |
422 | audio, however this is only called during stream parsing upon |
423 | seekable open. */ |
424 | static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ |
425 | ogg_page og; |
426 | ogg_int64_t accumulated=0; |
427 | long lastblock=-1; |
428 | int result; |
429 | int serialno = vf->os.serialno; |
430 | |
431 | while(1){ |
432 | ogg_packet op; |
433 | if(_get_next_page(vf,&og,-1)<0) |
434 | break; /* should not be possible unless the file is truncated/mangled */ |
435 | |
436 | if(ogg_page_bos(&og)) break; |
437 | if(ogg_page_serialno(&og)!=serialno) continue; |
438 | |
439 | /* count blocksizes of all frames in the page */ |
440 | ogg_stream_pagein(&vf->os,&og); |
441 | while((result=ogg_stream_packetout(&vf->os,&op))){ |
442 | if(result>0){ /* ignore holes */ |
443 | long thisblock=vorbis_packet_blocksize(vi,&op); |
444 | if(thisblock>=0){ |
445 | if(lastblock!=-1) |
446 | accumulated+=(lastblock+thisblock)>>2; |
447 | lastblock=thisblock; |
448 | } |
449 | } |
450 | } |
451 | |
452 | if(ogg_page_granulepos(&og)!=-1){ |
453 | /* pcm offset of last packet on the first audio page */ |
454 | accumulated= ogg_page_granulepos(&og)-accumulated; |
455 | break; |
456 | } |
457 | } |
458 | |
459 | /* less than zero? Either a corrupt file or a stream with samples |
460 | trimmed off the beginning, a normal occurrence; in both cases set |
461 | the offset to zero */ |
462 | if(accumulated<0)accumulated=0; |
463 | |
464 | return accumulated; |
465 | } |
466 | |
467 | /* finds each bitstream link one at a time using a bisection search |
468 | (has to begin by knowing the offset of the lb's initial page). |
469 | Recurses for each link so it can alloc the link storage after |
470 | finding them all, then unroll and fill the cache at the same time */ |
471 | static int _bisect_forward_serialno(OggVorbis_File *vf, |
472 | ogg_int64_t begin, |
473 | ogg_int64_t searched, |
474 | ogg_int64_t end, |
475 | ogg_int64_t endgran, |
476 | int endserial, |
477 | long *currentno_list, |
478 | int currentnos, |
479 | long m){ |
480 | ogg_int64_t pcmoffset; |
481 | ogg_int64_t dataoffset=searched; |
482 | ogg_int64_t endsearched=end; |
483 | ogg_int64_t next=end; |
484 | ogg_int64_t searchgran=-1; |
485 | ogg_page og; |
486 | ogg_int64_t ret,last; |
487 | int serialno = vf->os.serialno; |
488 | |
489 | /* invariants: |
490 | we have the headers and serialnos for the link beginning at 'begin' |
491 | we have the offset and granpos of the last page in the file (potentially |
492 | not a page we care about) |
493 | */ |
494 | |
495 | /* Is the last page in our list of current serialnumbers? */ |
496 | if(_lookup_serialno(endserial,currentno_list,currentnos)){ |
497 | |
498 | /* last page is in the starting serialno list, so we've bisected |
499 | down to (or just started with) a single link. Now we need to |
500 | find the last vorbis page belonging to the first vorbis stream |
501 | for this link. */ |
502 | searched = end; |
503 | while(endserial != serialno){ |
504 | endserial = serialno; |
505 | searched=_get_prev_page_serial(vf,searched,currentno_list,currentnos,&endserial,&endgran); |
506 | } |
507 | |
508 | vf->links=m+1; |
509 | if(vf->offsets)_ogg_free(vf->offsets); |
510 | if(vf->serialnos)_ogg_free(vf->serialnos); |
511 | if(vf->dataoffsets)_ogg_free(vf->dataoffsets); |
512 | |
513 | vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); |
514 | vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); |
515 | vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); |
516 | vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); |
517 | vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); |
518 | vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); |
519 | |
520 | vf->offsets[m+1]=end; |
521 | vf->offsets[m]=begin; |
522 | vf->pcmlengths[m*2+1]=(endgran<0?0:endgran); |
523 | |
524 | }else{ |
525 | |
526 | /* last page is not in the starting stream's serial number list, |
527 | so we have multiple links. Find where the stream that begins |
528 | our bisection ends. */ |
529 | |
530 | long *next_serialno_list=NULL; |
531 | int next_serialnos=0; |
532 | vorbis_info vi; |
533 | vorbis_comment vc; |
534 | int testserial = serialno+1; |
535 | |
536 | /* the below guards against garbage seperating the last and |
537 | first pages of two links. */ |
538 | while(searched<endsearched){ |
539 | ogg_int64_t bisect; |
540 | |
541 | if(endsearched-searched<CHUNKSIZE){ |
542 | bisect=searched; |
543 | }else{ |
544 | bisect=(searched+endsearched)/2; |
545 | } |
546 | |
547 | ret=_seek_helper(vf,bisect); |
548 | if(ret)return(ret); |
549 | |
550 | last=_get_next_page(vf,&og,-1); |
551 | if(last==OV_EREAD)return(OV_EREAD); |
552 | if(last<0 || !_lookup_page_serialno(&og,currentno_list,currentnos)){ |
553 | endsearched=bisect; |
554 | if(last>=0)next=last; |
555 | }else{ |
556 | searched=vf->offset; |
557 | } |
558 | } |
559 | |
560 | /* Bisection point found */ |
561 | /* for the time being, fetch end PCM offset the simple way */ |
562 | searched = next; |
563 | while(testserial != serialno){ |
564 | testserial = serialno; |
565 | searched = _get_prev_page_serial(vf,searched,currentno_list,currentnos,&testserial,&searchgran); |
566 | } |
567 | |
568 | ret=_seek_helper(vf,next); |
569 | if(ret)return(ret); |
570 | |
571 | ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL); |
572 | if(ret)return(ret); |
573 | serialno = vf->os.serialno; |
574 | dataoffset = vf->offset; |
575 | |
576 | /* this will consume a page, however the next bisection always |
577 | starts with a raw seek */ |
578 | pcmoffset = _initial_pcmoffset(vf,&vi); |
579 | |
580 | ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial, |
581 | next_serialno_list,next_serialnos,m+1); |
582 | if(ret)return(ret); |
583 | |
584 | if(next_serialno_list)_ogg_free(next_serialno_list); |
585 | |
586 | vf->offsets[m+1]=next; |
587 | vf->serialnos[m+1]=serialno; |
588 | vf->dataoffsets[m+1]=dataoffset; |
589 | |
590 | vf->vi[m+1]=vi; |
591 | vf->vc[m+1]=vc; |
592 | |
593 | vf->pcmlengths[m*2+1]=searchgran; |
594 | vf->pcmlengths[m*2+2]=pcmoffset; |
595 | vf->pcmlengths[m*2+3]-=pcmoffset; |
596 | if(vf->pcmlengths[m*2+3]<0)vf->pcmlengths[m*2+3]=0; |
597 | } |
598 | return(0); |
599 | } |
600 | |
601 | static int _make_decode_ready(OggVorbis_File *vf){ |
602 | if(vf->ready_state>STREAMSET)return 0; |
603 | if(vf->ready_state<STREAMSET)return OV_EFAULT; |
604 | if(vf->seekable){ |
605 | if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link)) |
606 | return OV_EBADLINK; |
607 | }else{ |
608 | if(vorbis_synthesis_init(&vf->vd,vf->vi)) |
609 | return OV_EBADLINK; |
610 | } |
611 | vorbis_block_init(&vf->vd,&vf->vb); |
612 | vf->ready_state=INITSET; |
613 | vf->bittrack=0.f; |
614 | vf->samptrack=0.f; |
615 | return 0; |
616 | } |
617 | |
618 | static int _open_seekable2(OggVorbis_File *vf){ |
619 | ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1; |
620 | int endserial=vf->os.serialno; |
621 | int serialno=vf->os.serialno; |
622 | |
623 | /* we're partially open and have a first link header state in |
624 | storage in vf */ |
625 | |
626 | /* fetch initial PCM offset */ |
627 | ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi); |
628 | |
629 | /* we can seek, so set out learning all about this file */ |
630 | if(vf->callbacks.seek_func && vf->callbacks.tell_func){ |
631 | (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); |
632 | vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); |
633 | }else{ |
634 | vf->offset=vf->end=-1; |
635 | } |
636 | |
637 | /* If seek_func is implemented, tell_func must also be implemented */ |
638 | if(vf->end==-1) return(OV_EINVAL); |
639 | |
640 | /* Get the offset of the last page of the physical bitstream, or, if |
641 | we're lucky the last vorbis page of this link as most OggVorbis |
642 | files will contain a single logical bitstream */ |
643 | end=_get_prev_page_serial(vf,vf->end,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); |
644 | if(end<0)return(end); |
645 | |
646 | /* now determine bitstream structure recursively */ |
647 | if(_bisect_forward_serialno(vf,0,dataoffset,end,endgran,endserial, |
648 | vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD); |
649 | |
650 | vf->offsets[0]=0; |
651 | vf->serialnos[0]=serialno; |
652 | vf->dataoffsets[0]=dataoffset; |
653 | vf->pcmlengths[0]=pcmoffset; |
654 | vf->pcmlengths[1]-=pcmoffset; |
655 | if(vf->pcmlengths[1]<0)vf->pcmlengths[1]=0; |
656 | |
657 | return(ov_raw_seek(vf,dataoffset)); |
658 | } |
659 | |
660 | /* clear out the current logical bitstream decoder */ |
661 | static void _decode_clear(OggVorbis_File *vf){ |
662 | vorbis_dsp_clear(&vf->vd); |
663 | vorbis_block_clear(&vf->vb); |
664 | vf->ready_state=OPENED; |
665 | } |
666 | |
667 | /* fetch and process a packet. Handles the case where we're at a |
668 | bitstream boundary and dumps the decoding machine. If the decoding |
669 | machine is unloaded, it loads it. It also keeps pcm_offset up to |
670 | date (seek and read both use this. seek uses a special hack with |
671 | readp). |
672 | |
673 | return: <0) error, OV_HOLE (lost packet) or OV_EOF |
674 | 0) need more data (only if readp==0) |
675 | 1) got a packet |
676 | */ |
677 | |
678 | static int _fetch_and_process_packet(OggVorbis_File *vf, |
679 | ogg_packet *op_in, |
680 | int readp, |
681 | int spanp){ |
682 | ogg_page og; |
683 | |
684 | /* handle one packet. Try to fetch it from current stream state */ |
685 | /* extract packets from page */ |
686 | while(1){ |
687 | |
688 | if(vf->ready_state==STREAMSET){ |
689 | int ret=_make_decode_ready(vf); |
690 | if(ret<0)return ret; |
691 | } |
692 | |
693 | /* process a packet if we can. */ |
694 | |
695 | if(vf->ready_state==INITSET){ |
696 | int hs=vorbis_synthesis_halfrate_p(vf->vi); |
697 | |
698 | while(1) { |
699 | ogg_packet op; |
700 | ogg_packet *op_ptr=(op_in?op_in:&op); |
701 | int result=ogg_stream_packetout(&vf->os,op_ptr); |
702 | ogg_int64_t granulepos; |
703 | |
704 | op_in=NULL; |
705 | if(result==-1)return(OV_HOLE); /* hole in the data. */ |
706 | if(result>0){ |
707 | /* got a packet. process it */ |
708 | granulepos=op_ptr->granulepos; |
709 | if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy |
710 | header handling. The |
711 | header packets aren't |
712 | audio, so if/when we |
713 | submit them, |
714 | vorbis_synthesis will |
715 | reject them */ |
716 | |
717 | /* suck in the synthesis data and track bitrate */ |
718 | { |
719 | int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); |
720 | /* for proper use of libvorbis within libvorbisfile, |
721 | oldsamples will always be zero. */ |
722 | if(oldsamples)return(OV_EFAULT); |
723 | |
724 | vorbis_synthesis_blockin(&vf->vd,&vf->vb); |
725 | vf->samptrack+=(vorbis_synthesis_pcmout(&vf->vd,NULL)<<hs); |
726 | vf->bittrack+=op_ptr->bytes*8; |
727 | } |
728 | |
729 | /* update the pcm offset. */ |
730 | if(granulepos!=-1 && !op_ptr->e_o_s){ |
731 | int link=(vf->seekable?vf->current_link:0); |
732 | int i,samples; |
733 | |
734 | /* this packet has a pcm_offset on it (the last packet |
735 | completed on a page carries the offset) After processing |
736 | (above), we know the pcm position of the *last* sample |
737 | ready to be returned. Find the offset of the *first* |
738 | |
739 | As an aside, this trick is inaccurate if we begin |
740 | reading anew right at the last page; the end-of-stream |
741 | granulepos declares the last frame in the stream, and the |
742 | last packet of the last page may be a partial frame. |
743 | So, we need a previous granulepos from an in-sequence page |
744 | to have a reference point. Thus the !op_ptr->e_o_s clause |
745 | above */ |
746 | |
747 | if(vf->seekable && link>0) |
748 | granulepos-=vf->pcmlengths[link*2]; |
749 | if(granulepos<0)granulepos=0; /* actually, this |
750 | shouldn't be possible |
751 | here unless the stream |
752 | is very broken */ |
753 | |
754 | samples=(vorbis_synthesis_pcmout(&vf->vd,NULL)<<hs); |
755 | |
756 | granulepos-=samples; |
757 | for(i=0;i<link;i++) |
758 | granulepos+=vf->pcmlengths[i*2+1]; |
759 | vf->pcm_offset=granulepos; |
760 | } |
761 | return(1); |
762 | } |
763 | } |
764 | else |
765 | break; |
766 | } |
767 | } |
768 | |
769 | if(vf->ready_state>=OPENED){ |
770 | ogg_int64_t ret; |
771 | |
772 | while(1){ |
773 | /* the loop is not strictly necessary, but there's no sense in |
774 | doing the extra checks of the larger loop for the common |
775 | case in a multiplexed bistream where the page is simply |
776 | part of a different logical bitstream; keep reading until |
777 | we get one with the correct serialno */ |
778 | |
779 | if(!readp)return(0); |
780 | if((ret=_get_next_page(vf,&og,-1))<0){ |
781 | return(OV_EOF); /* eof. leave unitialized */ |
782 | } |
783 | |
784 | /* bitrate tracking; add the header's bytes here, the body bytes |
785 | are done by packet above */ |
786 | vf->bittrack+=og.header_len*8; |
787 | |
788 | if(vf->ready_state==INITSET){ |
789 | if(vf->current_serialno!=ogg_page_serialno(&og)){ |
790 | |
791 | /* two possibilities: |
792 | 1) our decoding just traversed a bitstream boundary |
793 | 2) another stream is multiplexed into this logical section */ |
794 | |
795 | if(ogg_page_bos(&og)){ |
796 | /* boundary case */ |
797 | if(!spanp) |
798 | return(OV_EOF); |
799 | |
800 | _decode_clear(vf); |
801 | |
802 | if(!vf->seekable){ |
803 | vorbis_info_clear(vf->vi); |
804 | vorbis_comment_clear(vf->vc); |
805 | } |
806 | break; |
807 | |
808 | }else |
809 | continue; /* possibility #2 */ |
810 | } |
811 | } |
812 | |
813 | break; |
814 | } |
815 | } |
816 | |
817 | /* Do we need to load a new machine before submitting the page? */ |
818 | /* This is different in the seekable and non-seekable cases. |
819 | |
820 | In the seekable case, we already have all the header |
821 | information loaded and cached; we just initialize the machine |
822 | with it and continue on our merry way. |
823 | |
824 | In the non-seekable (streaming) case, we'll only be at a |
825 | boundary if we just left the previous logical bitstream and |
826 | we're now nominally at the header of the next bitstream |
827 | */ |
828 | |
829 | if(vf->ready_state!=INITSET){ |
830 | int link; |
831 | |
832 | if(vf->ready_state<STREAMSET){ |
833 | if(vf->seekable){ |
834 | long serialno = ogg_page_serialno(&og); |
835 | |
836 | /* match the serialno to bitstream section. We use this rather than |
837 | offset positions to avoid problems near logical bitstream |
838 | boundaries */ |
839 | |
840 | for(link=0;link<vf->links;link++) |
841 | if(vf->serialnos[link]==serialno)break; |
842 | |
843 | if(link==vf->links) continue; /* not the desired Vorbis |
844 | bitstream section; keep |
845 | trying */ |
846 | |
847 | vf->current_serialno=serialno; |
848 | vf->current_link=link; |
849 | |
850 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno); |
851 | vf->ready_state=STREAMSET; |
852 | |
853 | }else{ |
854 | /* we're streaming */ |
855 | /* fetch the three header packets, build the info struct */ |
856 | |
857 | int ret=_fetch_headers(vf,vf->vi,vf->vc,NULL,NULL,&og); |
858 | if(ret)return(ret); |
859 | vf->current_serialno=vf->os.serialno; |
860 | vf->current_link++; |
861 | link=0; |
862 | } |
863 | } |
864 | } |
865 | |
866 | /* the buffered page is the data we want, and we're ready for it; |
867 | add it to the stream state */ |
868 | ogg_stream_pagein(&vf->os,&og); |
869 | |
870 | } |
871 | } |
872 | |
873 | /* if, eg, 64 bit stdio is configured by default, this will build with |
874 | fseek64 */ |
875 | static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ |
876 | if(f==NULL)return(-1); |
877 | return fseek(f,off,whence); |
878 | } |
879 | |
880 | static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial, |
881 | long ibytes, ov_callbacks callbacks){ |
882 | int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1); |
883 | long *serialno_list=NULL; |
884 | int serialno_list_size=0; |
885 | int ret; |
886 | |
887 | memset(vf,0,sizeof(*vf)); |
888 | vf->datasource=f; |
889 | vf->callbacks = callbacks; |
890 | |
891 | /* init the framing state */ |
892 | ogg_sync_init(&vf->oy); |
893 | |
894 | /* perhaps some data was previously read into a buffer for testing |
895 | against other stream types. Allow initialization from this |
896 | previously read data (especially as we may be reading from a |
897 | non-seekable stream) */ |
898 | if(initial){ |
899 | char *buffer=ogg_sync_buffer(&vf->oy,ibytes); |
900 | memcpy(buffer,initial,ibytes); |
901 | ogg_sync_wrote(&vf->oy,ibytes); |
902 | } |
903 | |
904 | /* can we seek? Stevens suggests the seek test was portable */ |
905 | if(offsettest!=-1)vf->seekable=1; |
906 | |
907 | /* No seeking yet; Set up a 'single' (current) logical bitstream |
908 | entry for partial open */ |
909 | vf->links=1; |
910 | vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); |
911 | vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); |
912 | ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ |
913 | |
914 | /* Fetch all BOS pages, store the vorbis header and all seen serial |
915 | numbers, load subsequent vorbis setup headers */ |
916 | if((ret=_fetch_headers(vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){ |
917 | vf->datasource=NULL; |
918 | ov_clear(vf); |
919 | }else{ |
920 | /* serial number list for first link needs to be held somewhere |
921 | for second stage of seekable stream open; this saves having to |
922 | seek/reread first link's serialnumber data then. */ |
923 | vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos)); |
924 | vf->serialnos[0]=vf->current_serialno=vf->os.serialno; |
925 | vf->serialnos[1]=serialno_list_size; |
926 | memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos)); |
927 | |
928 | vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets)); |
929 | vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets)); |
930 | vf->offsets[0]=0; |
931 | vf->dataoffsets[0]=vf->offset; |
932 | |
933 | vf->ready_state=PARTOPEN; |
934 | } |
935 | if(serialno_list)_ogg_free(serialno_list); |
936 | return(ret); |
937 | } |
938 | |
939 | static int _ov_open2(OggVorbis_File *vf){ |
940 | if(vf->ready_state != PARTOPEN) return OV_EINVAL; |
941 | vf->ready_state=OPENED; |
942 | if(vf->seekable){ |
943 | int ret=_open_seekable2(vf); |
944 | if(ret){ |
945 | vf->datasource=NULL; |
946 | ov_clear(vf); |
947 | } |
948 | return(ret); |
949 | }else |
950 | vf->ready_state=STREAMSET; |
951 | |
952 | return 0; |
953 | } |
954 | |
955 | |
956 | /* clear out the OggVorbis_File struct */ |
957 | int ov_clear(OggVorbis_File *vf){ |
958 | if(vf){ |
959 | vorbis_block_clear(&vf->vb); |
960 | vorbis_dsp_clear(&vf->vd); |
961 | ogg_stream_clear(&vf->os); |
962 | |
963 | if(vf->vi && vf->links){ |
964 | int i; |
965 | for(i=0;i<vf->links;i++){ |
966 | vorbis_info_clear(vf->vi+i); |
967 | vorbis_comment_clear(vf->vc+i); |
968 | } |
969 | _ogg_free(vf->vi); |
970 | _ogg_free(vf->vc); |
971 | } |
972 | if(vf->dataoffsets)_ogg_free(vf->dataoffsets); |
973 | if(vf->pcmlengths)_ogg_free(vf->pcmlengths); |
974 | if(vf->serialnos)_ogg_free(vf->serialnos); |
975 | if(vf->offsets)_ogg_free(vf->offsets); |
976 | ogg_sync_clear(&vf->oy); |
977 | if(vf->datasource && vf->callbacks.close_func) |
978 | (vf->callbacks.close_func)(vf->datasource); |
979 | memset(vf,0,sizeof(*vf)); |
980 | } |
981 | #ifdef DEBUG_LEAKS |
982 | _VDBG_dump(); |
983 | #endif |
984 | return(0); |
985 | } |
986 | |
987 | /* inspects the OggVorbis file and finds/documents all the logical |
988 | bitstreams contained in it. Tries to be tolerant of logical |
989 | bitstream sections that are truncated/woogie. |
990 | |
991 | return: -1) error |
992 | 0) OK |
993 | */ |
994 | |
995 | int ov_open_callbacks(void *f,OggVorbis_File *vf, |
996 | const char *initial,long ibytes,ov_callbacks callbacks){ |
997 | int ret=_ov_open1(f,vf,initial,ibytes,callbacks); |
998 | if(ret)return ret; |
999 | return _ov_open2(vf); |
1000 | } |
1001 | |
1002 | int ov_open(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){ |
1003 | ov_callbacks callbacks = { |
1004 | (size_t (*)(void *, size_t, size_t, void *)) fread, |
1005 | (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, |
1006 | (int (*)(void *)) fclose, |
1007 | (long (*)(void *)) ftell |
1008 | }; |
1009 | |
1010 | return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); |
1011 | } |
1012 | |
1013 | int ov_fopen(const char *path,OggVorbis_File *vf){ |
1014 | int ret; |
1015 | FILE *f = fopen(path,"rb" ); |
1016 | if(!f) return -1; |
1017 | |
1018 | ret = ov_open(f,vf,NULL,0); |
1019 | if(ret) fclose(f); |
1020 | return ret; |
1021 | } |
1022 | |
1023 | |
1024 | /* cheap hack for game usage where downsampling is desirable; there's |
1025 | no need for SRC as we can just do it cheaply in libvorbis. */ |
1026 | |
1027 | int ov_halfrate(OggVorbis_File *vf,int flag){ |
1028 | int i; |
1029 | if(vf->vi==NULL)return OV_EINVAL; |
1030 | if(vf->ready_state>STREAMSET){ |
1031 | /* clear out stream state; dumping the decode machine is needed to |
1032 | reinit the MDCT lookups. */ |
1033 | vorbis_dsp_clear(&vf->vd); |
1034 | vorbis_block_clear(&vf->vb); |
1035 | vf->ready_state=STREAMSET; |
1036 | if(vf->pcm_offset>=0){ |
1037 | ogg_int64_t pos=vf->pcm_offset; |
1038 | vf->pcm_offset=-1; /* make sure the pos is dumped if unseekable */ |
1039 | ov_pcm_seek(vf,pos); |
1040 | } |
1041 | } |
1042 | |
1043 | for(i=0;i<vf->links;i++){ |
1044 | if(vorbis_synthesis_halfrate(vf->vi+i,flag)){ |
1045 | if(flag) ov_halfrate(vf,0); |
1046 | return OV_EINVAL; |
1047 | } |
1048 | } |
1049 | return 0; |
1050 | } |
1051 | |
1052 | int ov_halfrate_p(OggVorbis_File *vf){ |
1053 | if(vf->vi==NULL)return OV_EINVAL; |
1054 | return vorbis_synthesis_halfrate_p(vf->vi); |
1055 | } |
1056 | |
1057 | /* Only partially open the vorbis file; test for Vorbisness, and load |
1058 | the headers for the first chain. Do not seek (although test for |
1059 | seekability). Use ov_test_open to finish opening the file, else |
1060 | ov_clear to close/free it. Same return codes as open. |
1061 | |
1062 | Note that vorbisfile does _not_ take ownership of the file if the |
1063 | call fails; the calling applicaiton is responsible for closing the file |
1064 | if this call returns an error. */ |
1065 | |
1066 | int ov_test_callbacks(void *f,OggVorbis_File *vf, |
1067 | const char *initial,long ibytes,ov_callbacks callbacks) |
1068 | { |
1069 | return _ov_open1(f,vf,initial,ibytes,callbacks); |
1070 | } |
1071 | |
1072 | int ov_test(FILE *f,OggVorbis_File *vf,const char *initial,long ibytes){ |
1073 | ov_callbacks callbacks = { |
1074 | (size_t (*)(void *, size_t, size_t, void *)) fread, |
1075 | (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, |
1076 | (int (*)(void *)) fclose, |
1077 | (long (*)(void *)) ftell |
1078 | }; |
1079 | |
1080 | return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); |
1081 | } |
1082 | |
1083 | int ov_test_open(OggVorbis_File *vf){ |
1084 | if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); |
1085 | return _ov_open2(vf); |
1086 | } |
1087 | |
1088 | /* How many logical bitstreams in this physical bitstream? */ |
1089 | long ov_streams(OggVorbis_File *vf){ |
1090 | return vf->links; |
1091 | } |
1092 | |
1093 | /* Is the FILE * associated with vf seekable? */ |
1094 | long ov_seekable(OggVorbis_File *vf){ |
1095 | return vf->seekable; |
1096 | } |
1097 | |
1098 | /* returns the bitrate for a given logical bitstream or the entire |
1099 | physical bitstream. If the file is open for random access, it will |
1100 | find the *actual* average bitrate. If the file is streaming, it |
1101 | returns the nominal bitrate (if set) else the average of the |
1102 | upper/lower bounds (if set) else -1 (unset). |
1103 | |
1104 | If you want the actual bitrate field settings, get them from the |
1105 | vorbis_info structs */ |
1106 | |
1107 | long ov_bitrate(OggVorbis_File *vf,int i){ |
1108 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1109 | if(i>=vf->links)return(OV_EINVAL); |
1110 | if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); |
1111 | if(i<0){ |
1112 | ogg_int64_t bits=0; |
1113 | int i; |
1114 | float br; |
1115 | for(i=0;i<vf->links;i++) |
1116 | bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; |
1117 | /* This once read: return(rint(bits/ov_time_total(vf,-1))); |
1118 | * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, |
1119 | * so this is slightly transformed to make it work. |
1120 | */ |
1121 | br = bits/ov_time_total(vf,-1); |
1122 | return(rint(br)); |
1123 | }else{ |
1124 | if(vf->seekable){ |
1125 | /* return the actual bitrate */ |
1126 | return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i))); |
1127 | }else{ |
1128 | /* return nominal if set */ |
1129 | if(vf->vi[i].bitrate_nominal>0){ |
1130 | return vf->vi[i].bitrate_nominal; |
1131 | }else{ |
1132 | if(vf->vi[i].bitrate_upper>0){ |
1133 | if(vf->vi[i].bitrate_lower>0){ |
1134 | return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; |
1135 | }else{ |
1136 | return vf->vi[i].bitrate_upper; |
1137 | } |
1138 | } |
1139 | return(OV_FALSE); |
1140 | } |
1141 | } |
1142 | } |
1143 | } |
1144 | |
1145 | /* returns the actual bitrate since last call. returns -1 if no |
1146 | additional data to offer since last call (or at beginning of stream), |
1147 | EINVAL if stream is only partially open |
1148 | */ |
1149 | long ov_bitrate_instant(OggVorbis_File *vf){ |
1150 | int link=(vf->seekable?vf->current_link:0); |
1151 | long ret; |
1152 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1153 | if(vf->samptrack==0)return(OV_FALSE); |
1154 | ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5; |
1155 | vf->bittrack=0.f; |
1156 | vf->samptrack=0.f; |
1157 | return(ret); |
1158 | } |
1159 | |
1160 | /* Guess */ |
1161 | long ov_serialnumber(OggVorbis_File *vf,int i){ |
1162 | if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); |
1163 | if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); |
1164 | if(i<0){ |
1165 | return(vf->current_serialno); |
1166 | }else{ |
1167 | return(vf->serialnos[i]); |
1168 | } |
1169 | } |
1170 | |
1171 | /* returns: total raw (compressed) length of content if i==-1 |
1172 | raw (compressed) length of that logical bitstream for i==0 to n |
1173 | OV_EINVAL if the stream is not seekable (we can't know the length) |
1174 | or if stream is only partially open |
1175 | */ |
1176 | ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ |
1177 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1178 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); |
1179 | if(i<0){ |
1180 | ogg_int64_t acc=0; |
1181 | int i; |
1182 | for(i=0;i<vf->links;i++) |
1183 | acc+=ov_raw_total(vf,i); |
1184 | return(acc); |
1185 | }else{ |
1186 | return(vf->offsets[i+1]-vf->offsets[i]); |
1187 | } |
1188 | } |
1189 | |
1190 | /* returns: total PCM length (samples) of content if i==-1 PCM length |
1191 | (samples) of that logical bitstream for i==0 to n |
1192 | OV_EINVAL if the stream is not seekable (we can't know the |
1193 | length) or only partially open |
1194 | */ |
1195 | ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ |
1196 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1197 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); |
1198 | if(i<0){ |
1199 | ogg_int64_t acc=0; |
1200 | int i; |
1201 | for(i=0;i<vf->links;i++) |
1202 | acc+=ov_pcm_total(vf,i); |
1203 | return(acc); |
1204 | }else{ |
1205 | return(vf->pcmlengths[i*2+1]); |
1206 | } |
1207 | } |
1208 | |
1209 | /* returns: total seconds of content if i==-1 |
1210 | seconds in that logical bitstream for i==0 to n |
1211 | OV_EINVAL if the stream is not seekable (we can't know the |
1212 | length) or only partially open |
1213 | */ |
1214 | double ov_time_total(OggVorbis_File *vf,int i){ |
1215 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1216 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); |
1217 | if(i<0){ |
1218 | double acc=0; |
1219 | int i; |
1220 | for(i=0;i<vf->links;i++) |
1221 | acc+=ov_time_total(vf,i); |
1222 | return(acc); |
1223 | }else{ |
1224 | return((double)(vf->pcmlengths[i*2+1])/vf->vi[i].rate); |
1225 | } |
1226 | } |
1227 | |
1228 | /* seek to an offset relative to the *compressed* data. This also |
1229 | scans packets to update the PCM cursor. It will cross a logical |
1230 | bitstream boundary, but only if it can't get any packets out of the |
1231 | tail of the bitstream we seek to (so no surprises). |
1232 | |
1233 | returns zero on success, nonzero on failure */ |
1234 | |
1235 | int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ |
1236 | ogg_stream_state work_os; |
1237 | |
1238 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1239 | if(!vf->seekable) |
1240 | return(OV_ENOSEEK); /* don't dump machine if we can't seek */ |
1241 | |
1242 | if(pos<0 || pos>vf->end)return(OV_EINVAL); |
1243 | |
1244 | /* is the seek position outside our current link [if any]? */ |
1245 | if(vf->ready_state>=STREAMSET){ |
1246 | if(pos<vf->offsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1]) |
1247 | _decode_clear(vf); /* clear out stream state */ |
1248 | } |
1249 | |
1250 | /* don't yet clear out decoding machine (if it's initialized), in |
1251 | the case we're in the same link. Restart the decode lapping, and |
1252 | let _fetch_and_process_packet deal with a potential bitstream |
1253 | boundary */ |
1254 | vf->pcm_offset=-1; |
1255 | ogg_stream_reset_serialno(&vf->os, |
1256 | vf->current_serialno); /* must set serialno */ |
1257 | vorbis_synthesis_restart(&vf->vd); |
1258 | |
1259 | if(_seek_helper(vf,pos)) { |
1260 | /* dump the machine so we're in a known state */ |
1261 | vf->pcm_offset=-1; |
1262 | _decode_clear(vf); |
1263 | return OV_EBADLINK; |
1264 | } |
1265 | |
1266 | /* we need to make sure the pcm_offset is set, but we don't want to |
1267 | advance the raw cursor past good packets just to get to the first |
1268 | with a granulepos. That's not equivalent behavior to beginning |
1269 | decoding as immediately after the seek position as possible. |
1270 | |
1271 | So, a hack. We use two stream states; a local scratch state and |
1272 | the shared vf->os stream state. We use the local state to |
1273 | scan, and the shared state as a buffer for later decode. |
1274 | |
1275 | Unfortuantely, on the last page we still advance to last packet |
1276 | because the granulepos on the last page is not necessarily on a |
1277 | packet boundary, and we need to make sure the granpos is |
1278 | correct. |
1279 | */ |
1280 | |
1281 | { |
1282 | ogg_page og; |
1283 | ogg_packet op; |
1284 | int lastblock=0; |
1285 | int accblock=0; |
1286 | int thisblock=0; |
1287 | int lastflag=0; |
1288 | int firstflag=0; |
1289 | ogg_int64_t pagepos=-1; |
1290 | |
1291 | ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */ |
1292 | ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE |
1293 | return from not necessarily |
1294 | starting from the beginning */ |
1295 | |
1296 | while(1){ |
1297 | if(vf->ready_state>=STREAMSET){ |
1298 | /* snarf/scan a packet if we can */ |
1299 | int result=ogg_stream_packetout(&work_os,&op); |
1300 | |
1301 | if(result>0){ |
1302 | |
1303 | if(vf->vi[vf->current_link].codec_setup){ |
1304 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); |
1305 | if(thisblock<0){ |
1306 | ogg_stream_packetout(&vf->os,NULL); |
1307 | thisblock=0; |
1308 | }else{ |
1309 | |
1310 | /* We can't get a guaranteed correct pcm position out of the |
1311 | last page in a stream because it might have a 'short' |
1312 | granpos, which can only be detected in the presence of a |
1313 | preceding page. However, if the last page is also the first |
1314 | page, the granpos rules of a first page take precedence. Not |
1315 | only that, but for first==last, the EOS page must be treated |
1316 | as if its a normal first page for the stream to open/play. */ |
1317 | if(lastflag && !firstflag) |
1318 | ogg_stream_packetout(&vf->os,NULL); |
1319 | else |
1320 | if(lastblock)accblock+=(lastblock+thisblock)>>2; |
1321 | } |
1322 | |
1323 | if(op.granulepos!=-1){ |
1324 | int i,link=vf->current_link; |
1325 | ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; |
1326 | if(granulepos<0)granulepos=0; |
1327 | |
1328 | for(i=0;i<link;i++) |
1329 | granulepos+=vf->pcmlengths[i*2+1]; |
1330 | vf->pcm_offset=granulepos-accblock; |
1331 | if(vf->pcm_offset<0)vf->pcm_offset=0; |
1332 | break; |
1333 | } |
1334 | lastblock=thisblock; |
1335 | continue; |
1336 | }else |
1337 | ogg_stream_packetout(&vf->os,NULL); |
1338 | } |
1339 | } |
1340 | |
1341 | if(!lastblock){ |
1342 | pagepos=_get_next_page(vf,&og,-1); |
1343 | if(pagepos<0){ |
1344 | vf->pcm_offset=ov_pcm_total(vf,-1); |
1345 | break; |
1346 | } |
1347 | }else{ |
1348 | /* huh? Bogus stream with packets but no granulepos */ |
1349 | vf->pcm_offset=-1; |
1350 | break; |
1351 | } |
1352 | |
1353 | /* has our decoding just traversed a bitstream boundary? */ |
1354 | if(vf->ready_state>=STREAMSET){ |
1355 | if(vf->current_serialno!=ogg_page_serialno(&og)){ |
1356 | |
1357 | /* two possibilities: |
1358 | 1) our decoding just traversed a bitstream boundary |
1359 | 2) another stream is multiplexed into this logical section? */ |
1360 | |
1361 | if(ogg_page_bos(&og)){ |
1362 | /* we traversed */ |
1363 | _decode_clear(vf); /* clear out stream state */ |
1364 | ogg_stream_clear(&work_os); |
1365 | } /* else, do nothing; next loop will scoop another page */ |
1366 | } |
1367 | } |
1368 | |
1369 | if(vf->ready_state<STREAMSET){ |
1370 | int link; |
1371 | long serialno = ogg_page_serialno(&og); |
1372 | |
1373 | for(link=0;link<vf->links;link++) |
1374 | if(vf->serialnos[link]==serialno)break; |
1375 | |
1376 | if(link==vf->links) continue; /* not the desired Vorbis |
1377 | bitstream section; keep |
1378 | trying */ |
1379 | vf->current_link=link; |
1380 | vf->current_serialno=serialno; |
1381 | ogg_stream_reset_serialno(&vf->os,serialno); |
1382 | ogg_stream_reset_serialno(&work_os,serialno); |
1383 | vf->ready_state=STREAMSET; |
1384 | firstflag=(pagepos<=vf->dataoffsets[link]); |
1385 | } |
1386 | |
1387 | ogg_stream_pagein(&vf->os,&og); |
1388 | ogg_stream_pagein(&work_os,&og); |
1389 | lastflag=ogg_page_eos(&og); |
1390 | |
1391 | } |
1392 | } |
1393 | |
1394 | ogg_stream_clear(&work_os); |
1395 | vf->bittrack=0.f; |
1396 | vf->samptrack=0.f; |
1397 | return(0); |
1398 | } |
1399 | |
1400 | /* Page granularity seek (faster than sample granularity because we |
1401 | don't do the last bit of decode to find a specific sample). |
1402 | |
1403 | Seek to the last [granule marked] page preceding the specified pos |
1404 | location, such that decoding past the returned point will quickly |
1405 | arrive at the requested position. */ |
1406 | int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ |
1407 | int link=-1; |
1408 | ogg_int64_t result=0; |
1409 | ogg_int64_t total=ov_pcm_total(vf,-1); |
1410 | |
1411 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1412 | if(!vf->seekable)return(OV_ENOSEEK); |
1413 | |
1414 | if(pos<0 || pos>total)return(OV_EINVAL); |
1415 | |
1416 | /* which bitstream section does this pcm offset occur in? */ |
1417 | for(link=vf->links-1;link>=0;link--){ |
1418 | total-=vf->pcmlengths[link*2+1]; |
1419 | if(pos>=total)break; |
1420 | } |
1421 | |
1422 | /* Search within the logical bitstream for the page with the highest |
1423 | pcm_pos preceding pos. If we're looking for a position on the |
1424 | first page, bisection will halt without finding our position as |
1425 | it's before the first explicit granulepos fencepost. That case is |
1426 | handled separately below. |
1427 | |
1428 | There is a danger here; missing pages or incorrect frame number |
1429 | information in the bitstream could make our task impossible. |
1430 | Account for that (it would be an error condition) */ |
1431 | |
1432 | /* new search algorithm originally by HB (Nicholas Vinen) */ |
1433 | |
1434 | { |
1435 | ogg_int64_t end=vf->offsets[link+1]; |
1436 | ogg_int64_t begin=vf->dataoffsets[link]; |
1437 | ogg_int64_t begintime = vf->pcmlengths[link*2]; |
1438 | ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; |
1439 | ogg_int64_t target=pos-total+begintime; |
1440 | ogg_int64_t best=-1; |
1441 | int got_page=0; |
1442 | |
1443 | ogg_page og; |
1444 | |
1445 | /* if we have only one page, there will be no bisection. Grab the page here */ |
1446 | if(begin==end){ |
1447 | result=_seek_helper(vf,begin); |
1448 | if(result) goto seek_error; |
1449 | |
1450 | result=_get_next_page(vf,&og,1); |
1451 | if(result<0) goto seek_error; |
1452 | |
1453 | got_page=1; |
1454 | } |
1455 | |
1456 | /* bisection loop */ |
1457 | while(begin<end){ |
1458 | ogg_int64_t bisect; |
1459 | |
1460 | if(end-begin<CHUNKSIZE){ |
1461 | bisect=begin; |
1462 | }else{ |
1463 | /* take a (pretty decent) guess. */ |
1464 | bisect=begin + |
1465 | (ogg_int64_t)((double)(target-begintime)*(end-begin)/(endtime-begintime)) |
1466 | - CHUNKSIZE; |
1467 | if(bisect<begin+CHUNKSIZE) |
1468 | bisect=begin; |
1469 | } |
1470 | |
1471 | result=_seek_helper(vf,bisect); |
1472 | if(result) goto seek_error; |
1473 | |
1474 | /* read loop within the bisection loop */ |
1475 | while(begin<end){ |
1476 | result=_get_next_page(vf,&og,end-vf->offset); |
1477 | if(result==OV_EREAD) goto seek_error; |
1478 | if(result<0){ |
1479 | /* there is no next page! */ |
1480 | if(bisect<=begin+1) |
1481 | /* No bisection left to perform. We've either found the |
1482 | best candidate already or failed. Exit loop. */ |
1483 | end=begin; |
1484 | else{ |
1485 | /* We tried to load a fraction of the last page; back up a |
1486 | bit and try to get the whole last page */ |
1487 | if(bisect==0) goto seek_error; |
1488 | bisect-=CHUNKSIZE; |
1489 | |
1490 | /* don't repeat/loop on a read we've already performed */ |
1491 | if(bisect<=begin)bisect=begin+1; |
1492 | |
1493 | /* seek and cntinue bisection */ |
1494 | result=_seek_helper(vf,bisect); |
1495 | if(result) goto seek_error; |
1496 | } |
1497 | }else{ |
1498 | ogg_int64_t granulepos; |
1499 | got_page=1; |
1500 | |
1501 | /* got a page. analyze it */ |
1502 | /* only consider pages from primary vorbis stream */ |
1503 | if(ogg_page_serialno(&og)!=vf->serialnos[link]) |
1504 | continue; |
1505 | |
1506 | /* only consider pages with the granulepos set */ |
1507 | granulepos=ogg_page_granulepos(&og); |
1508 | if(granulepos==-1)continue; |
1509 | |
1510 | if(granulepos<target){ |
1511 | /* this page is a successful candidate! Set state */ |
1512 | |
1513 | best=result; /* raw offset of packet with granulepos */ |
1514 | begin=vf->offset; /* raw offset of next page */ |
1515 | begintime=granulepos; |
1516 | |
1517 | /* if we're before our target but within a short distance, |
1518 | don't bisect; read forward */ |
1519 | if(target-begintime>44100)break; |
1520 | |
1521 | bisect=begin; /* *not* begin + 1 as above */ |
1522 | }else{ |
1523 | |
1524 | /* This is one of our pages, but the granpos is |
1525 | post-target; it is not a bisection return |
1526 | candidate. (The only way we'd use it is if it's the |
1527 | first page in the stream; we handle that case later |
1528 | outside the bisection) */ |
1529 | if(bisect<=begin+1){ |
1530 | /* No bisection left to perform. We've either found the |
1531 | best candidate already or failed. Exit loop. */ |
1532 | end=begin; |
1533 | }else{ |
1534 | if(end==vf->offset){ |
1535 | /* bisection read to the end; use the known page |
1536 | boundary (result) to update bisection, back up a |
1537 | little bit, and try again */ |
1538 | end=result; |
1539 | bisect-=CHUNKSIZE; |
1540 | if(bisect<=begin)bisect=begin+1; |
1541 | result=_seek_helper(vf,bisect); |
1542 | if(result) goto seek_error; |
1543 | }else{ |
1544 | /* Normal bisection */ |
1545 | end=bisect; |
1546 | endtime=granulepos; |
1547 | break; |
1548 | } |
1549 | } |
1550 | } |
1551 | } |
1552 | } |
1553 | } |
1554 | |
1555 | /* Out of bisection: did it 'fail?' */ |
1556 | if(best == -1){ |
1557 | |
1558 | /* Check the 'looking for data in first page' special case; |
1559 | bisection would 'fail' because our search target was before the |
1560 | first PCM granule position fencepost. */ |
1561 | |
1562 | if(got_page && |
1563 | begin == vf->dataoffsets[link] && |
1564 | ogg_page_serialno(&og)==vf->serialnos[link]){ |
1565 | |
1566 | /* Yes, this is the beginning-of-stream case. We already have |
1567 | our page, right at the beginning of PCM data. Set state |
1568 | and return. */ |
1569 | |
1570 | vf->pcm_offset=total; |
1571 | |
1572 | if(link!=vf->current_link){ |
1573 | /* Different link; dump entire decode machine */ |
1574 | _decode_clear(vf); |
1575 | |
1576 | vf->current_link=link; |
1577 | vf->current_serialno=vf->serialnos[link]; |
1578 | vf->ready_state=STREAMSET; |
1579 | |
1580 | }else{ |
1581 | vorbis_synthesis_restart(&vf->vd); |
1582 | } |
1583 | |
1584 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno); |
1585 | ogg_stream_pagein(&vf->os,&og); |
1586 | |
1587 | }else |
1588 | goto seek_error; |
1589 | |
1590 | }else{ |
1591 | |
1592 | /* Bisection found our page. seek to it, update pcm offset. Easier case than |
1593 | raw_seek, don't keep packets preceding granulepos. */ |
1594 | |
1595 | ogg_page og; |
1596 | ogg_packet op; |
1597 | |
1598 | /* seek */ |
1599 | result=_seek_helper(vf,best); |
1600 | vf->pcm_offset=-1; |
1601 | if(result) goto seek_error; |
1602 | result=_get_next_page(vf,&og,-1); |
1603 | if(result<0) goto seek_error; |
1604 | |
1605 | if(link!=vf->current_link){ |
1606 | /* Different link; dump entire decode machine */ |
1607 | _decode_clear(vf); |
1608 | |
1609 | vf->current_link=link; |
1610 | vf->current_serialno=vf->serialnos[link]; |
1611 | vf->ready_state=STREAMSET; |
1612 | |
1613 | }else{ |
1614 | vorbis_synthesis_restart(&vf->vd); |
1615 | } |
1616 | |
1617 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno); |
1618 | ogg_stream_pagein(&vf->os,&og); |
1619 | |
1620 | /* pull out all but last packet; the one with granulepos */ |
1621 | while(1){ |
1622 | result=ogg_stream_packetpeek(&vf->os,&op); |
1623 | if(result==0){ |
1624 | /* No packet returned; we exited the bisection with 'best' |
1625 | pointing to a page with a granule position, so the packet |
1626 | finishing this page ('best') originated on a preceding |
1627 | page. Keep fetching previous pages until we get one with |
1628 | a granulepos or without the 'continued' flag set. Then |
1629 | just use raw_seek for simplicity. */ |
1630 | /* Do not rewind past the beginning of link data; if we do, |
1631 | it's either a bug or a broken stream */ |
1632 | result=best; |
1633 | while(result>vf->dataoffsets[link]){ |
1634 | result=_get_prev_page(vf,result,&og); |
1635 | if(result<0) goto seek_error; |
1636 | if(ogg_page_serialno(&og)==vf->current_serialno && |
1637 | (ogg_page_granulepos(&og)>-1 || |
1638 | !ogg_page_continued(&og))){ |
1639 | return ov_raw_seek(vf,result); |
1640 | } |
1641 | } |
1642 | } |
1643 | if(result<0){ |
1644 | result = OV_EBADPACKET; |
1645 | goto seek_error; |
1646 | } |
1647 | if(op.granulepos!=-1){ |
1648 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; |
1649 | if(vf->pcm_offset<0)vf->pcm_offset=0; |
1650 | vf->pcm_offset+=total; |
1651 | break; |
1652 | }else |
1653 | result=ogg_stream_packetout(&vf->os,NULL); |
1654 | } |
1655 | } |
1656 | } |
1657 | |
1658 | /* verify result */ |
1659 | if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ |
1660 | result=OV_EFAULT; |
1661 | goto seek_error; |
1662 | } |
1663 | vf->bittrack=0.f; |
1664 | vf->samptrack=0.f; |
1665 | return(0); |
1666 | |
1667 | seek_error: |
1668 | /* dump machine so we're in a known state */ |
1669 | vf->pcm_offset=-1; |
1670 | _decode_clear(vf); |
1671 | return (int)result; |
1672 | } |
1673 | |
1674 | /* seek to a sample offset relative to the decompressed pcm stream |
1675 | returns zero on success, nonzero on failure */ |
1676 | |
1677 | int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ |
1678 | int thisblock,lastblock=0; |
1679 | int ret=ov_pcm_seek_page(vf,pos); |
1680 | if(ret<0)return(ret); |
1681 | if((ret=_make_decode_ready(vf)))return ret; |
1682 | |
1683 | /* discard leading packets we don't need for the lapping of the |
1684 | position we want; don't decode them */ |
1685 | |
1686 | while(1){ |
1687 | ogg_packet op; |
1688 | ogg_page og; |
1689 | |
1690 | int ret=ogg_stream_packetpeek(&vf->os,&op); |
1691 | if(ret>0){ |
1692 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); |
1693 | if(thisblock<0){ |
1694 | ogg_stream_packetout(&vf->os,NULL); |
1695 | continue; /* non audio packet */ |
1696 | } |
1697 | if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; |
1698 | |
1699 | if(vf->pcm_offset+((thisblock+ |
1700 | vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; |
1701 | |
1702 | /* remove the packet from packet queue and track its granulepos */ |
1703 | ogg_stream_packetout(&vf->os,NULL); |
1704 | vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with |
1705 | only tracking, no |
1706 | pcm_decode */ |
1707 | vorbis_synthesis_blockin(&vf->vd,&vf->vb); |
1708 | |
1709 | /* end of logical stream case is hard, especially with exact |
1710 | length positioning. */ |
1711 | |
1712 | if(op.granulepos>-1){ |
1713 | int i; |
1714 | /* always believe the stream markers */ |
1715 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; |
1716 | if(vf->pcm_offset<0)vf->pcm_offset=0; |
1717 | for(i=0;i<vf->current_link;i++) |
1718 | vf->pcm_offset+=vf->pcmlengths[i*2+1]; |
1719 | } |
1720 | |
1721 | lastblock=thisblock; |
1722 | |
1723 | }else{ |
1724 | if(ret<0 && ret!=OV_HOLE)break; |
1725 | |
1726 | /* suck in a new page */ |
1727 | if(_get_next_page(vf,&og,-1)<0)break; |
1728 | if(ogg_page_bos(&og))_decode_clear(vf); |
1729 | |
1730 | if(vf->ready_state<STREAMSET){ |
1731 | long serialno=ogg_page_serialno(&og); |
1732 | int link; |
1733 | |
1734 | for(link=0;link<vf->links;link++) |
1735 | if(vf->serialnos[link]==serialno)break; |
1736 | if(link==vf->links) continue; |
1737 | vf->current_link=link; |
1738 | |
1739 | vf->ready_state=STREAMSET; |
1740 | vf->current_serialno=ogg_page_serialno(&og); |
1741 | ogg_stream_reset_serialno(&vf->os,serialno); |
1742 | ret=_make_decode_ready(vf); |
1743 | if(ret)return ret; |
1744 | lastblock=0; |
1745 | } |
1746 | |
1747 | ogg_stream_pagein(&vf->os,&og); |
1748 | } |
1749 | } |
1750 | |
1751 | vf->bittrack=0.f; |
1752 | vf->samptrack=0.f; |
1753 | /* discard samples until we reach the desired position. Crossing a |
1754 | logical bitstream boundary with abandon is OK. */ |
1755 | { |
1756 | /* note that halfrate could be set differently in each link, but |
1757 | vorbisfile encoforces all links are set or unset */ |
1758 | int hs=vorbis_synthesis_halfrate_p(vf->vi); |
1759 | while(vf->pcm_offset<((pos>>hs)<<hs)){ |
1760 | ogg_int64_t target=(pos-vf->pcm_offset)>>hs; |
1761 | long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); |
1762 | |
1763 | if(samples>target)samples=target; |
1764 | vorbis_synthesis_read(&vf->vd,samples); |
1765 | vf->pcm_offset+=samples<<hs; |
1766 | |
1767 | if(samples<target) |
1768 | if(_fetch_and_process_packet(vf,NULL,1,1)<=0) |
1769 | vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */ |
1770 | } |
1771 | } |
1772 | return 0; |
1773 | } |
1774 | |
1775 | /* seek to a playback time relative to the decompressed pcm stream |
1776 | returns zero on success, nonzero on failure */ |
1777 | int ov_time_seek(OggVorbis_File *vf,double seconds){ |
1778 | /* translate time to PCM position and call ov_pcm_seek */ |
1779 | |
1780 | int link=-1; |
1781 | ogg_int64_t pcm_total=0; |
1782 | double time_total=0.; |
1783 | |
1784 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1785 | if(!vf->seekable)return(OV_ENOSEEK); |
1786 | if(seconds<0)return(OV_EINVAL); |
1787 | |
1788 | /* which bitstream section does this time offset occur in? */ |
1789 | for(link=0;link<vf->links;link++){ |
1790 | double addsec = ov_time_total(vf,link); |
1791 | if(seconds<time_total+addsec)break; |
1792 | time_total+=addsec; |
1793 | pcm_total+=vf->pcmlengths[link*2+1]; |
1794 | } |
1795 | |
1796 | if(link==vf->links)return(OV_EINVAL); |
1797 | |
1798 | /* enough information to convert time offset to pcm offset */ |
1799 | { |
1800 | ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; |
1801 | return(ov_pcm_seek(vf,target)); |
1802 | } |
1803 | } |
1804 | |
1805 | /* page-granularity version of ov_time_seek |
1806 | returns zero on success, nonzero on failure */ |
1807 | int ov_time_seek_page(OggVorbis_File *vf,double seconds){ |
1808 | /* translate time to PCM position and call ov_pcm_seek */ |
1809 | |
1810 | int link=-1; |
1811 | ogg_int64_t pcm_total=0; |
1812 | double time_total=0.; |
1813 | |
1814 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1815 | if(!vf->seekable)return(OV_ENOSEEK); |
1816 | if(seconds<0)return(OV_EINVAL); |
1817 | |
1818 | /* which bitstream section does this time offset occur in? */ |
1819 | for(link=0;link<vf->links;link++){ |
1820 | double addsec = ov_time_total(vf,link); |
1821 | if(seconds<time_total+addsec)break; |
1822 | time_total+=addsec; |
1823 | pcm_total+=vf->pcmlengths[link*2+1]; |
1824 | } |
1825 | |
1826 | if(link==vf->links)return(OV_EINVAL); |
1827 | |
1828 | /* enough information to convert time offset to pcm offset */ |
1829 | { |
1830 | ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; |
1831 | return(ov_pcm_seek_page(vf,target)); |
1832 | } |
1833 | } |
1834 | |
1835 | /* tell the current stream offset cursor. Note that seek followed by |
1836 | tell will likely not give the set offset due to caching */ |
1837 | ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ |
1838 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1839 | return(vf->offset); |
1840 | } |
1841 | |
1842 | /* return PCM offset (sample) of next PCM sample to be read */ |
1843 | ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ |
1844 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1845 | return(vf->pcm_offset); |
1846 | } |
1847 | |
1848 | /* return time offset (seconds) of next PCM sample to be read */ |
1849 | double ov_time_tell(OggVorbis_File *vf){ |
1850 | int link=0; |
1851 | ogg_int64_t pcm_total=0; |
1852 | double time_total=0.f; |
1853 | |
1854 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1855 | if(vf->seekable){ |
1856 | pcm_total=ov_pcm_total(vf,-1); |
1857 | time_total=ov_time_total(vf,-1); |
1858 | |
1859 | /* which bitstream section does this time offset occur in? */ |
1860 | for(link=vf->links-1;link>=0;link--){ |
1861 | pcm_total-=vf->pcmlengths[link*2+1]; |
1862 | time_total-=ov_time_total(vf,link); |
1863 | if(vf->pcm_offset>=pcm_total)break; |
1864 | } |
1865 | } |
1866 | |
1867 | return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate); |
1868 | } |
1869 | |
1870 | /* link: -1) return the vorbis_info struct for the bitstream section |
1871 | currently being decoded |
1872 | 0-n) to request information for a specific bitstream section |
1873 | |
1874 | In the case of a non-seekable bitstream, any call returns the |
1875 | current bitstream. NULL in the case that the machine is not |
1876 | initialized */ |
1877 | |
1878 | vorbis_info *ov_info(OggVorbis_File *vf,int link){ |
1879 | if(vf->seekable){ |
1880 | if(link<0) |
1881 | if(vf->ready_state>=STREAMSET) |
1882 | return vf->vi+vf->current_link; |
1883 | else |
1884 | return vf->vi; |
1885 | else |
1886 | if(link>=vf->links) |
1887 | return NULL; |
1888 | else |
1889 | return vf->vi+link; |
1890 | }else{ |
1891 | return vf->vi; |
1892 | } |
1893 | } |
1894 | |
1895 | /* grr, strong typing, grr, no templates/inheritence, grr */ |
1896 | vorbis_comment *(OggVorbis_File *vf,int link){ |
1897 | if(vf->seekable){ |
1898 | if(link<0) |
1899 | if(vf->ready_state>=STREAMSET) |
1900 | return vf->vc+vf->current_link; |
1901 | else |
1902 | return vf->vc; |
1903 | else |
1904 | if(link>=vf->links) |
1905 | return NULL; |
1906 | else |
1907 | return vf->vc+link; |
1908 | }else{ |
1909 | return vf->vc; |
1910 | } |
1911 | } |
1912 | |
1913 | static int host_is_big_endian() { |
1914 | ogg_int32_t pattern = 0xfeedface; /* deadbeef */ |
1915 | unsigned char *bytewise = (unsigned char *)&pattern; |
1916 | if (bytewise[0] == 0xfe) return 1; |
1917 | return 0; |
1918 | } |
1919 | |
1920 | /* up to this point, everything could more or less hide the multiple |
1921 | logical bitstream nature of chaining from the toplevel application |
1922 | if the toplevel application didn't particularly care. However, at |
1923 | the point that we actually read audio back, the multiple-section |
1924 | nature must surface: Multiple bitstream sections do not necessarily |
1925 | have to have the same number of channels or sampling rate. |
1926 | |
1927 | ov_read returns the sequential logical bitstream number currently |
1928 | being decoded along with the PCM data in order that the toplevel |
1929 | application can take action on channel/sample rate changes. This |
1930 | number will be incremented even for streamed (non-seekable) streams |
1931 | (for seekable streams, it represents the actual logical bitstream |
1932 | index within the physical bitstream. Note that the accessor |
1933 | functions above are aware of this dichotomy). |
1934 | |
1935 | ov_read_filter is exactly the same as ov_read except that it processes |
1936 | the decoded audio data through a filter before packing it into the |
1937 | requested format. This gives greater accuracy than applying a filter |
1938 | after the audio has been converted into integral PCM. |
1939 | |
1940 | input values: buffer) a buffer to hold packed PCM data for return |
1941 | length) the byte length requested to be placed into buffer |
1942 | bigendianp) should the data be packed LSB first (0) or |
1943 | MSB first (1) |
1944 | word) word size for output. currently 1 (byte) or |
1945 | 2 (16 bit short) |
1946 | |
1947 | return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) |
1948 | 0) EOF |
1949 | n) number of bytes of PCM actually returned. The |
1950 | below works on a packet-by-packet basis, so the |
1951 | return length is not related to the 'length' passed |
1952 | in, just guaranteed to fit. |
1953 | |
1954 | *section) set to the logical bitstream number */ |
1955 | |
1956 | long ov_read_filter(OggVorbis_File *vf,char *buffer,int length, |
1957 | int bigendianp,int word,int sgned,int *bitstream, |
1958 | void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param){ |
1959 | int i,j; |
1960 | int host_endian = host_is_big_endian(); |
1961 | int hs; |
1962 | |
1963 | float **pcm; |
1964 | long samples; |
1965 | |
1966 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
1967 | if(word<=0)return(OV_EINVAL); |
1968 | |
1969 | while(1){ |
1970 | if(vf->ready_state==INITSET){ |
1971 | samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); |
1972 | if(samples)break; |
1973 | } |
1974 | |
1975 | /* suck in another packet */ |
1976 | { |
1977 | int ret=_fetch_and_process_packet(vf,NULL,1,1); |
1978 | if(ret==OV_EOF) |
1979 | return(0); |
1980 | if(ret<=0) |
1981 | return(ret); |
1982 | } |
1983 | |
1984 | } |
1985 | |
1986 | if(samples>0){ |
1987 | |
1988 | /* yay! proceed to pack data into the byte buffer */ |
1989 | |
1990 | long channels=ov_info(vf,-1)->channels; |
1991 | long bytespersample=word * channels; |
1992 | vorbis_fpu_control fpu; |
1993 | |
1994 | if(channels<1||channels>255)return(OV_EINVAL); |
1995 | if(samples>length/bytespersample)samples=length/bytespersample; |
1996 | |
1997 | if(samples <= 0) |
1998 | return OV_EINVAL; |
1999 | |
2000 | /* Here. */ |
2001 | if(filter) |
2002 | filter(pcm,channels,samples,filter_param); |
2003 | |
2004 | /* a tight loop to pack each size */ |
2005 | { |
2006 | int val; |
2007 | if(word==1){ |
2008 | int off=(sgned?0:128); |
2009 | vorbis_fpu_setround(&fpu); |
2010 | for(j=0;j<samples;j++) |
2011 | for(i=0;i<channels;i++){ |
2012 | val=vorbis_ftoi(pcm[i][j]*128.f); |
2013 | if(val>127)val=127; |
2014 | else if(val<-128)val=-128; |
2015 | *buffer++=val+off; |
2016 | } |
2017 | vorbis_fpu_restore(fpu); |
2018 | }else{ |
2019 | int off=(sgned?0:32768); |
2020 | |
2021 | if(host_endian==bigendianp){ |
2022 | if(sgned){ |
2023 | |
2024 | vorbis_fpu_setround(&fpu); |
2025 | for(i=0;i<channels;i++) { /* It's faster in this order */ |
2026 | float *src=pcm[i]; |
2027 | short *dest=((short *)buffer)+i; |
2028 | for(j=0;j<samples;j++) { |
2029 | val=vorbis_ftoi(src[j]*32768.f); |
2030 | if(val>32767)val=32767; |
2031 | else if(val<-32768)val=-32768; |
2032 | *dest=val; |
2033 | dest+=channels; |
2034 | } |
2035 | } |
2036 | vorbis_fpu_restore(fpu); |
2037 | |
2038 | }else{ |
2039 | |
2040 | vorbis_fpu_setround(&fpu); |
2041 | for(i=0;i<channels;i++) { |
2042 | float *src=pcm[i]; |
2043 | short *dest=((short *)buffer)+i; |
2044 | for(j=0;j<samples;j++) { |
2045 | val=vorbis_ftoi(src[j]*32768.f); |
2046 | if(val>32767)val=32767; |
2047 | else if(val<-32768)val=-32768; |
2048 | *dest=val+off; |
2049 | dest+=channels; |
2050 | } |
2051 | } |
2052 | vorbis_fpu_restore(fpu); |
2053 | |
2054 | } |
2055 | }else if(bigendianp){ |
2056 | |
2057 | vorbis_fpu_setround(&fpu); |
2058 | for(j=0;j<samples;j++) |
2059 | for(i=0;i<channels;i++){ |
2060 | val=vorbis_ftoi(pcm[i][j]*32768.f); |
2061 | if(val>32767)val=32767; |
2062 | else if(val<-32768)val=-32768; |
2063 | val+=off; |
2064 | *buffer++=(val>>8); |
2065 | *buffer++=(val&0xff); |
2066 | } |
2067 | vorbis_fpu_restore(fpu); |
2068 | |
2069 | }else{ |
2070 | int val; |
2071 | vorbis_fpu_setround(&fpu); |
2072 | for(j=0;j<samples;j++) |
2073 | for(i=0;i<channels;i++){ |
2074 | val=vorbis_ftoi(pcm[i][j]*32768.f); |
2075 | if(val>32767)val=32767; |
2076 | else if(val<-32768)val=-32768; |
2077 | val+=off; |
2078 | *buffer++=(val&0xff); |
2079 | *buffer++=(val>>8); |
2080 | } |
2081 | vorbis_fpu_restore(fpu); |
2082 | |
2083 | } |
2084 | } |
2085 | } |
2086 | |
2087 | vorbis_synthesis_read(&vf->vd,samples); |
2088 | hs=vorbis_synthesis_halfrate_p(vf->vi); |
2089 | vf->pcm_offset+=(samples<<hs); |
2090 | if(bitstream)*bitstream=vf->current_link; |
2091 | return(samples*bytespersample); |
2092 | }else{ |
2093 | return(samples); |
2094 | } |
2095 | } |
2096 | |
2097 | long ov_read(OggVorbis_File *vf,char *buffer,int length, |
2098 | int bigendianp,int word,int sgned,int *bitstream){ |
2099 | return ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, NULL, NULL); |
2100 | } |
2101 | |
2102 | /* input values: pcm_channels) a float vector per channel of output |
2103 | length) the sample length being read by the app |
2104 | |
2105 | return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) |
2106 | 0) EOF |
2107 | n) number of samples of PCM actually returned. The |
2108 | below works on a packet-by-packet basis, so the |
2109 | return length is not related to the 'length' passed |
2110 | in, just guaranteed to fit. |
2111 | |
2112 | *section) set to the logical bitstream number */ |
2113 | |
2114 | |
2115 | |
2116 | long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length, |
2117 | int *bitstream){ |
2118 | |
2119 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
2120 | |
2121 | while(1){ |
2122 | if(vf->ready_state==INITSET){ |
2123 | float **pcm; |
2124 | long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); |
2125 | if(samples){ |
2126 | int hs=vorbis_synthesis_halfrate_p(vf->vi); |
2127 | if(pcm_channels)*pcm_channels=pcm; |
2128 | if(samples>length)samples=length; |
2129 | vorbis_synthesis_read(&vf->vd,samples); |
2130 | vf->pcm_offset+=samples<<hs; |
2131 | if(bitstream)*bitstream=vf->current_link; |
2132 | return samples; |
2133 | |
2134 | } |
2135 | } |
2136 | |
2137 | /* suck in another packet */ |
2138 | { |
2139 | int ret=_fetch_and_process_packet(vf,NULL,1,1); |
2140 | if(ret==OV_EOF)return(0); |
2141 | if(ret<=0)return(ret); |
2142 | } |
2143 | |
2144 | } |
2145 | } |
2146 | |
2147 | extern const float *vorbis_window(vorbis_dsp_state *v,int W); |
2148 | |
2149 | static void _ov_splice(float **pcm,float **lappcm, |
2150 | int n1, int n2, |
2151 | int ch1, int ch2, |
2152 | const float *w1, const float *w2){ |
2153 | int i,j; |
2154 | const float *w=w1; |
2155 | int n=n1; |
2156 | |
2157 | if(n1>n2){ |
2158 | n=n2; |
2159 | w=w2; |
2160 | } |
2161 | |
2162 | /* splice */ |
2163 | for(j=0;j<ch1 && j<ch2;j++){ |
2164 | float *s=lappcm[j]; |
2165 | float *d=pcm[j]; |
2166 | |
2167 | for(i=0;i<n;i++){ |
2168 | float wd=w[i]*w[i]; |
2169 | float ws=1.-wd; |
2170 | d[i]=d[i]*wd + s[i]*ws; |
2171 | } |
2172 | } |
2173 | /* window from zero */ |
2174 | for(;j<ch2;j++){ |
2175 | float *d=pcm[j]; |
2176 | for(i=0;i<n;i++){ |
2177 | float wd=w[i]*w[i]; |
2178 | d[i]=d[i]*wd; |
2179 | } |
2180 | } |
2181 | |
2182 | } |
2183 | |
2184 | /* make sure vf is INITSET */ |
2185 | static int _ov_initset(OggVorbis_File *vf){ |
2186 | while(1){ |
2187 | if(vf->ready_state==INITSET)break; |
2188 | /* suck in another packet */ |
2189 | { |
2190 | int ret=_fetch_and_process_packet(vf,NULL,1,0); |
2191 | if(ret<0 && ret!=OV_HOLE)return(ret); |
2192 | } |
2193 | } |
2194 | return 0; |
2195 | } |
2196 | |
2197 | /* make sure vf is INITSET and that we have a primed buffer; if |
2198 | we're crosslapping at a stream section boundary, this also makes |
2199 | sure we're sanity checking against the right stream information */ |
2200 | static int _ov_initprime(OggVorbis_File *vf){ |
2201 | vorbis_dsp_state *vd=&vf->vd; |
2202 | while(1){ |
2203 | if(vf->ready_state==INITSET) |
2204 | if(vorbis_synthesis_pcmout(vd,NULL))break; |
2205 | |
2206 | /* suck in another packet */ |
2207 | { |
2208 | int ret=_fetch_and_process_packet(vf,NULL,1,0); |
2209 | if(ret<0 && ret!=OV_HOLE)return(ret); |
2210 | } |
2211 | } |
2212 | return 0; |
2213 | } |
2214 | |
2215 | /* grab enough data for lapping from vf; this may be in the form of |
2216 | unreturned, already-decoded pcm, remaining PCM we will need to |
2217 | decode, or synthetic postextrapolation from last packets. */ |
2218 | static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, |
2219 | float **lappcm,int lapsize){ |
2220 | int lapcount=0,i; |
2221 | float **pcm; |
2222 | |
2223 | /* try first to decode the lapping data */ |
2224 | while(lapcount<lapsize){ |
2225 | int samples=vorbis_synthesis_pcmout(vd,&pcm); |
2226 | if(samples){ |
2227 | if(samples>lapsize-lapcount)samples=lapsize-lapcount; |
2228 | for(i=0;i<vi->channels;i++) |
2229 | memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); |
2230 | lapcount+=samples; |
2231 | vorbis_synthesis_read(vd,samples); |
2232 | }else{ |
2233 | /* suck in another packet */ |
2234 | int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */ |
2235 | if(ret==OV_EOF)break; |
2236 | } |
2237 | } |
2238 | if(lapcount<lapsize){ |
2239 | /* failed to get lapping data from normal decode; pry it from the |
2240 | postextrapolation buffering, or the second half of the MDCT |
2241 | from the last packet */ |
2242 | int samples=vorbis_synthesis_lapout(&vf->vd,&pcm); |
2243 | if(samples==0){ |
2244 | for(i=0;i<vi->channels;i++) |
2245 | memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount); |
2246 | lapcount=lapsize; |
2247 | }else{ |
2248 | if(samples>lapsize-lapcount)samples=lapsize-lapcount; |
2249 | for(i=0;i<vi->channels;i++) |
2250 | memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); |
2251 | lapcount+=samples; |
2252 | } |
2253 | } |
2254 | } |
2255 | |
2256 | /* this sets up crosslapping of a sample by using trailing data from |
2257 | sample 1 and lapping it into the windowing buffer of sample 2 */ |
2258 | int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){ |
2259 | vorbis_info *vi1,*vi2; |
2260 | float **lappcm; |
2261 | float **pcm; |
2262 | const float *w1,*w2; |
2263 | int n1,n2,i,ret,hs1,hs2; |
2264 | |
2265 | if(vf1==vf2)return(0); /* degenerate case */ |
2266 | if(vf1->ready_state<OPENED)return(OV_EINVAL); |
2267 | if(vf2->ready_state<OPENED)return(OV_EINVAL); |
2268 | |
2269 | /* the relevant overlap buffers must be pre-checked and pre-primed |
2270 | before looking at settings in the event that priming would cross |
2271 | a bitstream boundary. So, do it now */ |
2272 | |
2273 | ret=_ov_initset(vf1); |
2274 | if(ret)return(ret); |
2275 | ret=_ov_initprime(vf2); |
2276 | if(ret)return(ret); |
2277 | |
2278 | vi1=ov_info(vf1,-1); |
2279 | vi2=ov_info(vf2,-1); |
2280 | hs1=ov_halfrate_p(vf1); |
2281 | hs2=ov_halfrate_p(vf2); |
2282 | |
2283 | lappcm=alloca(sizeof(*lappcm)*vi1->channels); |
2284 | n1=vorbis_info_blocksize(vi1,0)>>(1+hs1); |
2285 | n2=vorbis_info_blocksize(vi2,0)>>(1+hs2); |
2286 | w1=vorbis_window(&vf1->vd,0); |
2287 | w2=vorbis_window(&vf2->vd,0); |
2288 | |
2289 | for(i=0;i<vi1->channels;i++) |
2290 | lappcm[i]=alloca(sizeof(**lappcm)*n1); |
2291 | |
2292 | _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1); |
2293 | |
2294 | /* have a lapping buffer from vf1; now to splice it into the lapping |
2295 | buffer of vf2 */ |
2296 | /* consolidate and expose the buffer. */ |
2297 | vorbis_synthesis_lapout(&vf2->vd,&pcm); |
2298 | |
2299 | #if 0 |
2300 | _analysis_output_always("pcmL" ,0,pcm[0],n1*2,0,0,0); |
2301 | _analysis_output_always("pcmR" ,0,pcm[1],n1*2,0,0,0); |
2302 | #endif |
2303 | |
2304 | /* splice */ |
2305 | _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2); |
2306 | |
2307 | /* done */ |
2308 | return(0); |
2309 | } |
2310 | |
2311 | static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos, |
2312 | int (*localseek)(OggVorbis_File *,ogg_int64_t)){ |
2313 | vorbis_info *vi; |
2314 | float **lappcm; |
2315 | float **pcm; |
2316 | const float *w1,*w2; |
2317 | int n1,n2,ch1,ch2,hs; |
2318 | int i,ret; |
2319 | |
2320 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
2321 | ret=_ov_initset(vf); |
2322 | if(ret)return(ret); |
2323 | vi=ov_info(vf,-1); |
2324 | hs=ov_halfrate_p(vf); |
2325 | |
2326 | ch1=vi->channels; |
2327 | n1=vorbis_info_blocksize(vi,0)>>(1+hs); |
2328 | w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are |
2329 | persistent; even if the decode state |
2330 | from this link gets dumped, this |
2331 | window array continues to exist */ |
2332 | |
2333 | lappcm=alloca(sizeof(*lappcm)*ch1); |
2334 | for(i=0;i<ch1;i++) |
2335 | lappcm[i]=alloca(sizeof(**lappcm)*n1); |
2336 | _ov_getlap(vf,vi,&vf->vd,lappcm,n1); |
2337 | |
2338 | /* have lapping data; seek and prime the buffer */ |
2339 | ret=localseek(vf,pos); |
2340 | if(ret)return ret; |
2341 | ret=_ov_initprime(vf); |
2342 | if(ret)return(ret); |
2343 | |
2344 | /* Guard against cross-link changes; they're perfectly legal */ |
2345 | vi=ov_info(vf,-1); |
2346 | ch2=vi->channels; |
2347 | n2=vorbis_info_blocksize(vi,0)>>(1+hs); |
2348 | w2=vorbis_window(&vf->vd,0); |
2349 | |
2350 | /* consolidate and expose the buffer. */ |
2351 | vorbis_synthesis_lapout(&vf->vd,&pcm); |
2352 | |
2353 | /* splice */ |
2354 | _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); |
2355 | |
2356 | /* done */ |
2357 | return(0); |
2358 | } |
2359 | |
2360 | int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){ |
2361 | return _ov_64_seek_lap(vf,pos,ov_raw_seek); |
2362 | } |
2363 | |
2364 | int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){ |
2365 | return _ov_64_seek_lap(vf,pos,ov_pcm_seek); |
2366 | } |
2367 | |
2368 | int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){ |
2369 | return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page); |
2370 | } |
2371 | |
2372 | static int _ov_d_seek_lap(OggVorbis_File *vf,double pos, |
2373 | int (*localseek)(OggVorbis_File *,double)){ |
2374 | vorbis_info *vi; |
2375 | float **lappcm; |
2376 | float **pcm; |
2377 | const float *w1,*w2; |
2378 | int n1,n2,ch1,ch2,hs; |
2379 | int i,ret; |
2380 | |
2381 | if(vf->ready_state<OPENED)return(OV_EINVAL); |
2382 | ret=_ov_initset(vf); |
2383 | if(ret)return(ret); |
2384 | vi=ov_info(vf,-1); |
2385 | hs=ov_halfrate_p(vf); |
2386 | |
2387 | ch1=vi->channels; |
2388 | n1=vorbis_info_blocksize(vi,0)>>(1+hs); |
2389 | w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are |
2390 | persistent; even if the decode state |
2391 | from this link gets dumped, this |
2392 | window array continues to exist */ |
2393 | |
2394 | lappcm=alloca(sizeof(*lappcm)*ch1); |
2395 | for(i=0;i<ch1;i++) |
2396 | lappcm[i]=alloca(sizeof(**lappcm)*n1); |
2397 | _ov_getlap(vf,vi,&vf->vd,lappcm,n1); |
2398 | |
2399 | /* have lapping data; seek and prime the buffer */ |
2400 | ret=localseek(vf,pos); |
2401 | if(ret)return ret; |
2402 | ret=_ov_initprime(vf); |
2403 | if(ret)return(ret); |
2404 | |
2405 | /* Guard against cross-link changes; they're perfectly legal */ |
2406 | vi=ov_info(vf,-1); |
2407 | ch2=vi->channels; |
2408 | n2=vorbis_info_blocksize(vi,0)>>(1+hs); |
2409 | w2=vorbis_window(&vf->vd,0); |
2410 | |
2411 | /* consolidate and expose the buffer. */ |
2412 | vorbis_synthesis_lapout(&vf->vd,&pcm); |
2413 | |
2414 | /* splice */ |
2415 | _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); |
2416 | |
2417 | /* done */ |
2418 | return(0); |
2419 | } |
2420 | |
2421 | int ov_time_seek_lap(OggVorbis_File *vf,double pos){ |
2422 | return _ov_d_seek_lap(vf,pos,ov_time_seek); |
2423 | } |
2424 | |
2425 | int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){ |
2426 | return _ov_d_seek_lap(vf,pos,ov_time_seek_page); |
2427 | } |
2428 | |