📄 vorbisfile.c
字号:
ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE return from not necessarily starting from the beginning */ 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(thisblock<0){ ogg_stream_packetout(&vf->os,NULL); thisblock=0; }else{ 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-vf->pcmlengths[link*2]; if(granulepos<0)granulepos=0; for(i=0;i<link;i++) granulepos+=vf->pcmlengths[i*2+1]; vf->pcm_offset=granulepos-accblock; break; } lastblock=thisblock; continue; }else ogg_stream_packetout(&vf->os,NULL); } } 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_reset_serialno(&vf->os,vf->current_serialno); ogg_stream_reset_serialno(&work_os,vf->current_serialno); vf->ready_state=STREAMSET; } ogg_stream_pagein(&vf->os,&og); ogg_stream_pagein(&work_os,&og); eosflag=ogg_page_eos(&og); } } ogg_stream_clear(&work_os); vf->bittrack=0.f; vf->samptrack=0.f; return(0); seek_error: /* dump the machine so we're in a known state */ vf->pcm_offset=-1; ogg_stream_clear(&work_os); _decode_clear(vf); return OV_EBADLINK;}/* Page granularity seek (faster than sample granularity because we don't do the last bit of decode to find a specific sample). Seek to the last [granule marked] page preceeding the specified pos location, such that decoding past the returned point will quickly arrive at the requested position. */int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ int link=-1; ogg_int64_t result=0; ogg_int64_t total=ov_pcm_total(vf,-1); if(vf->ready_state<OPENED)return(OV_EINVAL); if(!vf->seekable)return(OV_ENOSEEK); if(pos<0 || pos>total)return(OV_EINVAL); /* which bitstream section does this pcm offset occur in? */ for(link=vf->links-1;link>=0;link--){ total-=vf->pcmlengths[link*2+1]; if(pos>=total)break; } /* search within the logical bitstream for the page with the highest pcm_pos preceeding (or equal to) pos. There is a danger here; missing pages or incorrect frame number information in the bitstream could make our task impossible. Account for that (it would be an error condition) */ /* new search algorithm by HB (Nicholas Vinen) */ { ogg_int64_t end=vf->offsets[link+1]; ogg_int64_t begin=vf->offsets[link]; ogg_int64_t begintime = vf->pcmlengths[link*2]; ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; ogg_int64_t target=pos-total+begintime; ogg_int64_t best=begin; ogg_page og; while(begin<end){ ogg_int64_t bisect; if(end-begin<CHUNKSIZE){ bisect=begin; }else{ /* take a (pretty decent) guess. */ bisect=begin + (target-begintime)*(end-begin)/(endtime-begintime) - CHUNKSIZE; if(bisect<=begin) bisect=begin+1; } _seek_helper(vf,bisect); while(begin<end){ result=_get_next_page(vf,&og,end-vf->offset); if(result==OV_EREAD) goto seek_error; if(result<0){ if(bisect<=begin+1) end=begin; /* found it */ else{ if(bisect==0) goto seek_error; bisect-=CHUNKSIZE; if(bisect<=begin)bisect=begin+1; _seek_helper(vf,bisect); } }else{ ogg_int64_t granulepos=ogg_page_granulepos(&og); if(granulepos==-1)continue; if(granulepos<target){ best=result; /* raw offset of packet with granulepos */ begin=vf->offset; /* raw offset of next page */ begintime=granulepos; if(target-begintime>44100)break; bisect=begin; /* *not* begin + 1 */ }else{ if(bisect<=begin+1) end=begin; /* found it */ else{ if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ end=result; bisect-=CHUNKSIZE; /* an endless loop otherwise. */ if(bisect<=begin)bisect=begin+1; _seek_helper(vf,bisect); }else{ end=result; endtime=granulepos; break; } } } } } } /* found our page. seek to it, update pcm offset. Easier case than raw_seek, don't keep packets preceeding granulepos. */ { ogg_page og; ogg_packet op; /* seek */ _seek_helper(vf,best); vf->pcm_offset=-1; if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* shouldn't happen */ if(link!=vf->current_link){ /* Different link; dump entire decode machine */ _decode_clear(vf); vf->current_link=link; vf->current_serialno=ogg_page_serialno(&og); vf->ready_state=STREAMSET; }else{ vorbis_synthesis_restart(&vf->vd); } ogg_stream_reset_serialno(&vf->os,vf->current_serialno); ogg_stream_pagein(&vf->os,&og); /* pull out all but last packet; the one with granulepos */ while(1){ result=ogg_stream_packetpeek(&vf->os,&op); if(result==0){ /* !!! the packet finishing this page originated on a preceeding page. Keep fetching previous pages until we get one with a granulepos or without the 'continued' flag set. Then just use raw_seek for simplicity. */ _seek_helper(vf,best); while(1){ result=_get_prev_page(vf,&og); if(result<0) goto seek_error; if(ogg_page_granulepos(&og)>-1 || !ogg_page_continued(&og)){ return ov_raw_seek(vf,result); } vf->offset=result; } } if(result<0){ result = OV_EBADPACKET; goto seek_error; } if(op.granulepos!=-1){ vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; if(vf->pcm_offset<0)vf->pcm_offset=0; vf->pcm_offset+=total; break; }else result=ogg_stream_packetout(&vf->os,NULL); } } } /* verify result */ if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ result=OV_EFAULT; goto seek_error; } vf->bittrack=0.f; vf->samptrack=0.f; return(0); seek_error: /* dump machine so we're in a known state */ vf->pcm_offset=-1; _decode_clear(vf); return (int)result;}/* seek to a sample offset relative to the decompressed pcm stream returns zero on success, nonzero on failure */int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ int thisblock,lastblock=0; int ret=ov_pcm_seek_page(vf,pos); if(ret<0)return(ret); if((ret=_make_decode_ready(vf)))return ret; /* discard leading packets we don't need for the lapping of the position we want; don't decode them */ while(1){ ogg_packet op; ogg_page og; int ret=ogg_stream_packetpeek(&vf->os,&op); if(ret>0){ thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); if(thisblock<0){ ogg_stream_packetout(&vf->os,NULL); continue; /* non audio packet */ } if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; if(vf->pcm_offset+((thisblock+ vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; /* remove the packet from packet queue and track its granulepos */ ogg_stream_packetout(&vf->os,NULL); vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with only tracking, no pcm_decode */ vorbis_synthesis_blockin(&vf->vd,&vf->vb); /* end of logical stream case is hard, especially with exact length positioning. */ if(op.granulepos>-1){ int i; /* always believe the stream markers */ vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; if(vf->pcm_offset<0)vf->pcm_offset=0; for(i=0;i<vf->current_link;i++) vf->pcm_offset+=vf->pcmlengths[i*2+1]; } lastblock=thisblock; }else{ if(ret<0 && ret!=OV_HOLE)break; /* suck in a new page */ if(_get_next_page(vf,&og,-1)<0)break; if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear(vf); 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)return(OV_EBADLINK); vf->current_link=link; ogg_stream_reset_serialno(&vf->os,vf->current_serialno); vf->ready_state=STREAMSET; ret=_make_decode_ready(vf); if(ret)return ret; lastblock=0; } ogg_stream_pagein(&vf->os,&og); } } vf->bittrack=0.f; vf->samptrack=0.f; /* discard samples until we reach the desired position. Crossing a logical bitstream boundary with abandon is OK. */ while(vf->pcm_offset<pos){ ogg_int64_t target=pos-vf->pcm_offset; long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); if(samples>target)samples=target; vorbis_synthesis_read(&vf->vd,samples); vf->pcm_offset+=samples; if(samples<target) if(_fetch_and_process_packet(vf,NULL,1,1)<=0) vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */ } return 0;}/* seek to a playback time relative to the decompressed pcm stream returns zero on success, nonzero on failure */int ov_time_seek(OggVorbis_File *vf,double seconds){ /* translate time to PCM position and call ov_pcm_seek */ int link=-1; ogg_int64_t pcm_total=ov_pcm_total(vf,-1); double time_total=ov_time_total(vf,-1); if(vf->ready_state<OPENED)return(OV_EINVAL); if(!vf->seekable)return(OV_ENOSEEK); if(seconds<0 || seconds>time_total)return(OV_EINVAL); /* which bitstream section does this time offset occur in? */ for(link=vf->links-1;link>=0;link--){ pcm_total-=vf->pcmlengths[link*2+1]; time_total-=ov_time_total(vf,link); if(seconds>=time_total)break; } /* enough information to convert time offset to pcm offset */ { ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; return(ov_pcm_seek(vf,target)); }}/* page-granularity version of ov_time_seek returns zero on success, nonzero on failure */int ov_time_seek_page(OggVorbis_File *vf,double seconds){ /* translate time to PCM position and call ov_pcm_seek */ int link=-1; ogg_int64_t pcm_total=ov_pcm_total(vf,-1); double time_total=ov_time_total(vf,-1); if(vf->ready_state<OPENED)return(OV_EINVAL); if(!vf->seekable)return(OV_ENOSEEK); if(seconds<0 || seconds>time_total)return(OV_EINVAL); /* which bitstream section does this time offset occur in? */ for(link=vf->links-1;link>=0;link--){ pcm_total-=vf->pcmlengths[link*2+1]; time_total-=ov_time_total(vf,link); if(seconds>=time_total)break; } /* enough information to convert time offset to pcm offset */ { ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; return(ov_pcm_seek_page(vf,target)); }}/* tell the current stream offset cursor. Note that seek followed by tell will likely not give the set offset due to caching */ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ if(vf->ready_state<OPENED)return(OV_EINVAL); return(vf->offset);}/* return PCM offset (sample) of next PCM sample to be read */ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ if(vf->ready_state<OPENED)return(OV_EINVAL); return(vf->pcm_offset);}/* return time offset (seconds) of next PCM sample to be read */double ov_time_tell(OggVorbis_File *vf){ int link=0; ogg_int64_t pcm_total=0; double time_total=0.f; if(vf->ready_state<OPENED)return(OV_EINVAL); if(vf->seekable){ pcm_total=ov_pcm_total(vf,-1); time_total=ov_time_total(vf,-1); /* which bitstream section does this time offset occur in? */ for(link=vf->links-1;link>=0;link--){ pcm_total-=vf->pcmlengths[link*2+1]; time_total-=ov_time_total(vf,link); if(vf->pcm_offset>=pcm_total)break; } } return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate);}/* link: -1) return the vorbis_info struct for the bitstream section currently being decoded 0-n) to request information for a specific bitstream section In the case of a non-seekable bitstream, any call returns the current bitstream. NULL in the case that the machine is not initialized */vorbis_info *ov_info(OggVorbis_File *vf,int link){ if(vf->seekable){ if(link<0) if(vf->ready_state>=STREAMSET) return vf->vi+vf->current_link; else return vf->vi; else if(link>=vf->links) return NULL; else return vf->vi+link; }else{ return vf->vi; }}/* grr, strong typing, grr, no templates/inheritence, grr */vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ if(vf->seekable){ if(link<0) if(vf->ready_state>=STREAMSET) return vf->vc+vf->current_link; else return vf->vc; else if(link>=vf->links) return NULL; else return vf->vc+link; }else{ return vf->vc; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -