diff options
Diffstat (limited to 'apps/codecs/libtremor')
-rw-r--r-- | apps/codecs/libtremor/info.c | 25 | ||||
-rw-r--r-- | apps/codecs/libtremor/ivorbiscodec.h | 1 | ||||
-rw-r--r-- | apps/codecs/libtremor/vorbisfile.c | 886 |
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? */ | ||
236 | int 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); | |||
166 | extern void vorbis_dsp_clear(vorbis_dsp_state *v); | 166 | extern void vorbis_dsp_clear(vorbis_dsp_state *v); |
167 | 167 | ||
168 | /* Vorbis PRIMITIVES: synthesis layer *******************************/ | 168 | /* Vorbis PRIMITIVES: synthesis layer *******************************/ |
169 | extern int vorbis_synthesis_idheader(ogg_packet *op); | ||
169 | extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, | 170 | extern 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 */ |
74 | static void _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ | 74 | static 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 | 184 | static 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 | |
175 | static 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 */ | ||
198 | static 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 | |||
208 | static 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. */ | ||
219 | static 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, | |||
235 | static int _fetch_headers(OggVorbis_File *vf, | 281 | static 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. */ |
429 | static 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 */ | ||
306 | static 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; | 478 | static 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 | ||
408 | static void _make_decode_ready(OggVorbis_File *vf){ | 611 | static 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 | ||
422 | static int _open_seekable2(OggVorbis_File *vf){ | 628 | static 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, | |||
660 | static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, | 896 | static 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){ | |||
1143 | int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ | 1420 | int 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 */ | ||
1558 | ogg_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 */ |
1277 | ogg_int64_t ov_time_tell(OggVorbis_File *vf) ICODE_ATTR_TREMOR_NOT_MDCT; | ||
1278 | ogg_int64_t ov_time_tell(OggVorbis_File *vf){ | 1564 | ogg_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; |