/* we no longer preload all vorbis_info (and the associated codec_setup) structs. Call this to seek and fetch the info from the bitstream, if needed */ static int _set_link_number(OggVorbis_File *vf,int link){ if(link != vf->current_link) _decode_clear(vf); if(vf->ready_state<STREAMSET){ _seek_helper(vf,vf->offsets[link]); ogg_stream_reset_serialno(vf->os,vf->serialnos[link]); vf->current_serialno=vf->serialnos[link]; vf->current_link=link; return _fetch_headers(vf,&vf->vi,&vf->vc,&vf->current_serialno,NULL); } return 0; }
int ov_raw_seek(OggVorbis_File *vf,long pos){ ogg_stream_state work_os; if(vf->ready_state<OPENED)return(OV_EINVAL); if(!vf->seekable) return(OV_ENOSEEK); /* don't dump machine if we can't seek */ if(pos<0 || pos>vf->offsets[vf->links])return(OV_EINVAL); /* clear out decoding machine state */ vf->pcm_offset=-1; _decode_clear(vf); _seek_helper(vf,pos); /* we need to make sure the pcm_offset is set, but we don't want to advance the raw cursor past good packets just to get to the first with a granulepos. That's not equivalent behavior to beginning decoding as immediately after the seek position as possible. So, a hack. We use two stream states; a local scratch state and a the shared vf->os stream state. We use the local state to scan, and the shared state as a buffer for later decode. Unfortuantely, on the last page we still advance to last packet because the granulepos on the last page is not necessarily on a packet boundary, and we need to make sure the granpos is correct. */ { ogg_page og; ogg_packet op; int lastblock=0; int accblock=0; int thisblock=-1; int eosflag=0; memset(&work_os,0,sizeof(work_os));/* so that it's safe to clear it later even if we don't init it */ while(1){ if(vf->ready_state==STREAMSET){ /* snarf/scan a packet if we can */ int result=ogg_stream_packetout(&work_os,&op); if(result>0){ if(vf->vi[vf->current_link].codec_setup) thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); if(eosflag) ogg_stream_packetout(&vf->os,NULL); else if(lastblock)accblock+=(lastblock+thisblock)>>2; if(op.granulepos!=-1){ int i,link=vf->current_link; ogg_int64_t granulepos=op.granulepos; for(i=0;i<link;i++) granulepos+=vf->pcmlengths[i]; vf->pcm_offset=granulepos-accblock; break; } lastblock=thisblock; continue; } } if(!lastblock){ if(_get_next_page(vf,&og,-1)<0){ vf->pcm_offset=ov_pcm_total(vf,-1); break; } }else{ /* huh? Bogus stream with packets but no granulepos */ vf->pcm_offset=-1; break; } /* has our decoding just traversed a bitstream boundary? */ if(vf->ready_state==STREAMSET) if(vf->current_serialno!=ogg_page_serialno(&og)){ _decode_clear(vf); /* clear out stream state */ ogg_stream_clear(&work_os); } if(vf->ready_state<STREAMSET){ int link; vf->current_serialno=ogg_page_serialno(&og); for(link=0;link<vf->links;link++) if(vf->serialnos[link]==vf->current_serialno)break; if(link==vf->links)goto seek_error; /* sign of a bogus stream. error out, leave machine uninitialized */ vf->current_link=link; ogg_stream_init(&vf->os,vf->current_serialno); ogg_stream_reset(&vf->os); ogg_stream_init(&work_os,vf->current_serialno); ogg_stream_reset(&work_os); vf->ready_state=STREAMSET; } ogg_stream_pagein(&vf->os,&og); ogg_stream_pagein(&work_os,&og); eosflag=ogg_page_eos(&og); } }
static int _process_packet(OggVorbis_File *vf,int readp){ ogg_page og; /* handle one packet. Try to fetch it from current stream state */ /* extract packets from page */ while(1){ /* process a packet if we can. If the machine isn't loaded, neither is a page */ if(vf->ready_state==INITSET){ while(1) { ogg_packet op; int result=ogg_stream_packetout(&vf->os,&op); ogg_int64_t granulepos; if(result==-1)return(OV_HOLE); /* hole in the data. */ if(result>0){ /* got a packet. process it */ granulepos=op.granulepos; if(!vorbis_synthesis(&vf->vb,&op)){ /* lazy check for lazy header handling. The header packets aren't audio, so if/when we submit them, vorbis_synthesis will reject them */ /* suck in the synthesis data and track bitrate */ { int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); vorbis_synthesis_blockin(&vf->vd,&vf->vb); vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; vf->bittrack+=op.bytes*8; } /* update the pcm offset. */ if(granulepos!=-1 && !op.e_o_s){ int link=(vf->seekable?vf->current_link:0); int i,samples; /* this packet has a pcm_offset on it (the last packet completed on a page carries the offset) After processing (above), we know the pcm position of the *last* sample ready to be returned. Find the offset of the *first* As an aside, this trick is inaccurate if we begin reading anew right at the last page; the end-of-stream granulepos declares the last frame in the stream, and the last packet of the last page may be a partial frame. So, we need a previous granulepos from an in-sequence page to have a reference point. Thus the !op.e_o_s clause above */ samples=vorbis_synthesis_pcmout(&vf->vd,NULL); granulepos-=samples; for(i=0;i<link;i++) granulepos+=vf->pcmlengths[i]; vf->pcm_offset=granulepos; } return(1); } } else break; } } if(vf->ready_state>=OPENED){ if(!readp)return(0); if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* eof. leave unitialized */ /* bitrate tracking; add the header's bytes here, the body bytes are done by packet above */ vf->bittrack+=og.header_len*8; /* has our decoding just traversed a bitstream boundary? */ if(vf->ready_state==INITSET){ if(vf->current_serialno!=ogg_page_serialno(&og)){ _decode_clear(vf); if(!vf->seekable){ vorbis_info_clear(vf->vi); vorbis_comment_clear(vf->vc); } } } } /* Do we need to load a new machine before submitting the page? */ /* This is different in the seekable and non-seekable cases. In the seekable case, we already have all the header information loaded and cached; we just initialize the machine with it and continue on our merry way. In the non-seekable (streaming) case, we'll only be at a boundary if we just left the previous logical bitstream and we're now nominally at the header of the next bitstream */ if(vf->ready_state!=INITSET){ int link; if(vf->ready_state<STREAMSET){ if(vf->seekable){ vf->current_serialno=ogg_page_serialno(&og); /* match the serialno to bitstream section. We use this rather than offset positions to avoid problems near logical bitstream boundaries */ for(link=0;link<vf->links;link++) if(vf->serialnos[link]==vf->current_serialno)break; if(link==vf->links)return(OV_EBADLINK); /* sign of a bogus stream. error out, leave machine uninitialized */ vf->current_link=link; ogg_stream_init(&vf->os,vf->current_serialno); ogg_stream_reset(&vf->os); vf->ready_state=STREAMSET; }else{ /* we're streaming */ /* fetch the three header packets, build the info struct */ int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og); if(ret)return(ret); vf->current_link++; link=0; } } _make_decode_ready(vf); } ogg_stream_pagein(&vf->os,&og); } }
static int _fetch_headers(OggVorbis_File *vf, vorbis_info *vi, vorbis_comment *vc, ogg_uint32_t *serialno, ogg_page *og_ptr){ ogg_page og={0,0,0,0}; ogg_packet op={0,0,0,0,0,0}; int i,ret; if(vf->ready_state>OPENED)_decode_clear(vf); if(!og_ptr){ ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); if(llret==OV_EREAD)return OV_EREAD; if(llret<0)return OV_ENOTVORBIS; og_ptr=&og; } ogg_stream_reset_serialno(vf->os,ogg_page_serialno(og_ptr)); if(serialno)*serialno=vf->os->serialno; /* extract the initial header from the first page and verify that the Ogg bitstream is in fact Vorbis data */ vorbis_info_init(vi); vorbis_comment_init(vc); i=0; while(i<3){ ogg_stream_pagein(vf->os,og_ptr); while(i<3){ int result=ogg_stream_packetout(vf->os,&op); if(result==0)break; if(result==-1){ ret=OV_EBADHEADER; goto bail_header; } if((ret=vorbis_dsp_headerin(vi,vc,&op))){ goto bail_header; } i++; } if(i<3) if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ ret=OV_EBADHEADER; goto bail_header; } } ogg_packet_release(&op); ogg_page_release(&og); vf->ready_state=LINKSET; return 0; bail_header: ogg_packet_release(&op); ogg_page_release(&og); vorbis_info_clear(vi); vorbis_comment_clear(vc); vf->ready_state=OPENED; return ret; }