Commit d8282fdb authored by Monty's avatar Monty
Browse files

Single link files seeking fixes:

fix pcm exact seeking at very beginning (a rejected packet was being
interpreted as a negative pcm offset) and very end of files (short
final frames require processing from previous page's granulepos to get
length of final frame correct)

svn path=/trunk/vorbis/; revision=3115
parent 48e5deaf
......@@ -11,7 +11,7 @@
********************************************************************
function: simple example encoder
last mod: $Id: encoder_example.c,v 1.36 2002/01/23 16:04:55 segher Exp $
last mod: $Id: encoder_example.c,v 1.37 2002/02/28 04:12:47 xiphmont Exp $
********************************************************************/
......@@ -97,7 +97,7 @@ int main(){
/* (quality mode .4: 44kHz stereo coupled, roughly 128kbps VBR) */
vorbis_info_init(&vi);
vorbis_encode_init_vbr(&vi,2,44100,.1); /* max compression */
vorbis_encode_init_vbr(&vi,2,44100,.4); /* max compression */
/* add a comment */
vorbis_comment_init(&vc);
......
......@@ -11,7 +11,7 @@
********************************************************************
function: illustrate seeking, and test it too
last mod: $Id: seeking_example.c,v 1.12 2001/12/20 01:00:24 segher Exp $
last mod: $Id: seeking_example.c,v 1.13 2002/02/28 04:12:47 xiphmont Exp $
********************************************************************/
......@@ -54,6 +54,16 @@ void _verify(OggVorbis_File *ov,ogg_int64_t pos,
for(j=0;j<bread;j++){
if(buffer[j]!=bigassbuffer[j+pos*2]){
printf("data position after seek doesn't match pcm position\n");
{
FILE *f=fopen("a.m","w");
for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)buffer[j]);
fclose(f);
f=fopen("b.m","w");
for(j=0;j<bread;j++)fprintf(f,"%d\n",(int)bigassbuffer[j+pos*2]);
fclose(f);
}
exit(1);
}
}
......@@ -94,7 +104,6 @@ int main(){
/* because we want to do sample-level verification that the seek
does what it claimed, decode the entire file into memory */
printf("loading....\n");
fflush(stdout);
pcmlength=ov_pcm_total(&ov,-1);
bigassbuffer=malloc(pcmlength*2); /* w00t */
......@@ -107,6 +116,8 @@ int main(){
}else{
pcmlength=i/2;
}
fprintf(stderr,"\rloading.... [%ld left] ",
(long)(pcmlength*2-i));
}
/* Exercise all the real seeking cases; ov_raw_seek,
......@@ -114,7 +125,7 @@ int main(){
on pcm_seek */
{
ogg_int64_t length=ov.end;
printf("testing raw seeking to random places in %ld bytes....\n",
printf("\rtesting raw seeking to random places in %ld bytes....\n",
(long)length);
for(i=0;i<1000;i++){
......@@ -172,7 +183,7 @@ int main(){
exit(1);
}
if(ov_pcm_tell(&ov)!=val){
printf("Decalred position didn't perfectly match request: %ld != %ld\n",
printf("Declared position didn't perfectly match request: %ld != %ld\n",
(long)val,(long)ov_pcm_tell(&ov));
exit(1);
}
......
......@@ -11,7 +11,7 @@
********************************************************************
function: libvorbis codec headers
last mod: $Id: codec.h,v 1.39 2001/12/12 09:45:23 xiphmont Exp $
last mod: $Id: codec.h,v 1.40 2002/02/28 04:12:47 xiphmont Exp $
********************************************************************/
......@@ -201,6 +201,7 @@ extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,
extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi);
extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op);
extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op);
extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb);
extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm);
extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples);
......
......@@ -11,7 +11,7 @@
********************************************************************
function: PCM data vector blocking, windowing and dis/reassembly
last mod: $Id: block.c,v 1.58 2002/01/22 11:59:00 xiphmont Exp $
last mod: $Id: block.c,v 1.59 2002/02/28 04:12:48 xiphmont Exp $
Handle windowing, overlap-add, etc of the PCM vectors. This is made
more amusing by Vorbis' current two allowed block sizes.
......@@ -620,23 +620,19 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
codec_setup_info *ci=vi->codec_setup;
int i,j;
if(!vb)return(OV_EINVAL);
if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL);
v->lW=v->W;
v->W=vb->W;
v->nW=-1;
v->glue_bits+=vb->glue_bits;
v->time_bits+=vb->time_bits;
v->floor_bits+=vb->floor_bits;
v->res_bits+=vb->res_bits;
if(v->sequence+1 != vb->sequence)v->granulepos=-1; /* out of sequence;
lose count */
lose count */
v->sequence=vb->sequence;
{
if(vb->pcm){ /* not pcm to process if vorbis_synthesis_trackonly
was called on block */
int n=ci->blocksizes[v->W]/2;
int n0=ci->blocksizes[0]/2;
int n1=ci->blocksizes[1]/2;
......@@ -644,12 +640,17 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
int thisCenter;
int prevCenter;
v->glue_bits+=vb->glue_bits;
v->time_bits+=vb->time_bits;
v->floor_bits+=vb->floor_bits;
v->res_bits+=vb->res_bits;
if(v->centerW){
thisCenter=n1;
prevCenter=0;
}else{
thisCenter=0;
prevCenter=n1;
thisCenter=0;
prevCenter=n1;
}
/* v->pcm is now used like a two-stage double buffer. We don't want
......@@ -709,7 +710,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
/* deal with initial packet state; we do this using the explicit
pcm_returned==-1 flag otherwise we're sensitive to first block
being short or long */
if(v->pcm_returned==-1){
v->pcm_returned=thisCenter;
v->pcm_current=thisCenter;
......@@ -719,57 +720,56 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){
ci->blocksizes[v->lW]/4+
ci->blocksizes[v->W]/4;
}
}
/* track the frame number... This is for convenience, but also
making sure our last packet doesn't end with added padding. If
the last packet is partial, the number of samples we'll have to
return will be past the vb->granulepos.
This is not foolproof! It will be confused if we begin
decoding at the last page after a seek or hole. In that case,
we don't have a starting point to judge where the last frame
is. For this reason, vorbisfile will always try to make sure
it reads the last two marked pages in proper sequence */
if(v->granulepos==-1)
if(vb->granulepos==-1){
v->granulepos=0;
}else{
v->granulepos=vb->granulepos;
}
else{
v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4;
if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){
if(v->granulepos>vb->granulepos){
long extra=v->granulepos-vb->granulepos;
if(vb->eofflag){
/* partial last frame. Strip the extra samples off */
v->pcm_current-=extra;
}else if(vb->sequence == 1){
/* ^^^ argh, this can be 1 from seeking! */
/* partial first frame. Discard extra leading samples */
v->pcm_returned+=extra;
if(v->pcm_returned>v->pcm_current)
v->pcm_returned=v->pcm_current;
}
/* track the frame number... This is for convenience, but also
making sure our last packet doesn't end with added padding. If
the last packet is partial, the number of samples we'll have to
return will be past the vb->granulepos.
This is not foolproof! It will be confused if we begin
decoding at the last page after a seek or hole. In that case,
we don't have a starting point to judge where the last frame
is. For this reason, vorbisfile will always try to make sure
it reads the last two marked pages in proper sequence */
if(v->granulepos==-1){
if(vb->granulepos!=-1){ /* only set if we have a position to set to */
v->granulepos=vb->granulepos;
}
}else{
v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4;
if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){
if(v->granulepos>vb->granulepos){
long extra=v->granulepos-vb->granulepos;
if(vb->eofflag){
/* partial last frame. Strip the extra samples off */
v->pcm_current-=extra;
}else if(vb->sequence == 1){
/* ^^^ argh, this can be 1 from seeking! */
}/* else{ Shouldn't happen *unless* the bitstream is out of
spec. Either way, believe the bitstream } */
v->granulepos=vb->granulepos;
}
/* partial first frame. Discard extra leading samples */
v->pcm_returned+=extra;
if(v->pcm_returned>v->pcm_current)
v->pcm_returned=v->pcm_current;
} /* else {Shouldn't happen *unless* the bitstream is out of
spec. Either way, believe the bitstream } */
} /* else {Shouldn't happen *unless* the bitstream is out of
spec. Either way, believe the bitstream } */
v->granulepos=vb->granulepos;
}
/* Update, cleanup */
if(vb->eofflag)v->eofflag=1;
}
/* Update, cleanup */
if(vb->eofflag)v->eofflag=1;
return(0);
}
/* pcm==NULL indicates we just want the pending samples, no more */
......@@ -787,9 +787,9 @@ int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){
return(0);
}
int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){
if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL);
v->pcm_returned+=bytes;
int vorbis_synthesis_read(vorbis_dsp_state *v,int n){
if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL);
v->pcm_returned+=n;
return(0);
}
......@@ -11,7 +11,7 @@
********************************************************************
function: channel mapping 0 implementation
last mod: $Id: mapping0.c,v 1.44 2002/01/22 11:59:00 xiphmont Exp $
last mod: $Id: mapping0.c,v 1.45 2002/02/28 04:12:48 xiphmont Exp $
********************************************************************/
......@@ -681,7 +681,7 @@ static int mapping0_inverse(vorbis_block *vb,vorbis_look_mapping *l){
pcm[j]=0.f;
}
/* all done! */
return(0);
}
......@@ -697,3 +697,4 @@ vorbis_func_mapping mapping0_exportbundle={
&mapping0_forward,
&mapping0_inverse
};
......@@ -11,7 +11,7 @@
********************************************************************
function: single-block PCM synthesis
last mod: $Id: synthesis.c,v 1.25 2001/12/20 01:00:30 segher Exp $
last mod: $Id: synthesis.c,v 1.26 2002/02/28 04:12:48 xiphmont Exp $
********************************************************************/
......@@ -73,6 +73,53 @@ int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){
return(_mapping_P[type]->inverse(vb,b->mode[mode]));
}
/* used to track pcm position without actually performing decode.
Useful for sequential 'fast forward' */
int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){
vorbis_dsp_state *vd=vb->vd;
backend_lookup_state *b=vd->backend_state;
vorbis_info *vi=vd->vi;
codec_setup_info *ci=vi->codec_setup;
oggpack_buffer *opb=&vb->opb;
int mode;
/* first things first. Make sure decode is ready */
_vorbis_block_ripcord(vb);
oggpack_readinit(opb,op->packet,op->bytes);
/* Check the packet type */
if(oggpack_read(opb,1)!=0){
/* Oops. This is not an audio data packet */
return(OV_ENOTAUDIO);
}
/* read our mode and pre/post windowsize */
mode=oggpack_read(opb,b->modebits);
if(mode==-1)return(OV_EBADPACKET);
vb->mode=mode;
vb->W=ci->mode_param[mode]->blockflag;
if(vb->W){
vb->lW=oggpack_read(opb,1);
vb->nW=oggpack_read(opb,1);
if(vb->nW==-1) return(OV_EBADPACKET);
}else{
vb->lW=0;
vb->nW=0;
}
/* more setup */
vb->granulepos=op->granulepos;
vb->sequence=op->packetno-3; /* first block is third packet */
vb->eofflag=op->e_o_s;
/* no pcm */
vb->pcmend=0;
vb->pcm=NULL;
return(0);
}
long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){
codec_setup_info *ci=vi->codec_setup;
oggpack_buffer opb;
......
......@@ -11,7 +11,7 @@
********************************************************************
function: stdio-based convenience library for opening/seeking/decoding
last mod: $Id: vorbisfile.c,v 1.55 2002/01/22 08:06:08 xiphmont Exp $
last mod: $Id: vorbisfile.c,v 1.56 2002/02/28 04:12:48 xiphmont Exp $
********************************************************************/
......@@ -416,7 +416,9 @@ static void _decode_clear(OggVorbis_File *vf){
1) got a packet
*/
static int _process_packet(OggVorbis_File *vf,int readp){
static int _fetch_and_process_packet(OggVorbis_File *vf,
ogg_packet *op_in,
int readp){
ogg_page og;
/* handle one packet. Try to fetch it from current stream state */
......@@ -428,31 +430,37 @@ static int _process_packet(OggVorbis_File *vf,int readp){
if(vf->ready_state==INITSET){
while(1) {
ogg_packet op;
int result=ogg_stream_packetout(&vf->os,&op);
ogg_packet *op_ptr=(op_in?op_in:&op);
int result=ogg_stream_packetout(&vf->os,op_ptr);
ogg_int64_t granulepos;
op_in=NULL;
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 */
granulepos=op_ptr->granulepos;
if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* 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);
/* for proper use of libvorbis within libvorbisfile,
oldsamples will always be zero. */
if(oldsamples)return(OV_EFAULT);
vorbis_synthesis_blockin(&vf->vd,&vf->vb);
vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
vf->bittrack+=op.bytes*8;
vf->bittrack+=op_ptr->bytes*8;
}
/* update the pcm offset. */
if(granulepos!=-1 && !op.e_o_s){
if(granulepos!=-1 && !op_ptr->e_o_s){
int link=(vf->seekable?vf->current_link:0);
int i,samples;
......@@ -466,7 +474,7 @@ static int _process_packet(OggVorbis_File *vf,int readp){
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
to have a reference point. Thus the !op_ptr->e_o_s clause
above */
samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
......@@ -1125,6 +1133,7 @@ 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 */
......@@ -1136,17 +1145,22 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
int ret=ogg_stream_packetpeek(&vf->os,&op);
if(ret>0){
thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
if(thisblock<0)thisblock=0; /* 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. */
length positioning. */
if(op.granulepos>-1){
int i;
/* always believe the stream markers */
......@@ -1154,9 +1168,9 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
for(i=0;i<vf->current_link;i++)
vf->pcm_offset+=vf->pcmlengths[i];
}
lastblock=thisblock;
}else{
if(ret<0 && ret!=OV_HOLE)break;
......@@ -1176,15 +1190,16 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
ogg_stream_init(&vf->os,vf->current_serialno);
ogg_stream_reset(&vf->os);
vf->ready_state=STREAMSET;
_make_decode_ready(vf);
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){
float **pcm;
long target=pos-vf->pcm_offset;
......@@ -1195,7 +1210,7 @@ int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){
vf->pcm_offset+=samples;
if(samples<target)
if(_process_packet(vf,1)<=0)
if(_fetch_and_process_packet(vf,NULL,1)<=0)
vf->pcm_offset=ov_pcm_total(vf,-1); /* eof */
}
return 0;
......@@ -1393,7 +1408,7 @@ long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int *bitstream){
/* suck in another packet */
{
int ret=_process_packet(vf,1);
int ret=_fetch_and_process_packet(vf,NULL,1);
if(ret==OV_EOF)return(0);
if(ret<=0)return(ret);
}
......@@ -1419,7 +1434,7 @@ long ov_read(OggVorbis_File *vf,char *buffer,int length,
/* suck in another packet */
{
int ret=_process_packet(vf,1);
int ret=_fetch_and_process_packet(vf,NULL,1);
if(ret==OV_EOF)return(0);
if(ret<=0)return(ret);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment