summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Holmgren <magnushol@gmail.com>2009-07-13 15:23:07 +0000
committerMagnus Holmgren <magnushol@gmail.com>2009-07-13 15:23:07 +0000
commit5e2f11ad655051296b64af86b33a8bbb0229272b (patch)
treecd696153542e7a19056e8b23aeda9d0b1b6bc3d6
parent23ea8a53a10977b53e926665f44d0d3810552575 (diff)
downloadrockbox-5e2f11ad655051296b64af86b33a8bbb0229272b.tar.gz
rockbox-5e2f11ad655051296b64af86b33a8bbb0229272b.zip
Import Vorbis seeking improvements from Tremor SVN.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21841 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/codecs/libtremor/info.c25
-rw-r--r--apps/codecs/libtremor/ivorbiscodec.h1
-rw-r--r--apps/codecs/libtremor/vorbisfile.c886
3 files changed, 612 insertions, 300 deletions
diff --git a/apps/codecs/libtremor/info.c b/apps/codecs/libtremor/info.c
index cb3860bbff..4273f97dc1 100644
--- a/apps/codecs/libtremor/info.c
+++ b/apps/codecs/libtremor/info.c
@@ -232,6 +232,31 @@ static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){
232 return(OV_EBADHEADER); 232 return(OV_EBADHEADER);
233} 233}
234 234
235/* Is this packet a vorbis ID header? */
236int vorbis_synthesis_idheader(ogg_packet *op){
237 oggpack_buffer opb;
238 char buffer[6];
239
240 if(op){
241 oggpack_readinit(&opb,op->packet);
242
243 if(!op->b_o_s)
244 return(0); /* Not the initial packet */
245
246 if(oggpack_read(&opb,8) != 1)
247 return 0; /* not an ID header */
248
249 memset(buffer,0,6);
250 _v_readstring(&opb,buffer,6);
251 if(memcmp(buffer,"vorbis",6))
252 return 0; /* not vorbis */
253
254 return 1;
255 }
256
257 return 0;
258}
259
235/* The Vorbis header is in three packets; the initial small packet in 260/* The Vorbis header is in three packets; the initial small packet in
236 the first page that identifies basic parameters, a second packet 261 the first page that identifies basic parameters, a second packet
237 with bitstream comments and a third packet that holds the 262 with bitstream comments and a third packet that holds the
diff --git a/apps/codecs/libtremor/ivorbiscodec.h b/apps/codecs/libtremor/ivorbiscodec.h
index c651aadb53..f59d79194e 100644
--- a/apps/codecs/libtremor/ivorbiscodec.h
+++ b/apps/codecs/libtremor/ivorbiscodec.h
@@ -166,6 +166,7 @@ extern int vorbis_block_clear(vorbis_block *vb);
166extern void vorbis_dsp_clear(vorbis_dsp_state *v); 166extern void vorbis_dsp_clear(vorbis_dsp_state *v);
167 167
168/* Vorbis PRIMITIVES: synthesis layer *******************************/ 168/* Vorbis PRIMITIVES: synthesis layer *******************************/
169extern int vorbis_synthesis_idheader(ogg_packet *op);
169extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, 170extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
170 ogg_packet *op); 171 ogg_packet *op);
171 172
diff --git a/apps/codecs/libtremor/vorbisfile.c b/apps/codecs/libtremor/vorbisfile.c
index ca18ecb3f7..2a805361e6 100644
--- a/apps/codecs/libtremor/vorbisfile.c
+++ b/apps/codecs/libtremor/vorbisfile.c
@@ -71,15 +71,18 @@ static long _get_data(OggVorbis_File *vf){
71} 71}
72 72
73/* save a tiny smidge of verbosity to make the code more readable */ 73/* save a tiny smidge of verbosity to make the code more readable */
74static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ 74static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){
75 if(vf->datasource){ 75 if(vf->datasource){
76 (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET); 76 if(!(vf->callbacks.seek_func)||
77 (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1)
78 return OV_EREAD;
77 vf->offset=offset; 79 vf->offset=offset;
78 ogg_sync_reset(vf->oy); 80 ogg_sync_reset(vf->oy);
79 }else{ 81 }else{
80 /* shouldn't happen unless someone writes a broken callback */ 82 /* shouldn't happen unless someone writes a broken callback */
81 return; 83 return OV_EFAULT;
82 } 84 }
85 return 0;
83} 86}
84 87
85/* The read/seek functions track absolute position within the stream */ 88/* The read/seek functions track absolute position within the stream */
@@ -146,7 +149,10 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
146 begin-=CHUNKSIZE; 149 begin-=CHUNKSIZE;
147 if(begin<0) 150 if(begin<0)
148 begin=0; 151 begin=0;
149 _seek_helper(vf,begin); 152
153 ret=_seek_helper(vf,begin);
154 if(ret)return(ret);
155
150 while(vf->offset<end){ 156 while(vf->offset<end){
151 ret=_get_next_page(vf,og,end-vf->offset); 157 ret=_get_next_page(vf,og,end-vf->offset);
152 if(ret==OV_EREAD)return(OV_EREAD); 158 if(ret==OV_EREAD)return(OV_EREAD);
@@ -158,74 +164,114 @@ static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){
158 } 164 }
159 } 165 }
160 166
161 /* we have the offset. Actually snork and hold the page now */ 167 /* In a fully compliant, non-multiplexed stream, we'll still be
162 _seek_helper(vf,offset); 168 holding the last page. In multiplexed (or noncompliant streams),
163 ret=_get_next_page(vf,og,CHUNKSIZE); 169 we will probably have to re-read the last page we saw */
164 if(ret<0) 170 if(og->header_len==0){
165 /* this shouldn't be possible */ 171 ogg_page_release(og);
166 return(OV_EFAULT); 172 ret=_seek_helper(vf,offset);
173 if(ret)return(ret);
174
175 ret=_get_next_page(vf,og,CHUNKSIZE);
176 if(ret<0)
177 /* this shouldn't be possible */
178 return(OV_EFAULT);
179 }
167 180
168 return(offset); 181 return(offset);
169} 182}
170 183
171/* finds each bitstream link one at a time using a bisection search 184static void _add_serialno(ogg_page *og,ogg_uint32_t **serialno_list, int *n){
172 (has to begin by knowing the offset of the lb's initial page). 185 long s = ogg_page_serialno(og);
173 Recurses for each link so it can alloc the link storage after 186 (*n)++;
174 finding them all, then unroll and fill the cache at the same time */ 187
175static int _bisect_forward_serialno(OggVorbis_File *vf, 188 if(*serialno_list){
176 ogg_int64_t begin, 189 *serialno_list = _ogg_realloc(*serialno_list, sizeof(**serialno_list)*(*n));
177 ogg_int64_t searched, 190 }else{
178 ogg_int64_t end, 191 *serialno_list = _ogg_malloc(sizeof(**serialno_list));
179 ogg_uint32_t currentno, 192 }
180 long m){ 193
181 ogg_int64_t endsearched=end; 194 (*serialno_list)[(*n)-1] = s;
182 ogg_int64_t next=end; 195}
196
197/* returns nonzero if found */
198static int _lookup_serialno(long s, ogg_uint32_t *serialno_list, int n){
199 if(serialno_list){
200 while(n--){
201 if(*serialno_list == (ogg_uint32_t) s) return 1;
202 serialno_list++;
203 }
204 }
205 return 0;
206}
207
208static int _lookup_page_serialno(ogg_page *og, ogg_uint32_t *serialno_list, int n){
209 long s = ogg_page_serialno(og);
210 return _lookup_serialno(s,serialno_list,n);
211}
212
213/* performs the same search as _get_prev_page, but prefers pages of
214 the specified serial number. If a page of the specified serialno is
215 spotted during the seek-back-and-read-forward, it will return the
216 info of last page of the matching serial number instead of the very
217 last page. If no page of the specified serialno is seen, it will
218 return the info of last page and alter *serialno. */
219static ogg_int64_t _get_prev_page_serial(OggVorbis_File *vf,
220 ogg_uint32_t *serial_list, int serial_n,
221 int *serialno, ogg_int64_t *granpos){
183 ogg_page og={0,0,0,0}; 222 ogg_page og={0,0,0,0};
223 ogg_int64_t begin=vf->offset;
224 ogg_int64_t end=begin;
184 ogg_int64_t ret; 225 ogg_int64_t ret;
185 226
186 /* the below guards against garbage seperating the last and 227 ogg_int64_t prefoffset=-1;
187 first pages of two links. */ 228 ogg_int64_t offset=-1;
188 while(searched<endsearched){ 229 ogg_uint32_t ret_serialno=-1;
189 ogg_int64_t bisect; 230 ogg_int64_t ret_gran=-1;
190 231
191 if(endsearched-searched<CHUNKSIZE){ 232 while(offset==-1){
192 bisect=searched; 233 begin-=CHUNKSIZE;
193 }else{ 234 if(begin<0)
194 bisect=(searched+endsearched)/2; 235 begin=0;
195 } 236
196 237 ret=_seek_helper(vf,begin);
197 _seek_helper(vf,bisect); 238 if(ret)return(ret);
198 ret=_get_next_page(vf,&og,-1); 239
199 if(ret==OV_EREAD)return(OV_EREAD); 240 while(vf->offset<end){
200 if(ret<0 || ogg_page_serialno(&og)!=currentno){ 241 ret=_get_next_page(vf,&og,end-vf->offset);
201 endsearched=bisect; 242 if(ret==OV_EREAD)return(OV_EREAD);
202 if(ret>=0)next=ret; 243 if(ret<0){
203 }else{ 244 ogg_page_release(&og);
204 searched=ret+og.header_len+og.body_len; 245 break;
246 }else{
247 ret_serialno=ogg_page_serialno(&og);
248 ret_gran=ogg_page_granulepos(&og);
249 offset=ret;
250 ogg_page_release(&og);
251
252 if(ret_serialno == (ogg_uint32_t) *serialno){
253 prefoffset=ret;
254 *granpos=ret_gran;
255 }
256
257 if(!_lookup_serialno(ret_serialno,serial_list,serial_n)){
258 /* we fell off the end of the link, which means we seeked
259 back too far and shouldn't have been looking in that link
260 to begin with. If we found the preferred serial number,
261 forget that we saw it. */
262 prefoffset=-1;
263 }
264 }
205 } 265 }
206 ogg_page_release(&og);
207 } 266 }
208 267
209 _seek_helper(vf,next); 268 /* we're not interested in the page... just the serialno and granpos. */
210 ret=_get_next_page(vf,&og,-1); 269 if(prefoffset>=0)return(prefoffset);
211 if(ret==OV_EREAD)return(OV_EREAD); 270
212 271 *serialno = ret_serialno;
213 if(searched>=end || ret<0){ 272 *granpos = ret_gran;
214 ogg_page_release(&og); 273 return(offset);
215 vf->links=m+1; 274
216 vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets));
217 vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos));
218 vf->offsets[m+1]=searched;
219 }else{
220 ret=_bisect_forward_serialno(vf,next,vf->offset,
221 end,ogg_page_serialno(&og),m+1);
222 ogg_page_release(&og);
223 if(ret==OV_EREAD)return(OV_EREAD);
224 }
225
226 vf->offsets[m]=begin;
227 vf->serialnos[m]=currentno;
228 return(0);
229} 275}
230 276
231/* uses the local ogg_stream storage in vf; this is important for 277/* uses the local ogg_stream storage in vf; this is important for
@@ -235,11 +281,13 @@ static int _bisect_forward_serialno(OggVorbis_File *vf,
235static int _fetch_headers(OggVorbis_File *vf, 281static int _fetch_headers(OggVorbis_File *vf,
236 vorbis_info *vi, 282 vorbis_info *vi,
237 vorbis_comment *vc, 283 vorbis_comment *vc,
238 ogg_uint32_t *serialno, 284 ogg_uint32_t **serialno_list,
285 int *serialno_n,
239 ogg_page *og_ptr){ 286 ogg_page *og_ptr){
240 ogg_page og={0,0,0,0}; 287 ogg_page og={0,0,0,0};
241 ogg_packet op={0,0,0,0,0,0}; 288 ogg_packet op={0,0,0,0,0,0};
242 int i,ret; 289 int i,ret;
290 int allbos=0;
243 291
244 if(!og_ptr){ 292 if(!og_ptr){
245 ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); 293 ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE);
@@ -248,41 +296,121 @@ static int _fetch_headers(OggVorbis_File *vf,
248 og_ptr=&og; 296 og_ptr=&og;
249 } 297 }
250 298
251 ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
252 if(serialno)*serialno=vf->os->serialno;
253 vf->ready_state=STREAMSET;
254
255 /* extract the initial header from the first page and verify that the
256 Ogg bitstream is in fact Vorbis data */
257
258 vorbis_info_init(vi); 299 vorbis_info_init(vi);
259 vorbis_comment_init(vc); 300 vorbis_comment_init(vc);
260 301 vf->ready_state=OPENED;
261 i=0; 302
262 while(i<3){ 303 /* extract the serialnos of all BOS pages + the first set of vorbis
263 ogg_stream_pagein(vf->os,og_ptr); 304 headers we see in the link */
264 while(i<3){ 305
265 int result=ogg_stream_packetout(vf->os,&op); 306 while(ogg_page_bos(og_ptr)){
266 if(result==0)break; 307 if(serialno_list){
267 if(result==-1){ 308 if(_lookup_page_serialno(og_ptr,*serialno_list,*serialno_n)){
309 /* a dupe serialnumber in an initial header packet set == invalid stream */
310 if(*serialno_list)_ogg_free(*serialno_list);
311 *serialno_list=0;
312 *serialno_n=0;
268 ret=OV_EBADHEADER; 313 ret=OV_EBADHEADER;
269 goto bail_header; 314 goto bail_header;
270 } 315 }
271 if((ret=vorbis_synthesis_headerin(vi,vc,&op))){ 316
272 goto bail_header; 317 _add_serialno(og_ptr,serialno_list,serialno_n);
318 }
319
320 if(vf->ready_state<STREAMSET){
321 /* we don't have a vorbis stream in this link yet, so begin
322 prospective stream setup. We need a stream to get packets */
323 ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr));
324 ogg_stream_pagein(vf->os,og_ptr);
325
326 if(ogg_stream_packetout(vf->os,&op) > 0 &&
327 vorbis_synthesis_idheader(&op)){
328 /* vorbis header; continue setup */
329 vf->ready_state=STREAMSET;
330 if((ret=vorbis_synthesis_headerin(vi,vc,&op))){
331 ret=OV_EBADHEADER;
332 goto bail_header;
333 }
273 } 334 }
274 i++;
275 } 335 }
276 if(i<3) 336
277 if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ 337 /* get next page */
278 ret=OV_EBADHEADER; 338 {
339 ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE);
340 if(llret==OV_EREAD){
341 ret=OV_EREAD;
342 goto bail_header;
343 }
344 if(llret<0){
345 ret=OV_ENOTVORBIS;
279 goto bail_header; 346 goto bail_header;
280 } 347 }
348
349 /* if this page also belongs to our vorbis stream, submit it and break */
350 if(vf->ready_state==STREAMSET &&
351 (ogg_uint32_t) vf->os->serialno == ogg_page_serialno(og_ptr)){
352 ogg_stream_pagein(vf->os,og_ptr);
353 break;
354 }
355 }
281 } 356 }
282 357
283 ogg_packet_release(&op); 358 if(vf->ready_state!=STREAMSET){
284 ogg_page_release(&og); 359 ret = OV_ENOTVORBIS;
285 return 0; 360 goto bail_header;
361 }
362
363 while(1){
364
365 i=0;
366 while(i<2){ /* get a page loop */
367
368 while(i<2){ /* get a packet loop */
369
370 int result=ogg_stream_packetout(vf->os,&op);
371 if(result==0)break;
372 if(result==-1){
373 ret=OV_EBADHEADER;
374 goto bail_header;
375 }
376
377 if((ret=vorbis_synthesis_headerin(vi,vc,&op)))
378 goto bail_header;
379
380 i++;
381 }
382
383 while(i<2){
384 if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){
385 ret=OV_EBADHEADER;
386 goto bail_header;
387 }
388
389 /* if this page belongs to the correct stream, go parse it */
390 if((ogg_uint32_t) vf->os->serialno == ogg_page_serialno(og_ptr)){
391 ogg_stream_pagein(vf->os,og_ptr);
392 break;
393 }
394
395 /* if we never see the final vorbis headers before the link
396 ends, abort */
397 if(ogg_page_bos(og_ptr)){
398 if(allbos){
399 ret = OV_EBADHEADER;
400 goto bail_header;
401 }else
402 allbos=1;
403 }
404
405 /* otherwise, keep looking */
406 }
407 }
408
409 ogg_packet_release(&op);
410 ogg_page_release(&og);
411
412 return 0;
413 }
286 414
287 bail_header: 415 bail_header:
288 ogg_packet_release(&op); 416 ogg_packet_release(&op);
@@ -294,168 +422,248 @@ static int _fetch_headers(OggVorbis_File *vf,
294 return ret; 422 return ret;
295} 423}
296 424
297/* last step of the OggVorbis_File initialization; get all the 425/* Starting from current cursor position, get initial PCM offset of
298 vorbis_info structs and PCM positions. Only called by the seekable 426 next page. Consumes the page in the process without decoding
299 initialization (local stream storage is hacked slightly; pay 427 audio, however this is only called during stream parsing upon
300 attention to how that's done) */ 428 seekable open. */
429static ogg_int64_t _initial_pcmoffset(OggVorbis_File *vf, vorbis_info *vi){
430 ogg_page og={0,0,0,0};
431 ogg_int64_t accumulated=0,pos;
432 long lastblock=-1;
433 int result;
434 int serialno = vf->os->serialno;
301 435
302/* this is void and does not propogate errors up because we want to be 436 while(1){
303 able to open and use damaged bitstreams as well as we can. Just 437 ogg_packet op={0,0,0,0,0,0};
304 watch out for missing information for links in the OggVorbis_File
305 struct */
306static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){
307 ogg_page og={0,0,0,0};
308 int i;
309 ogg_int64_t ret;
310
311 vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
312 vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
313 vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
314 vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
315
316 for(i=0;i<vf->links;i++){
317 if(i==0){
318 /* we already grabbed the initial header earlier. Just set the offset */
319 vf->dataoffsets[i]=dataoffset;
320 _seek_helper(vf,dataoffset);
321 438
322 }else{ 439 if(_get_next_page(vf,&og,-1)<0)
440 break; /* should not be possible unless the file is truncated/mangled */
323 441
324 /* seek to the location of the initial header */ 442 if(ogg_page_bos(&og)) break;
443 if(ogg_page_serialno(&og)!=(ogg_uint32_t) serialno) continue;
444 pos=ogg_page_granulepos(&og);
325 445
326 _seek_helper(vf,vf->offsets[i]); 446 /* count blocksizes of all frames in the page */
327 if(_fetch_headers(vf,vf->vi+i,vf->vc+i,NULL,NULL)<0){ 447 ogg_stream_pagein(vf->os,&og);
328 vf->dataoffsets[i]=-1; 448 while((result=ogg_stream_packetout(vf->os,&op))){
329 }else{ 449 if(result>0){ /* ignore holes */
330 vf->dataoffsets[i]=vf->offset; 450 long thisblock=vorbis_packet_blocksize(vi,&op);
451 if(lastblock!=-1)
452 accumulated+=(lastblock+thisblock)>>2;
453 lastblock=thisblock;
331 } 454 }
332 } 455 }
456 ogg_packet_release(&op);
333 457
334 /* fetch beginning PCM offset */ 458 if(pos!=-1){
459 /* pcm offset of last packet on the first audio page */
460 accumulated= pos-accumulated;
461 break;
462 }
463 }
335 464
336 if(vf->dataoffsets[i]!=-1){ 465 /* less than zero? This is a stream with samples trimmed off
337 ogg_int64_t accumulated=0,pos; 466 the beginning, a normal occurrence; set the offset to zero */
338 long lastblock=-1; 467 if(accumulated<0)accumulated=0;
339 int result;
340 468
341 ogg_stream_reset_serialno(vf->os,vf->serialnos[i]); 469 ogg_page_release(&og);
470 return accumulated;
471}
342 472
343 while(1){
344 ogg_packet op={0,0,0,0,0,0};
345 473
346 ret=_get_next_page(vf,&og,-1); 474/* finds each bitstream link one at a time using a bisection search
347 if(ret<0) 475 (has to begin by knowing the offset of the lb's initial page).
348 /* this should not be possible unless the file is 476 Recurses for each link so it can alloc the link storage after
349 truncated/mangled */ 477 finding them all, then unroll and fill the cache at the same time */
350 break; 478static int _bisect_forward_serialno(OggVorbis_File *vf,
351 479 ogg_int64_t begin,
352 if(ogg_page_serialno(&og)!=vf->serialnos[i]) 480 ogg_int64_t searched,
353 break; 481 ogg_int64_t end,
354 482 ogg_int64_t endgran,
355 pos=ogg_page_granulepos(&og); 483 int endserial,
484 ogg_uint32_t *currentno_list,
485 int currentnos,
486 long m){
356 487
357 /* count blocksizes of all frames in the page */ 488 ogg_int64_t pcmoffset;
358 ogg_stream_pagein(vf->os,&og); 489 ogg_int64_t dataoffset=searched;
359 while((result=ogg_stream_packetout(vf->os,&op))){ 490 ogg_int64_t endsearched=end;
360 if(result>0){ /* ignore holes */ 491 ogg_int64_t next=end;
361 long thisblock=vorbis_packet_blocksize(vf->vi+i,&op); 492 ogg_int64_t searchgran=-1;
362 if(lastblock!=-1) 493 ogg_int64_t ret,last;
363 accumulated+=(lastblock+thisblock)>>2; 494 int serialno = vf->os->serialno;
364 lastblock=thisblock; 495
365 } 496 /* invariants:
366 } 497 we have the headers and serialnos for the link beginning at 'begin'
367 ogg_packet_release(&op); 498 we have the offset and granpos of the last page in the file (potentially
499 not a page we care about)
500 */
368 501
369 if(pos!=-1){ 502 /* Is the last page in our list of current serialnumbers? */
370 /* pcm offset of last packet on the first audio page */ 503 if(_lookup_serialno(endserial,currentno_list,currentnos)){
371 accumulated= pos-accumulated; 504
372 break; 505 /* last page is in the starting serialno list, so we've bisected
373 } 506 down to (or just started with) a single link. Now we need to
507 find the last vorbis page belonging to the first vorbis stream
508 for this link. */
509
510 while(endserial != serialno){
511 endserial = serialno;
512 vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&endserial,&endgran);
513 }
514
515 vf->links=m+1;
516 if(vf->offsets)_ogg_free(vf->offsets);
517 if(vf->serialnos)_ogg_free(vf->serialnos);
518 if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
519
520 vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets));
521 vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
522 vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
523 vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos));
524 vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
525 vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths));
526
527 vf->offsets[m+1]=end;
528 vf->offsets[m]=begin;
529 vf->pcmlengths[m*2+1]=endgran;
530
531 }else{
532
533 ogg_uint32_t *next_serialno_list=NULL;
534 int next_serialnos=0;
535 vorbis_info vi;
536 vorbis_comment vc;
537
538 /* the below guards against garbage seperating the last and
539 first pages of two links. */
540 while(searched<endsearched){
541 ogg_page og={0,0,0,0};
542 ogg_int64_t bisect;
543
544 if(endsearched-searched<CHUNKSIZE){
545 bisect=searched;
546 }else{
547 bisect=(searched+endsearched)/2;
374 } 548 }
375 549
376 /* less than zero? This is a stream with samples trimmed off 550 ret=_seek_helper(vf,bisect);
377 the beginning, a normal occurrence; set the offset to zero */ 551 if(ret)return(ret);
378 if(accumulated<0)accumulated=0;
379 552
380 vf->pcmlengths[i*2]=accumulated; 553 last=_get_next_page(vf,&og,-1);
554 if(last==OV_EREAD)return(OV_EREAD);
555 if(last<0 || !_lookup_page_serialno(&og,currentno_list,currentnos)){
556 endsearched=bisect;
557 if(last>=0)next=last;
558 }else{
559 searched=last+og.header_len+og.body_len;
560 }
561 ogg_page_release(&og);
381 } 562 }
382 563
383 /* get the PCM length of this link. To do this, 564 /* Bisection point found */
384 get the last page of the stream */
385 {
386 ogg_int64_t end=vf->offsets[i+1];
387 _seek_helper(vf,end);
388 565
389 while(1){ 566 /* for the time being, fetch end PCM offset the simple way */
390 ret=_get_prev_page(vf,&og); 567 {
391 if(ret<0){ 568 int testserial = serialno+1;
392 /* this should not be possible */ 569 vf->offset = next;
393 vorbis_info_clear(vf->vi+i); 570 while(testserial != serialno){
394 vorbis_comment_clear(vf->vc+i); 571 testserial = serialno;
395 break; 572 vf->offset=_get_prev_page_serial(vf,currentno_list,currentnos,&testserial,&searchgran);
396 }
397 if(ogg_page_granulepos(&og)!=-1){
398 vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2];
399 break;
400 }
401 vf->offset=ret;
402 } 573 }
403 } 574 }
575
576 if(vf->offset!=next){
577 ret=_seek_helper(vf,next);
578 if(ret)return(ret);
579 }
580
581 ret=_fetch_headers(vf,&vi,&vc,&next_serialno_list,&next_serialnos,NULL);
582 if(ret)return(ret);
583 serialno = vf->os->serialno;
584 dataoffset = vf->offset;
585
586 /* this will consume a page, however the next bistection always
587 starts with a raw seek */
588 pcmoffset = _initial_pcmoffset(vf,&vi);
589
590 ret=_bisect_forward_serialno(vf,next,vf->offset,end,endgran,endserial,
591 next_serialno_list,next_serialnos,m+1);
592 if(ret)return(ret);
593
594 if(next_serialno_list)_ogg_free(next_serialno_list);
595
596 vf->offsets[m+1]=next;
597 vf->serialnos[m+1]=serialno;
598 vf->dataoffsets[m+1]=dataoffset;
599
600 vf->vi[m+1]=vi;
601 vf->vc[m+1]=vc;
602
603 vf->pcmlengths[m*2+1]=searchgran;
604 vf->pcmlengths[m*2+2]=pcmoffset;
605 vf->pcmlengths[m*2+3]-=pcmoffset;
606
404 } 607 }
405 ogg_page_release(&og); 608 return(0);
406} 609}
407 610
408static void _make_decode_ready(OggVorbis_File *vf){ 611static int _make_decode_ready(OggVorbis_File *vf){
409 if(vf->ready_state!=STREAMSET)return; 612 if(vf->ready_state>STREAMSET)return 0;
613 if(vf->ready_state<STREAMSET)return OV_EFAULT;
410 if(vf->seekable){ 614 if(vf->seekable){
411 vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link); 615 if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link))
616 return OV_EBADLINK;
412 }else{ 617 }else{
413 vorbis_synthesis_init(&vf->vd,vf->vi); 618 if(vorbis_synthesis_init(&vf->vd,vf->vi))
414 } 619 return OV_EBADLINK;
620 }
415 vorbis_block_init(&vf->vd,&vf->vb); 621 vorbis_block_init(&vf->vd,&vf->vb);
416 vf->ready_state=INITSET; 622 vf->ready_state=INITSET;
417 vf->bittrack=0; 623 vf->bittrack=0;
418 vf->samptrack=0; 624 vf->samptrack=0;
419 return; 625 return 0;
420} 626}
421 627
422static int _open_seekable2(OggVorbis_File *vf){ 628static int _open_seekable2(OggVorbis_File *vf){
423 ogg_uint32_t serialno=vf->current_serialno; 629 ogg_int64_t dataoffset=vf->dataoffsets[0],end,endgran=-1;
424 ogg_uint32_t tempserialno; 630 int endserial=vf->os->serialno;
425 ogg_int64_t dataoffset=vf->offset, end; 631 int serialno=vf->os->serialno;
426 ogg_page og={0,0,0,0};
427 632
428 /* we're partially open and have a first link header state in 633 /* we're partially open and have a first link header state in
429 storage in vf */ 634 storage in vf */
430 /* we can seek, so set out learning all about this file */
431 (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
432 vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
433
434 /* We get the offset for the last page of the physical bitstream.
435 Most OggVorbis files will contain a single logical bitstream */
436 end=_get_prev_page(vf,&og);
437 if(end<0)return(end);
438 635
439 /* more than one logical bitstream? */ 636 /* fetch initial PCM offset */
440 tempserialno=ogg_page_serialno(&og); 637 ogg_int64_t pcmoffset = _initial_pcmoffset(vf,vf->vi);
441 ogg_page_release(&og);
442 638
443 if(tempserialno!=serialno){ 639 /* we can seek, so set out learning all about this file */
640 if(vf->callbacks.seek_func && vf->callbacks.tell_func){
641 (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END);
642 vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource);
643 }else{
644 vf->offset=vf->end=-1;
645 }
444 646
445 /* Chained bitstream. Bisect-search each logical bitstream 647 /* If seek_func is implemented, tell_func must also be implemented */
446 section. Do so based on serial number only */ 648 if(vf->end==-1) return(OV_EINVAL);
447 if(_bisect_forward_serialno(vf,0,0,end+1,serialno,0)<0)return(OV_EREAD);
448 649
449 }else{ 650 /* Get the offset of the last page of the physical bitstream, or, if
651 we're lucky the last vorbis page of this link as most OggVorbis
652 files will contain a single logical bitstream */
653 end=_get_prev_page_serial(vf,vf->serialnos+2,vf->serialnos[1],&endserial,&endgran);
654 if(end<0)return(end);
450 655
451 /* Only one logical bitstream */ 656 /* now determine bitstream structure recursively */
452 if(_bisect_forward_serialno(vf,0,end,end+1,serialno,0))return(OV_EREAD); 657 if(_bisect_forward_serialno(vf,0,dataoffset,vf->offset,endgran,endserial,
658 vf->serialnos+2,vf->serialnos[1],0)<0)return(OV_EREAD);
453 659
454 } 660 vf->offsets[0]=0;
661 vf->serialnos[0]=serialno;
662 vf->dataoffsets[0]=dataoffset;
663 vf->pcmlengths[0]=pcmoffset;
664 vf->pcmlengths[1]-=pcmoffset;
455 665
456 /* the initial header memory is referenced by vf after; don't free it */ 666 return(ov_raw_seek(vf,dataoffset));
457 _prefetch_all_headers(vf,dataoffset);
458 return(ov_raw_seek(vf,0));
459} 667}
460 668
461/* clear out the current logical bitstream decoder */ 669/* clear out the current logical bitstream decoder */
@@ -490,6 +698,11 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
490 /* extract packets from page */ 698 /* extract packets from page */
491 while(1){ 699 while(1){
492 700
701 if(vf->ready_state==STREAMSET){
702 ret=_make_decode_ready(vf);
703 if(ret<0) goto cleanup;
704 }
705
493 /* process a packet if we can. If the machine isn't loaded, 706 /* process a packet if we can. If the machine isn't loaded,
494 neither is a page */ 707 neither is a page */
495 if(vf->ready_state==INITSET){ 708 if(vf->ready_state==INITSET){
@@ -497,7 +710,7 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
497 int result=ogg_stream_packetout(vf->os,&op); 710 int result=ogg_stream_packetout(vf->os,&op);
498 ogg_int64_t granulepos; 711 ogg_int64_t granulepos;
499 712
500 if(result<0){ 713 if(result==-1){
501 ret=OV_HOLE; /* hole in the data. */ 714 ret=OV_HOLE; /* hole in the data. */
502 goto cleanup; 715 goto cleanup;
503 } 716 }
@@ -553,7 +766,7 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
553 is very broken */ 766 is very broken */
554 767
555 samples=vorbis_synthesis_pcmout(&vf->vd,NULL); 768 samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
556 769
557 granulepos-=samples; 770 granulepos-=samples;
558 for(i=0;i<link;i++) 771 for(i=0;i<link;i++)
559 granulepos+=vf->pcmlengths[i*2+1]; 772 granulepos+=vf->pcmlengths[i*2+1];
@@ -569,35 +782,55 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
569 } 782 }
570 783
571 if(vf->ready_state>=OPENED){ 784 if(vf->ready_state>=OPENED){
572 int ret; 785 ogg_int64_t lret;
573 if(!readp){ 786
574 ret=0; 787 while(1){
575 goto cleanup; 788 /* the loop is not strictly necessary, but there's no sense in
576 } 789 doing the extra checks of the larger loop for the common
577 if((ret=_get_next_page(vf,&og,-1))<0){ 790 case in a multiplexed bistream where the page is simply
578 ret=OV_EOF; /* eof. leave unitialized */ 791 part of a different logical bitstream; keep reading until
579 goto cleanup; 792 we get one with the correct serialno */
580 } 793
794 if(!readp){
795 ret=0;
796 goto cleanup;
797 }
798 if((lret=_get_next_page(vf,&og,-1))<0){
799 ret=OV_EOF; /* eof. leave unitialized */
800 goto cleanup;
801 }
581 802
582 /* bitrate tracking; add the header's bytes here, the body bytes 803 /* bitrate tracking; add the header's bytes here, the body bytes
583 are done by packet above */ 804 are done by packet above */
584 vf->bittrack+=og.header_len*8; 805 vf->bittrack+=og.header_len*8;
585
586 /* has our decoding just traversed a bitstream boundary? */
587 if(vf->ready_state==INITSET){
588 if(vf->current_serialno!=ogg_page_serialno(&og)){
589 if(!spanp){
590 ret=OV_EOF;
591 goto cleanup;
592 }
593 806
594 _decode_clear(vf); 807 if(vf->ready_state==INITSET){
595 808 if(vf->current_serialno!=ogg_page_serialno(&og)){
596 if(!vf->seekable){ 809
597 vorbis_info_clear(vf->vi); 810 /* two possibilities:
598 vorbis_comment_clear(vf->vc); 811 1) our decoding just traversed a bitstream boundary
812 2) another stream is multiplexed into this logical section */
813
814 if(ogg_page_bos(&og)){
815 /* boundary case */
816 if(!spanp){
817 ret=OV_EOF;
818 goto cleanup;
819 }
820
821 _decode_clear(vf);
822
823 if(!vf->seekable){
824 vorbis_info_clear(vf->vi);
825 vorbis_comment_clear(vf->vc);
826 }
827 break;
828
829 }else
830 continue; /* possibility #2 */
599 } 831 }
600 } 832 }
833 break;
601 } 834 }
602 } 835 }
603 836
@@ -618,37 +851,40 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
618 851
619 if(vf->ready_state<STREAMSET){ 852 if(vf->ready_state<STREAMSET){
620 if(vf->seekable){ 853 if(vf->seekable){
621 vf->current_serialno=ogg_page_serialno(&og); 854 long serialno=ogg_page_serialno(&og);
622 855
623 /* match the serialno to bitstream section. We use this rather than 856 /* match the serialno to bitstream section. We use this rather than
624 offset positions to avoid problems near logical bitstream 857 offset positions to avoid problems near logical bitstream
625 boundaries */ 858 boundaries */
859
626 for(link=0;link<vf->links;link++) 860 for(link=0;link<vf->links;link++)
627 if(vf->serialnos[link]==vf->current_serialno)break; 861 if(vf->serialnos[link]==(ogg_uint32_t) serialno)break;
628 if(link==vf->links){ 862
629 ret=OV_EBADLINK; /* sign of a bogus stream. error out, 863 if(link==vf->links) continue; /* not the desired Vorbis
630 leave machine uninitialized */ 864 bitstream section; keep
631 goto cleanup; 865 trying */
632 } 866
633 867 vf->current_serialno=serialno;
634 vf->current_link=link; 868 vf->current_link=link;
635 869
636 ogg_stream_reset_serialno(vf->os,vf->current_serialno); 870 ogg_stream_reset_serialno(vf->os,vf->current_serialno);
637 vf->ready_state=STREAMSET; 871 vf->ready_state=STREAMSET;
638 872
639 }else{ 873 }else{
640 /* we're streaming */ 874 /* we're streaming */
641 /* fetch the three header packets, build the info struct */ 875 /* fetch the three header packets, build the info struct */
642 876
643 int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og); 877 int ret=_fetch_headers(vf,vf->vi,vf->vc,NULL,NULL,&og);
644 if(ret) goto cleanup; 878 if(ret) goto cleanup;
879 vf->current_serialno=vf->os->serialno;
645 vf->current_link++; 880 vf->current_link++;
646 link=0; 881 link=0;
647 } 882 }
648 } 883 }
649
650 _make_decode_ready(vf);
651 } 884 }
885
886 /* the buffered page is the data we want, and we're ready for it;
887 add it to the stream state */
652 ogg_stream_pagein(vf->os,&og); 888 ogg_stream_pagein(vf->os,&og);
653 } 889 }
654 cleanup: 890 cleanup:
@@ -660,6 +896,8 @@ static int _fetch_and_process_packet(OggVorbis_File *vf,
660static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, 896static int _ov_open1(void *f,OggVorbis_File *vf,char *initial,
661 long ibytes, ov_callbacks callbacks){ 897 long ibytes, ov_callbacks callbacks){
662 int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1); 898 int offsettest=(f?callbacks.seek_func(f,0,SEEK_CUR):-1);
899 ogg_uint32_t *serialno_list=NULL;
900 int serialno_list_size=0;
663 int ret; 901 int ret;
664 902
665 memset(vf,0,sizeof(*vf)); 903 memset(vf,0,sizeof(*vf));
@@ -671,10 +909,10 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial,
671 909
672 /* perhaps some data was previously read into a buffer for testing 910 /* perhaps some data was previously read into a buffer for testing
673 against other stream types. Allow initialization from this 911 against other stream types. Allow initialization from this
674 previously read data (as we may be reading from a non-seekable 912 previously read data (especially as we may be reading from a
675 stream) */ 913 non-seekable stream) */
676 if(initial){ 914 if(initial){
677 char *buffer=(char *)ogg_sync_bufferin(vf->oy,ibytes); 915 unsigned char *buffer=ogg_sync_bufferin(vf->oy,ibytes);
678 memcpy(buffer,initial,ibytes); 916 memcpy(buffer,initial,ibytes);
679 ogg_sync_wrote(vf->oy,ibytes); 917 ogg_sync_wrote(vf->oy,ibytes);
680 } 918 }
@@ -689,12 +927,29 @@ static int _ov_open1(void *f,OggVorbis_File *vf,char *initial,
689 vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); 927 vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc));
690 vf->os=ogg_stream_create(-1); /* fill in the serialno later */ 928 vf->os=ogg_stream_create(-1); /* fill in the serialno later */
691 929
692 /* Try to fetch the headers, maintaining all the storage */ 930 /* Fetch all BOS pages, store the vorbis header and all seen serial
693 if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){ 931 numbers, load subsequent vorbis setup headers */
932 if((ret=_fetch_headers(vf,vf->vi,vf->vc,&serialno_list,&serialno_list_size,NULL))<0){
694 vf->datasource=NULL; 933 vf->datasource=NULL;
695 ov_clear(vf); 934 ov_clear(vf);
696 }else if(vf->ready_state < PARTOPEN) 935 }else{
936 /* serial number list for first link needs to be held somewhere
937 for second stage of seekable stream open; this saves having to
938 seek/reread first link's serialnumber data then. */
939 vf->serialnos=_ogg_calloc(serialno_list_size+2,sizeof(*vf->serialnos));
940 vf->serialnos[0]=vf->current_serialno;
941 vf->serialnos[1]=serialno_list_size;
942 memcpy(vf->serialnos+2,serialno_list,serialno_list_size*sizeof(*vf->serialnos));
943
944 vf->offsets=_ogg_calloc(1,sizeof(*vf->offsets));
945 vf->dataoffsets=_ogg_calloc(1,sizeof(*vf->dataoffsets));
946 vf->offsets[0]=0;
947 vf->dataoffsets[0]=vf->offset;
948 vf->current_serialno=vf->os->serialno;
949
697 vf->ready_state=PARTOPEN; 950 vf->ready_state=PARTOPEN;
951 }
952 if(serialno_list)_ogg_free(serialno_list);
698 return(ret); 953 return(ret);
699} 954}
700 955
@@ -735,7 +990,8 @@ int ov_clear(OggVorbis_File *vf){
735 if(vf->offsets)_ogg_free(vf->offsets); 990 if(vf->offsets)_ogg_free(vf->offsets);
736 ogg_sync_destroy(vf->oy); 991 ogg_sync_destroy(vf->oy);
737 992
738 if(vf->datasource)(vf->callbacks.close_func)(vf->datasource); 993 if(vf->datasource && vf->callbacks.close_func)
994 (vf->callbacks.close_func)(vf->datasource);
739 memset(vf,0,sizeof(*vf)); 995 memset(vf,0,sizeof(*vf));
740 } 996 }
741#ifdef DEBUG_LEAKS 997#ifdef DEBUG_LEAKS
@@ -814,7 +1070,8 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
814 ogg_stream_state *work_os=NULL; 1070 ogg_stream_state *work_os=NULL;
815 ogg_page og={0,0,0,0}; 1071 ogg_page og={0,0,0,0};
816 ogg_packet op={0,0,0,0,0,0}; 1072 ogg_packet op={0,0,0,0,0,0};
817 1073 int ret;
1074
818 if(vf->ready_state<OPENED)return(OV_EINVAL); 1075 if(vf->ready_state<OPENED)return(OV_EINVAL);
819 if(!vf->seekable) 1076 if(!vf->seekable)
820 return(OV_ENOSEEK); /* don't dump machine if we can't seek */ 1077 return(OV_ENOSEEK); /* don't dump machine if we can't seek */
@@ -830,7 +1087,8 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
830 vf->current_serialno); /* must set serialno */ 1087 vf->current_serialno); /* must set serialno */
831 vorbis_synthesis_restart(&vf->vd); 1088 vorbis_synthesis_restart(&vf->vd);
832 1089
833 _seek_helper(vf,pos); 1090 ret=_seek_helper(vf,pos);
1091 if(ret)goto seek_error;
834 1092
835 /* we need to make sure the pcm_offset is set, but we don't want to 1093 /* we need to make sure the pcm_offset is set, but we don't want to
836 advance the raw cursor past good packets just to get to the first 1094 advance the raw cursor past good packets just to get to the first
@@ -851,7 +1109,9 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
851 int lastblock=0; 1109 int lastblock=0;
852 int accblock=0; 1110 int accblock=0;
853 int thisblock; 1111 int thisblock;
854 int eosflag=0; 1112 int lastflag=0;
1113 int firstflag=0;
1114 ogg_int64_t pagepos=-1;
855 1115
856 work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */ 1116 work_os=ogg_stream_create(vf->current_serialno); /* get the memory ready */
857 while(1){ 1117 while(1){
@@ -868,7 +1128,14 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
868 thisblock=0; 1128 thisblock=0;
869 }else{ 1129 }else{
870 1130
871 if(eosflag) 1131 /* We can't get a guaranteed correct pcm position out of the
1132 last page in a stream because it might have a 'short'
1133 granpos, which can only be detected in the presence of a
1134 preceeding page. However, if the last page is also the first
1135 page, the granpos rules of a first page take precedence. Not
1136 only that, but for first==last, the EOS page must be treated
1137 as if its a normal first page for the stream to open/play. */
1138 if(lastflag && !firstflag)
872 ogg_stream_packetout(vf->os,NULL); 1139 ogg_stream_packetout(vf->os,NULL);
873 else 1140 else
874 if(lastblock)accblock+=(lastblock+thisblock)>>2; 1141 if(lastblock)accblock+=(lastblock+thisblock)>>2;
@@ -882,6 +1149,7 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
882 for(i=0;i<link;i++) 1149 for(i=0;i<link;i++)
883 granulepos+=vf->pcmlengths[i*2+1]; 1150 granulepos+=vf->pcmlengths[i*2+1];
884 vf->pcm_offset=granulepos-accblock; 1151 vf->pcm_offset=granulepos-accblock;
1152 if(vf->pcm_offset<0)vf->pcm_offset=0;
885 break; 1153 break;
886 } 1154 }
887 lastblock=thisblock; 1155 lastblock=thisblock;
@@ -892,7 +1160,8 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
892 } 1160 }
893 1161
894 if(!lastblock){ 1162 if(!lastblock){
895 if(_get_next_page(vf,&og,-1)<0){ 1163 pagepos=_get_next_page(vf,&og,-1);
1164 if(pagepos<0){
896 vf->pcm_offset=ov_pcm_total(vf,-1); 1165 vf->pcm_offset=ov_pcm_total(vf,-1);
897 break; 1166 break;
898 } 1167 }
@@ -903,34 +1172,42 @@ int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){
903 } 1172 }
904 1173
905 /* has our decoding just traversed a bitstream boundary? */ 1174 /* has our decoding just traversed a bitstream boundary? */
906 if(vf->ready_state>=STREAMSET) 1175 if(vf->ready_state>=STREAMSET){
907 if(vf->current_serialno!=ogg_page_serialno(&og)){ 1176 if(vf->current_serialno!=ogg_page_serialno(&og)){
908 _decode_clear(vf); /* clear out stream state */ 1177 /* two possibilities:
909 ogg_stream_destroy(work_os); 1178 1) our decoding just traversed a bitstream boundary
1179 2) another stream is multiplexed into this logical section? */
1180
1181 if(ogg_page_bos(&og)){
1182 /* we traversed */
1183 _decode_clear(vf); /* clear out stream state */
1184 ogg_stream_destroy(work_os);
1185 } /* else, do nothing; next loop will scoop another page */
910 } 1186 }
1187 }
911 1188
912 if(vf->ready_state<STREAMSET){ 1189 if(vf->ready_state<STREAMSET){
913 int link; 1190 int link;
914 1191 long serialno = ogg_page_serialno(&og);
915 vf->current_serialno=ogg_page_serialno(&og); 1192
916 for(link=0;link<vf->links;link++) 1193 for(link=0;link<vf->links;link++)
917 if(vf->serialnos[link]==vf->current_serialno)break; 1194 if(vf->serialnos[link]==vf->current_serialno)break;
918 if(link==vf->links) 1195
919 goto seek_error; /* sign of a bogus stream. error out, 1196 if(link==vf->links) continue; /* not the desired Vorbis
920 leave machine uninitialized */ 1197 bitstream section; keep
921 1198 trying */
922 vf->current_link=link; 1199 vf->current_link=link;
923 1200 vf->current_serialno=serialno;
924 ogg_stream_reset_serialno(vf->os,vf->current_serialno); 1201 ogg_stream_reset_serialno(vf->os,vf->current_serialno);
925 ogg_stream_reset_serialno(work_os,vf->current_serialno); 1202 ogg_stream_reset_serialno(work_os,vf->current_serialno);
926 vf->ready_state=STREAMSET; 1203 vf->ready_state=STREAMSET;
927 1204 firstflag=(pagepos<=vf->dataoffsets[link]);
928 } 1205 }
929 1206
930 { 1207 {
931 ogg_page dup; 1208 ogg_page dup;
932 ogg_page_dup(&dup,&og); 1209 ogg_page_dup(&dup,&og);
933 eosflag=ogg_page_eos(&og); 1210 lastflag=ogg_page_eos(&og);
934 ogg_stream_pagein(vf->os,&og); 1211 ogg_stream_pagein(vf->os,&og);
935 ogg_stream_pagein(work_os,&dup); 1212 ogg_stream_pagein(work_os,&dup);
936 } 1213 }
@@ -1055,13 +1332,11 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
1055 { 1332 {
1056 1333
1057 /* seek */ 1334 /* seek */
1058 _seek_helper(vf,best); 1335 result=_seek_helper(vf,best);
1059 vf->pcm_offset=-1; 1336 vf->pcm_offset=-1;
1060 1337 if(result) goto seek_error;
1061 if(_get_next_page(vf,&og,-1)<0){ 1338 result=_get_next_page(vf,&og,-1);
1062 ogg_page_release(&og); 1339 if(result<0) goto seek_error;
1063 return(OV_EOF); /* shouldn't happen */
1064 }
1065 1340
1066 if(link!=vf->current_link){ 1341 if(link!=vf->current_link){
1067 /* Different link; dump entire decode machine */ 1342 /* Different link; dump entire decode machine */
@@ -1087,13 +1362,15 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
1087 get one with a granulepos or without the 'continued' flag 1362 get one with a granulepos or without the 'continued' flag
1088 set. Then just use raw_seek for simplicity. */ 1363 set. Then just use raw_seek for simplicity. */
1089 1364
1090 _seek_helper(vf,best); 1365 result=_seek_helper(vf,best);
1091 1366 if(result<0) goto seek_error;
1367
1092 while(1){ 1368 while(1){
1093 result=_get_prev_page(vf,&og); 1369 result=_get_prev_page(vf,&og);
1094 if(result<0) goto seek_error; 1370 if(result<0) goto seek_error;
1095 if(ogg_page_granulepos(&og)>-1 || 1371 if(ogg_page_serialno(&og)==vf->current_serialno &&
1096 !ogg_page_continued(&og)){ 1372 (ogg_page_granulepos(&og)>-1 ||
1373 !ogg_page_continued(&og))){
1097 return ov_raw_seek(vf,result); 1374 return ov_raw_seek(vf,result);
1098 } 1375 }
1099 vf->offset=result; 1376 vf->offset=result;
@@ -1143,7 +1420,6 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){
1143int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ 1420int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
1144 ogg_packet op={0,0,0,0,0,0}; 1421 ogg_packet op={0,0,0,0,0,0};
1145 ogg_page og={0,0,0,0}; 1422 ogg_page og={0,0,0,0};
1146
1147 int thisblock,lastblock=0; 1423 int thisblock,lastblock=0;
1148 int ret=ov_pcm_seek_page(vf,pos); 1424 int ret=ov_pcm_seek_page(vf,pos);
1149 if(ret<0)return(ret); 1425 if(ret<0)return(ret);
@@ -1192,24 +1468,26 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
1192 1468
1193 /* suck in a new page */ 1469 /* suck in a new page */
1194 if(_get_next_page(vf,&og,-1)<0)break; 1470 if(_get_next_page(vf,&og,-1)<0)break;
1195 if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf); 1471 if(ogg_page_bos(&og))_decode_clear(vf);
1196 1472
1197 if(vf->ready_state<STREAMSET){ 1473 if(vf->ready_state<STREAMSET){
1474 long serialno=ogg_page_serialno(&og);
1198 int link; 1475 int link;
1199 1476
1200 vf->current_serialno=ogg_page_serialno(&og);
1201 for(link=0;link<vf->links;link++) 1477 for(link=0;link<vf->links;link++)
1202 if(vf->serialnos[link]==vf->current_serialno)break; 1478 if(vf->serialnos[link]==(ogg_uint32_t) serialno)break;
1203 if(link==vf->links){ 1479 if(link==vf->links) continue;
1480 vf->current_link=link;
1481
1482 vf->ready_state=STREAMSET;
1483 vf->current_serialno=ogg_page_serialno(&og);
1484 ogg_stream_reset_serialno(vf->os,serialno);
1485 ret=_make_decode_ready(vf);
1486 if(ret){
1204 ogg_page_release(&og); 1487 ogg_page_release(&og);
1205 ogg_packet_release(&op); 1488 ogg_packet_release(&op);
1206 return(OV_EBADLINK); 1489 return ret;
1207 } 1490 }
1208 vf->current_link=link;
1209
1210 ogg_stream_reset_serialno(vf->os,vf->current_serialno);
1211 vf->ready_state=STREAMSET;
1212 _make_decode_ready(vf);
1213 lastblock=0; 1491 lastblock=0;
1214 } 1492 }
1215 1493
@@ -1245,20 +1523,23 @@ int ov_time_seek(OggVorbis_File *vf,ogg_int64_t milliseconds){
1245 /* translate time to PCM position and call ov_pcm_seek */ 1523 /* translate time to PCM position and call ov_pcm_seek */
1246 1524
1247 int link=-1; 1525 int link=-1;
1248 ogg_int64_t pcm_total=ov_pcm_total(vf,-1); 1526 ogg_int64_t pcm_total=0;
1249 ogg_int64_t time_total=ov_time_total(vf,-1); 1527 ogg_int64_t time_total=0;
1250 1528
1251 if(vf->ready_state<OPENED)return(OV_EINVAL); 1529 if(vf->ready_state<OPENED)return(OV_EINVAL);
1252 if(!vf->seekable)return(OV_ENOSEEK); 1530 if(!vf->seekable)return(OV_ENOSEEK);
1253 if(milliseconds<0 || milliseconds>time_total)return(OV_EINVAL); 1531 if(milliseconds<0)return(OV_EINVAL);
1254 1532
1255 /* which bitstream section does this time offset occur in? */ 1533 /* which bitstream section does this time offset occur in? */
1256 for(link=vf->links-1;link>=0;link--){ 1534 for(link=0;link<vf->links;link++){
1257 pcm_total-=vf->pcmlengths[link*2+1]; 1535 ogg_int64_t addsec = ov_time_total(vf,link);
1258 time_total-=ov_time_total(vf,link); 1536 if(milliseconds<time_total+addsec)break;
1259 if(milliseconds>=time_total)break; 1537 time_total+=addsec;
1538 pcm_total+=vf->pcmlengths[link*2+1];
1260 } 1539 }
1261 1540
1541 if(link==vf->links)return(OV_EINVAL);
1542
1262 /* enough information to convert time offset to pcm offset */ 1543 /* enough information to convert time offset to pcm offset */
1263 { 1544 {
1264 ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; 1545 ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000;
@@ -1273,8 +1554,13 @@ ogg_int64_t ov_raw_tell(OggVorbis_File *vf){
1273 return(vf->offset); 1554 return(vf->offset);
1274} 1555}
1275 1556
1557/* return PCM offset (sample) of next PCM sample to be read */
1558ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){
1559 if(vf->ready_state<OPENED)return(OV_EINVAL);
1560 return(vf->pcm_offset);
1561}
1562
1276/* return time offset (milliseconds) of next PCM sample to be read */ 1563/* return time offset (milliseconds) of next PCM sample to be read */
1277ogg_int64_t ov_time_tell(OggVorbis_File *vf) ICODE_ATTR_TREMOR_NOT_MDCT;
1278ogg_int64_t ov_time_tell(OggVorbis_File *vf){ 1564ogg_int64_t ov_time_tell(OggVorbis_File *vf){
1279 int link=0; 1565 int link=0;
1280 ogg_int64_t pcm_total=0; 1566 ogg_int64_t pcm_total=0;