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 _fetch_headers(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 | bail_header:
|
---|
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 | float br;
|
---|
1114 | for(i=0;i<vf->links;i++)
|
---|
1115 | bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
|
---|
1116 | /* This once read: return(rint(bits/ov_time_total(vf,-1)));
|
---|
1117 | * gcc 3.x on x86 miscompiled this at optimisation level 2 and above,
|
---|
1118 | * so this is slightly transformed to make it work.
|
---|
1119 | */
|
---|
1120 | br = bits/ov_time_total(vf,-1);
|
---|
1121 | return(rint(br));
|
---|
1122 | }else{
|
---|
1123 | if(vf->seekable){
|
---|
1124 | /* return the actual bitrate */
|
---|
1125 | return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i)));
|
---|
1126 | }else{
|
---|
1127 | /* return nominal if set */
|
---|
1128 | if(vf->vi[i].bitrate_nominal>0){
|
---|
1129 | return vf->vi[i].bitrate_nominal;
|
---|
1130 | }else{
|
---|
1131 | if(vf->vi[i].bitrate_upper>0){
|
---|
1132 | if(vf->vi[i].bitrate_lower>0){
|
---|
1133 | return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2;
|
---|
1134 | }else{
|
---|
1135 | return vf->vi[i].bitrate_upper;
|
---|
1136 | }
|
---|
1137 | }
|
---|
1138 | return(OV_FALSE);
|
---|
1139 | }
|
---|
1140 | }
|
---|
1141 | }
|
---|
1142 | }
|
---|
1143 |
|
---|
1144 | /* returns the actual bitrate since last call. returns -1 if no
|
---|
1145 | additional data to offer since last call (or at beginning of stream),
|
---|
1146 | EINVAL if stream is only partially open
|
---|
1147 | */
|
---|
1148 | long ov_bitrate_instant(OggVorbis_File *vf){
|
---|
1149 | int link=(vf->seekable?vf->current_link:0);
|
---|
1150 | long ret;
|
---|
1151 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1152 | if(vf->samptrack==0)return(OV_FALSE);
|
---|
1153 | ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5;
|
---|
1154 | vf->bittrack=0.f;
|
---|
1155 | vf->samptrack=0.f;
|
---|
1156 | return(ret);
|
---|
1157 | }
|
---|
1158 |
|
---|
1159 | /* Guess */
|
---|
1160 | long ov_serialnumber(OggVorbis_File *vf,int i){
|
---|
1161 | if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1));
|
---|
1162 | if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1));
|
---|
1163 | if(i<0){
|
---|
1164 | return(vf->current_serialno);
|
---|
1165 | }else{
|
---|
1166 | return(vf->serialnos[i]);
|
---|
1167 | }
|
---|
1168 | }
|
---|
1169 |
|
---|
1170 | /* returns: total raw (compressed) length of content if i==-1
|
---|
1171 | raw (compressed) length of that logical bitstream for i==0 to n
|
---|
1172 | OV_EINVAL if the stream is not seekable (we can't know the length)
|
---|
1173 | or if stream is only partially open
|
---|
1174 | */
|
---|
1175 | ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){
|
---|
1176 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1177 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
|
---|
1178 | if(i<0){
|
---|
1179 | ogg_int64_t acc=0;
|
---|
1180 | for(i=0;i<vf->links;i++)
|
---|
1181 | acc+=ov_raw_total(vf,i);
|
---|
1182 | return(acc);
|
---|
1183 | }else{
|
---|
1184 | return(vf->offsets[i+1]-vf->offsets[i]);
|
---|
1185 | }
|
---|
1186 | }
|
---|
1187 |
|
---|
1188 | /* returns: total PCM length (samples) of content if i==-1 PCM length
|
---|
1189 | (samples) of that logical bitstream for i==0 to n
|
---|
1190 | OV_EINVAL if the stream is not seekable (we can't know the
|
---|
1191 | length) or only partially open
|
---|
1192 | */
|
---|
1193 | ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){
|
---|
1194 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1195 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
|
---|
1196 | if(i<0){
|
---|
1197 | ogg_int64_t acc=0;
|
---|
1198 | for(i=0;i<vf->links;i++)
|
---|
1199 | acc+=ov_pcm_total(vf,i);
|
---|
1200 | return(acc);
|
---|
1201 | }else{
|
---|
1202 | return(vf->pcmlengths[i*2+1]);
|
---|
1203 | }
|
---|
1204 | }
|
---|
1205 |
|
---|
1206 | /* returns: total seconds of content if i==-1
|
---|
1207 | seconds in that logical bitstream for i==0 to n
|
---|
1208 | OV_EINVAL if the stream is not seekable (we can't know the
|
---|
1209 | length) or only partially open
|
---|
1210 | */
|
---|
1211 | double ov_time_total(OggVorbis_File *vf,int i){
|
---|
1212 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1213 | if(!vf->seekable || i>=vf->links)return(OV_EINVAL);
|
---|
1214 | if(i<0){
|
---|
1215 | double acc=0;
|
---|
1216 | for(i=0;i<vf->links;i++)
|
---|
1217 | acc+=ov_time_total(vf,i);
|
---|
1218 | return(acc);
|
---|
1219 | }else{
|
---|
1220 | return((double)(vf->pcmlengths[i*2+1])/vf->vi[i].rate);
|
---|
1221 | }
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | /* seek to an offset relative to the *compressed* data. This also
|
---|
1225 | scans packets to update the PCM cursor. It will cross a logical
|
---|
1226 | bitstream boundary, but only if it can't get any packets out of the
|
---|
1227 | tail of the bitstream we seek to (so no surprises).
|
---|
1228 |
|
---|
1229 | returns zero on success, nonzero on failure */
|
---|
1230 |
|
---|
1231 | int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
|
---|
1232 | ogg_stream_state work_os;
|
---|
1233 |
|
---|
1234 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1235 | if(!vf->seekable)
|
---|
1236 | return(OV_ENOSEEK); /* don't dump machine if we can't seek */
|
---|
1237 |
|
---|
1238 | if(pos<0 || pos>vf->end)return(OV_EINVAL);
|
---|
1239 |
|
---|
1240 | /* is the seek position outside our current link [if any]? */
|
---|
1241 | if(vf->ready_state>=STREAMSET){
|
---|
1242 | if(pos<vf->offsets[vf->current_link] || pos>=vf->offsets[vf->current_link+1])
|
---|
1243 | _decode_clear(vf); /* clear out stream state */
|
---|
1244 | }
|
---|
1245 |
|
---|
1246 | /* don't yet clear out decoding machine (if it's initialized), in
|
---|
1247 | the case we're in the same link. Restart the decode lapping, and
|
---|
1248 | let _fetch_and_process_packet deal with a potential bitstream
|
---|
1249 | boundary */
|
---|
1250 | vf->pcm_offset=-1;
|
---|
1251 | ogg_stream_reset_serialno(&vf->os,
|
---|
1252 | vf->current_serialno); /* must set serialno */
|
---|
1253 | vorbis_synthesis_restart(&vf->vd);
|
---|
1254 |
|
---|
1255 | if(_seek_helper(vf,pos)) {
|
---|
1256 | /* dump the machine so we're in a known state */
|
---|
1257 | vf->pcm_offset=-1;
|
---|
1258 | _decode_clear(vf);
|
---|
1259 | return OV_EBADLINK;
|
---|
1260 | }
|
---|
1261 |
|
---|
1262 | /* we need to make sure the pcm_offset is set, but we don't want to
|
---|
1263 | advance the raw cursor past good packets just to get to the first
|
---|
1264 | with a granulepos. That's not equivalent behavior to beginning
|
---|
1265 | decoding as immediately after the seek position as possible.
|
---|
1266 |
|
---|
1267 | So, a hack. We use two stream states; a local scratch state and
|
---|
1268 | the shared vf->os stream state. We use the local state to
|
---|
1269 | scan, and the shared state as a buffer for later decode.
|
---|
1270 |
|
---|
1271 | Unfortuantely, on the last page we still advance to last packet
|
---|
1272 | because the granulepos on the last page is not necessarily on a
|
---|
1273 | packet boundary, and we need to make sure the granpos is
|
---|
1274 | correct.
|
---|
1275 | */
|
---|
1276 |
|
---|
1277 | {
|
---|
1278 | ogg_page og;
|
---|
1279 | ogg_packet op;
|
---|
1280 | int lastblock=0;
|
---|
1281 | int accblock=0;
|
---|
1282 | int thisblock=0;
|
---|
1283 | int lastflag=0;
|
---|
1284 | int firstflag=0;
|
---|
1285 | ogg_int64_t pagepos=-1;
|
---|
1286 |
|
---|
1287 | ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */
|
---|
1288 | ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE
|
---|
1289 | return from not necessarily
|
---|
1290 | starting from the beginning */
|
---|
1291 |
|
---|
1292 | while(1){
|
---|
1293 | if(vf->ready_state>=STREAMSET){
|
---|
1294 | /* snarf/scan a packet if we can */
|
---|
1295 | int result=ogg_stream_packetout(&work_os,&op);
|
---|
1296 |
|
---|
1297 | if(result>0){
|
---|
1298 |
|
---|
1299 | if(vf->vi[vf->current_link].codec_setup){
|
---|
1300 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
|
---|
1301 | if(thisblock<0){
|
---|
1302 | ogg_stream_packetout(&vf->os,NULL);
|
---|
1303 | thisblock=0;
|
---|
1304 | }else{
|
---|
1305 |
|
---|
1306 | /* We can't get a guaranteed correct pcm position out of the
|
---|
1307 | last page in a stream because it might have a 'short'
|
---|
1308 | granpos, which can only be detected in the presence of a
|
---|
1309 | preceding page. However, if the last page is also the first
|
---|
1310 | page, the granpos rules of a first page take precedence. Not
|
---|
1311 | only that, but for first==last, the EOS page must be treated
|
---|
1312 | as if its a normal first page for the stream to open/play. */
|
---|
1313 | if(lastflag && !firstflag)
|
---|
1314 | ogg_stream_packetout(&vf->os,NULL);
|
---|
1315 | else
|
---|
1316 | if(lastblock)accblock+=(lastblock+thisblock)>>2;
|
---|
1317 | }
|
---|
1318 |
|
---|
1319 | if(op.granulepos!=-1){
|
---|
1320 | int i,link=vf->current_link;
|
---|
1321 | ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2];
|
---|
1322 | if(granulepos<0)granulepos=0;
|
---|
1323 |
|
---|
1324 | for(i=0;i<link;i++)
|
---|
1325 | granulepos+=vf->pcmlengths[i*2+1];
|
---|
1326 | vf->pcm_offset=granulepos-accblock;
|
---|
1327 | if(vf->pcm_offset<0)vf->pcm_offset=0;
|
---|
1328 | break;
|
---|
1329 | }
|
---|
1330 | lastblock=thisblock;
|
---|
1331 | continue;
|
---|
1332 | }else
|
---|
1333 | ogg_stream_packetout(&vf->os,NULL);
|
---|
1334 | }
|
---|
1335 | }
|
---|
1336 |
|
---|
1337 | if(!lastblock){
|
---|
1338 | pagepos=_get_next_page(vf,&og,-1);
|
---|
1339 | if(pagepos<0){
|
---|
1340 | vf->pcm_offset=ov_pcm_total(vf,-1);
|
---|
1341 | break;
|
---|
1342 | }
|
---|
1343 | }else{
|
---|
1344 | /* huh? Bogus stream with packets but no granulepos */
|
---|
1345 | vf->pcm_offset=-1;
|
---|
1346 | break;
|
---|
1347 | }
|
---|
1348 |
|
---|
1349 | /* has our decoding just traversed a bitstream boundary? */
|
---|
1350 | if(vf->ready_state>=STREAMSET){
|
---|
1351 | if(vf->current_serialno!=ogg_page_serialno(&og)){
|
---|
1352 |
|
---|
1353 | /* two possibilities:
|
---|
1354 | 1) our decoding just traversed a bitstream boundary
|
---|
1355 | 2) another stream is multiplexed into this logical section? */
|
---|
1356 |
|
---|
1357 | if(ogg_page_bos(&og)){
|
---|
1358 | /* we traversed */
|
---|
1359 | _decode_clear(vf); /* clear out stream state */
|
---|
1360 | ogg_stream_clear(&work_os);
|
---|
1361 | } /* else, do nothing; next loop will scoop another page */
|
---|
1362 | }
|
---|
1363 | }
|
---|
1364 |
|
---|
1365 | if(vf->ready_state<STREAMSET){
|
---|
1366 | int link;
|
---|
1367 | long serialno = ogg_page_serialno(&og);
|
---|
1368 |
|
---|
1369 | for(link=0;link<vf->links;link++)
|
---|
1370 | if(vf->serialnos[link]==serialno)break;
|
---|
1371 |
|
---|
1372 | if(link==vf->links) continue; /* not the desired Vorbis
|
---|
1373 | bitstream section; keep
|
---|
1374 | trying */
|
---|
1375 | vf->current_link=link;
|
---|
1376 | vf->current_serialno=serialno;
|
---|
1377 | ogg_stream_reset_serialno(&vf->os,serialno);
|
---|
1378 | ogg_stream_reset_serialno(&work_os,serialno);
|
---|
1379 | vf->ready_state=STREAMSET;
|
---|
1380 | firstflag=(pagepos<=vf->dataoffsets[link]);
|
---|
1381 | }
|
---|
1382 |
|
---|
1383 | ogg_stream_pagein(&vf->os,&og);
|
---|
1384 | ogg_stream_pagein(&work_os,&og);
|
---|
1385 | lastflag=ogg_page_eos(&og);
|
---|
1386 |
|
---|
1387 | }
|
---|
1388 | }
|
---|
1389 |
|
---|
1390 | ogg_stream_clear(&work_os);
|
---|
1391 | vf->bittrack=0.f;
|
---|
1392 | vf->samptrack=0.f;
|
---|
1393 | return(0);
|
---|
1394 | }
|
---|
1395 |
|
---|
1396 | /* Page granularity seek (faster than sample granularity because we
|
---|
1397 | don't do the last bit of decode to find a specific sample).
|
---|
1398 |
|
---|
1399 | Seek to the last [granule marked] page preceding the specified pos
|
---|
1400 | location, such that decoding past the returned point will quickly
|
---|
1401 | arrive at the requested position. */
|
---|
1402 | int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
|
---|
1403 | int link=-1;
|
---|
1404 | ogg_int64_t result=0;
|
---|
1405 | ogg_int64_t total=ov_pcm_total(vf,-1);
|
---|
1406 |
|
---|
1407 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1408 | if(!vf->seekable)return(OV_ENOSEEK);
|
---|
1409 |
|
---|
1410 | if(pos<0 || pos>total)return(OV_EINVAL);
|
---|
1411 |
|
---|
1412 | /* which bitstream section does this pcm offset occur in? */
|
---|
1413 | for(link=vf->links-1;link>=0;link--){
|
---|
1414 | total-=vf->pcmlengths[link*2+1];
|
---|
1415 | if(pos>=total)break;
|
---|
1416 | }
|
---|
1417 |
|
---|
1418 | /* Search within the logical bitstream for the page with the highest
|
---|
1419 | pcm_pos preceding pos. If we're looking for a position on the
|
---|
1420 | first page, bisection will halt without finding our position as
|
---|
1421 | it's before the first explicit granulepos fencepost. That case is
|
---|
1422 | handled separately below.
|
---|
1423 |
|
---|
1424 | There is a danger here; missing pages or incorrect frame number
|
---|
1425 | information in the bitstream could make our task impossible.
|
---|
1426 | Account for that (it would be an error condition) */
|
---|
1427 |
|
---|
1428 | /* new search algorithm originally by HB (Nicholas Vinen) */
|
---|
1429 |
|
---|
1430 | {
|
---|
1431 | ogg_int64_t end=vf->offsets[link+1];
|
---|
1432 | ogg_int64_t begin=vf->dataoffsets[link];
|
---|
1433 | ogg_int64_t begintime = vf->pcmlengths[link*2];
|
---|
1434 | ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime;
|
---|
1435 | ogg_int64_t target=pos-total+begintime;
|
---|
1436 | ogg_int64_t best=-1;
|
---|
1437 | int got_page=0;
|
---|
1438 |
|
---|
1439 | ogg_page og;
|
---|
1440 |
|
---|
1441 | /* if we have only one page, there will be no bisection. Grab the page here */
|
---|
1442 | if(begin==end){
|
---|
1443 | result=_seek_helper(vf,begin);
|
---|
1444 | if(result) goto seek_error;
|
---|
1445 |
|
---|
1446 | result=_get_next_page(vf,&og,1);
|
---|
1447 | if(result<0) goto seek_error;
|
---|
1448 |
|
---|
1449 | got_page=1;
|
---|
1450 | }
|
---|
1451 |
|
---|
1452 | /* bisection loop */
|
---|
1453 | while(begin<end){
|
---|
1454 | ogg_int64_t bisect;
|
---|
1455 |
|
---|
1456 | if(end-begin<CHUNKSIZE){
|
---|
1457 | bisect=begin;
|
---|
1458 | }else{
|
---|
1459 | /* take a (pretty decent) guess. */
|
---|
1460 | bisect=begin +
|
---|
1461 | (ogg_int64_t)((double)(target-begintime)*(end-begin)/(endtime-begintime))
|
---|
1462 | - CHUNKSIZE;
|
---|
1463 | if(bisect<begin+CHUNKSIZE)
|
---|
1464 | bisect=begin;
|
---|
1465 | }
|
---|
1466 |
|
---|
1467 | result=_seek_helper(vf,bisect);
|
---|
1468 | if(result) goto seek_error;
|
---|
1469 |
|
---|
1470 | /* read loop within the bisection loop */
|
---|
1471 | while(begin<end){
|
---|
1472 | result=_get_next_page(vf,&og,end-vf->offset);
|
---|
1473 | if(result==OV_EREAD) goto seek_error;
|
---|
1474 | if(result<0){
|
---|
1475 | /* there is no next page! */
|
---|
1476 | if(bisect<=begin+1)
|
---|
1477 | /* No bisection left to perform. We've either found the
|
---|
1478 | best candidate already or failed. Exit loop. */
|
---|
1479 | end=begin;
|
---|
1480 | else{
|
---|
1481 | /* We tried to load a fraction of the last page; back up a
|
---|
1482 | bit and try to get the whole last page */
|
---|
1483 | if(bisect==0) goto seek_error;
|
---|
1484 | bisect-=CHUNKSIZE;
|
---|
1485 |
|
---|
1486 | /* don't repeat/loop on a read we've already performed */
|
---|
1487 | if(bisect<=begin)bisect=begin+1;
|
---|
1488 |
|
---|
1489 | /* seek and cntinue bisection */
|
---|
1490 | result=_seek_helper(vf,bisect);
|
---|
1491 | if(result) goto seek_error;
|
---|
1492 | }
|
---|
1493 | }else{
|
---|
1494 | ogg_int64_t granulepos;
|
---|
1495 | got_page=1;
|
---|
1496 |
|
---|
1497 | /* got a page. analyze it */
|
---|
1498 | /* only consider pages from primary vorbis stream */
|
---|
1499 | if(ogg_page_serialno(&og)!=vf->serialnos[link])
|
---|
1500 | continue;
|
---|
1501 |
|
---|
1502 | /* only consider pages with the granulepos set */
|
---|
1503 | granulepos=ogg_page_granulepos(&og);
|
---|
1504 | if(granulepos==-1)continue;
|
---|
1505 |
|
---|
1506 | if(granulepos<target){
|
---|
1507 | /* this page is a successful candidate! Set state */
|
---|
1508 |
|
---|
1509 | best=result; /* raw offset of packet with granulepos */
|
---|
1510 | begin=vf->offset; /* raw offset of next page */
|
---|
1511 | begintime=granulepos;
|
---|
1512 |
|
---|
1513 | /* if we're before our target but within a short distance,
|
---|
1514 | don't bisect; read forward */
|
---|
1515 | if(target-begintime>44100)break;
|
---|
1516 |
|
---|
1517 | bisect=begin; /* *not* begin + 1 as above */
|
---|
1518 | }else{
|
---|
1519 |
|
---|
1520 | /* This is one of our pages, but the granpos is
|
---|
1521 | post-target; it is not a bisection return
|
---|
1522 | candidate. (The only way we'd use it is if it's the
|
---|
1523 | first page in the stream; we handle that case later
|
---|
1524 | outside the bisection) */
|
---|
1525 | if(bisect<=begin+1){
|
---|
1526 | /* No bisection left to perform. We've either found the
|
---|
1527 | best candidate already or failed. Exit loop. */
|
---|
1528 | end=begin;
|
---|
1529 | }else{
|
---|
1530 | if(end==vf->offset){
|
---|
1531 | /* bisection read to the end; use the known page
|
---|
1532 | boundary (result) to update bisection, back up a
|
---|
1533 | little bit, and try again */
|
---|
1534 | end=result;
|
---|
1535 | bisect-=CHUNKSIZE;
|
---|
1536 | if(bisect<=begin)bisect=begin+1;
|
---|
1537 | result=_seek_helper(vf,bisect);
|
---|
1538 | if(result) goto seek_error;
|
---|
1539 | }else{
|
---|
1540 | /* Normal bisection */
|
---|
1541 | end=bisect;
|
---|
1542 | endtime=granulepos;
|
---|
1543 | break;
|
---|
1544 | }
|
---|
1545 | }
|
---|
1546 | }
|
---|
1547 | }
|
---|
1548 | }
|
---|
1549 | }
|
---|
1550 |
|
---|
1551 | /* Out of bisection: did it 'fail?' */
|
---|
1552 | if(best == -1){
|
---|
1553 |
|
---|
1554 | /* Check the 'looking for data in first page' special case;
|
---|
1555 | bisection would 'fail' because our search target was before the
|
---|
1556 | first PCM granule position fencepost. */
|
---|
1557 |
|
---|
1558 | if(got_page &&
|
---|
1559 | begin == vf->dataoffsets[link] &&
|
---|
1560 | ogg_page_serialno(&og)==vf->serialnos[link]){
|
---|
1561 |
|
---|
1562 | /* Yes, this is the beginning-of-stream case. We already have
|
---|
1563 | our page, right at the beginning of PCM data. Set state
|
---|
1564 | and return. */
|
---|
1565 |
|
---|
1566 | vf->pcm_offset=total;
|
---|
1567 |
|
---|
1568 | if(link!=vf->current_link){
|
---|
1569 | /* Different link; dump entire decode machine */
|
---|
1570 | _decode_clear(vf);
|
---|
1571 |
|
---|
1572 | vf->current_link=link;
|
---|
1573 | vf->current_serialno=vf->serialnos[link];
|
---|
1574 | vf->ready_state=STREAMSET;
|
---|
1575 |
|
---|
1576 | }else{
|
---|
1577 | vorbis_synthesis_restart(&vf->vd);
|
---|
1578 | }
|
---|
1579 |
|
---|
1580 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
|
---|
1581 | ogg_stream_pagein(&vf->os,&og);
|
---|
1582 |
|
---|
1583 | }else
|
---|
1584 | goto seek_error;
|
---|
1585 |
|
---|
1586 | }else{
|
---|
1587 |
|
---|
1588 | /* Bisection found our page. seek to it, update pcm offset. Easier case than
|
---|
1589 | raw_seek, don't keep packets preceding granulepos. */
|
---|
1590 |
|
---|
1591 | ogg_packet op;
|
---|
1592 |
|
---|
1593 | /* seek */
|
---|
1594 | result=_seek_helper(vf,best);
|
---|
1595 | vf->pcm_offset=-1;
|
---|
1596 | if(result) goto seek_error;
|
---|
1597 | result=_get_next_page(vf,&og,-1);
|
---|
1598 | if(result<0) goto seek_error;
|
---|
1599 |
|
---|
1600 | if(link!=vf->current_link){
|
---|
1601 | /* Different link; dump entire decode machine */
|
---|
1602 | _decode_clear(vf);
|
---|
1603 |
|
---|
1604 | vf->current_link=link;
|
---|
1605 | vf->current_serialno=vf->serialnos[link];
|
---|
1606 | vf->ready_state=STREAMSET;
|
---|
1607 |
|
---|
1608 | }else{
|
---|
1609 | vorbis_synthesis_restart(&vf->vd);
|
---|
1610 | }
|
---|
1611 |
|
---|
1612 | ogg_stream_reset_serialno(&vf->os,vf->current_serialno);
|
---|
1613 | ogg_stream_pagein(&vf->os,&og);
|
---|
1614 |
|
---|
1615 | /* pull out all but last packet; the one with granulepos */
|
---|
1616 | while(1){
|
---|
1617 | result=ogg_stream_packetpeek(&vf->os,&op);
|
---|
1618 | if(result==0){
|
---|
1619 | /* No packet returned; we exited the bisection with 'best'
|
---|
1620 | pointing to a page with a granule position, so the packet
|
---|
1621 | finishing this page ('best') originated on a preceding
|
---|
1622 | page. Keep fetching previous pages until we get one with
|
---|
1623 | a granulepos or without the 'continued' flag set. Then
|
---|
1624 | just use raw_seek for simplicity. */
|
---|
1625 | /* Do not rewind past the beginning of link data; if we do,
|
---|
1626 | it's either a bug or a broken stream */
|
---|
1627 | result=best;
|
---|
1628 | while(result>vf->dataoffsets[link]){
|
---|
1629 | result=_get_prev_page(vf,result,&og);
|
---|
1630 | if(result<0) goto seek_error;
|
---|
1631 | if(ogg_page_serialno(&og)==vf->current_serialno &&
|
---|
1632 | (ogg_page_granulepos(&og)>-1 ||
|
---|
1633 | !ogg_page_continued(&og))){
|
---|
1634 | return ov_raw_seek(vf,result);
|
---|
1635 | }
|
---|
1636 | }
|
---|
1637 | }
|
---|
1638 | if(result<0){
|
---|
1639 | result = OV_EBADPACKET;
|
---|
1640 | goto seek_error;
|
---|
1641 | }
|
---|
1642 | if(op.granulepos!=-1){
|
---|
1643 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
|
---|
1644 | if(vf->pcm_offset<0)vf->pcm_offset=0;
|
---|
1645 | vf->pcm_offset+=total;
|
---|
1646 | break;
|
---|
1647 | }else
|
---|
1648 | result=ogg_stream_packetout(&vf->os,NULL);
|
---|
1649 | }
|
---|
1650 | }
|
---|
1651 | }
|
---|
1652 |
|
---|
1653 | /* verify result */
|
---|
1654 | if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){
|
---|
1655 | result=OV_EFAULT;
|
---|
1656 | goto seek_error;
|
---|
1657 | }
|
---|
1658 | vf->bittrack=0.f;
|
---|
1659 | vf->samptrack=0.f;
|
---|
1660 | return(0);
|
---|
1661 |
|
---|
1662 | seek_error:
|
---|
1663 | /* dump machine so we're in a known state */
|
---|
1664 | vf->pcm_offset=-1;
|
---|
1665 | _decode_clear(vf);
|
---|
1666 | return (int)result;
|
---|
1667 | }
|
---|
1668 |
|
---|
1669 | /* seek to a sample offset relative to the decompressed pcm stream
|
---|
1670 | returns zero on success, nonzero on failure */
|
---|
1671 |
|
---|
1672 | int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
|
---|
1673 | int thisblock,lastblock=0;
|
---|
1674 | int ret=ov_pcm_seek_page(vf,pos);
|
---|
1675 | if(ret<0)return(ret);
|
---|
1676 | if((ret=_make_decode_ready(vf)))return ret;
|
---|
1677 |
|
---|
1678 | /* discard leading packets we don't need for the lapping of the
|
---|
1679 | position we want; don't decode them */
|
---|
1680 |
|
---|
1681 | while(1){
|
---|
1682 | ogg_packet op;
|
---|
1683 | ogg_page og;
|
---|
1684 |
|
---|
1685 | ret=ogg_stream_packetpeek(&vf->os,&op);
|
---|
1686 | if(ret>0){
|
---|
1687 | thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
|
---|
1688 | if(thisblock<0){
|
---|
1689 | ogg_stream_packetout(&vf->os,NULL);
|
---|
1690 | continue; /* non audio packet */
|
---|
1691 | }
|
---|
1692 | if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
|
---|
1693 |
|
---|
1694 | if(vf->pcm_offset+((thisblock+
|
---|
1695 | vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break;
|
---|
1696 |
|
---|
1697 | /* remove the packet from packet queue and track its granulepos */
|
---|
1698 | ogg_stream_packetout(&vf->os,NULL);
|
---|
1699 | vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with
|
---|
1700 | only tracking, no
|
---|
1701 | pcm_decode */
|
---|
1702 | vorbis_synthesis_blockin(&vf->vd,&vf->vb);
|
---|
1703 |
|
---|
1704 | /* end of logical stream case is hard, especially with exact
|
---|
1705 | length positioning. */
|
---|
1706 |
|
---|
1707 | if(op.granulepos>-1){
|
---|
1708 | int i;
|
---|
1709 | /* always believe the stream markers */
|
---|
1710 | vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2];
|
---|
1711 | if(vf->pcm_offset<0)vf->pcm_offset=0;
|
---|
1712 | for(i=0;i<vf->current_link;i++)
|
---|
1713 | vf->pcm_offset+=vf->pcmlengths[i*2+1];
|
---|
1714 | }
|
---|
1715 |
|
---|
1716 | lastblock=thisblock;
|
---|
1717 |
|
---|
1718 | }else{
|
---|
1719 | if(ret<0 && ret!=OV_HOLE)break;
|
---|
1720 |
|
---|
1721 | /* suck in a new page */
|
---|
1722 | if(_get_next_page(vf,&og,-1)<0)break;
|
---|
1723 | if(ogg_page_bos(&og))_decode_clear(vf);
|
---|
1724 |
|
---|
1725 | if(vf->ready_state<STREAMSET){
|
---|
1726 | long serialno=ogg_page_serialno(&og);
|
---|
1727 | int link;
|
---|
1728 |
|
---|
1729 | for(link=0;link<vf->links;link++)
|
---|
1730 | if(vf->serialnos[link]==serialno)break;
|
---|
1731 | if(link==vf->links) continue;
|
---|
1732 | vf->current_link=link;
|
---|
1733 |
|
---|
1734 | vf->ready_state=STREAMSET;
|
---|
1735 | vf->current_serialno=ogg_page_serialno(&og);
|
---|
1736 | ogg_stream_reset_serialno(&vf->os,serialno);
|
---|
1737 | ret=_make_decode_ready(vf);
|
---|
1738 | if(ret)return ret;
|
---|
1739 | lastblock=0;
|
---|
1740 | }
|
---|
1741 |
|
---|
1742 | ogg_stream_pagein(&vf->os,&og);
|
---|
1743 | }
|
---|
1744 | }
|
---|
1745 |
|
---|
1746 | vf->bittrack=0.f;
|
---|
1747 | vf->samptrack=0.f;
|
---|
1748 | /* discard samples until we reach the desired position. Crossing a
|
---|
1749 | logical bitstream boundary with abandon is OK. */
|
---|
1750 | {
|
---|
1751 | /* note that halfrate could be set differently in each link, but
|
---|
1752 | vorbisfile encoforces all links are set or unset */
|
---|
1753 | int hs=vorbis_synthesis_halfrate_p(vf->vi);
|
---|
1754 | while(vf->pcm_offset<((pos>>hs)<<hs)){
|
---|
1755 | ogg_int64_t target=(pos-vf->pcm_offset)>>hs;
|
---|
1756 | long samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
|
---|
1757 |
|
---|
1758 | if(samples>target)samples=target;
|
---|
1759 | vorbis_synthesis_read(&vf->vd,samples);
|
---|
1760 | vf->pcm_offset+=samples<<hs;
|
---|
1761 |
|
---|
1762 | if(samples<target)
|
---|
1763 | if(_fetch_and_process_packet(vf,NULL,1,1)<=0)
|
---|
1764 | vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
|
---|
1765 | }
|
---|
1766 | }
|
---|
1767 | return 0;
|
---|
1768 | }
|
---|
1769 |
|
---|
1770 | /* seek to a playback time relative to the decompressed pcm stream
|
---|
1771 | returns zero on success, nonzero on failure */
|
---|
1772 | int ov_time_seek(OggVorbis_File *vf,double seconds){
|
---|
1773 | /* translate time to PCM position and call ov_pcm_seek */
|
---|
1774 |
|
---|
1775 | int link=-1;
|
---|
1776 | ogg_int64_t pcm_total=0;
|
---|
1777 | double time_total=0.;
|
---|
1778 |
|
---|
1779 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1780 | if(!vf->seekable)return(OV_ENOSEEK);
|
---|
1781 | if(seconds<0)return(OV_EINVAL);
|
---|
1782 |
|
---|
1783 | /* which bitstream section does this time offset occur in? */
|
---|
1784 | for(link=0;link<vf->links;link++){
|
---|
1785 | double addsec = ov_time_total(vf,link);
|
---|
1786 | if(seconds<time_total+addsec)break;
|
---|
1787 | time_total+=addsec;
|
---|
1788 | pcm_total+=vf->pcmlengths[link*2+1];
|
---|
1789 | }
|
---|
1790 |
|
---|
1791 | if(link==vf->links)return(OV_EINVAL);
|
---|
1792 |
|
---|
1793 | /* enough information to convert time offset to pcm offset */
|
---|
1794 | {
|
---|
1795 | ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
|
---|
1796 | return(ov_pcm_seek(vf,target));
|
---|
1797 | }
|
---|
1798 | }
|
---|
1799 |
|
---|
1800 | /* page-granularity version of ov_time_seek
|
---|
1801 | returns zero on success, nonzero on failure */
|
---|
1802 | int ov_time_seek_page(OggVorbis_File *vf,double seconds){
|
---|
1803 | /* translate time to PCM position and call ov_pcm_seek */
|
---|
1804 |
|
---|
1805 | int link=-1;
|
---|
1806 | ogg_int64_t pcm_total=0;
|
---|
1807 | double time_total=0.;
|
---|
1808 |
|
---|
1809 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1810 | if(!vf->seekable)return(OV_ENOSEEK);
|
---|
1811 | if(seconds<0)return(OV_EINVAL);
|
---|
1812 |
|
---|
1813 | /* which bitstream section does this time offset occur in? */
|
---|
1814 | for(link=0;link<vf->links;link++){
|
---|
1815 | double addsec = ov_time_total(vf,link);
|
---|
1816 | if(seconds<time_total+addsec)break;
|
---|
1817 | time_total+=addsec;
|
---|
1818 | pcm_total+=vf->pcmlengths[link*2+1];
|
---|
1819 | }
|
---|
1820 |
|
---|
1821 | if(link==vf->links)return(OV_EINVAL);
|
---|
1822 |
|
---|
1823 | /* enough information to convert time offset to pcm offset */
|
---|
1824 | {
|
---|
1825 | ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
|
---|
1826 | return(ov_pcm_seek_page(vf,target));
|
---|
1827 | }
|
---|
1828 | }
|
---|
1829 |
|
---|
1830 | /* tell the current stream offset cursor. Note that seek followed by
|
---|
1831 | tell will likely not give the set offset due to caching */
|
---|
1832 | ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
|
---|
1833 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1834 | return(vf->offset);
|
---|
1835 | }
|
---|
1836 |
|
---|
1837 | /* return PCM offset (sample) of next PCM sample to be read */
|
---|
1838 | ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
|
---|
1839 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1840 | return(vf->pcm_offset);
|
---|
1841 | }
|
---|
1842 |
|
---|
1843 | /* return time offset (seconds) of next PCM sample to be read */
|
---|
1844 | double ov_time_tell(OggVorbis_File *vf){
|
---|
1845 | int link=0;
|
---|
1846 | ogg_int64_t pcm_total=0;
|
---|
1847 | double time_total=0.f;
|
---|
1848 |
|
---|
1849 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1850 | if(vf->seekable){
|
---|
1851 | pcm_total=ov_pcm_total(vf,-1);
|
---|
1852 | time_total=ov_time_total(vf,-1);
|
---|
1853 |
|
---|
1854 | /* which bitstream section does this time offset occur in? */
|
---|
1855 | for(link=vf->links-1;link>=0;link--){
|
---|
1856 | pcm_total-=vf->pcmlengths[link*2+1];
|
---|
1857 | time_total-=ov_time_total(vf,link);
|
---|
1858 | if(vf->pcm_offset>=pcm_total)break;
|
---|
1859 | }
|
---|
1860 | }
|
---|
1861 |
|
---|
1862 | return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate);
|
---|
1863 | }
|
---|
1864 |
|
---|
1865 | /* link: -1) return the vorbis_info struct for the bitstream section
|
---|
1866 | currently being decoded
|
---|
1867 | 0-n) to request information for a specific bitstream section
|
---|
1868 |
|
---|
1869 | In the case of a non-seekable bitstream, any call returns the
|
---|
1870 | current bitstream. NULL in the case that the machine is not
|
---|
1871 | initialized */
|
---|
1872 |
|
---|
1873 | vorbis_info *ov_info(OggVorbis_File *vf,int link){
|
---|
1874 | if(vf->seekable){
|
---|
1875 | if(link<0)
|
---|
1876 | if(vf->ready_state>=STREAMSET)
|
---|
1877 | return vf->vi+vf->current_link;
|
---|
1878 | else
|
---|
1879 | return vf->vi;
|
---|
1880 | else
|
---|
1881 | if(link>=vf->links)
|
---|
1882 | return NULL;
|
---|
1883 | else
|
---|
1884 | return vf->vi+link;
|
---|
1885 | }else{
|
---|
1886 | return vf->vi;
|
---|
1887 | }
|
---|
1888 | }
|
---|
1889 |
|
---|
1890 | /* grr, strong typing, grr, no templates/inheritence, grr */
|
---|
1891 | vorbis_comment *ov_comment(OggVorbis_File *vf,int link){
|
---|
1892 | if(vf->seekable){
|
---|
1893 | if(link<0)
|
---|
1894 | if(vf->ready_state>=STREAMSET)
|
---|
1895 | return vf->vc+vf->current_link;
|
---|
1896 | else
|
---|
1897 | return vf->vc;
|
---|
1898 | else
|
---|
1899 | if(link>=vf->links)
|
---|
1900 | return NULL;
|
---|
1901 | else
|
---|
1902 | return vf->vc+link;
|
---|
1903 | }else{
|
---|
1904 | return vf->vc;
|
---|
1905 | }
|
---|
1906 | }
|
---|
1907 |
|
---|
1908 | static int host_is_big_endian() {
|
---|
1909 | ogg_int32_t pattern = 0xfeedface; /* deadbeef */
|
---|
1910 | unsigned char *bytewise = (unsigned char *)&pattern;
|
---|
1911 | if (bytewise[0] == 0xfe) return 1;
|
---|
1912 | return 0;
|
---|
1913 | }
|
---|
1914 |
|
---|
1915 | /* up to this point, everything could more or less hide the multiple
|
---|
1916 | logical bitstream nature of chaining from the toplevel application
|
---|
1917 | if the toplevel application didn't particularly care. However, at
|
---|
1918 | the point that we actually read audio back, the multiple-section
|
---|
1919 | nature must surface: Multiple bitstream sections do not necessarily
|
---|
1920 | have to have the same number of channels or sampling rate.
|
---|
1921 |
|
---|
1922 | ov_read returns the sequential logical bitstream number currently
|
---|
1923 | being decoded along with the PCM data in order that the toplevel
|
---|
1924 | application can take action on channel/sample rate changes. This
|
---|
1925 | number will be incremented even for streamed (non-seekable) streams
|
---|
1926 | (for seekable streams, it represents the actual logical bitstream
|
---|
1927 | index within the physical bitstream. Note that the accessor
|
---|
1928 | functions above are aware of this dichotomy).
|
---|
1929 |
|
---|
1930 | ov_read_filter is exactly the same as ov_read except that it processes
|
---|
1931 | the decoded audio data through a filter before packing it into the
|
---|
1932 | requested format. This gives greater accuracy than applying a filter
|
---|
1933 | after the audio has been converted into integral PCM.
|
---|
1934 |
|
---|
1935 | input values: buffer) a buffer to hold packed PCM data for return
|
---|
1936 | length) the byte length requested to be placed into buffer
|
---|
1937 | bigendianp) should the data be packed LSB first (0) or
|
---|
1938 | MSB first (1)
|
---|
1939 | word) word size for output. currently 1 (byte) or
|
---|
1940 | 2 (16 bit short)
|
---|
1941 |
|
---|
1942 | return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
|
---|
1943 | 0) EOF
|
---|
1944 | n) number of bytes of PCM actually returned. The
|
---|
1945 | below works on a packet-by-packet basis, so the
|
---|
1946 | return length is not related to the 'length' passed
|
---|
1947 | in, just guaranteed to fit.
|
---|
1948 |
|
---|
1949 | *section) set to the logical bitstream number */
|
---|
1950 |
|
---|
1951 | long ov_read_filter(OggVorbis_File *vf,char *buffer,int length,
|
---|
1952 | int bigendianp,int word,int sgned,int *bitstream,
|
---|
1953 | void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param){
|
---|
1954 | int i,j;
|
---|
1955 | int host_endian = host_is_big_endian();
|
---|
1956 | int hs;
|
---|
1957 |
|
---|
1958 | float **pcm;
|
---|
1959 | long samples;
|
---|
1960 |
|
---|
1961 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
1962 | if(word<=0)return(OV_EINVAL);
|
---|
1963 |
|
---|
1964 | while(1){
|
---|
1965 | if(vf->ready_state==INITSET){
|
---|
1966 | samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
|
---|
1967 | if(samples)break;
|
---|
1968 | }
|
---|
1969 |
|
---|
1970 | /* suck in another packet */
|
---|
1971 | {
|
---|
1972 | int ret=_fetch_and_process_packet(vf,NULL,1,1);
|
---|
1973 | if(ret==OV_EOF)
|
---|
1974 | return(0);
|
---|
1975 | if(ret<=0)
|
---|
1976 | return(ret);
|
---|
1977 | }
|
---|
1978 |
|
---|
1979 | }
|
---|
1980 |
|
---|
1981 | if(samples>0){
|
---|
1982 |
|
---|
1983 | /* yay! proceed to pack data into the byte buffer */
|
---|
1984 |
|
---|
1985 | long channels=ov_info(vf,-1)->channels;
|
---|
1986 | long bytespersample=word * channels;
|
---|
1987 | vorbis_fpu_control fpu;
|
---|
1988 |
|
---|
1989 | if(channels<1||channels>255)return(OV_EINVAL);
|
---|
1990 | if(samples>length/bytespersample)samples=length/bytespersample;
|
---|
1991 |
|
---|
1992 | if(samples <= 0)
|
---|
1993 | return OV_EINVAL;
|
---|
1994 |
|
---|
1995 | /* Here. */
|
---|
1996 | if(filter)
|
---|
1997 | filter(pcm,channels,samples,filter_param);
|
---|
1998 |
|
---|
1999 | /* a tight loop to pack each size */
|
---|
2000 | {
|
---|
2001 | int val;
|
---|
2002 | if(word==1){
|
---|
2003 | int off=(sgned?0:128);
|
---|
2004 | vorbis_fpu_setround(&fpu);
|
---|
2005 | for(j=0;j<samples;j++)
|
---|
2006 | for(i=0;i<channels;i++){
|
---|
2007 | val=vorbis_ftoi(pcm[i][j]*128.f);
|
---|
2008 | if(val>127)val=127;
|
---|
2009 | else if(val<-128)val=-128;
|
---|
2010 | *buffer++=val+off;
|
---|
2011 | }
|
---|
2012 | vorbis_fpu_restore(fpu);
|
---|
2013 | }else{
|
---|
2014 | int off=(sgned?0:32768);
|
---|
2015 |
|
---|
2016 | if(host_endian==bigendianp){
|
---|
2017 | if(sgned){
|
---|
2018 |
|
---|
2019 | vorbis_fpu_setround(&fpu);
|
---|
2020 | for(i=0;i<channels;i++) { /* It's faster in this order */
|
---|
2021 | float *src=pcm[i];
|
---|
2022 | short *dest=((short *)buffer)+i;
|
---|
2023 | for(j=0;j<samples;j++) {
|
---|
2024 | val=vorbis_ftoi(src[j]*32768.f);
|
---|
2025 | if(val>32767)val=32767;
|
---|
2026 | else if(val<-32768)val=-32768;
|
---|
2027 | *dest=val;
|
---|
2028 | dest+=channels;
|
---|
2029 | }
|
---|
2030 | }
|
---|
2031 | vorbis_fpu_restore(fpu);
|
---|
2032 |
|
---|
2033 | }else{
|
---|
2034 |
|
---|
2035 | vorbis_fpu_setround(&fpu);
|
---|
2036 | for(i=0;i<channels;i++) {
|
---|
2037 | float *src=pcm[i];
|
---|
2038 | short *dest=((short *)buffer)+i;
|
---|
2039 | for(j=0;j<samples;j++) {
|
---|
2040 | val=vorbis_ftoi(src[j]*32768.f);
|
---|
2041 | if(val>32767)val=32767;
|
---|
2042 | else if(val<-32768)val=-32768;
|
---|
2043 | *dest=val+off;
|
---|
2044 | dest+=channels;
|
---|
2045 | }
|
---|
2046 | }
|
---|
2047 | vorbis_fpu_restore(fpu);
|
---|
2048 |
|
---|
2049 | }
|
---|
2050 | }else if(bigendianp){
|
---|
2051 |
|
---|
2052 | vorbis_fpu_setround(&fpu);
|
---|
2053 | for(j=0;j<samples;j++)
|
---|
2054 | for(i=0;i<channels;i++){
|
---|
2055 | val=vorbis_ftoi(pcm[i][j]*32768.f);
|
---|
2056 | if(val>32767)val=32767;
|
---|
2057 | else if(val<-32768)val=-32768;
|
---|
2058 | val+=off;
|
---|
2059 | *buffer++=(val>>8);
|
---|
2060 | *buffer++=(val&0xff);
|
---|
2061 | }
|
---|
2062 | vorbis_fpu_restore(fpu);
|
---|
2063 |
|
---|
2064 | }else{
|
---|
2065 | vorbis_fpu_setround(&fpu);
|
---|
2066 | for(j=0;j<samples;j++)
|
---|
2067 | for(i=0;i<channels;i++){
|
---|
2068 | val=vorbis_ftoi(pcm[i][j]*32768.f);
|
---|
2069 | if(val>32767)val=32767;
|
---|
2070 | else if(val<-32768)val=-32768;
|
---|
2071 | val+=off;
|
---|
2072 | *buffer++=(val&0xff);
|
---|
2073 | *buffer++=(val>>8);
|
---|
2074 | }
|
---|
2075 | vorbis_fpu_restore(fpu);
|
---|
2076 |
|
---|
2077 | }
|
---|
2078 | }
|
---|
2079 | }
|
---|
2080 |
|
---|
2081 | vorbis_synthesis_read(&vf->vd,samples);
|
---|
2082 | hs=vorbis_synthesis_halfrate_p(vf->vi);
|
---|
2083 | vf->pcm_offset+=(samples<<hs);
|
---|
2084 | if(bitstream)*bitstream=vf->current_link;
|
---|
2085 | return(samples*bytespersample);
|
---|
2086 | }else{
|
---|
2087 | return(samples);
|
---|
2088 | }
|
---|
2089 | }
|
---|
2090 |
|
---|
2091 | long ov_read(OggVorbis_File *vf,char *buffer,int length,
|
---|
2092 | int bigendianp,int word,int sgned,int *bitstream){
|
---|
2093 | return ov_read_filter(vf, buffer, length, bigendianp, word, sgned, bitstream, NULL, NULL);
|
---|
2094 | }
|
---|
2095 |
|
---|
2096 | /* input values: pcm_channels) a float vector per channel of output
|
---|
2097 | length) the sample length being read by the app
|
---|
2098 |
|
---|
2099 | return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
|
---|
2100 | 0) EOF
|
---|
2101 | n) number of samples of PCM actually returned. The
|
---|
2102 | below works on a packet-by-packet basis, so the
|
---|
2103 | return length is not related to the 'length' passed
|
---|
2104 | in, just guaranteed to fit.
|
---|
2105 |
|
---|
2106 | *section) set to the logical bitstream number */
|
---|
2107 |
|
---|
2108 |
|
---|
2109 |
|
---|
2110 | long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length,
|
---|
2111 | int *bitstream){
|
---|
2112 |
|
---|
2113 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
2114 |
|
---|
2115 | while(1){
|
---|
2116 | if(vf->ready_state==INITSET){
|
---|
2117 | float **pcm;
|
---|
2118 | long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
|
---|
2119 | if(samples){
|
---|
2120 | int hs=vorbis_synthesis_halfrate_p(vf->vi);
|
---|
2121 | if(pcm_channels)*pcm_channels=pcm;
|
---|
2122 | if(samples>length)samples=length;
|
---|
2123 | vorbis_synthesis_read(&vf->vd,samples);
|
---|
2124 | vf->pcm_offset+=samples<<hs;
|
---|
2125 | if(bitstream)*bitstream=vf->current_link;
|
---|
2126 | return samples;
|
---|
2127 |
|
---|
2128 | }
|
---|
2129 | }
|
---|
2130 |
|
---|
2131 | /* suck in another packet */
|
---|
2132 | {
|
---|
2133 | int ret=_fetch_and_process_packet(vf,NULL,1,1);
|
---|
2134 | if(ret==OV_EOF)return(0);
|
---|
2135 | if(ret<=0)return(ret);
|
---|
2136 | }
|
---|
2137 |
|
---|
2138 | }
|
---|
2139 | }
|
---|
2140 |
|
---|
2141 | extern const float *vorbis_window(vorbis_dsp_state *v,int W);
|
---|
2142 |
|
---|
2143 | static void _ov_splice(float **pcm,float **lappcm,
|
---|
2144 | int n1, int n2,
|
---|
2145 | int ch1, int ch2,
|
---|
2146 | const float *w1, const float *w2){
|
---|
2147 | int i,j;
|
---|
2148 | const float *w=w1;
|
---|
2149 | int n=n1;
|
---|
2150 |
|
---|
2151 | if(n1>n2){
|
---|
2152 | n=n2;
|
---|
2153 | w=w2;
|
---|
2154 | }
|
---|
2155 |
|
---|
2156 | /* splice */
|
---|
2157 | for(j=0;j<ch1 && j<ch2;j++){
|
---|
2158 | float *s=lappcm[j];
|
---|
2159 | float *d=pcm[j];
|
---|
2160 |
|
---|
2161 | for(i=0;i<n;i++){
|
---|
2162 | float wd=w[i]*w[i];
|
---|
2163 | float ws=1.-wd;
|
---|
2164 | d[i]=d[i]*wd + s[i]*ws;
|
---|
2165 | }
|
---|
2166 | }
|
---|
2167 | /* window from zero */
|
---|
2168 | for(;j<ch2;j++){
|
---|
2169 | float *d=pcm[j];
|
---|
2170 | for(i=0;i<n;i++){
|
---|
2171 | float wd=w[i]*w[i];
|
---|
2172 | d[i]=d[i]*wd;
|
---|
2173 | }
|
---|
2174 | }
|
---|
2175 |
|
---|
2176 | }
|
---|
2177 |
|
---|
2178 | /* make sure vf is INITSET */
|
---|
2179 | static int _ov_initset(OggVorbis_File *vf){
|
---|
2180 | while(1){
|
---|
2181 | if(vf->ready_state==INITSET)break;
|
---|
2182 | /* suck in another packet */
|
---|
2183 | {
|
---|
2184 | int ret=_fetch_and_process_packet(vf,NULL,1,0);
|
---|
2185 | if(ret<0 && ret!=OV_HOLE)return(ret);
|
---|
2186 | }
|
---|
2187 | }
|
---|
2188 | return 0;
|
---|
2189 | }
|
---|
2190 |
|
---|
2191 | /* make sure vf is INITSET and that we have a primed buffer; if
|
---|
2192 | we're crosslapping at a stream section boundary, this also makes
|
---|
2193 | sure we're sanity checking against the right stream information */
|
---|
2194 | static int _ov_initprime(OggVorbis_File *vf){
|
---|
2195 | vorbis_dsp_state *vd=&vf->vd;
|
---|
2196 | while(1){
|
---|
2197 | if(vf->ready_state==INITSET)
|
---|
2198 | if(vorbis_synthesis_pcmout(vd,NULL))break;
|
---|
2199 |
|
---|
2200 | /* suck in another packet */
|
---|
2201 | {
|
---|
2202 | int ret=_fetch_and_process_packet(vf,NULL,1,0);
|
---|
2203 | if(ret<0 && ret!=OV_HOLE)return(ret);
|
---|
2204 | }
|
---|
2205 | }
|
---|
2206 | return 0;
|
---|
2207 | }
|
---|
2208 |
|
---|
2209 | /* grab enough data for lapping from vf; this may be in the form of
|
---|
2210 | unreturned, already-decoded pcm, remaining PCM we will need to
|
---|
2211 | decode, or synthetic postextrapolation from last packets. */
|
---|
2212 | static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd,
|
---|
2213 | float **lappcm,int lapsize){
|
---|
2214 | int lapcount=0,i;
|
---|
2215 | float **pcm;
|
---|
2216 |
|
---|
2217 | /* try first to decode the lapping data */
|
---|
2218 | while(lapcount<lapsize){
|
---|
2219 | int samples=vorbis_synthesis_pcmout(vd,&pcm);
|
---|
2220 | if(samples){
|
---|
2221 | if(samples>lapsize-lapcount)samples=lapsize-lapcount;
|
---|
2222 | for(i=0;i<vi->channels;i++)
|
---|
2223 | memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
|
---|
2224 | lapcount+=samples;
|
---|
2225 | vorbis_synthesis_read(vd,samples);
|
---|
2226 | }else{
|
---|
2227 | /* suck in another packet */
|
---|
2228 | int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */
|
---|
2229 | if(ret==OV_EOF)break;
|
---|
2230 | }
|
---|
2231 | }
|
---|
2232 | if(lapcount<lapsize){
|
---|
2233 | /* failed to get lapping data from normal decode; pry it from the
|
---|
2234 | postextrapolation buffering, or the second half of the MDCT
|
---|
2235 | from the last packet */
|
---|
2236 | int samples=vorbis_synthesis_lapout(&vf->vd,&pcm);
|
---|
2237 | if(samples==0){
|
---|
2238 | for(i=0;i<vi->channels;i++)
|
---|
2239 | memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount);
|
---|
2240 | lapcount=lapsize;
|
---|
2241 | }else{
|
---|
2242 | if(samples>lapsize-lapcount)samples=lapsize-lapcount;
|
---|
2243 | for(i=0;i<vi->channels;i++)
|
---|
2244 | memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples);
|
---|
2245 | lapcount+=samples;
|
---|
2246 | }
|
---|
2247 | }
|
---|
2248 | }
|
---|
2249 |
|
---|
2250 | /* this sets up crosslapping of a sample by using trailing data from
|
---|
2251 | sample 1 and lapping it into the windowing buffer of sample 2 */
|
---|
2252 | int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){
|
---|
2253 | vorbis_info *vi1,*vi2;
|
---|
2254 | float **lappcm;
|
---|
2255 | float **pcm;
|
---|
2256 | const float *w1,*w2;
|
---|
2257 | int n1,n2,i,ret,hs1,hs2;
|
---|
2258 |
|
---|
2259 | if(vf1==vf2)return(0); /* degenerate case */
|
---|
2260 | if(vf1->ready_state<OPENED)return(OV_EINVAL);
|
---|
2261 | if(vf2->ready_state<OPENED)return(OV_EINVAL);
|
---|
2262 |
|
---|
2263 | /* the relevant overlap buffers must be pre-checked and pre-primed
|
---|
2264 | before looking at settings in the event that priming would cross
|
---|
2265 | a bitstream boundary. So, do it now */
|
---|
2266 |
|
---|
2267 | ret=_ov_initset(vf1);
|
---|
2268 | if(ret)return(ret);
|
---|
2269 | ret=_ov_initprime(vf2);
|
---|
2270 | if(ret)return(ret);
|
---|
2271 |
|
---|
2272 | vi1=ov_info(vf1,-1);
|
---|
2273 | vi2=ov_info(vf2,-1);
|
---|
2274 | hs1=ov_halfrate_p(vf1);
|
---|
2275 | hs2=ov_halfrate_p(vf2);
|
---|
2276 |
|
---|
2277 | lappcm=alloca(sizeof(*lappcm)*vi1->channels);
|
---|
2278 | n1=vorbis_info_blocksize(vi1,0)>>(1+hs1);
|
---|
2279 | n2=vorbis_info_blocksize(vi2,0)>>(1+hs2);
|
---|
2280 | w1=vorbis_window(&vf1->vd,0);
|
---|
2281 | w2=vorbis_window(&vf2->vd,0);
|
---|
2282 |
|
---|
2283 | for(i=0;i<vi1->channels;i++)
|
---|
2284 | lappcm[i]=alloca(sizeof(**lappcm)*n1);
|
---|
2285 |
|
---|
2286 | _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1);
|
---|
2287 |
|
---|
2288 | /* have a lapping buffer from vf1; now to splice it into the lapping
|
---|
2289 | buffer of vf2 */
|
---|
2290 | /* consolidate and expose the buffer. */
|
---|
2291 | vorbis_synthesis_lapout(&vf2->vd,&pcm);
|
---|
2292 |
|
---|
2293 | #if 0
|
---|
2294 | _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0);
|
---|
2295 | _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0);
|
---|
2296 | #endif
|
---|
2297 |
|
---|
2298 | /* splice */
|
---|
2299 | _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2);
|
---|
2300 |
|
---|
2301 | /* done */
|
---|
2302 | return(0);
|
---|
2303 | }
|
---|
2304 |
|
---|
2305 | static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos,
|
---|
2306 | int (*localseek)(OggVorbis_File *,ogg_int64_t)){
|
---|
2307 | vorbis_info *vi;
|
---|
2308 | float **lappcm;
|
---|
2309 | float **pcm;
|
---|
2310 | const float *w1,*w2;
|
---|
2311 | int n1,n2,ch1,ch2,hs;
|
---|
2312 | int i,ret;
|
---|
2313 |
|
---|
2314 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
2315 | ret=_ov_initset(vf);
|
---|
2316 | if(ret)return(ret);
|
---|
2317 | vi=ov_info(vf,-1);
|
---|
2318 | hs=ov_halfrate_p(vf);
|
---|
2319 |
|
---|
2320 | ch1=vi->channels;
|
---|
2321 | n1=vorbis_info_blocksize(vi,0)>>(1+hs);
|
---|
2322 | w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are
|
---|
2323 | persistent; even if the decode state
|
---|
2324 | from this link gets dumped, this
|
---|
2325 | window array continues to exist */
|
---|
2326 |
|
---|
2327 | lappcm=alloca(sizeof(*lappcm)*ch1);
|
---|
2328 | for(i=0;i<ch1;i++)
|
---|
2329 | lappcm[i]=alloca(sizeof(**lappcm)*n1);
|
---|
2330 | _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
|
---|
2331 |
|
---|
2332 | /* have lapping data; seek and prime the buffer */
|
---|
2333 | ret=localseek(vf,pos);
|
---|
2334 | if(ret)return ret;
|
---|
2335 | ret=_ov_initprime(vf);
|
---|
2336 | if(ret)return(ret);
|
---|
2337 |
|
---|
2338 | /* Guard against cross-link changes; they're perfectly legal */
|
---|
2339 | vi=ov_info(vf,-1);
|
---|
2340 | ch2=vi->channels;
|
---|
2341 | n2=vorbis_info_blocksize(vi,0)>>(1+hs);
|
---|
2342 | w2=vorbis_window(&vf->vd,0);
|
---|
2343 |
|
---|
2344 | /* consolidate and expose the buffer. */
|
---|
2345 | vorbis_synthesis_lapout(&vf->vd,&pcm);
|
---|
2346 |
|
---|
2347 | /* splice */
|
---|
2348 | _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
|
---|
2349 |
|
---|
2350 | /* done */
|
---|
2351 | return(0);
|
---|
2352 | }
|
---|
2353 |
|
---|
2354 | int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
|
---|
2355 | return _ov_64_seek_lap(vf,pos,ov_raw_seek);
|
---|
2356 | }
|
---|
2357 |
|
---|
2358 | int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){
|
---|
2359 | return _ov_64_seek_lap(vf,pos,ov_pcm_seek);
|
---|
2360 | }
|
---|
2361 |
|
---|
2362 | int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){
|
---|
2363 | return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page);
|
---|
2364 | }
|
---|
2365 |
|
---|
2366 | static int _ov_d_seek_lap(OggVorbis_File *vf,double pos,
|
---|
2367 | int (*localseek)(OggVorbis_File *,double)){
|
---|
2368 | vorbis_info *vi;
|
---|
2369 | float **lappcm;
|
---|
2370 | float **pcm;
|
---|
2371 | const float *w1,*w2;
|
---|
2372 | int n1,n2,ch1,ch2,hs;
|
---|
2373 | int i,ret;
|
---|
2374 |
|
---|
2375 | if(vf->ready_state<OPENED)return(OV_EINVAL);
|
---|
2376 | ret=_ov_initset(vf);
|
---|
2377 | if(ret)return(ret);
|
---|
2378 | vi=ov_info(vf,-1);
|
---|
2379 | hs=ov_halfrate_p(vf);
|
---|
2380 |
|
---|
2381 | ch1=vi->channels;
|
---|
2382 | n1=vorbis_info_blocksize(vi,0)>>(1+hs);
|
---|
2383 | w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are
|
---|
2384 | persistent; even if the decode state
|
---|
2385 | from this link gets dumped, this
|
---|
2386 | window array continues to exist */
|
---|
2387 |
|
---|
2388 | lappcm=alloca(sizeof(*lappcm)*ch1);
|
---|
2389 | for(i=0;i<ch1;i++)
|
---|
2390 | lappcm[i]=alloca(sizeof(**lappcm)*n1);
|
---|
2391 | _ov_getlap(vf,vi,&vf->vd,lappcm,n1);
|
---|
2392 |
|
---|
2393 | /* have lapping data; seek and prime the buffer */
|
---|
2394 | ret=localseek(vf,pos);
|
---|
2395 | if(ret)return ret;
|
---|
2396 | ret=_ov_initprime(vf);
|
---|
2397 | if(ret)return(ret);
|
---|
2398 |
|
---|
2399 | /* Guard against cross-link changes; they're perfectly legal */
|
---|
2400 | vi=ov_info(vf,-1);
|
---|
2401 | ch2=vi->channels;
|
---|
2402 | n2=vorbis_info_blocksize(vi,0)>>(1+hs);
|
---|
2403 | w2=vorbis_window(&vf->vd,0);
|
---|
2404 |
|
---|
2405 | /* consolidate and expose the buffer. */
|
---|
2406 | vorbis_synthesis_lapout(&vf->vd,&pcm);
|
---|
2407 |
|
---|
2408 | /* splice */
|
---|
2409 | _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2);
|
---|
2410 |
|
---|
2411 | /* done */
|
---|
2412 | return(0);
|
---|
2413 | }
|
---|
2414 |
|
---|
2415 | int ov_time_seek_lap(OggVorbis_File *vf,double pos){
|
---|
2416 | return _ov_d_seek_lap(vf,pos,ov_time_seek);
|
---|
2417 | }
|
---|
2418 |
|
---|
2419 | int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){
|
---|
2420 | return _ov_d_seek_lap(vf,pos,ov_time_seek_page);
|
---|
2421 | }
|
---|