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