diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libtremor/vorbisfile.c | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libtremor/vorbisfile.c')
-rw-r--r-- | lib/rbcodec/codecs/libtremor/vorbisfile.c | 1671 |
1 files changed, 1671 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libtremor/vorbisfile.c b/lib/rbcodec/codecs/libtremor/vorbisfile.c new file mode 100644 index 0000000000..271e5a09e3 --- /dev/null +++ b/lib/rbcodec/codecs/libtremor/vorbisfile.c | |||
@@ -0,0 +1,1671 @@ | |||
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 "config-tremor.h" | ||
20 | #include <stdio.h> | ||
21 | #include <errno.h> | ||
22 | #include <string.h> | ||
23 | #include <math.h> | ||
24 | #include "system.h" | ||
25 | |||
26 | #include "ivorbiscodec.h" | ||
27 | #include "ivorbisfile.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 | |||
62 | /* read a little more data from the file/pipe into the ogg_sync framer */ | ||
63 | static long _get_data(OggVorbis_File *vf){ | ||
64 | if(!(vf->callbacks.read_func))return(-1); | ||
65 | if(vf->datasource){ | ||
66 | char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE); | ||
67 | long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); | ||
68 | if(bytes>0)ogg_sync_wrote(&vf->oy,bytes); | ||
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 int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ | ||
76 | if(vf->datasource){ | ||
77 | if(!(vf->callbacks.seek_func)|| | ||
78 | (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) | ||
79 | return OV_EREAD; | ||
80 | vf->offset=offset; | ||
81 | ogg_sync_reset(&vf->oy); | ||
82 | }else{ | ||
83 | /* shouldn't happen unless someone writes a broken callback */ | ||
84 | return OV_EFAULT; | ||
85 | } | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* The read/seek functions track absolute position within the stream */ | ||
90 | |||
91 | /* from the head of the stream, get the next page. boundary specifies | ||
92 | if the function is allowed to fetch more data from the stream (and | ||
93 | how much) or only use internally buffered data. | ||
94 | |||
95 | boundary: -1) unbounded search | ||
96 | 0) read no additional data; use cached only | ||
97 | n) search for a new page beginning for n bytes | ||
98 | |||
99 | return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) | ||
100 | n) found a page at absolute offset n */ | ||
101 | |||
102 | static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, | ||
103 | ogg_int64_t boundary){ | ||
104 | if(boundary>0)boundary+=vf->offset; | ||
105 | while(1){ | ||
106 | long more; | ||
107 | |||
108 | if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); | ||
109 | more=ogg_sync_pageseek(&vf->oy,og); | ||
110 | |||
111 | if(more<0){ | ||
112 | /* skipped n bytes */ | ||
113 | vf->offset-=more; | ||
114 | }else{ | ||
115 | if(more==0){ | ||
116 | /* send more paramedics */ | ||
117 | if(!boundary)return(OV_FALSE); | ||
118 | { | ||
119 | long ret=_get_data(vf); | ||
120 | if(ret==0)return(OV_EOF); | ||
121 | if(ret<0)return(OV_EREAD); | ||
122 | } | ||
123 | }else{ | ||
124 | /* got a page. Return the offset at the page beginning, | ||
125 | advance the internal offset past the page end */ | ||
126 | ogg_int64_t ret=vf->offset; | ||
127 | vf->offset+=more; | ||
128 | return(ret); | ||
129 | |||
130 | } | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /* This is a nasty hack to work around the huge allocations we get from | ||
136 | huge comment packets, usually due to embedded album art */ | ||
137 | static int ogg_stream_discard_packet(OggVorbis_File *vf,ogg_page *og, | ||
138 | ogg_int64_t boundary){ | ||
139 | int ret; | ||
140 | while((ret = ogg_stream_packetout(&vf->os, NULL)) == 0) { | ||
141 | if(_get_next_page(vf, og, boundary)<0) | ||
142 | break; | ||
143 | ogg_stream_pagein(&vf->os,og,false); | ||
144 | } | ||
145 | if (ret < 0) | ||
146 | return -1; | ||
147 | /* We might be pretending to have filled in more of the buffer than there is | ||
148 | actual space, in this case the body storage must be expanded before we | ||
149 | start writing to it */ | ||
150 | if (vf->os.body_fill < og->body_len || vf->os.body_storage < vf->os.body_fill) | ||
151 | if(_os_body_expand(&vf->os, vf->os.body_fill - vf->os.body_storage + og->body_len)) | ||
152 | return -1; | ||
153 | memcpy(vf->os.body_data+vf->os.body_fill-og->body_len, og->body, og->body_len); | ||
154 | return 1; | ||
155 | } | ||
156 | |||
157 | /* find the latest page beginning before the current stream cursor | ||
158 | position. Much dirtier than the above as Ogg doesn't have any | ||
159 | backward search linkage. no 'readp' as it will certainly have to | ||
160 | read. */ | ||
161 | /* returns offset or OV_EREAD, OV_FAULT */ | ||
162 | static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ | ||
163 | ogg_int64_t begin=vf->offset; | ||
164 | ogg_int64_t end=begin; | ||
165 | ogg_int64_t ret; | ||
166 | ogg_int64_t offset=-1; | ||
167 | |||
168 | while(offset==-1){ | ||
169 | begin-=CHUNKSIZE; | ||
170 | if(begin<0) | ||
171 | begin=0; | ||
172 | |||
173 | ret=_seek_helper(vf,begin); | ||
174 | if(ret)return(ret); | ||
175 | |||
176 | while(vf->offset<end){ | ||
177 | memset(og,0,sizeof(*og)); | ||
178 | ret=_get_next_page(vf,og,end-vf->offset); | ||
179 | if(ret==OV_EREAD)return(OV_EREAD); | ||
180 | if(ret<0){ | ||
181 | break; | ||
182 | }else{ | ||
183 | offset=ret; | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /* In a fully compliant, non-multiplexed stream, we'll still be | ||
189 | holding the last page. In multiplexed (or noncompliant streams), | ||
190 | we will probably have to re-read the last page we saw */ | ||
191 | if(og->header_len==0){ | ||
192 | ret=_seek_helper(vf,offset); | ||
193 | if(ret)return(ret); | ||
194 | |||
195 | ret=_get_next_page(vf,og,CHUNKSIZE); | ||
196 | if(ret<0) | ||
197 | /* this shouldn't be possible */ | ||
198 | return(OV_EFAULT); | ||
199 | } | ||
200 | |||
201 | return(offset); | ||
202 | } | ||
203 | |||
204 | static void _add_serialno(ogg_page *og,ogg_uint32_t **serialno_list, int *n){ | ||
205 | long s = ogg_page_serialno(og); | ||
206 | (*n)++; | ||
207 | |||
208 | if(*serialno_list){ | ||
209 | *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n)); | ||
210 | }else{ | ||
211 | *serialno_list = _ogg_malloc(sizeof(**serialno_list)); | ||
212 | } | ||
213 | |||
214 | (*serialno_list)[(*n)-1] = s; | ||
215 | } | ||
216 | |||
217 | /* returns nonzero if found */ | ||
218 | static int _lookup_serialno(ogg_uint32_t s, ogg_uint32_t *serialno_list, int n){ | ||
219 | if(serialno_list){ | ||
220 | while(n--){ | ||
221 | if(*serialno_list == (ogg_uint32_t) s) return 1; | ||
222 | serialno_list++; | ||
223 | } | ||
224 | } | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | static int _lookup_page_serialno(ogg_page *og, ogg_uint32_t *serialno_list, int n){ | ||
229 | ogg_uint32_t s = ogg_page_serialno(og); | ||
230 | return _lookup_serialno(s,serialno_list,n); | ||
231 | } | ||
232 | |||
233 | /* performs the same search as _get_prev_page, but prefers pages of | ||
234 | the specified serial number. If a page of the specified serialno is | ||
235 | spotted during the seek-back-and-read-forward, it will return the | ||
236 | info of last page of the matching serial number instead of the very | ||
237 | last page. If no page of the specified serialno is seen, it will | ||
238 | return the info of last page and alter *serialno. */ | ||
239 | static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf, | ||
240 | ogg_uint32_t *serial_list, int serial_n, | ||
241 | int *serialno, ogg_int64_t *granpos){ | ||
242 | ogg_page og; | ||
243 | ogg_int64_t begin=vf->offset; | ||
244 | ogg_int64_t end=begin; | ||
245 | ogg_int64_t ret; | ||
246 | |||
247 | ogg_int64_t prefoffset=-1; | ||
248 | ogg_int64_t offset=-1; | ||
249 | ogg_int64_t ret_serialno=-1; | ||
250 | ogg_int64_t ret_gran=-1; | ||
251 | |||
252 | while(offset==-1){ | ||
253 | begin-=CHUNKSIZE; | ||
254 | if(begin<0) | ||
255 | begin=0; | ||
256 | |||
257 | ret=_seek_helper(vf,begin); | ||
258 | if(ret)return(ret); | ||
259 | |||
260 | while(vf->offset<end){ | ||
261 | ret=_get_next_page(vf,&og,end-vf->offset); | ||
262 | if(ret==OV_EREAD)return(OV_EREAD); | ||
263 | if(ret<0){ | ||
264 | break; | ||
265 | }else{ | ||
266 | ret_serialno=ogg_page_serialno(&og); | ||
267 | ret_gran=ogg_page_granulepos(&og); | ||
268 | offset=ret; | ||
269 | |||
270 | if((ogg_uint32_t)ret_serialno == (ogg_uint32_t)*serialno){ | ||
271 | prefoffset=ret; | ||
272 | *granpos=ret_gran; | ||
273 | } | ||
274 | |||
275 | if(!_lookup_serialno((ogg_uint32_t)ret_serialno,serial_list,serial_n)){ | ||
276 | /* we fell off the end of the link, which means we seeked | ||
277 | back too far and shouldn't have been looking in that link | ||
278 | to begin with. If we found the preferred serial number, | ||
279 | forget that we saw it. */ | ||
280 | prefoffset=-1; | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | /* we're not interested in the page... just the serialno and granpos. */ | ||
287 | if(prefoffset>=0)return(prefoffset); | ||
288 | |||
289 | *serialno = ret_serialno; | ||
290 | *granpos = ret_gran; | ||
291 | return(offset); | ||
292 | |||
293 | } | ||
294 | |||
295 | /* uses the local ogg_stream storage in vf; this is important for | ||
296 | non-streaming input sources */ | ||
297 | static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi, | ||
298 | ogg_uint32_t **serialno_list, int *serialno_n, | ||
299 | ogg_page *og_ptr){ | ||
300 | ogg_page og; | ||
301 | ogg_packet op; | ||
302 | int i,ret; | ||
303 | int allbos=0; | ||
304 | |||
305 | if(!og_ptr){ | ||
306 | ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); | ||
307 | if(llret==OV_EREAD)return(OV_EREAD); | ||
308 | if(llret<0)return(OV_ENOTVORBIS); | ||
309 | og_ptr=&og; | ||
310 | } | ||
311 | |||
312 | vorbis_info_init(vi); | ||
313 | /* vorbis_comment_init(vc); */ | ||
314 | vf->ready_state=OPENED; | ||
315 | |||
316 | /* extract the serialnos of all BOS pages + the first set of vorbis | ||
317 | headers we see in the link */ | ||
318 | |||
319 | while(ogg_page_bos(og_ptr)){ | ||
320 | if(serialno_list){ | ||
321 | if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){ | ||
322 | /* a dupe serialnumber in an initial header packet set == invalid stream */ | ||
323 | if(*serialno_list)_ogg_free(*serialno_list); | ||
324 | *serialno_list=0; | ||
325 | *serialno_n=0; | ||
326 | ret=OV_EBADHEADER; | ||
327 | goto bail_header; | ||
328 | } | ||
329 | |||
330 | _add_serialno(og_ptr,serialno_list,serialno_n); | ||
331 | } | ||
332 | |||
333 | if(vf->ready_state<STREAMSET){ | ||
334 | /* we don't have a vorbis stream in this link yet, so begin | ||
335 | prospective stream setup. We need a stream to get packets */ | ||
336 | ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr)); | ||
337 | ogg_stream_pagein(&vf->os,og_ptr,true); | ||
338 | |||
339 | if(ogg_stream_packetout(&vf->os,&op) > 0 && | ||
340 | vorbis_synthesis_idheader(&op)){ | ||
341 | /* vorbis header; continue setup */ | ||
342 | vf->ready_state=STREAMSET; | ||
343 | if((ret=vorbis_synthesis_headerin(vi,&op))){ | ||
344 | ret=OV_EBADHEADER; | ||
345 | goto bail_header; | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | |||
350 | /* get next page */ | ||
351 | { | ||
352 | ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE); | ||
353 | if(llret==OV_EREAD){ | ||
354 | ret=OV_EREAD; | ||
355 | goto bail_header; | ||
356 | } | ||
357 | if(llret<0){ | ||
358 | ret=OV_ENOTVORBIS; | ||
359 | goto bail_header; | ||
360 | } | ||
361 | |||
362 | /* if this page also belongs to our vorbis stream, submit it and break */ | ||
363 | if(vf->ready_state==STREAMSET && | ||
364 | vf->os.serialno == ogg_page_serialno(og_ptr)){ | ||
365 | ogg_stream_pagein(&vf->os,og_ptr,true); | ||
366 | break; | ||
367 | } | ||
368 | } | ||
369 | } | ||
370 | |||
371 | if(vf->ready_state!=STREAMSET){ | ||
372 | ret = OV_ENOTVORBIS; | ||
373 | goto bail_header; | ||
374 | } | ||
375 | |||
376 | while(1){ | ||
377 | |||
378 | i=0; | ||
379 | /* discard comment packet */ | ||
380 | if(ogg_stream_discard_packet(vf,og_ptr,CHUNKSIZE) < 0){ | ||
381 | ret=OV_EBADHEADER; | ||
382 | goto bail_header; | ||
383 | } | ||
384 | i++; | ||
385 | |||
386 | while(i<2){ /* get a page loop */ | ||
387 | |||
388 | while(i<2){ /* get a packet loop */ | ||
389 | int result=ogg_stream_packetout(&vf->os,&op); | ||
390 | if(result==0)break; | ||
391 | if(result==-1){ | ||
392 | ret=OV_EBADHEADER; | ||
393 | goto bail_header; | ||
394 | } | ||
395 | |||
396 | if((ret=vorbis_synthesis_headerin(vi,&op))) | ||
397 | goto bail_header; | ||
398 | |||
399 | i++; | ||
400 | } | ||
401 | |||
402 | while(i<2){ | ||
403 | if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ | ||
404 | ret=OV_EBADHEADER; | ||
405 | goto bail_header; | ||
406 | } | ||
407 | |||
408 | /* if this page belongs to the correct stream, go parse it */ | ||
409 | if(vf->os.serialno == ogg_page_serialno(og_ptr)){ | ||
410 | ogg_stream_pagein(&vf->os,og_ptr,true); | ||
411 | break; | ||
412 | } | ||
413 | |||
414 | /* if we never see the final vorbis headers before the link | ||
415 | ends, abort */ | ||
416 | if(ogg_page_bos(og_ptr)){ | ||
417 | if(allbos){ | ||
418 | ret = OV_EBADHEADER; | ||
419 | goto bail_header; | ||
420 | }else | ||
421 | allbos=1; | ||
422 | } | ||
423 | |||
424 | /* otherwise, keep looking */ | ||
425 | } | ||
426 | } | ||
427 | |||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | bail_header: | ||
432 | vorbis_info_clear(vi); | ||
433 | /* vorbis_comment_clear(vc); */ | ||
434 | vf->ready_state=OPENED; | ||
435 | |||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | /* Starting from current cursor position, get initial PCM offset of | ||
440 | next page. Consumes the page in the process without decoding | ||
441 | audio, however this is only called during stream parsing upon | ||
442 | seekable open. */ | ||
443 | static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){ | ||
444 | ogg_page og; | ||
445 | ogg_int64_t accumulated=0; | ||
446 | long lastblock=-1; | ||
447 | int result; | ||
448 | ogg_uint32_t serialno = vf->os.serialno; | ||
449 | |||
450 | while(1){ | ||
451 | ogg_packet op; | ||
452 | if(_get_next_page(vf,&og,-1)<0) | ||
453 | break; /* should not be possible unless the file is truncated/mangled */ | ||
454 | |||
455 | if(ogg_page_bos(&og)) break; | ||
456 | if(ogg_page_serialno(&og)!= serialno) continue; | ||
457 | |||
458 | /* count blocksizes of all frames in the page */ | ||
459 | ogg_stream_pagein(&vf->os,&og,true); | ||
460 | while((result=ogg_stream_packetout(&vf->os,&op))){ | ||
461 | if(result>0){ /* ignore holes */ | ||
462 | long thisblock=vorbis_packet_blocksize(vi,&op); | ||
463 | if(lastblock!=-1) | ||
464 | accumulated+=(lastblock+thisblock)>>2; | ||
465 | lastblock=thisblock; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | if(ogg_page_granulepos(&og)!=-1){ | ||
470 | /* pcm offset of last packet on the first audio page */ | ||
471 | accumulated= ogg_page_granulepos(&og)-accumulated; | ||
472 | break; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | /* less than zero? This is a stream with samples trimmed off | ||
477 | the beginning, a normal occurrence; set the offset to zero */ | ||
478 | if(accumulated<0)accumulated=0; | ||
479 | |||
480 | return accumulated; | ||
481 | } | ||
482 | |||
483 | /* finds each bitstream link one at a time using a bisection search | ||
484 | (has to begin by knowing the offset of the lb's initial page). | ||
485 | Recurses for each link so it can alloc the link storage after | ||
486 | finding them all, then unroll and fill the cache at the same time */ | ||
487 | static int _bisect_forward_serialno(OggVorbis_File *vf, | ||
488 | ogg_int64_t begin, | ||
489 | ogg_int64_t searched, | ||
490 | ogg_int64_t end, | ||
491 | ogg_int64_t endgran, | ||
492 | int endserial, | ||
493 | ogg_uint32_t *currentno_list, | ||
494 | int currentnos, | ||
495 | long m){ | ||
496 | ogg_int64_t pcmoffset; | ||
497 | ogg_int64_t dataoffset=searched; | ||
498 | ogg_int64_t endsearched=end; | ||
499 | ogg_int64_t next=end; | ||
500 | ogg_int64_t searchgran=-1; | ||
501 | ogg_page og; | ||
502 | ogg_int64_t ret,last; | ||
503 | int serialno = vf->os.serialno; | ||
504 | |||
505 | /* invariants: | ||
506 | we have the headers and serialnos for the link beginning at 'begin' | ||
507 | we have the offset and granpos of the last page in the file (potentially | ||
508 | not a page we care about) | ||
509 | */ | ||
510 | |||
511 | /* Is the last page in our list of current serialnumbers? */ | ||
512 | if(_lookup_serialno(endserial,currentno_list,currentnos)){ | ||
513 | |||
514 | /* last page is in the starting serialno list, so we've bisected | ||
515 | down to (or just started with) a single link. Now we need to | ||
516 | find the last vorbis page belonging to the first vorbis stream | ||
517 | for this link. */ | ||
518 | |||
519 | while(endserial != serialno){ | ||
520 | endserial = serialno; | ||
521 | vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&endserial,&endgran); | ||
522 | } | ||
523 | |||
524 | vf->links=m+1; | ||
525 | if(vf->offsets)_ogg_free(vf->offsets); | ||
526 | if(vf->serialnos)_ogg_free(vf->serialnos); | ||
527 | if(vf->dataoffsets)_ogg_free(vf->dataoffsets); | ||
528 | |||
529 | vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); | ||
530 | vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); | ||
531 | /* vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));*/ | ||
532 | vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); | ||
533 | vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); | ||
534 | vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); | ||
535 | |||
536 | vf->offsets[m+1]=end; | ||
537 | vf->offsets[m]=begin; | ||
538 | vf->pcmlengths[m*2+1]=endgran; | ||
539 | |||
540 | }else{ | ||
541 | |||
542 | ogg_uint32_t *next_serialno_list=NULL; | ||
543 | int next_serialnos=0; | ||
544 | vorbis_info vi; | ||
545 | /* vorbis_comment vc; */ | ||
546 | |||
547 | /* the below guards against garbage seperating the last and | ||
548 | first pages of two links. */ | ||
549 | while(searched<endsearched){ | ||
550 | ogg_int64_t bisect; | ||
551 | |||
552 | if(endsearched-searched<CHUNKSIZE){ | ||
553 | bisect=searched; | ||
554 | }else{ | ||
555 | bisect=(searched+endsearched)/2; | ||
556 | } | ||
557 | |||
558 | if(bisect != vf->offset){ | ||
559 | ret=_seek_helper(vf,bisect); | ||
560 | if(ret)return(ret); | ||
561 | } | ||
562 | |||
563 | last=_get_next_page(vf,&og,-1); | ||
564 | if(last==OV_EREAD)return(OV_EREAD); | ||
565 | if(last<0 || !_lookup_page_serialno(&og,currentno_list,currentnos)){ | ||
566 | endsearched=bisect; | ||
567 | if(last>=0)next=last; | ||
568 | }else{ | ||
569 | searched=vf->offset; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /* Bisection point found */ | ||
574 | |||
575 | /* for the time being, fetch end PCM offset the simple way */ | ||
576 | { | ||
577 | int testserial = serialno+1; | ||
578 | vf->offset = next; | ||
579 | while(testserial != serialno){ | ||
580 | testserial = serialno; | ||
581 | vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&testserial,&searchgran); | ||
582 | } | ||
583 | } | ||
584 | |||
585 | if(vf->offset!=next){ | ||
586 | ret=_seek_helper(vf,next); | ||
587 | if(ret)return(ret); | ||
588 | } | ||
589 | |||
590 | ret=_fetch_headers(vf,&vi,&next_serialno_list,&next_serialnos,NULL); | ||
591 | if(ret)return(ret); | ||
592 | serialno = vf->os.serialno; | ||
593 | dataoffset = vf->offset; | ||
594 | |||
595 | /* this will consume a page, however the next bistection always | ||
596 | starts with a raw seek */ | ||
597 | pcmoffset = _initial_pcmoffset(vf,&vi); | ||
598 | |||
599 | ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial, | ||
600 | next_serialno_list,next_serialnos,m+1); | ||
601 | if(ret)return(ret); | ||
602 | |||
603 | if(next_serialno_list)_ogg_free(next_serialno_list); | ||
604 | |||
605 | vf->offsets[m+1]=next; | ||
606 | vf->serialnos[m+1]=serialno; | ||
607 | vf->dataoffsets[m+1]=dataoffset; | ||
608 | |||
609 | vf->vi[m+1]=vi; | ||
610 | /* vf->vc[m+1]=vc; */ | ||
611 | |||
612 | vf->pcmlengths[m*2+1]=searchgran; | ||
613 | vf->pcmlengths[m*2+2]=pcmoffset; | ||
614 | vf->pcmlengths[m*2+3]-=pcmoffset; | ||
615 | |||
616 | } | ||
617 | return(0); | ||
618 | } | ||
619 | |||
620 | static int _make_decode_ready(OggVorbis_File *vf){ | ||
621 | if(vf->ready_state>STREAMSET)return 0; | ||
622 | if(vf->ready_state<STREAMSET)return OV_EFAULT; | ||
623 | if(vf->seekable){ | ||
624 | if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link)) | ||
625 | return OV_EBADLINK; | ||
626 | }else{ | ||
627 | if(vorbis_synthesis_init(&vf->vd,vf->vi)) | ||
628 | return OV_EBADLINK; | ||
629 | } | ||
630 | vorbis_block_init(&vf->vd,&vf->vb); | ||
631 | vf->ready_state=INITSET; | ||
632 | vf->bittrack=0; | ||
633 | vf->samptrack=0; | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | static int _open_seekable2(OggVorbis_File *vf){ | ||
638 | ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1; | ||
639 | int endserial=vf->os.serialno; | ||
640 | int serialno=vf->os.serialno; | ||
641 | |||
642 | /* we're partially open and have a first link header state in | ||
643 | storage in vf */ | ||
644 | |||
645 | /* fetch initial PCM offset */ | ||
646 | ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi); | ||
647 | |||
648 | /* we can seek, so set out learning all about this file */ | ||
649 | if(vf->callbacks.seek_func && vf->callbacks.tell_func){ | ||
650 | (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); | ||
651 | vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); | ||
652 | }else{ | ||
653 | vf->offset=vf->end=-1; | ||
654 | } | ||
655 | |||
656 | /* If seek_func is implemented, tell_func must also be implemented */ | ||
657 | if(vf->end==-1) return(OV_EINVAL); | ||
658 | |||
659 | /* Get the offset of the last page of the physical bitstream, or, if | ||
660 | we're lucky the last vorbis page of this link as most OggVorbis | ||
661 | files will contain a single logical bitstream */ | ||
662 | end=_get_prev_page_serial(vf,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran); | ||
663 | if(end<0)return(end); | ||
664 | |||
665 | /* now determine bitstream structure recursively */ | ||
666 | if(_bisect_forward_serialno(vf,0,dataoffset,vf->offset,endgran,endserial, | ||
667 | vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD); | ||
668 | |||
669 | vf->offsets[0]=0; | ||
670 | vf->serialnos[0]=serialno; | ||
671 | vf->dataoffsets[0]=dataoffset; | ||
672 | vf->pcmlengths[0]=pcmoffset; | ||
673 | vf->pcmlengths[1]-=pcmoffset; | ||
674 | |||
675 | return(ov_raw_seek(vf,dataoffset)); | ||
676 | } | ||
677 | |||
678 | /* clear out the current logical bitstream decoder */ | ||
679 | static void _decode_clear(OggVorbis_File *vf){ | ||
680 | vorbis_dsp_clear(&vf->vd); | ||
681 | vorbis_block_clear(&vf->vb); | ||
682 | vf->ready_state=OPENED; | ||
683 | } | ||
684 | |||
685 | /* fetch and process a packet. Handles the case where we're at a | ||
686 | bitstream boundary and dumps the decoding machine. If the decoding | ||
687 | machine is unloaded, it loads it. It also keeps pcm_offset up to | ||
688 | date (seek and read both use this. seek uses a special hack with | ||
689 | readp). | ||
690 | |||
691 | return: <0) error, OV_HOLE (lost packet) or OV_EOF | ||
692 | 0) need more data (only if readp==0) | ||
693 | 1) got a packet | ||
694 | */ | ||
695 | |||
696 | static int _fetch_and_process_packet(OggVorbis_File *vf, | ||
697 | ogg_packet *op_in, | ||
698 | int readp, | ||
699 | int spanp){ | ||
700 | ogg_page og; | ||
701 | |||
702 | /* handle one packet. Try to fetch it from current stream state */ | ||
703 | /* extract packets from page */ | ||
704 | while(1){ | ||
705 | |||
706 | if(vf->ready_state==STREAMSET){ | ||
707 | int ret=_make_decode_ready(vf); | ||
708 | if(ret<0)return ret; | ||
709 | } | ||
710 | |||
711 | /* process a packet if we can. If the machine isn't loaded, | ||
712 | neither is a page */ | ||
713 | if(vf->ready_state==INITSET){ | ||
714 | while(1) { | ||
715 | ogg_packet op; | ||
716 | ogg_packet *op_ptr=(op_in?op_in:&op); | ||
717 | int result=ogg_stream_packetout(&vf->os,op_ptr); | ||
718 | ogg_int64_t granulepos; | ||
719 | |||
720 | op_in=NULL; | ||
721 | if(result==-1)return(OV_HOLE); /* hole in the data. */ | ||
722 | if(result>0){ | ||
723 | /* got a packet. process it */ | ||
724 | granulepos=op_ptr->granulepos; | ||
725 | if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy | ||
726 | header handling. The | ||
727 | header packets aren't | ||
728 | audio, so if/when we | ||
729 | submit them, | ||
730 | vorbis_synthesis will | ||
731 | reject them */ | ||
732 | |||
733 | /* suck in the synthesis data and track bitrate */ | ||
734 | { | ||
735 | int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); | ||
736 | /* for proper use of libvorbis within libvorbisfile, | ||
737 | oldsamples will always be zero. */ | ||
738 | if(oldsamples)return(OV_EFAULT); | ||
739 | |||
740 | vorbis_synthesis_blockin(&vf->vd,&vf->vb); | ||
741 | vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; | ||
742 | vf->bittrack+=op_ptr->bytes*8; | ||
743 | } | ||
744 | |||
745 | /* update the pcm offset. */ | ||
746 | if(granulepos!=-1 && !op_ptr->e_o_s){ | ||
747 | int link=(vf->seekable?vf->current_link:0); | ||
748 | int i,samples; | ||
749 | |||
750 | /* this packet has a pcm_offset on it (the last packet | ||
751 | completed on a page carries the offset) After processing | ||
752 | (above), we know the pcm position of the *last* sample | ||
753 | ready to be returned. Find the offset of the *first* | ||
754 | |||
755 | As an aside, this trick is inaccurate if we begin | ||
756 | reading anew right at the last page; the end-of-stream | ||
757 | granulepos declares the last frame in the stream, and the | ||
758 | last packet of the last page may be a partial frame. | ||
759 | So, we need a previous granulepos from an in-sequence page | ||
760 | to have a reference point. Thus the !op_ptr->e_o_s clause | ||
761 | above */ | ||
762 | |||
763 | if(vf->seekable && link>0) | ||
764 | granulepos-=vf->pcmlengths[link*2]; | ||
765 | if(granulepos<0)granulepos=0; /* actually, this | ||
766 | shouldn't be possible | ||
767 | here unless the stream | ||
768 | is very broken */ | ||
769 | |||
770 | samples=vorbis_synthesis_pcmout(&vf->vd,NULL); | ||
771 | |||
772 | granulepos-=samples; | ||
773 | for(i=0;i<link;i++) | ||
774 | granulepos+=vf->pcmlengths[i*2+1]; | ||
775 | vf->pcm_offset=granulepos; | ||
776 | } | ||
777 | return(1); | ||
778 | } | ||
779 | } | ||
780 | else | ||
781 | break; | ||
782 | } | ||
783 | } | ||
784 | |||
785 | if(vf->ready_state>=OPENED){ | ||
786 | ogg_int64_t ret; | ||
787 | |||
788 | while(1){ | ||
789 | /* the loop is not strictly necessary, but there's no sense in | ||
790 | doing the extra checks of the larger loop for the common | ||
791 | case in a multiplexed bistream where the page is simply | ||
792 | part of a different logical bitstream; keep reading until | ||
793 | we get one with the correct serialno */ | ||
794 | |||
795 | if(!readp)return(0); | ||
796 | if((ret=_get_next_page(vf,&og,-1))<0){ | ||
797 | return(OV_EOF); /* eof. leave unitialized */ | ||
798 | } | ||
799 | |||
800 | /* bitrate tracking; add the header's bytes here, the body bytes | ||
801 | are done by packet above */ | ||
802 | vf->bittrack+=og.header_len*8; | ||
803 | |||
804 | if(vf->ready_state==INITSET){ | ||
805 | if(vf->current_serialno!=ogg_page_serialno(&og)){ | ||
806 | |||
807 | /* two possibilities: | ||
808 | 1) our decoding just traversed a bitstream boundary | ||
809 | 2) another stream is multiplexed into this logical section */ | ||
810 | |||
811 | if(ogg_page_bos(&og)){ | ||
812 | /* boundary case */ | ||
813 | if(!spanp) | ||
814 | return(OV_EOF); | ||
815 | |||
816 | _decode_clear(vf); | ||
817 | |||
818 | if(!vf->seekable){ | ||
819 | vorbis_info_clear(vf->vi); | ||
820 | /* vorbis_comment_clear(vf->vc); */ | ||
821 | } | ||
822 | break; | ||
823 | |||
824 | }else | ||
825 | continue; /* possibility #2 */ | ||
826 | } | ||
827 | } | ||
828 | |||
829 | break; | ||
830 | } | ||
831 | } | ||
832 | |||
833 | /* Do we need to load a new machine before submitting the page? */ | ||
834 | /* This is different in the seekable and non-seekable cases. | ||
835 | |||
836 | In the seekable case, we already have all the header | ||
837 | information loaded and cached; we just initialize the machine | ||
838 | with it and continue on our merry way. | ||
839 | |||
840 | In the non-seekable (streaming) case, we'll only be at a | ||
841 | boundary if we just left the previous logical bitstream and | ||
842 | we're now nominally at the header of the next bitstream | ||
843 | */ | ||
844 | |||
845 | if(vf->ready_state!=INITSET){ | ||
846 | int link; | ||
847 | |||
848 | if(vf->ready_state<STREAMSET){ | ||
849 | if(vf->seekable){ | ||
850 | ogg_uint32_t serialno = ogg_page_serialno(&og); | ||
851 | |||
852 | /* match the serialno to bitstream section. We use this rather than | ||
853 | offset positions to avoid problems near logical bitstream | ||
854 | boundaries */ | ||
855 | |||
856 | for(link=0;link<vf->links;link++) | ||
857 | if(vf->serialnos[link]==serialno)break; | ||
858 | |||
859 | if(link==vf->links) continue; /* not the desired Vorbis | ||
860 | bitstream section; keep | ||
861 | trying */ | ||
862 | |||
863 | vf->current_serialno=serialno; | ||
864 | vf->current_link=link; | ||
865 | |||
866 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno); | ||
867 | vf->ready_state=STREAMSET; | ||
868 | |||
869 | }else{ | ||
870 | /* we're streaming */ | ||
871 | /* fetch the three header packets, build the info struct */ | ||
872 | |||
873 | int ret=_fetch_headers(vf,vf->vi,NULL,NULL,&og); | ||
874 | if(ret)return(ret); | ||
875 | vf->current_serialno=vf->os.serialno; | ||
876 | vf->current_link++; | ||
877 | link=0; | ||
878 | } | ||
879 | } | ||
880 | } | ||
881 | |||
882 | /* the buffered page is the data we want, and we're ready for it; | ||
883 | add it to the stream state */ | ||
884 | ogg_stream_pagein(&vf->os,&og,true); | ||
885 | |||
886 | } | ||
887 | } | ||
888 | |||
889 | static int _ov_open1(void *f,OggVorbis_File *vf,const char *initial, | ||
890 | long ibytes, ov_callbacks callbacks){ | ||
891 | int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1); | ||
892 | ogg_uint32_t *serialno_list=NULL; | ||
893 | int serialno_list_size=0; | ||
894 | int ret; | ||
895 | |||
896 | memset(vf,0,sizeof(*vf)); | ||
897 | vf->datasource=f; | ||
898 | vf->callbacks = callbacks; | ||
899 | |||
900 | /* init the framing state */ | ||
901 | ogg_sync_init(&vf->oy); | ||
902 | |||
903 | /* perhaps some data was previously read into a buffer for testing | ||
904 | against other stream types. Allow initialization from this | ||
905 | previously read data (especially as we may be reading from a | ||
906 | non-seekable stream) */ | ||
907 | if(initial){ | ||
908 | char *buffer=ogg_sync_buffer(&vf->oy,ibytes); | ||
909 | memcpy(buffer,initial,ibytes); | ||
910 | ogg_sync_wrote(&vf->oy,ibytes); | ||
911 | } | ||
912 | |||
913 | /* can we seek? Stevens suggests the seek test was portable */ | ||
914 | if(offsettest!=-1)vf->seekable=1; | ||
915 | |||
916 | /* No seeking yet; Set up a 'single' (current) logical bitstream | ||
917 | entry for partial open */ | ||
918 | vf->links=1; | ||
919 | vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); | ||
920 | /* vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); */ | ||
921 | ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ | ||
922 | |||
923 | /* Fetch all BOS pages, store the vorbis header and all seen serial | ||
924 | numbers, load subsequent vorbis setup headers */ | ||
925 | if((ret=_fetch_headers(vf,vf->vi,&serialno_list,&serialno_list_size,NULL))<0){ | ||
926 | vf->datasource=NULL; | ||
927 | ov_clear(vf); | ||
928 | }else{ | ||
929 | /* serial number list for first link needs to be held somewhere | ||
930 | for second stage of seekable stream open; this saves having to | ||
931 | seek/reread first link's serialnumber data then. */ | ||
932 | vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos)); | ||
933 | vf->serialnos[0]=vf->current_serialno=vf->os.serialno; | ||
934 | vf->serialnos[1]=serialno_list_size; | ||
935 | memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos)); | ||
936 | |||
937 | vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets)); | ||
938 | vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets)); | ||
939 | vf->offsets[0]=0; | ||
940 | vf->dataoffsets[0]=vf->offset; | ||
941 | |||
942 | vf->ready_state=PARTOPEN; | ||
943 | } | ||
944 | if(serialno_list)_ogg_free(serialno_list); | ||
945 | return(ret); | ||
946 | } | ||
947 | |||
948 | static int _ov_open2(OggVorbis_File *vf){ | ||
949 | if(vf->ready_state != PARTOPEN) return OV_EINVAL; | ||
950 | vf->ready_state=OPENED; | ||
951 | if(vf->seekable){ | ||
952 | int ret=_open_seekable2(vf); | ||
953 | if(ret){ | ||
954 | vf->datasource=NULL; | ||
955 | ov_clear(vf); | ||
956 | } | ||
957 | return(ret); | ||
958 | }else | ||
959 | vf->ready_state=STREAMSET; | ||
960 | |||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | |||
965 | /* clear out the OggVorbis_File struct */ | ||
966 | int ov_clear(OggVorbis_File *vf){ | ||
967 | if(vf){ | ||
968 | vorbis_block_clear(&vf->vb); | ||
969 | vorbis_dsp_clear(&vf->vd); | ||
970 | ogg_stream_clear(&vf->os); | ||
971 | |||
972 | if(vf->vi && vf->links){ | ||
973 | int i; | ||
974 | for(i=0;i<vf->links;i++){ | ||
975 | vorbis_info_clear(vf->vi+i); | ||
976 | /* vorbis_comment_clear(vf->vc+i); */ | ||
977 | } | ||
978 | _ogg_free(vf->vi); | ||
979 | /* _ogg_free(vf->vc); */ | ||
980 | } | ||
981 | if(vf->dataoffsets)_ogg_free(vf->dataoffsets); | ||
982 | if(vf->pcmlengths)_ogg_free(vf->pcmlengths); | ||
983 | if(vf->serialnos)_ogg_free(vf->serialnos); | ||
984 | if(vf->offsets)_ogg_free(vf->offsets); | ||
985 | ogg_sync_clear(&vf->oy); | ||
986 | if(vf->datasource && vf->callbacks.close_func) | ||
987 | (vf->callbacks.close_func)(vf->datasource); | ||
988 | memset(vf,0,sizeof(*vf)); | ||
989 | } | ||
990 | #ifdef DEBUG_LEAKS | ||
991 | _VDBG_dump(); | ||
992 | #endif | ||
993 | return(0); | ||
994 | } | ||
995 | |||
996 | /* inspects the OggVorbis file and finds/documents all the logical | ||
997 | bitstreams contained in it. Tries to be tolerant of logical | ||
998 | bitstream sections that are truncated/woogie. | ||
999 | |||
1000 | return: -1) error | ||
1001 | 0) OK | ||
1002 | */ | ||
1003 | |||
1004 | int ov_open_callbacks(void *f,OggVorbis_File *vf, | ||
1005 | const char *initial,long ibytes,ov_callbacks callbacks){ | ||
1006 | #if defined(CPU_COLDFIRE) | ||
1007 | /* this seems to be the closest we get to an init function, let's init emac | ||
1008 | here. rounding is disabled because of MULT31_SHIFT15, which will be | ||
1009 | inaccurate with rounding in its current incarnation */ | ||
1010 | coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); | ||
1011 | #endif | ||
1012 | int ret=_ov_open1(f,vf,initial,ibytes,callbacks); | ||
1013 | if(ret)return ret; | ||
1014 | return _ov_open2(vf); | ||
1015 | } | ||
1016 | |||
1017 | /* returns: total PCM length (samples) of content if i==-1 PCM length | ||
1018 | (samples) of that logical bitstream for i==0 to n | ||
1019 | OV_EINVAL if the stream is not seekable (we can't know the | ||
1020 | length) or only partially open | ||
1021 | */ | ||
1022 | ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ | ||
1023 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1024 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); | ||
1025 | if(i<0){ | ||
1026 | ogg_int64_t acc=0; | ||
1027 | int i; | ||
1028 | for(i=0;i<vf->links;i++) | ||
1029 | acc+=ov_pcm_total(vf,i); | ||
1030 | return(acc); | ||
1031 | }else{ | ||
1032 | return(vf->pcmlengths[i*2+1]); | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | /* returns: total milliseconds of content if i==-1 | ||
1037 | milliseconds in that logical bitstream for i==0 to n | ||
1038 | OV_EINVAL if the stream is not seekable (we can't know the | ||
1039 | length) or only partially open | ||
1040 | */ | ||
1041 | ogg_int64_t ov_time_total(OggVorbis_File *vf,int i){ | ||
1042 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1043 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL); | ||
1044 | if(i<0){ | ||
1045 | ogg_int64_t acc=0; | ||
1046 | int i; | ||
1047 | for(i=0;i<vf->links;i++) | ||
1048 | acc+=ov_time_total(vf,i); | ||
1049 | return(acc); | ||
1050 | }else{ | ||
1051 | return(((ogg_int64_t)vf->pcmlengths[i*2+1])*1000/vf->vi[i].rate); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | /* seek to an offset relative to the *compressed* data. This also | ||
1056 | scans packets to update the PCM cursor. It will cross a logical | ||
1057 | bitstream boundary, but only if it can't get any packets out of the | ||
1058 | tail of the bitstream we seek to (so no surprises). | ||
1059 | |||
1060 | returns zero on success, nonzero on failure */ | ||
1061 | |||
1062 | int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ | ||
1063 | ogg_stream_state work_os; | ||
1064 | int ret; | ||
1065 | |||
1066 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1067 | if(!vf->seekable) | ||
1068 | return(OV_ENOSEEK); /* don't dump machine if we can't seek */ | ||
1069 | |||
1070 | if(pos<0 || pos>vf->end)return(OV_EINVAL); | ||
1071 | |||
1072 | /* is the seek position outside our current link [if any]? */ | ||
1073 | if(vf->ready_state>=STREAMSET){ | ||
1074 | if(pos<vf->offsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1]) | ||
1075 | _decode_clear(vf); /* clear out stream state */ | ||
1076 | } | ||
1077 | |||
1078 | /* don't yet clear out decoding machine (if it's initialized), in | ||
1079 | the case we're in the same link. Restart the decode lapping, and | ||
1080 | let _fetch_and_process_packet deal with a potential bitstream | ||
1081 | boundary */ | ||
1082 | vf->pcm_offset=-1; | ||
1083 | ogg_stream_reset_serialno(&vf->os, | ||
1084 | vf->current_serialno); /* must set serialno */ | ||
1085 | vorbis_synthesis_restart(&vf->vd); | ||
1086 | |||
1087 | ret=_seek_helper(vf,pos); | ||
1088 | if(ret)goto seek_error; | ||
1089 | |||
1090 | /* we need to make sure the pcm_offset is set, but we don't want to | ||
1091 | advance the raw cursor past good packets just to get to the first | ||
1092 | with a granulepos. That's not equivalent behavior to beginning | ||
1093 | decoding as immediately after the seek position as possible. | ||
1094 | |||
1095 | So, a hack. We use two stream states; a local scratch state and | ||
1096 | the shared vf->os stream state. We use the local state to | ||
1097 | scan, and the shared state as a buffer for later decode. | ||
1098 | |||
1099 | Unfortuantely, on the last page we still advance to last packet | ||
1100 | because the granulepos on the last page is not necessarily on a | ||
1101 | packet boundary, and we need to make sure the granpos is | ||
1102 | correct. | ||
1103 | */ | ||
1104 | |||
1105 | { | ||
1106 | ogg_page og; | ||
1107 | ogg_packet op; | ||
1108 | int lastblock=0; | ||
1109 | int accblock=0; | ||
1110 | int thisblock=0; | ||
1111 | int lastflag=0; | ||
1112 | int firstflag=0; | ||
1113 | ogg_int64_t pagepos=-1; | ||
1114 | |||
1115 | ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */ | ||
1116 | ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE | ||
1117 | return from not necessarily | ||
1118 | starting from the beginning */ | ||
1119 | |||
1120 | while(1){ | ||
1121 | if(vf->ready_state>=STREAMSET){ | ||
1122 | /* snarf/scan a packet if we can */ | ||
1123 | int result=ogg_stream_packetout(&work_os,&op); | ||
1124 | |||
1125 | if(result>0){ | ||
1126 | |||
1127 | if(vf->vi[vf->current_link].codec_setup){ | ||
1128 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); | ||
1129 | if(thisblock<0){ | ||
1130 | ogg_stream_packetout(&vf->os,NULL); | ||
1131 | thisblock=0; | ||
1132 | }else{ | ||
1133 | |||
1134 | /* We can't get a guaranteed correct pcm position out of the | ||
1135 | last page in a stream because it might have a 'short' | ||
1136 | granpos, which can only be detected in the presence of a | ||
1137 | preceding page. However, if the last page is also the first | ||
1138 | page, the granpos rules of a first page take precedence. Not | ||
1139 | only that, but for first==last, the EOS page must be treated | ||
1140 | as if its a normal first page for the stream to open/play. */ | ||
1141 | if(lastflag && !firstflag) | ||
1142 | ogg_stream_packetout(&vf->os,NULL); | ||
1143 | else | ||
1144 | if(lastblock)accblock+=(lastblock+thisblock)>>2; | ||
1145 | } | ||
1146 | |||
1147 | if(op.granulepos!=-1){ | ||
1148 | int i,link=vf->current_link; | ||
1149 | ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; | ||
1150 | if(granulepos<0)granulepos=0; | ||
1151 | |||
1152 | for(i=0;i<link;i++) | ||
1153 | granulepos+=vf->pcmlengths[i*2+1]; | ||
1154 | vf->pcm_offset=granulepos-accblock; | ||
1155 | if(vf->pcm_offset<0)vf->pcm_offset=0; | ||
1156 | break; | ||
1157 | } | ||
1158 | lastblock=thisblock; | ||
1159 | continue; | ||
1160 | }else | ||
1161 | ogg_stream_packetout(&vf->os,NULL); | ||
1162 | } | ||
1163 | } | ||
1164 | |||
1165 | if(!lastblock){ | ||
1166 | pagepos=_get_next_page(vf,&og,-1); | ||
1167 | if(pagepos<0){ | ||
1168 | vf->pcm_offset=ov_pcm_total(vf,-1); | ||
1169 | break; | ||
1170 | } | ||
1171 | }else{ | ||
1172 | /* huh? Bogus stream with packets but no granulepos */ | ||
1173 | vf->pcm_offset=-1; | ||
1174 | break; | ||
1175 | } | ||
1176 | |||
1177 | /* has our decoding just traversed a bitstream boundary? */ | ||
1178 | if(vf->ready_state>=STREAMSET){ | ||
1179 | if(vf->current_serialno!=ogg_page_serialno(&og)){ | ||
1180 | |||
1181 | /* two possibilities: | ||
1182 | 1) our decoding just traversed a bitstream boundary | ||
1183 | 2) another stream is multiplexed into this logical section? */ | ||
1184 | |||
1185 | if(ogg_page_bos(&og)){ | ||
1186 | /* we traversed */ | ||
1187 | _decode_clear(vf); /* clear out stream state */ | ||
1188 | ogg_stream_clear(&work_os); | ||
1189 | } /* else, do nothing; next loop will scoop another page */ | ||
1190 | } | ||
1191 | } | ||
1192 | |||
1193 | if(vf->ready_state<STREAMSET){ | ||
1194 | int link; | ||
1195 | ogg_uint32_t serialno = ogg_page_serialno(&og); | ||
1196 | |||
1197 | for(link=0;link<vf->links;link++) | ||
1198 | if(vf->serialnos[link]==serialno)break; | ||
1199 | |||
1200 | if(link==vf->links) continue; /* not the desired Vorbis | ||
1201 | bitstream section; keep | ||
1202 | trying */ | ||
1203 | vf->current_link=link; | ||
1204 | vf->current_serialno=serialno; | ||
1205 | ogg_stream_reset_serialno(&vf->os,serialno); | ||
1206 | ogg_stream_reset_serialno(&work_os,serialno); | ||
1207 | vf->ready_state=STREAMSET; | ||
1208 | firstflag=(pagepos<=vf->dataoffsets[link]); | ||
1209 | } | ||
1210 | |||
1211 | ogg_stream_pagein(&vf->os,&og,true); | ||
1212 | ogg_stream_pagein(&work_os,&og,true); | ||
1213 | lastflag=ogg_page_eos(&og); | ||
1214 | |||
1215 | } | ||
1216 | } | ||
1217 | |||
1218 | ogg_stream_clear(&work_os); | ||
1219 | vf->bittrack=0; | ||
1220 | vf->samptrack=0; | ||
1221 | return(0); | ||
1222 | |||
1223 | seek_error: | ||
1224 | /* dump the machine so we're in a known state */ | ||
1225 | vf->pcm_offset=-1; | ||
1226 | ogg_stream_clear(&work_os); | ||
1227 | _decode_clear(vf); | ||
1228 | return OV_EBADLINK; | ||
1229 | } | ||
1230 | |||
1231 | /* rescales the number x from the range of [0,from] to [0,to] | ||
1232 | x is in the range [0,from] | ||
1233 | from, to are in the range [1, 1<<62-1] */ | ||
1234 | ogg_int64_t rescale64(ogg_int64_t x, ogg_int64_t from, ogg_int64_t to){ | ||
1235 | ogg_int64_t frac=0; | ||
1236 | ogg_int64_t ret=0; | ||
1237 | int i; | ||
1238 | if(x >= from) return to; | ||
1239 | if(x <= 0) return 0; | ||
1240 | |||
1241 | for(i=0;i<64;i++){ | ||
1242 | if(x>=from){ | ||
1243 | frac|=1; | ||
1244 | x-=from; | ||
1245 | } | ||
1246 | x<<=1; | ||
1247 | frac<<=1; | ||
1248 | } | ||
1249 | |||
1250 | for(i=0;i<64;i++){ | ||
1251 | if(frac & 1){ | ||
1252 | ret+=to; | ||
1253 | } | ||
1254 | frac>>=1; | ||
1255 | ret>>=1; | ||
1256 | } | ||
1257 | |||
1258 | return ret; | ||
1259 | } | ||
1260 | |||
1261 | /* Page granularity seek (faster than sample granularity because we | ||
1262 | don't do the last bit of decode to find a specific sample). | ||
1263 | |||
1264 | Seek to the last [granule marked] page preceding the specified pos | ||
1265 | location, such that decoding past the returned point will quickly | ||
1266 | arrive at the requested position. */ | ||
1267 | int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ | ||
1268 | int link=-1; | ||
1269 | ogg_int64_t result=0; | ||
1270 | ogg_int64_t total=ov_pcm_total(vf,-1); | ||
1271 | |||
1272 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1273 | if(!vf->seekable)return(OV_ENOSEEK); | ||
1274 | |||
1275 | if(pos<0 || pos>total)return(OV_EINVAL); | ||
1276 | |||
1277 | /* which bitstream section does this pcm offset occur in? */ | ||
1278 | for(link=vf->links-1;link>=0;link--){ | ||
1279 | total-=vf->pcmlengths[link*2+1]; | ||
1280 | if(pos>=total)break; | ||
1281 | } | ||
1282 | |||
1283 | /* search within the logical bitstream for the page with the highest | ||
1284 | pcm_pos preceding (or equal to) pos. There is a danger here; | ||
1285 | missing pages or incorrect frame number information in the | ||
1286 | bitstream could make our task impossible. Account for that (it | ||
1287 | would be an error condition) */ | ||
1288 | |||
1289 | /* new search algorithm by HB (Nicholas Vinen) */ | ||
1290 | { | ||
1291 | ogg_int64_t end=vf->offsets[link+1]; | ||
1292 | ogg_int64_t begin=vf->offsets[link]; | ||
1293 | ogg_int64_t begintime = vf->pcmlengths[link*2]; | ||
1294 | ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; | ||
1295 | ogg_int64_t target=pos-total+begintime; | ||
1296 | ogg_int64_t best=begin; | ||
1297 | |||
1298 | ogg_page og; | ||
1299 | while(begin<end){ | ||
1300 | ogg_int64_t bisect; | ||
1301 | |||
1302 | if(end-begin<CHUNKSIZE){ | ||
1303 | bisect=begin; | ||
1304 | }else{ | ||
1305 | /* take a (pretty decent) guess. */ | ||
1306 | bisect=begin + rescale64(target-begintime, | ||
1307 | endtime-begintime, | ||
1308 | end-begin) - CHUNKSIZE; | ||
1309 | if(bisect<begin+CHUNKSIZE) | ||
1310 | bisect=begin; | ||
1311 | } | ||
1312 | |||
1313 | if(bisect!=vf->offset){ | ||
1314 | result=_seek_helper(vf,bisect); | ||
1315 | if(result) goto seek_error; | ||
1316 | } | ||
1317 | |||
1318 | while(begin<end){ | ||
1319 | result=_get_next_page(vf,&og,end-vf->offset); | ||
1320 | if(result==OV_EREAD) goto seek_error; | ||
1321 | if(result<0){ | ||
1322 | if(bisect<=begin+1) | ||
1323 | end=begin; /* found it */ | ||
1324 | else{ | ||
1325 | if(bisect==0) goto seek_error; | ||
1326 | bisect-=CHUNKSIZE; | ||
1327 | if(bisect<=begin)bisect=begin+1; | ||
1328 | result=_seek_helper(vf,bisect); | ||
1329 | if(result) goto seek_error; | ||
1330 | } | ||
1331 | }else{ | ||
1332 | ogg_int64_t granulepos; | ||
1333 | |||
1334 | if(ogg_page_serialno(&og)!=vf->serialnos[link]) | ||
1335 | continue; | ||
1336 | |||
1337 | granulepos=ogg_page_granulepos(&og); | ||
1338 | if(granulepos==-1)continue; | ||
1339 | |||
1340 | if(granulepos<target){ | ||
1341 | best=result; /* raw offset of packet with granulepos */ | ||
1342 | begin=vf->offset; /* raw offset of next page */ | ||
1343 | begintime=granulepos; | ||
1344 | |||
1345 | if(target-begintime>44100)break; | ||
1346 | bisect=begin; /* *not* begin + 1 */ | ||
1347 | }else{ | ||
1348 | if(bisect<=begin+1) | ||
1349 | end=begin; /* found it */ | ||
1350 | else{ | ||
1351 | if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ | ||
1352 | end=result; | ||
1353 | bisect-=CHUNKSIZE; /* an endless loop otherwise. */ | ||
1354 | if(bisect<=begin)bisect=begin+1; | ||
1355 | result=_seek_helper(vf,bisect); | ||
1356 | if(result) goto seek_error; | ||
1357 | }else{ | ||
1358 | end=bisect; | ||
1359 | endtime=granulepos; | ||
1360 | break; | ||
1361 | } | ||
1362 | } | ||
1363 | } | ||
1364 | } | ||
1365 | } | ||
1366 | } | ||
1367 | |||
1368 | /* found our page. seek to it, update pcm offset. Easier case than | ||
1369 | raw_seek, don't keep packets preceding granulepos. */ | ||
1370 | { | ||
1371 | ogg_page og; | ||
1372 | ogg_packet op; | ||
1373 | |||
1374 | /* seek */ | ||
1375 | result=_seek_helper(vf,best); | ||
1376 | vf->pcm_offset=-1; | ||
1377 | if(result) goto seek_error; | ||
1378 | result=_get_next_page(vf,&og,-1); | ||
1379 | if(result<0) goto seek_error; | ||
1380 | |||
1381 | if(link!=vf->current_link){ | ||
1382 | /* Different link; dump entire decode machine */ | ||
1383 | _decode_clear(vf); | ||
1384 | |||
1385 | vf->current_link=link; | ||
1386 | vf->current_serialno=vf->serialnos[link]; | ||
1387 | vf->ready_state=STREAMSET; | ||
1388 | |||
1389 | }else{ | ||
1390 | vorbis_synthesis_restart(&vf->vd); | ||
1391 | } | ||
1392 | |||
1393 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno); | ||
1394 | ogg_stream_pagein(&vf->os,&og,true); | ||
1395 | |||
1396 | /* pull out all but last packet; the one with granulepos */ | ||
1397 | while(1){ | ||
1398 | result=ogg_stream_packetpeek(&vf->os,&op); | ||
1399 | if(result==0){ | ||
1400 | /* !!! the packet finishing this page originated on a | ||
1401 | preceding page. Keep fetching previous pages until we | ||
1402 | get one with a granulepos or without the 'continued' flag | ||
1403 | set. Then just use raw_seek for simplicity. */ | ||
1404 | |||
1405 | result=_seek_helper(vf,best); | ||
1406 | if(result<0) goto seek_error; | ||
1407 | |||
1408 | while(1){ | ||
1409 | result=_get_prev_page(vf,&og); | ||
1410 | if(result<0) goto seek_error; | ||
1411 | if(ogg_page_serialno(&og)==vf->current_serialno && | ||
1412 | (ogg_page_granulepos(&og)>-1 || | ||
1413 | !ogg_page_continued(&og))){ | ||
1414 | return ov_raw_seek(vf,result); | ||
1415 | } | ||
1416 | vf->offset=result; | ||
1417 | } | ||
1418 | } | ||
1419 | if(result<0){ | ||
1420 | result = OV_EBADPACKET; | ||
1421 | goto seek_error; | ||
1422 | } | ||
1423 | if(op.granulepos!=-1){ | ||
1424 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; | ||
1425 | if(vf->pcm_offset<0)vf->pcm_offset=0; | ||
1426 | vf->pcm_offset+=total; | ||
1427 | break; | ||
1428 | }else | ||
1429 | result=ogg_stream_packetout(&vf->os,NULL); | ||
1430 | } | ||
1431 | } | ||
1432 | } | ||
1433 | |||
1434 | /* verify result */ | ||
1435 | if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ | ||
1436 | result=OV_EFAULT; | ||
1437 | goto seek_error; | ||
1438 | } | ||
1439 | vf->bittrack=0; | ||
1440 | vf->samptrack=0; | ||
1441 | return(0); | ||
1442 | |||
1443 | seek_error: | ||
1444 | /* dump machine so we're in a known state */ | ||
1445 | vf->pcm_offset=-1; | ||
1446 | _decode_clear(vf); | ||
1447 | return (int)result; | ||
1448 | } | ||
1449 | |||
1450 | /* seek to a sample offset relative to the decompressed pcm stream | ||
1451 | returns zero on success, nonzero on failure */ | ||
1452 | |||
1453 | int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ | ||
1454 | int thisblock,lastblock=0; | ||
1455 | int ret=ov_pcm_seek_page(vf,pos); | ||
1456 | if(ret<0)return(ret); | ||
1457 | if((ret=_make_decode_ready(vf)))return ret; | ||
1458 | |||
1459 | /* discard leading packets we don't need for the lapping of the | ||
1460 | position we want; don't decode them */ | ||
1461 | |||
1462 | while(1){ | ||
1463 | ogg_packet op; | ||
1464 | ogg_page og; | ||
1465 | |||
1466 | int ret=ogg_stream_packetpeek(&vf->os,&op); | ||
1467 | if(ret>0){ | ||
1468 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); | ||
1469 | if(thisblock<0){ | ||
1470 | ogg_stream_packetout(&vf->os,NULL); | ||
1471 | continue; /* non audio packet */ | ||
1472 | } | ||
1473 | if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; | ||
1474 | |||
1475 | if(vf->pcm_offset+((thisblock+ | ||
1476 | vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; | ||
1477 | |||
1478 | /* remove the packet from packet queue and track its granulepos */ | ||
1479 | ogg_stream_packetout(&vf->os,NULL); | ||
1480 | vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with | ||
1481 | only tracking, no | ||
1482 | pcm_decode */ | ||
1483 | vorbis_synthesis_blockin(&vf->vd,&vf->vb); | ||
1484 | |||
1485 | /* end of logical stream case is hard, especially with exact | ||
1486 | length positioning. */ | ||
1487 | |||
1488 | if(op.granulepos>-1){ | ||
1489 | int i; | ||
1490 | /* always believe the stream markers */ | ||
1491 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; | ||
1492 | if(vf->pcm_offset<0)vf->pcm_offset=0; | ||
1493 | for(i=0;i<vf->current_link;i++) | ||
1494 | vf->pcm_offset+=vf->pcmlengths[i*2+1]; | ||
1495 | } | ||
1496 | |||
1497 | lastblock=thisblock; | ||
1498 | |||
1499 | }else{ | ||
1500 | if(ret<0 && ret!=OV_HOLE)break; | ||
1501 | |||
1502 | /* suck in a new page */ | ||
1503 | if(_get_next_page(vf,&og,-1)<0)break; | ||
1504 | if(ogg_page_bos(&og))_decode_clear(vf); | ||
1505 | |||
1506 | if(vf->ready_state<STREAMSET){ | ||
1507 | long serialno=ogg_page_serialno(&og); | ||
1508 | int link; | ||
1509 | |||
1510 | for(link=0;link<vf->links;link++) | ||
1511 | if(vf->serialnos[link]==(ogg_uint32_t) serialno)break; | ||
1512 | if(link==vf->links) continue; | ||
1513 | vf->current_link=link; | ||
1514 | |||
1515 | vf->ready_state=STREAMSET; | ||
1516 | vf->current_serialno=ogg_page_serialno(&og); | ||
1517 | ogg_stream_reset_serialno(&vf->os,serialno); | ||
1518 | ret=_make_decode_ready(vf); | ||
1519 | if(ret)return ret; | ||
1520 | lastblock=0; | ||
1521 | } | ||
1522 | |||
1523 | ogg_stream_pagein(&vf->os,&og,true); | ||
1524 | } | ||
1525 | } | ||
1526 | |||
1527 | vf->bittrack=0; | ||
1528 | vf->samptrack=0; | ||
1529 | /* discard samples until we reach the desired position. Crossing a | ||
1530 | logical bitstream boundary with abandon is OK. */ | ||
1531 | while(vf->pcm_offset<pos){ | ||
1532 | ogg_int64_t target=pos-vf->pcm_offset; | ||
1533 | long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); | ||
1534 | |||
1535 | if(samples>target)samples=target; | ||
1536 | vorbis_synthesis_read(&vf->vd,samples); | ||
1537 | vf->pcm_offset+=samples; | ||
1538 | |||
1539 | if(samples<target) | ||
1540 | if(_fetch_and_process_packet(vf,NULL,1,1)<=0) | ||
1541 | vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */ | ||
1542 | } | ||
1543 | return 0; | ||
1544 | } | ||
1545 | |||
1546 | /* seek to a playback time relative to the decompressed pcm stream | ||
1547 | returns zero on success, nonzero on failure */ | ||
1548 | int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){ | ||
1549 | /* translate time to PCM position and call ov_pcm_seek */ | ||
1550 | |||
1551 | int link=-1; | ||
1552 | ogg_int64_t pcm_total=0; | ||
1553 | ogg_int64_t time_total=0; | ||
1554 | |||
1555 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1556 | if(!vf->seekable)return(OV_ENOSEEK); | ||
1557 | if(milliseconds<0)return(OV_EINVAL); | ||
1558 | |||
1559 | /* which bitstream section does this time offset occur in? */ | ||
1560 | for(link=0;link<vf->links;link++){ | ||
1561 | ogg_int64_t addsec = ov_time_total(vf,link); | ||
1562 | if(milliseconds<time_total+addsec)break; | ||
1563 | time_total+=addsec; | ||
1564 | pcm_total+=vf->pcmlengths[link*2+1]; | ||
1565 | } | ||
1566 | |||
1567 | if(link==vf->links)return(OV_EINVAL); | ||
1568 | |||
1569 | /* enough information to convert time offset to pcm offset */ | ||
1570 | { | ||
1571 | ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; | ||
1572 | return(ov_pcm_seek(vf,target)); | ||
1573 | } | ||
1574 | } | ||
1575 | |||
1576 | /* tell the current stream offset cursor. Note that seek followed by | ||
1577 | tell will likely not give the set offset due to caching */ | ||
1578 | ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ | ||
1579 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1580 | return(vf->offset); | ||
1581 | } | ||
1582 | |||
1583 | /* return time offset (milliseconds) of next PCM sample to be read */ | ||
1584 | ogg_int64_t ov_time_tell(OggVorbis_File *vf){ | ||
1585 | int link=0; | ||
1586 | ogg_int64_t pcm_total=0; | ||
1587 | ogg_int64_t time_total=0; | ||
1588 | |||
1589 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1590 | if(vf->seekable){ | ||
1591 | pcm_total=ov_pcm_total(vf,-1); | ||
1592 | time_total=ov_time_total(vf,-1); | ||
1593 | |||
1594 | /* which bitstream section does this time offset occur in? */ | ||
1595 | for(link=vf->links-1;link>=0;link--){ | ||
1596 | pcm_total-=vf->pcmlengths[link*2+1]; | ||
1597 | time_total-=ov_time_total(vf,link); | ||
1598 | if(vf->pcm_offset>=pcm_total)break; | ||
1599 | } | ||
1600 | } | ||
1601 | |||
1602 | return(time_total+(1000*vf->pcm_offset-pcm_total)/vf->vi[link].rate); | ||
1603 | } | ||
1604 | |||
1605 | /* link: -1) return the vorbis_info struct for the bitstream section | ||
1606 | currently being decoded | ||
1607 | 0-n) to request information for a specific bitstream section | ||
1608 | |||
1609 | In the case of a non-seekable bitstream, any call returns the | ||
1610 | current bitstream. NULL in the case that the machine is not | ||
1611 | initialized */ | ||
1612 | |||
1613 | vorbis_info *ov_info(OggVorbis_File *vf,int link){ | ||
1614 | if(vf->seekable){ | ||
1615 | if(link<0) | ||
1616 | if(vf->ready_state>=STREAMSET) | ||
1617 | return vf->vi+vf->current_link; | ||
1618 | else | ||
1619 | return vf->vi; | ||
1620 | else | ||
1621 | if(link>=vf->links) | ||
1622 | return NULL; | ||
1623 | else | ||
1624 | return vf->vi+link; | ||
1625 | }else{ | ||
1626 | return vf->vi; | ||
1627 | } | ||
1628 | } | ||
1629 | |||
1630 | /* input values: pcm_channels) a float vector per channel of output | ||
1631 | length) the sample length being read by the app | ||
1632 | |||
1633 | return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) | ||
1634 | 0) EOF | ||
1635 | n) number of samples of PCM actually returned. The | ||
1636 | below works on a packet-by-packet basis, so the | ||
1637 | return length is not related to the 'length' passed | ||
1638 | in, just guaranteed to fit. | ||
1639 | |||
1640 | *section) set to the logical bitstream number */ | ||
1641 | |||
1642 | long ov_read_fixed(OggVorbis_File *vf,ogg_int32_t ***pcm_channels,int length, | ||
1643 | int *bitstream){ | ||
1644 | if(vf->ready_state<OPENED)return(OV_EINVAL); | ||
1645 | |||
1646 | while(1){ | ||
1647 | if(vf->ready_state==INITSET){ | ||
1648 | ogg_int32_t **pcm; | ||
1649 | long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); | ||
1650 | if(samples){ | ||
1651 | if(pcm_channels)*pcm_channels=pcm; | ||
1652 | if(samples>length)samples=length; | ||
1653 | vorbis_synthesis_read(&vf->vd,samples); | ||
1654 | vf->pcm_offset+=samples; | ||
1655 | if(bitstream)*bitstream=vf->current_link; | ||
1656 | return samples; | ||
1657 | |||
1658 | } | ||
1659 | } | ||
1660 | |||
1661 | /* suck in another packet */ | ||
1662 | { | ||
1663 | int ret=_fetch_and_process_packet(vf,NULL,1,1); | ||
1664 | if(ret==OV_EOF) | ||
1665 | return(0); | ||
1666 | if(ret<=0) | ||
1667 | return(ret); | ||
1668 | } | ||
1669 | } | ||
1670 | } | ||
1671 | |||