📄 vorbisfile.c
字号:
ogg_stream_pagein(&vf->os,&og); ogg_stream_pagein(&work_os,&og); eosflag=ogg_page_eos(&og); } } ogg_stream_clear(&work_os); 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; long ret; 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]; 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 target=pos-total; long end=vf->offsets[link+1]; long begin=vf->offsets[link]; ogg_int64_t endtime = vf->pcmlengths[link]; ogg_int64_t begintime = 0; long best=begin; ogg_page og; while(begin<end){ long 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){ ret=_get_next_page(vf,&og,end-bisect); if(ret==OV_EREAD) goto seek_error; if(ret<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<target){ best=ret; /* raw offset of packet with granulepos */ begin=vf->offset; /* raw offset of next page */ begintime=granulepos; if(target-begin>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=ret; bisect-=CHUNKSIZE; /* an endless loop otherwise. */ if(bisect<=begin)bisect=begin+1; _seek_helper(vf,bisect); }else{ end=ret; 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; /* clear out decoding machine state */ _decode_clear(vf); /* seek */ _seek_helper(vf,best); if(_get_next_page(vf,&og,-1)<0)return(OV_EOF); /* shouldn't happen */ vf->current_serialno=ogg_page_serialno(&og); vf->current_link=link; ogg_stream_init(&vf->os,vf->current_serialno); ogg_stream_reset(&vf->os); vf->ready_state=STREAMSET; ogg_stream_pagein(&vf->os,&og); /* pull out all but last packet; the one with granulepos */ while(1){ ret=ogg_stream_packetpeek(&vf->os,&op); if(ret==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. */ while(1){ ret=_get_prev_page(vf,&og); if(ret<0)goto seek_error; if(ogg_page_granulepos(&og)>-1 || !ogg_page_continued(&og)){ return ov_raw_seek(vf,ret); } vf->offset=ret; } } if(ret<0)goto seek_error; if(op.granulepos!=-1){ vf->pcm_offset=op.granulepos+total; break; }else ret=ogg_stream_packetout(&vf->os,NULL); } } } /* verify result */ if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ ret=OV_EFAULT; goto seek_error; } return(0); seek_error: /* dump machine so we're in a known state */ vf->pcm_offset=-1; _decode_clear(vf); return ret;}/* 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); /* 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(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; if(vf->pcm_offset+((thisblock+ vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; ogg_stream_packetout(&vf->os,NULL); /* 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; for(i=0;i<vf->current_link;i++) vf->pcm_offset+=vf->pcmlengths[i]; } 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_init(&vf->os,vf->current_serialno); ogg_stream_reset(&vf->os); vf->ready_state=STREAMSET; lastblock=0; } ogg_stream_pagein(&vf->os,&og); } } /* discard samples until we reach the desired position. Crossing a logical bitstream boundary with abandon is OK. */ _make_decode_ready(vf); while(vf->pcm_offset<pos){ ogg_int32_t **pcm; long target=pos-vf->pcm_offset; long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); if(samples>target)samples=target; vorbis_synthesis_read(&vf->vd,samples); vf->pcm_offset+=samples; if(samples<target) if(_process_packet(vf,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,ogg_int64_t milliseconds){ /* translate time to PCM position and call ov_pcm_seek */ int link=-1; ogg_int64_t pcm_total=ov_pcm_total(vf,-1); ogg_int64_t time_total=ov_time_total(vf,-1); if(vf->ready_state<OPENED)return(OV_EINVAL); if(!vf->seekable)return(OV_ENOSEEK); if(milliseconds<0 || milliseconds>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]; time_total-=ov_time_total(vf,link); if(milliseconds>=time_total)break; } /* enough information to convert time offset to pcm offset */ { ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; 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,ogg_int64_t milliseconds){ /* translate time to PCM position and call ov_pcm_seek */ int link=-1; ogg_int64_t pcm_total=ov_pcm_total(vf,-1); ogg_int64_t time_total=ov_time_total(vf,-1); if(vf->ready_state<OPENED)return(OV_EINVAL); if(!vf->seekable)return(OV_ENOSEEK); if(milliseconds<0 || milliseconds>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]; time_total-=ov_time_total(vf,link); if(milliseconds>=time_total)break; } /* enough information to convert time offset to pcm offset */ { ogg_int64_t target=pcm_total+(milliseconds-time_total)*vf->vi[link].rate/1000; 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 (milliseconds) of next PCM sample to be read */ogg_int64_t ov_time_tell(OggVorbis_File *vf){ /* translate time to PCM position and call ov_pcm_seek */ int link=0; ogg_int64_t pcm_total=0; ogg_int64_t time_total=0; 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]; time_total-=ov_time_total(vf,link); if(vf->pcm_offset>=pcm_total)break; } } return(time_total+(1000*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; }}/* up to this point, everything could more or less hide the multiple logical bitstream nature of chaining from the toplevel application if the toplevel application didn't particularly care. However, at the point that we actually read audio back, the multiple-section nature must surface: Multiple bitstream sections do not necessarily have to have the same number of channels or sampling rate. ov_read returns the sequential logical bitstream number currently being decoded along with the PCM data in order that the toplevel application can take action on channel/sample rate changes. This number will be incremented even for streamed (non-seekable) streams (for seekable streams, it represents the actual logical bitstream index within the physical bitstream. Note that the accessor functions above are aware of this dichotomy). input values: buffer) a buffer to hold packed PCM data for return length) the byte length requested to be placed into buffer return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) 0) EOF n) number of bytes of PCM actually returned. The below works on a packet-by-packet basis, so the return length is not related to the 'length' passed in, just guaranteed to fit. *section) set to the logical bitstream number */long ov_read(OggVorbis_File *vf,char *buffer,int bytes_req,int *bitstream){ int i,j; ogg_int32_t **pcm; long samples; if(vf->ready_state<OPENED)return(OV_EINVAL); while(1){ if(vf->ready_state>=STREAMSET){ samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); if(samples)break; } /* suck in another packet */ { int ret=_process_packet(vf,1); if(ret==OV_EOF)return(0); if(ret<=0)return(ret); } } if(samples>0){ /* yay! proceed to pack data into the byte buffer */ long channels=ov_info(vf,-1)->channels; if(channels==1){ if(samples>(bytes_req/2)) samples=bytes_req/2; }else{ if(samples>(bytes_req/4)) samples=bytes_req/4; } for(i=0;i<channels;i++) { /* It's faster in this order */ ogg_int32_t *src=pcm[i]; short *dest=((short *)buffer)+i; for(j=0;j<samples;j++) { *dest=CLIP_TO_15(src[j]>>9); dest+=channels; } } vorbis_synthesis_read(&vf->vd,samples); vf->pcm_offset+=samples; if(bitstream)*bitstream=vf->current_link; return(samples*2*channels); }else{ return(samples); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -