* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
- * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 *
+ * by the Xiph.Org Foundation http://www.xiph.org/ *
* *
********************************************************************
function: bitrate tracking and management
- last mod: $Id: bitrate.c,v 1.22 2003/12/30 11:02:22 xiphmont Exp $
+ last mod: $Id$
********************************************************************/
#include "misc.h"
#include "bitrate.h"
-/* compute bitrate tracking setup, allocate circular packet size queue */
+/* compute bitrate tracking setup */
void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){
codec_setup_info *ci=vi->codec_setup;
bitrate_manager_info *bi=&ci->bi;
memset(bm,0,sizeof(*bm));
-
+
if(bi && (bi->reservoir_bits>0)){
long ratesamples=vi->rate;
int halfsamples=ci->blocksizes[0]>>1;
bm->avg_bitsper= rint(1.*bi->avg_rate*halfsamples/ratesamples);
bm->min_bitsper= rint(1.*bi->min_rate*halfsamples/ratesamples);
bm->max_bitsper= rint(1.*bi->max_rate*halfsamples/ratesamples);
-
- bm->avgfloat=PACKETBLOBS/2;
- }
+ bm->avgfloat=PACKETBLOBS/2;
+
+ /* not a necessary fix, but one that leads to a more balanced
+ typical initialization */
+ {
+ long desired_fill=bi->reservoir_bits*bi->reservoir_bias;
+ bm->minmax_reservoir=desired_fill;
+ bm->avg_reservoir=desired_fill;
+ }
+
+ }
}
void vorbis_bitrate_clear(bitrate_manager_state *bm){
int vorbis_bitrate_managed(vorbis_block *vb){
vorbis_dsp_state *vd=vb->vd;
- private_state *b=vd->backend_state;
+ private_state *b=vd->backend_state;
bitrate_manager_state *bm=&b->bms;
if(bm && bm->managed)return(1);
int vorbis_bitrate_addblock(vorbis_block *vb){
vorbis_block_internal *vbi=vb->internal;
vorbis_dsp_state *vd=vb->vd;
- private_state *b=vd->backend_state;
+ private_state *b=vd->backend_state;
bitrate_manager_state *bm=&b->bms;
vorbis_info *vi=vd->vi;
codec_setup_info *ci=vi->codec_setup;
long desired_fill=bi->reservoir_bits*bi->reservoir_bias;
if(!bm->managed){
/* not a bitrate managed stream, but for API simplicity, we'll
- buffer one packet to keep the code path clean */
-
+ buffer the packet to keep the code path clean */
+
if(bm->vb)return(-1); /* one has been submitted without
- being claimed */
+ being claimed */
bm->vb=vb;
return(0);
}
bm->vb=vb;
-
+
/* look ahead for avg floater */
if(bm->avg_bitsper>0){
double slew=0.;
if(bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){
while(choice>0 && this_bits>avg_target_bits &&
- bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){
- choice--;
- this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+ bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){
+ choice--;
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
}
}else if(bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){
while(choice+1<PACKETBLOBS && this_bits<avg_target_bits &&
- bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){
- choice++;
- this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+ bm->avg_reservoir+(this_bits-avg_target_bits)<desired_fill){
+ choice++;
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
}
}
/* do we need to force the bitrate up? */
if(this_bits<min_target_bits){
while(bm->minmax_reservoir-(min_target_bits-this_bits)<0){
- choice++;
- if(choice>=PACKETBLOBS)break;
- this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+ choice++;
+ if(choice>=PACKETBLOBS)break;
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
}
}
}
-
+
/* enforce max (if used) on the current floater (if used) */
if(bm->max_bitsper>0){
/* do we need to force the bitrate down? */
if(this_bits>max_target_bits){
while(bm->minmax_reservoir+(this_bits-max_target_bits)>bi->reservoir_bits){
- choice--;
- if(choice<0)break;
- this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+ choice--;
+ if(choice<0)break;
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
}
}
}
/* Choice of packetblobs now made based on floater, and min/max
requirements. Now boundary check extreme choices */
- if(choice>=PACKETBLOBS){
- /* choosing a larger packetblob is insufficient to prop up bitrate.
- pad this frame out with zeroes */
- long minsize=(min_target_bits-bm->minmax_reservoir+7)/8;
- bm->choice=choice=PACKETBLOBS-1;
-
- minsize-=oggpack_bytes(vbi->packetblob[choice]);
-
- while(minsize--)oggpack_write(vbi->packetblob[choice],0,8);
- this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
-
- }else if(choice<0){
+ if(choice<0){
/* choosing a smaller packetblob is insufficient to trim bitrate.
frame will need to be truncated */
long maxsize=(max_target_bits+(bi->reservoir_bits-bm->minmax_reservoir))/8;
oggpack_writetrunc(vbi->packetblob[choice],maxsize*8);
this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
}
- }else
+ }else{
+ long minsize=(min_target_bits-bm->minmax_reservoir+7)/8;
+ if(choice>=PACKETBLOBS)
+ choice=PACKETBLOBS-1;
+
bm->choice=choice;
+ /* prop up bitrate according to demand. pad this frame out with zeroes */
+ minsize-=oggpack_bytes(vbi->packetblob[choice]);
+ while(minsize-->0)oggpack_write(vbi->packetblob[choice],0,8);
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+
+ }
+
/* now we have the final packet and the final packet size. Update statistics */
/* min and max reservoir */
if(bm->min_bitsper>0 || bm->max_bitsper>0){
}else{
/* inbetween; we want to take reservoir toward but not past desired_fill */
if(bm->minmax_reservoir>desired_fill){
- bm->minmax_reservoir+=(this_bits-max_target_bits);
- if(bm->minmax_reservoir<desired_fill)bm->minmax_reservoir=desired_fill;
+ if(max_target_bits>0){ /* logical bulletproofing against initialization state */
+ bm->minmax_reservoir+=(this_bits-max_target_bits);
+ if(bm->minmax_reservoir<desired_fill)bm->minmax_reservoir=desired_fill;
+ }else{
+ bm->minmax_reservoir=desired_fill;
+ }
}else{
- bm->minmax_reservoir+=(this_bits-min_target_bits);
- if(bm->minmax_reservoir>desired_fill)bm->minmax_reservoir=desired_fill;
+ if(min_target_bits>0){ /* logical bulletproofing against initialization state */
+ bm->minmax_reservoir+=(this_bits-min_target_bits);
+ if(bm->minmax_reservoir>desired_fill)bm->minmax_reservoir=desired_fill;
+ }else{
+ bm->minmax_reservoir=desired_fill;
+ }
}
}
}
/* avg reservoir */
if(bm->avg_bitsper>0){
- long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper);
+ long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper);
bm->avg_reservoir+=this_bits-avg_target_bits;
}
if(op){
vorbis_block_internal *vbi=vb->internal;
-
+
if(vorbis_bitrate_managed(vb))
choice=bm->choice;
-
+
op->packet=oggpack_get_buffer(vbi->packetblob[choice]);
op->bytes=oggpack_bytes(vbi->packetblob[choice]);
op->b_o_s=0;
op->granulepos=vb->granulepos;
op->packetno=vb->sequence; /* for sake of completeness */
}
-
+
bm->vb=0;
return(1);
}