📄 vorbisfile.c
字号:
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);
_make_decode_ready(vf);
/* 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;
_make_decode_ready(vf);
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;
ogg_int32_t samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
if(samples>target) samples = (ogg_int32_t)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,float seconds){
/* translate time to PCM position and call ov_pcm_seek */
int link=-1;
ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
float 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 = (ogg_int64_t)(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,float seconds){
/* translate time to PCM position and call ov_pcm_seek */
int link=-1;
ogg_int64_t pcm_total=ov_pcm_total(vf,-1);
float 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 = (ogg_int64_t)(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 */
float ov_time_tell(OggVorbis_File *vf){
int link=0;
ogg_int64_t pcm_total=0;
float time_total=0.0f;
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((float)time_total+(float)(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;
}
}
static int host_is_big_endian() {
ogg_int32_t pattern = 0xfeedface; /* deadbeef */
unsigned char *bytewise = (unsigned char *)&pattern;
if (bytewise[0] == 0xfe) return 1;
return 0;
}
/* 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
bigendianp) should the data be packed LSB first (0) or
MSB first (1)
word) word size for output. currently 1 (byte) or
2 (16 bit short)
return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -