'greedy' bit reservoir system.
Advantages:
no additional encoder latency
more predictable buffering
lower memory usage
These changes introduce two new vorbis_encode_ctls to get to the new
functionality. The old ectl calls are still supported for
compatability and binary library compatability is not broken (but
libvorbis/file/enc need to be upgraded as a set).
...now going to update oggenc.
Monty
svn path=/trunk/vorbis/; revision=5726
********************************************************************
function: vorbis encode-engine setup
- last mod: $Id: vorbisenc.h,v 1.10 2002/07/01 11:20:10 xiphmont Exp $
+ last mod: $Id: vorbisenc.h,v 1.11 2003/12/30 11:02:21 xiphmont Exp $
********************************************************************/
extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
+ /* deprecated rate management supported only for compatability */
#define OV_ECTL_RATEMANAGE_GET 0x10
-
#define OV_ECTL_RATEMANAGE_SET 0x11
#define OV_ECTL_RATEMANAGE_AVG 0x12
#define OV_ECTL_RATEMANAGE_HARD 0x13
-#define OV_ECTL_LOWPASS_GET 0x20
-#define OV_ECTL_LOWPASS_SET 0x21
-
-#define OV_ECTL_IBLOCK_GET 0x30
-#define OV_ECTL_IBLOCK_SET 0x31
-
struct ovectl_ratemanage_arg {
int management_active;
double bitrate_av_window_center;
};
+
+ /* new rate setup */
+#define OV_ECTL_RATEMANAGE2_GET 0x14
+#define OV_ECTL_RATEMANAGE2_SET 0x15
+
+struct ovectl_ratemanage2_arg {
+ int management_active;
+
+ long bitrate_limit_min_kbps;
+ long bitrate_limit_max_kbps;
+ long bitrate_limit_reservoir_bits;
+ double bitrate_limit_reservoir_bias;
+
+ long bitrate_average_kbps;
+ double bitrate_average_damping;
+};
+
+
+
+#define OV_ECTL_LOWPASS_GET 0x20
+#define OV_ECTL_LOWPASS_SET 0x21
+
+#define OV_ECTL_IBLOCK_GET 0x30
+#define OV_ECTL_IBLOCK_SET 0x31
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
********************************************************************
function: single-block PCM analysis mode dispatch
- last mod: $Id: analysis.c,v 1.55 2002/07/11 06:40:48 xiphmont Exp $
+ last mod: $Id: analysis.c,v 1.56 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
/* decides between modes, dispatches to the appropriate mapping. */
int vorbis_analysis(vorbis_block *vb, ogg_packet *op){
- int ret;
+ int ret,i;
+ vorbis_block_internal *vbi=vb->internal;
vb->glue_bits=0;
vb->time_bits=0;
vb->res_bits=0;
/* first things first. Make sure encode is ready */
- oggpack_reset(&vb->opb);
+ for(i=0;i<PACKETBLOBS;i++)
+ oggpack_reset(vbi->packetblob[i]);
/* we only have one mapping type (0), and we let the mapping code
itself figure out what soft mode to use. This allows easier
function: libvorbis backend and mapping structures; needed for
static mode headers
- last mod: $Id: backends.h,v 1.14 2002/07/11 06:40:48 xiphmont Exp $
+ last mod: $Id: backends.h,v 1.15 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
void (*free_look) (vorbis_look_residue *);
long **(*class) (struct vorbis_block *,vorbis_look_residue *,
float **,int *,int);
- int (*forward) (struct vorbis_block *,vorbis_look_residue *,
+ int (*forward) (oggpack_buffer *,struct vorbis_block *,
+ vorbis_look_residue *,
float **,float **,int *,int,long **);
int (*inverse) (struct vorbis_block *,vorbis_look_residue *,
float **,int *,int);
********************************************************************
function: bitrate tracking and management
- last mod: $Id: bitrate.c,v 1.21 2002/10/11 11:14:41 xiphmont Exp $
+ last mod: $Id: bitrate.c,v 1.22 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "misc.h"
#include "bitrate.h"
-
-static long BINBYTES(bitrate_manager_state *bm,long pos,long bin){
- int bins=bm->queue_bins;
- return(bm->queue_binned[pos*bins+bin]);
-}
-
-#define LIMITBYTES(pos,bin) (bm->minmax_binstack[(pos)*bins*2+((bin)+bins)])
-
-static long LACING_ADJUST(long bytes){
- int addto=bytes/255+1;
- return(bytes+addto);
-}
-
-static int floater_interpolate(bitrate_manager_state *bm,vorbis_info *vi,
- double desired_rate){
- int bin=rint(bm->avgfloat);
- double lobitrate,hibitrate;
-
-
- lobitrate=(double)(bm->avg_binacc[bin]*8)/bm->avg_sampleacc*vi->rate;
- while(lobitrate>desired_rate && bin>0){
- bin--;
- lobitrate=(double)(bm->avg_binacc[bin]*8)/bm->avg_sampleacc*vi->rate;
- }
-
- if(bin+1<bm->queue_bins){
- hibitrate=(double)(bm->avg_binacc[bin+1]*8)/bm->avg_sampleacc*vi->rate;
- if(fabs(hibitrate-desired_rate) < fabs(lobitrate-desired_rate))bin++;
- }
- return(bin);
-}
-
-/* try out a new limit */
-static long limit_sum(bitrate_manager_state *bm,int limit){
- int i=bm->minmax_stackptr;
- long acc=bm->minmax_acctotal;
- long bins=bm->queue_bins;
-
- acc-=LIMITBYTES(i,0);
- acc+=LIMITBYTES(i,limit);
-
- while(i-->0){
- if(bm->minmax_limitstack[i]<=limit)break;
- acc-=LIMITBYTES(i,bm->minmax_limitstack[i]);
- acc+=LIMITBYTES(i,limit);
- }
- return(acc);
-}
-
/* compute bitrate tracking setup, allocate circular packet size queue */
void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){
- int i;
codec_setup_info *ci=vi->codec_setup;
bitrate_manager_info *bi=&ci->bi;
- long maxlatency;
memset(bm,0,sizeof(*bm));
- if(bi){
-
- bm->avg_sampledesired=bi->queue_avg_time*vi->rate;
- bm->avg_centerdesired=bi->queue_avg_time*vi->rate*bi->queue_avg_center;
- bm->minmax_sampledesired=bi->queue_minmax_time*vi->rate;
-
- /* first find the max possible needed queue size */
- maxlatency=max(bm->avg_sampledesired-bm->avg_centerdesired,
- bm->minmax_sampledesired)+bm->avg_centerdesired;
+ if(bi && (bi->reservoir_bits>0)){
+ long ratesamples=vi->rate;
+ int halfsamples=ci->blocksizes[0]>>1;
+
+ bm->short_per_long=ci->blocksizes[1]/ci->blocksizes[0];
+ bm->managed=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);
- if(maxlatency>0 &&
- (bi->queue_avgmin>0 || bi->queue_avgmax>0 || bi->queue_hardmax>0 ||
- bi->queue_hardmin>0)){
- long maxpackets=maxlatency/(ci->blocksizes[0]>>1)+3;
- long bins=PACKETBLOBS;
-
- bm->queue_size=maxpackets;
- bm->queue_bins=bins;
- bm->queue_binned=_ogg_calloc(maxpackets,bins*sizeof(*bm->queue_binned));
- bm->queue_actual=_ogg_calloc(maxpackets,sizeof(*bm->queue_actual));
-
- if((bi->queue_avgmin>0 || bi->queue_avgmax>0) &&
- bi->queue_avg_time>0){
-
- bm->avg_binacc=_ogg_calloc(bins,sizeof(*bm->avg_binacc));
- bm->avgfloat=PACKETBLOBS/2;
-
- }else{
- bm->avg_tail= -1;
- }
-
- if((bi->queue_hardmin>0 || bi->queue_hardmax>0) &&
- bi->queue_minmax_time>0){
-
- bm->minmax_binstack=_ogg_calloc((bins*2+1)*bins*2,
- sizeof(*bm->minmax_binstack));
- bm->minmax_posstack=_ogg_calloc((bins*2+1),
- sizeof(*bm->minmax_posstack));
- bm->minmax_limitstack=_ogg_calloc((bins*2+1),
- sizeof(*bm->minmax_limitstack));
- }else{
- bm->minmax_tail= -1;
- }
-
- /* space for the packet queueing */
- bm->packetbuffers=_ogg_calloc(maxpackets,sizeof(*bm->packetbuffers));
- bm->packets=_ogg_calloc(maxpackets,sizeof(*bm->packets));
- for(i=0;i<maxpackets;i++)
- oggpack_writeinit(bm->packetbuffers+i);
-
- }else{
- bm->packetbuffers=_ogg_calloc(1,sizeof(*bm->packetbuffers));
- bm->packets=_ogg_calloc(1,sizeof(*bm->packets));
- oggpack_writeinit(bm->packetbuffers);
+ bm->avgfloat=PACKETBLOBS/2;
- }
- }
+ }
}
void vorbis_bitrate_clear(bitrate_manager_state *bm){
- int i;
- if(bm){
- if(bm->queue_binned)_ogg_free(bm->queue_binned);
- if(bm->queue_actual)_ogg_free(bm->queue_actual);
- if(bm->avg_binacc)_ogg_free(bm->avg_binacc);
- if(bm->minmax_binstack)_ogg_free(bm->minmax_binstack);
- if(bm->minmax_posstack)_ogg_free(bm->minmax_posstack);
- if(bm->minmax_limitstack)_ogg_free(bm->minmax_limitstack);
-
- if(bm->packetbuffers){
- if(bm->queue_size==0){
- oggpack_writeclear(bm->packetbuffers);
- }else{
- for(i=0;i<bm->queue_size;i++)
- oggpack_writeclear(bm->packetbuffers+i);
- }
- _ogg_free(bm->packetbuffers);
- }
- if(bm->packets)_ogg_free(bm->packets);
-
- memset(bm,0,sizeof(*bm));
- }
+ memset(bm,0,sizeof(*bm));
+ return;
}
int vorbis_bitrate_managed(vorbis_block *vb){
private_state *b=vd->backend_state;
bitrate_manager_state *bm=&b->bms;
- if(bm->queue_binned)return(1);
+ if(bm && bm->managed)return(1);
return(0);
}
/* finish taking in the block we just processed */
int vorbis_bitrate_addblock(vorbis_block *vb){
- int i;
vorbis_block_internal *vbi=vb->internal;
vorbis_dsp_state *vd=vb->vd;
private_state *b=vd->backend_state;
vorbis_info *vi=vd->vi;
codec_setup_info *ci=vi->codec_setup;
bitrate_manager_info *bi=&ci->bi;
- int eofflag=vb->eofflag;
- int head=bm->queue_head;
- int next_head=head+1;
- int bins=bm->queue_bins;
- int minmax_head,new_minmax_head;
-
- ogg_uint32_t *head_ptr;
- oggpack_buffer temp;
- if(!bm->queue_binned){
- oggpack_buffer temp;
+ int choice=rint(bm->avgfloat);
+ long this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+ long min_target_bits=(vb->W?bm->min_bitsper*bm->short_per_long:bm->min_bitsper);
+ long max_target_bits=(vb->W?bm->max_bitsper*bm->short_per_long:bm->max_bitsper);
+ int samples=ci->blocksizes[vb->W]>>1;
+ 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 */
- if(bm->queue_head)return(-1); /* one has been submitted without
- being claimed */
- bm->queue_head++;
-
- bm->packets[0].packet=oggpack_get_buffer(&vb->opb);
- bm->packets[0].bytes=oggpack_bytes(&vb->opb);
- bm->packets[0].b_o_s=0;
- bm->packets[0].e_o_s=vb->eofflag;
- bm->packets[0].granulepos=vb->granulepos;
- bm->packets[0].packetno=vb->sequence; /* for sake of completeness */
-
- memcpy(&temp,bm->packetbuffers,sizeof(vb->opb));
- memcpy(bm->packetbuffers,&vb->opb,sizeof(vb->opb));
- memcpy(&vb->opb,&temp,sizeof(vb->opb));
-
+ if(bm->vb)return(-1); /* one has been submitted without
+ being claimed */
+ bm->vb=vb;
return(0);
}
- /* add encoded packet to head */
- if(next_head>=bm->queue_size)next_head=0;
- head_ptr=bm->queue_binned+bins*head;
-
- /* is there room to add a block? In proper use of the API, this will
- never come up... but guard it anyway */
- if(next_head==bm->avg_tail || next_head==bm->minmax_tail)return(-1);
-
- /* add the block to the toplevel queue */
- bm->queue_head=next_head;
- bm->queue_actual[head]=(vb->W?0x80000000UL:0);
-
- /* buffer packet fields */
- bm->packets[head].packet=oggpack_get_buffer(&vb->opb);
- bm->packets[head].bytes=oggpack_bytes(&vb->opb);
- bm->packets[head].b_o_s=0;
- bm->packets[head].e_o_s=vb->eofflag;
- bm->packets[head].granulepos=vb->granulepos;
- bm->packets[head].packetno=vb->sequence; /* for sake of completeness */
-
- /* swap packet buffers */
- memcpy(&temp,bm->packetbuffers+head,sizeof(vb->opb));
- memcpy(bm->packetbuffers+head,&vb->opb,sizeof(vb->opb));
- memcpy(&vb->opb,&temp,sizeof(vb->opb));
-
- /* save markers */
- head_ptr[0]=vbi->packetblob_markers[0];
- for(i=1;i<PACKETBLOBS;i++){
- head_ptr[i]=vbi->packetblob_markers[i]-vbi->packetblob_markers[i-1];
- }
-
- if(bm->avg_binacc)
- new_minmax_head=minmax_head=bm->avg_center;
- else
- new_minmax_head=minmax_head=head;
-
- /* the average tracking queue is updated first; its results (if it's
- in use) are taken into account by the min/max limiter (if min/max
- is in use) */
- if(bm->avg_binacc){
- unsigned long desired_center=bm->avg_centerdesired;
- if(eofflag)desired_center=0;
-
- /* update the avg head */
- for(i=0;i<bins;i++)
- bm->avg_binacc[i]+=LACING_ADJUST(head_ptr[i]);
- bm->avg_sampleacc+=ci->blocksizes[vb->W]>>1;
- bm->avg_centeracc+=ci->blocksizes[vb->W]>>1;
-
- if(bm->avg_sampleacc>bm->avg_sampledesired || eofflag){
-
- /* update the avg center */
- if(bm->avg_centeracc>desired_center){
- /* choose the new average floater */
- int samples=ci->blocksizes[vb->W]>>1;
- double upper=floater_interpolate(bm,vi,bi->queue_avgmax);
- double lower=floater_interpolate(bm,vi,bi->queue_avgmin);
- double new=PACKETBLOBS/2.,slew;
- int bin;
-
- if(upper<new)new=upper;
- if(lower>new)new=lower;
-
- slew=(new-bm->avgfloat)/samples*vi->rate;
-
- if(slew<bi->avgfloat_downslew_max)
- new=bm->avgfloat+bi->avgfloat_downslew_max/vi->rate*samples;
- if(slew>bi->avgfloat_upslew_max)
- new=bm->avgfloat+bi->avgfloat_upslew_max/vi->rate*samples;
-
- bm->avgfloat=new;
- /* apply the average floater to new blocks */
- bin=rint(bm->avgfloat);
-
- /*fprintf(stderr,"%d ",bin);*/
-
- while(bm->avg_centeracc>desired_center){
- samples=ci->blocksizes[bm->queue_actual[bm->avg_center]&
- 0x80000000UL?1:0]>>1;
-
- bm->queue_actual[bm->avg_center]|=bin;
-
- bm->avg_centeracc-=samples;
- bm->avg_center++;
- if(bm->avg_center>=bm->queue_size)bm->avg_center=0;
- }
- new_minmax_head=bm->avg_center;
-
+ bm->vb=vb;
+
+ /* look ahead for avg floater */
+ if(bm->avg_bitsper>0){
+ double slew=0.;
+ long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper);
+ double slewlimit= 15./bi->slew_damp;
+
+ /* choosing a new floater:
+ if we're over target, we slew down
+ if we're under target, we slew up
+
+ choose slew as follows: look through packetblobs of this frame
+ and set slew as the first in the appropriate direction that
+ gives us the slew we want. This may mean no slew if delta is
+ already favorable.
+
+ Then limit slew to slew max */
+
+ 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;
}
-
- /* update the avg tail if needed */
- while(bm->avg_sampleacc>bm->avg_sampledesired){
- int samples=
- ci->blocksizes[bm->queue_actual[bm->avg_tail]&0x80000000UL?1:0]>>1;
- for(i=0;i<bm->queue_bins;i++)
- bm->avg_binacc[i]-=LACING_ADJUST(bm->queue_binned[bins*bm->avg_tail+i]);
- bm->avg_sampleacc-=samples;
- bm->avg_tail++;
- if(bm->avg_tail>=bm->queue_size)bm->avg_tail=0;
+ }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;
}
-
-
}
- }else{
- /* if we're not using an average tracker, the 'float' is nailed to
- the avgfloat_initial value. It needs to be set for the min/max
- to deal properly */
- long bin=PACKETBLOBS/2;
- bm->queue_actual[head]|=bin;
- new_minmax_head=next_head;
- }
-
- /* update the min/max queues and enforce limits */
- if(bm->minmax_binstack){
- unsigned long sampledesired=eofflag?0:bm->minmax_sampledesired;
-
- /* add to stack recent */
- while(minmax_head!=new_minmax_head){
- unsigned int i;
- int samples=ci->blocksizes[bm->queue_actual[minmax_head]&
- 0x80000000UL?1:0]>>1;
- int actual=bm->queue_actual[minmax_head]&0x7fffffffUL;
-
- for(i=0;i<(unsigned int)bins;i++){
- bm->minmax_binstack[bm->minmax_stackptr*bins*2+bins+i]+=
- LACING_ADJUST(BINBYTES(bm,minmax_head,
- actual>i?actual:i));
-
- bm->minmax_binstack[bm->minmax_stackptr*bins*2+i]+=
- LACING_ADJUST(BINBYTES(bm,minmax_head,
- actual<i?actual:i));
- }
-
- bm->minmax_posstack[bm->minmax_stackptr]=minmax_head; /* not one
- past
- like
- typical */
- bm->minmax_limitstack[bm->minmax_stackptr]=0;
- bm->minmax_sampleacc+=samples;
- bm->minmax_acctotal+=
- LACING_ADJUST(BINBYTES(bm,minmax_head,actual));
-
- minmax_head++;
- if(minmax_head>=bm->queue_size)minmax_head=0;
+
+ slew=rint(choice-bm->avgfloat)/samples*vi->rate;
+ if(slew<-slewlimit)slew=-slewlimit;
+ if(slew>slewlimit)slew=slewlimit;
+ choice=rint(bm->avgfloat+= slew/vi->rate*samples);
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
+ }
- }
-
- /* check limits, enforce changes */
- if(bm->minmax_sampleacc>sampledesired){
- double bitrate=(double)(bm->minmax_acctotal*8)/
- bm->minmax_sampleacc*vi->rate;
- int limit=0;
-
- if((bi->queue_hardmax>0 && bitrate>bi->queue_hardmax) ||
- (bi->queue_hardmin>0 && bitrate<bi->queue_hardmin)){
- int newstack;
- int stackctr;
- long bitsum=bm->minmax_acctotal*8;
-
- bitrate=(double)bitsum/bm->minmax_sampleacc*vi->rate;
-
- /* we're off rate. Iteratively try out new hard floater
- limits until we find one that brings us inside. Here's
- where we see the whole point of the limit stacks. */
- if(bi->queue_hardmax>0 && bitrate>bi->queue_hardmax){
- for(limit=-1;limit>-bins+1;limit--){
- long bitsum=limit_sum(bm,limit)*8;
- bitrate=(double)bitsum/bm->minmax_sampleacc*vi->rate;
- if(bitrate<=bi->queue_hardmax)break;
- }
- }else if(bitrate<bi->queue_hardmin){
- for(limit=1;limit<bins-1;limit++){
- long bitsum=limit_sum(bm,limit)*8;
- bitrate=(double)bitsum/bm->minmax_sampleacc*vi->rate;
- if(bitrate>=bi->queue_hardmin)break;
- }
- if(bitrate>bi->queue_hardmax)limit--;
- }
-
- /* trace the limit backward, stop when we see a lower limit */
- newstack=bm->minmax_stackptr-1;
- while(newstack>=0){
- if(bm->minmax_limitstack[newstack]<limit)break;
- newstack--;
- }
-
- /* update bit counter with new limit and replace any stack
- limits that have been replaced by our new lower limit */
- stackctr=bm->minmax_stackptr;
- while(stackctr>newstack){
- bm->minmax_acctotal-=
- LIMITBYTES(stackctr,bm->minmax_limitstack[stackctr]);
- bm->minmax_acctotal+=LIMITBYTES(stackctr,limit);
-
- if(stackctr<bm->minmax_stackptr)
- for(i=0;i<bins*2;i++)
- bm->minmax_binstack[stackctr*bins*2+i]+=
- bm->minmax_binstack[(stackctr+1)*bins*2+i];
-
- stackctr--;
- }
- stackctr++;
- bm->minmax_posstack[stackctr]=bm->minmax_posstack[bm->minmax_stackptr];
- bm->minmax_limitstack[stackctr]=limit;
-
- /* set up new blank stack entry */
- stackctr++;
- bm->minmax_stackptr=stackctr;
- memset(&bm->minmax_binstack[stackctr*bins*2],
- 0,
- sizeof(*bm->minmax_binstack)*bins*2);
- bm->minmax_limitstack[stackctr]=0;
- bm->minmax_posstack[stackctr]=-1;
-
+
+ /* enforce min(if used) on the current floater (if used) */
+ if(bm->min_bitsper>0){
+ /* 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;
}
}
-
- /* remove from tail */
- while(bm->minmax_sampleacc>sampledesired){
- int samples=
- ci->blocksizes[bm->queue_actual[bm->minmax_tail]&0x80000000UL?1:0]>>1;
- int actual=bm->queue_actual[bm->minmax_tail]&0x7fffffffUL;
-
- for(i=0;i<bins;i++){
- bm->minmax_binstack[bins+i]-= /* always comes off the stack bottom */
- LACING_ADJUST(BINBYTES(bm,bm->minmax_tail,
- actual>i?
- actual:i));
- bm->minmax_binstack[i]-=
- LACING_ADJUST(BINBYTES(bm,bm->minmax_tail,
- actual<i?
- actual:i));
- }
-
- if(bm->minmax_limitstack[0]>actual)
- actual=bm->minmax_limitstack[0];
- if(bins+bm->minmax_limitstack[0]<actual)
- actual=bins+bm->minmax_limitstack[0];
-
- bm->minmax_acctotal-=LACING_ADJUST(BINBYTES(bm,bm->minmax_tail,actual));
- bm->minmax_sampleacc-=samples;
-
- /* revise queue_actual to reflect the limit */
- bm->queue_actual[bm->minmax_tail]&=0x80000000UL;
- bm->queue_actual[bm->minmax_tail]|=actual;
-
- if(bm->minmax_tail==bm->minmax_posstack[0]){
- /* the stack becomes a FIFO; the first data has fallen off */
- memmove(bm->minmax_binstack,bm->minmax_binstack+bins*2,
- sizeof(*bm->minmax_binstack)*bins*2*bm->minmax_stackptr);
- memmove(bm->minmax_posstack,bm->minmax_posstack+1,
- sizeof(*bm->minmax_posstack)*bm->minmax_stackptr);
- memmove(bm->minmax_limitstack,bm->minmax_limitstack+1,
- sizeof(*bm->minmax_limitstack)*bm->minmax_stackptr);
- bm->minmax_stackptr--;
+ }
+
+ /* 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;
}
-
- bm->minmax_tail++;
- if(bm->minmax_tail>=bm->queue_size)bm->minmax_tail=0;
-
}
-
-
- bm->last_to_flush=bm->minmax_tail;
- }else{
- bm->last_to_flush=bm->avg_center;
}
- if(eofflag)
- bm->last_to_flush=bm->queue_head;
- return(0);
-}
-
-int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,ogg_packet *op){
- private_state *b=vd->backend_state;
- bitrate_manager_state *bm=&b->bms;
- if(bm->queue_size==0){
- if(bm->queue_head==0)return(0);
+ /* Choice of packetblobs now made based on floater, and min/max
+ requirements. Now boundary check extreme choices */
- memcpy(op,bm->packets,sizeof(*op));
- bm->queue_head=0;
+ 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;
- }else{
+ minsize-=oggpack_bytes(vbi->packetblob[choice]);
- if(bm->next_to_flush==bm->last_to_flush)return(0);
+ while(minsize--)oggpack_write(vbi->packetblob[choice],0,8);
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
- {
- long bin=bm->queue_actual[bm->next_to_flush]&0x7fffffff,i;
- long bins=bm->queue_bins;
- ogg_uint32_t *markers=bm->queue_binned+bins*bm->next_to_flush;
- long bytes=markers[bin];
+ }else 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;
+ bm->choice=choice=0;
- memcpy(op,bm->packets+bm->next_to_flush,sizeof(*op));
+ if(oggpack_bytes(vbi->packetblob[choice])>maxsize){
- /* we have [PACKETBLOBS] possible packets all squished together in
- the buffer, in sequence. count in to number [bin] */
- for(i=0;i<bin;i++)
- op->packet+=markers[i];
- op->bytes=bytes;
-
+ oggpack_writetrunc(vbi->packetblob[choice],maxsize*8);
+ this_bits=oggpack_bytes(vbi->packetblob[choice])*8;
}
+ }else
+ bm->choice=choice;
- bm->next_to_flush++;
- if(bm->next_to_flush>=bm->queue_size)bm->next_to_flush=0;
+ /* 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){
+
+ if(max_target_bits>0 && this_bits>max_target_bits){
+ bm->minmax_reservoir+=(this_bits-max_target_bits);
+ }else if(min_target_bits>0 && this_bits<min_target_bits){
+ bm->minmax_reservoir+=(this_bits-min_target_bits);
+ }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;
+ }else{
+ bm->minmax_reservoir+=(this_bits-min_target_bits);
+ if(bm->minmax_reservoir>desired_fill)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);
+ bm->avg_reservoir+=this_bits-avg_target_bits;
}
+ return(0);
+}
+
+int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,ogg_packet *op){
+ private_state *b=vd->backend_state;
+ bitrate_manager_state *bm=&b->bms;
+ vorbis_block *vb=bm->vb;
+ int choice=PACKETBLOBS/2;
+ if(!vb)return 0;
+
+ 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->e_o_s=vb->eofflag;
+ op->granulepos=vb->granulepos;
+ op->packetno=vb->sequence; /* for sake of completeness */
+ }
+
+ bm->vb=0;
return(1);
}
********************************************************************
function: bitrate tracking and management
- last mod: $Id: bitrate.h,v 1.7 2002/07/11 06:40:48 xiphmont Exp $
+ last mod: $Id: bitrate.h,v 1.8 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
/* encode side bitrate tracking */
typedef struct bitrate_manager_state {
- ogg_uint32_t *queue_binned;
- ogg_uint32_t *queue_actual;
- int queue_size;
+ int managed;
- int queue_head;
- int queue_bins;
+ long avg_reservoir;
+ long minmax_reservoir;
+ long avg_bitsper;
+ long min_bitsper;
+ long max_bitsper;
- long *avg_binacc;
- int avg_center;
- int avg_tail;
- ogg_uint32_t avg_centeracc;
- ogg_uint32_t avg_sampleacc;
- ogg_uint32_t avg_sampledesired;
- ogg_uint32_t avg_centerdesired;
-
- long *minmax_binstack;
- long *minmax_posstack;
- long *minmax_limitstack;
- long minmax_stackptr;
-
- long minmax_acctotal;
- int minmax_tail;
- ogg_uint32_t minmax_sampleacc;
- ogg_uint32_t minmax_sampledesired;
-
- int next_to_flush;
- int last_to_flush;
-
+ long short_per_long;
double avgfloat;
- /* unfortunately, we need to hold queued packet data somewhere */
- oggpack_buffer *packetbuffers;
- ogg_packet *packets;
-
+ vorbis_block *vb;
+ int choice;
} bitrate_manager_state;
typedef struct bitrate_manager_info{
- /* detailed bitrate management setup */
- double queue_avg_time;
- double queue_avg_center;
- double queue_minmax_time;
- double queue_hardmin;
- double queue_hardmax;
- double queue_avgmin;
- double queue_avgmax;
+ long avg_rate;
+ long min_rate;
+ long max_rate;
+ long reservoir_bits;
+ double reservoir_bias;
- double avgfloat_downslew_max;
- double avgfloat_upslew_max;
+ double slew_damp;
} bitrate_manager_info;
********************************************************************
function: PCM data vector blocking, windowing and dis/reassembly
- last mod: $Id: block.c,v 1.75 2003/09/02 04:39:26 xiphmont Exp $
+ last mod: $Id: block.c,v 1.76 2003/12/30 11:02:22 xiphmont Exp $
Handle windowing, overlap-add, etc of the PCM vectors. This is made
more amusing by Vorbis' current two allowed block sizes.
#endif
int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){
+ int i;
memset(vb,0,sizeof(*vb));
vb->vd=v;
vb->localalloc=0;
if(v->analysisp){
vorbis_block_internal *vbi=
vb->internal=_ogg_calloc(1,sizeof(vorbis_block_internal));
- oggpack_writeinit(&vb->opb);
vbi->ampmax=-9999;
+
+ for(i=0;i<PACKETBLOBS;i++){
+ if(i==PACKETBLOBS/2){
+ vbi->packetblob[i]=&vb->opb;
+ }else{
+ vbi->packetblob[i]=
+ _ogg_calloc(1,sizeof(oggpack_buffer));
+ }
+ oggpack_writeinit(vbi->packetblob[i]);
+ }
}
return(0);
}
int vorbis_block_clear(vorbis_block *vb){
- if(vb->vd)
- if(vb->vd->analysisp)
- oggpack_writeclear(&vb->opb);
+ int i;
+ vorbis_block_internal *vbi=vb->internal;
+
_vorbis_block_ripcord(vb);
if(vb->localstore)_ogg_free(vb->localstore);
- if(vb->internal)
- _ogg_free(vb->internal);
-
+ if(vbi){
+ for(i=0;i<PACKETBLOBS;i++){
+ oggpack_writeclear(vbi->packetblob[i]);
+ if(i!=PACKETBLOBS/2)_ogg_free(vbi->packetblob[i]);
+ }
+ _ogg_free(vbi);
+ }
memset(vb,0,sizeof(*vb));
return(0);
}
********************************************************************
function: libvorbis codec headers
- last mod: $Id: codec_internal.h,v 1.17 2003/08/18 05:34:01 xiphmont Exp $
+ last mod: $Id: codec_internal.h,v 1.18 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
float ampmax;
int blocktype;
- ogg_uint32_t packetblob_markers[PACKETBLOBS];
+ oggpack_buffer *packetblob[PACKETBLOBS]; /* initialized, must be freed;
+ blob [PACKETBLOBS/2] points to
+ the oggpack_buffer in the
+ main vorbis_block */
} vorbis_block_internal;
typedef void vorbis_look_floor;
********************************************************************
function: floor backend 1 implementation
- last mod: $Id: floor1.c,v 1.26 2003/02/15 07:10:07 xiphmont Exp $
+ last mod: $Id: floor1.c,v 1.27 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
}
-int floor1_encode(vorbis_block *vb,vorbis_look_floor1 *look,
+int floor1_encode(oggpack_buffer *opb,vorbis_block *vb,
+ vorbis_look_floor1 *look,
int *post,int *ilogmask){
long i,j;
/* we have everything we need. pack it out */
/* mark nontrivial floor */
- oggpack_write(&vb->opb,1,1);
+ oggpack_write(opb,1,1);
/* beginning/end post */
look->frames++;
look->postbits+=ilog(look->quant_q-1)*2;
- oggpack_write(&vb->opb,out[0],ilog(look->quant_q-1));
- oggpack_write(&vb->opb,out[1],ilog(look->quant_q-1));
+ oggpack_write(opb,out[0],ilog(look->quant_q-1));
+ oggpack_write(opb,out[1],ilog(look->quant_q-1));
/* partition by partition */
}
/* write it */
look->phrasebits+=
- vorbis_book_encode(books+info->class_book[class],cval,&vb->opb);
+ vorbis_book_encode(books+info->class_book[class],cval,opb);
#ifdef TRAIN_FLOOR1
{
/* hack to allow training with 'bad' books */
if(out[j+k]<(books+book)->entries)
look->postbits+=vorbis_book_encode(books+book,
- out[j+k],&vb->opb);
+ out[j+k],opb);
/*else
fprintf(stderr,"+!");*/
return(1);
}
}else{
- oggpack_write(&vb->opb,0,1);
+ oggpack_write(opb,0,1);
memset(ilogmask,0,vb->pcmend/2*sizeof(*ilogmask));
seq++;
return(0);
********************************************************************
function: highlevel encoder setup struct seperated out for vorbisenc clarity
- last mod: $Id: highlevel.h,v 1.4 2002/07/01 11:20:11 xiphmont Exp $
+ last mod: $Id: highlevel.h,v 1.5 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
int managed;
long bitrate_min;
- long bitrate_av_lo;
- long bitrate_av_hi;
+ long bitrate_av;
+ double bitrate_av_damp;
long bitrate_max;
- double bitrate_limit_window;
- double bitrate_av_window;
- double bitrate_av_window_center;
+ long bitrate_reservoir;
+ double bitrate_reservoir_bias;
int impulse_block_p;
int noise_normalize_p;
********************************************************************
function: maintain the info structure, info <-> header packets
- last mod: $Id: info.c,v 1.62 2003/09/10 01:10:18 xiphmont Exp $
+ last mod: $Id: info.c,v 1.63 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
}
static int _vorbis_pack_comment(oggpack_buffer *opb,vorbis_comment *vc){
- char temp[]="Xiph.Org libVorbis I 20030909";
+ char temp[]="Xiph.Org libVorbis I 20031230";
int bytes = strlen(temp);
/* preamble */
********************************************************************
function: channel mapping 0 implementation
- last mod: $Id: mapping0.c,v 1.60 2003/09/01 23:05:49 xiphmont Exp $
+ last mod: $Id: mapping0.c,v 1.61 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
extern int *floor1_interpolate_fit(vorbis_block *vb,vorbis_look_floor *look,
int *A,int *B,
int del);
-extern int floor1_encode(vorbis_block *vb,vorbis_look_floor *look,
+extern int floor1_encode(oggpack_buffer *opb,vorbis_block *vb,
+ vorbis_look_floor *look,
int *post,int *ilogmask);
for(k=(vorbis_bitrate_managed(vb)?0:PACKETBLOBS/2);
k<=(vorbis_bitrate_managed(vb)?PACKETBLOBS-1:PACKETBLOBS/2);
k++){
+ oggpack_buffer *opb=vbi->packetblob[k];
/* start out our new packet blob with packet type and mode */
/* Encode the packet type */
- oggpack_write(&vb->opb,0,1);
+ oggpack_write(opb,0,1);
/* Encode the modenumber */
/* Encode frame mode, pre,post windowsize, then dispatch */
- oggpack_write(&vb->opb,modenumber,b->modebits);
+ oggpack_write(opb,modenumber,b->modebits);
if(vb->W){
- oggpack_write(&vb->opb,vb->lW,1);
- oggpack_write(&vb->opb,vb->nW,1);
+ oggpack_write(opb,vb->lW,1);
+ oggpack_write(opb,vb->nW,1);
}
/* encode floor, compute masking curve, sep out residue */
int *ilogmask=ilogmaskch[i]=
_vorbis_block_alloc(vb,n/2*sizeof(**gmdct));
- nonzero[i]=floor1_encode(vb,b->flr[info->floorsubmap[submap]],
+ nonzero[i]=floor1_encode(opb,vb,b->flr[info->floorsubmap[submap]],
floor_posts[i][k],
ilogmask);
#if 0
class(vb,b->residue[resnum],couple_bundle,zerobundle,ch_in_bundle);
_residue_P[ci->residue_type[resnum]]->
- forward(vb,b->residue[resnum],
+ forward(opb,vb,b->residue[resnum],
couple_bundle,NULL,zerobundle,ch_in_bundle,classifications);
}
- /* ok, done encoding. Mark this protopacket and prepare next. */
- oggpack_writealign(&vb->opb);
- vbi->packetblob_markers[k]=oggpack_bytes(&vb->opb);
-
+ /* ok, done encoding. Next protopacket. */
}
}
********************************************************************
function: residue backend 0, 1 and 2 implementation
- last mod: $Id: res0.c,v 1.49 2003/01/18 08:28:37 xiphmont Exp $
+ last mod: $Id: res0.c,v 1.50 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
return(partword);
}
-static int _01forward(vorbis_block *vb,vorbis_look_residue *vl,
+static int _01forward(oggpack_buffer *opb,
+ vorbis_block *vb,vorbis_look_residue *vl,
float **in,int ch,
long **partword,
int (*encode)(oggpack_buffer *,float *,int,
/* training hack */
if(val<look->phrasebook->entries)
- look->phrasebits+=vorbis_book_encode(look->phrasebook,val,&vb->opb);
+ look->phrasebits+=vorbis_book_encode(look->phrasebook,val,opb);
#if 0 /*def TRAIN_RES*/
else
fprintf(stderr,"!");
}
#endif
- ret=encode(&vb->opb,in[j]+offset,samples_per_partition,
+ ret=encode(opb,in[j]+offset,samples_per_partition,
statebook,accumulator);
look->postbits+=ret;
return(0);
}
-int res1_forward(vorbis_block *vb,vorbis_look_residue *vl,
+int res1_forward(oggpack_buffer *opb,vorbis_block *vb,vorbis_look_residue *vl,
float **in,float **out,int *nonzero,int ch,
long **partword){
int i,j,used=0,n=vb->pcmend/2;
}
if(used){
- int ret=_01forward(vb,vl,in,used,partword,_encodepart);
+ int ret=_01forward(opb,vb,vl,in,used,partword,_encodepart);
if(out){
used=0;
for(i=0;i<ch;i++)
/* res2 is slightly more different; all the channels are interleaved
into a single vector and encoded. */
-int res2_forward(vorbis_block *vb,vorbis_look_residue *vl,
+int res2_forward(oggpack_buffer *opb,
+ vorbis_block *vb,vorbis_look_residue *vl,
float **in,float **out,int *nonzero,int ch,
long **partword){
long i,j,k,n=vb->pcmend/2,used=0;
}
if(used){
- int ret=_01forward(vb,vl,&work,1,partword,_encodepart);
+ int ret=_01forward(opb,vb,vl,&work,1,partword,_encodepart);
/* update the sofar vector */
if(out){
for(i=0;i<ch;i++){
********************************************************************
function: simple programmatic interface for encoder mode setup
- last mod: $Id: vorbisenc.c,v 1.47 2002/07/11 06:40:50 xiphmont Exp $
+ last mod: $Id: vorbisenc.c,v 1.48 2003/12/30 11:02:22 xiphmont Exp $
********************************************************************/
vi->bitrate_nominal=setting_to_approx_bitrate(vi);
vi->bitrate_lower=hi->bitrate_min;
vi->bitrate_upper=hi->bitrate_max;
- vi->bitrate_window=hi->bitrate_limit_window;
+ vi->bitrate_window=(double)hi->bitrate_reservoir/vi->rate;
if(hi->managed){
- ci->bi.queue_avg_time=hi->bitrate_av_window;
- ci->bi.queue_avg_center=hi->bitrate_av_window_center;
- ci->bi.queue_minmax_time=hi->bitrate_limit_window;
- ci->bi.queue_hardmin=hi->bitrate_min;
- ci->bi.queue_hardmax=hi->bitrate_max;
- ci->bi.queue_avgmin=hi->bitrate_av_lo;
- ci->bi.queue_avgmax=hi->bitrate_av_hi;
- ci->bi.avgfloat_downslew_max=-999999.f;
- ci->bi.avgfloat_upslew_max=999999.f;
+ ci->bi.avg_rate=hi->bitrate_av;
+ ci->bi.min_rate=hi->bitrate_min;
+ ci->bi.max_rate=hi->bitrate_max;
+
+ ci->bi.reservoir_bits=hi->bitrate_reservoir;
+ ci->bi.reservoir_bias=
+ hi->bitrate_reservoir_bias;
+
+ ci->bi.slew_damp=hi->bitrate_av_damp;
+
}
return(0);
}
/* initialize management with sane defaults */
- /* initialize management with sane defaults */
hi->managed=1;
- hi->bitrate_av_window=4.;
- hi->bitrate_av_window_center=.5;
- hi->bitrate_limit_window=2.;
hi->bitrate_min=min_bitrate;
hi->bitrate_max=max_bitrate;
- hi->bitrate_av_lo=tnominal;
- hi->bitrate_av_hi=tnominal;
+ hi->bitrate_av=tnominal;
+ hi->bitrate_av_damp=.75f; /* full range in no less than 3/4 second */
+ hi->bitrate_reservoir=tnominal;
+ hi->bitrate_reservoir_bias=.2; /* bias toward hoarding bits */
return(ret);
int setp=(number&0xf); /* a read request has a low nibble of 0 */
if(setp && hi->set_in_stone)return(OV_EINVAL);
-
+
switch(number){
+
+ /* now deprecated *****************/
case OV_ECTL_RATEMANAGE_GET:
{
(struct ovectl_ratemanage_arg *)arg;
ai->management_active=hi->managed;
- ai->bitrate_av_window=hi->bitrate_av_window;
- ai->bitrate_av_window_center=hi->bitrate_av_window_center;
- ai->bitrate_hard_window=hi->bitrate_limit_window;
+ ai->bitrate_hard_window=ai->bitrate_av_window=
+ (double)hi->bitrate_reservoir/vi->rate;
+ ai->bitrate_av_window_center=1.;
ai->bitrate_hard_min=hi->bitrate_min;
ai->bitrate_hard_max=hi->bitrate_max;
- ai->bitrate_av_lo=hi->bitrate_av_lo;
- ai->bitrate_av_hi=hi->bitrate_av_hi;
+ ai->bitrate_av_lo=hi->bitrate_av;
+ ai->bitrate_av_hi=hi->bitrate_av;
}
return(0);
+ /* now deprecated *****************/
case OV_ECTL_RATEMANAGE_SET:
{
struct ovectl_ratemanage_arg *ai=
}
return 0;
+ /* now deprecated *****************/
case OV_ECTL_RATEMANAGE_AVG:
{
struct ovectl_ratemanage_arg *ai=
(struct ovectl_ratemanage_arg *)arg;
if(ai==NULL){
- hi->bitrate_av_lo=0;
- hi->bitrate_av_hi=0;
- hi->bitrate_av_window=0;
+ hi->bitrate_av=0;
}else{
- hi->bitrate_av_window=ai->bitrate_av_window;
- hi->bitrate_av_window_center=ai->bitrate_av_window_center;
- hi->bitrate_av_lo=ai->bitrate_av_lo;
- hi->bitrate_av_hi=ai->bitrate_av_hi;
+ hi->bitrate_av=(ai->bitrate_av_lo+ai->bitrate_av_hi)*.5;
}
-
- if(hi->bitrate_av_window<.25)hi->bitrate_av_window=.25;
- if(hi->bitrate_av_window>10.)hi->bitrate_av_window=10.;
- if(hi->bitrate_av_window_center<0.)hi->bitrate_av_window=0.;
- if(hi->bitrate_av_window_center>1.)hi->bitrate_av_window=1.;
-
- if( ( (hi->bitrate_av_lo<=0 && hi->bitrate_av_hi<=0)||
- (hi->bitrate_av_window<=0) ) &&
- ( (hi->bitrate_min<=0 && hi->bitrate_max<=0)||
- (hi->bitrate_limit_window<=0) ))
- hi->managed=0;
}
return(0);
+ /* now deprecated *****************/
case OV_ECTL_RATEMANAGE_HARD:
{
struct ovectl_ratemanage_arg *ai=
if(ai==NULL){
hi->bitrate_min=0;
hi->bitrate_max=0;
- hi->bitrate_limit_window=0;
}else{
- hi->bitrate_limit_window=ai->bitrate_hard_window;
hi->bitrate_min=ai->bitrate_hard_min;
hi->bitrate_max=ai->bitrate_hard_max;
+ hi->bitrate_reservoir=ai->bitrate_hard_window*
+ (hi->bitrate_max+hi->bitrate_min)*.5;
}
- if(hi->bitrate_limit_window<0.)hi->bitrate_limit_window=0.;
- if(hi->bitrate_limit_window>10.)hi->bitrate_limit_window=10.;
+ if(hi->bitrate_reservoir<128.)
+ hi->bitrate_reservoir=128.;
+ }
+ return(0);
+
+ /* replacement ratemanage interface */
+ case OV_ECTL_RATEMANAGE2_GET:
+ {
+ struct ovectl_ratemanage2_arg *ai=
+ (struct ovectl_ratemanage2_arg *)arg;
+ if(ai==NULL)return OV_EINVAL;
- if( ( (hi->bitrate_av_lo<=0 && hi->bitrate_av_hi<=0)||
- (hi->bitrate_av_window<=0) ) &&
- ( (hi->bitrate_min<=0 && hi->bitrate_max<=0)||
- (hi->bitrate_limit_window<=0) ))
- hi->managed=0;
+ ai->management_active=hi->managed;
+ ai->bitrate_limit_min_kbps=hi->bitrate_min;
+ ai->bitrate_limit_max_kbps=hi->bitrate_max;
+ ai->bitrate_average_kbps=hi->bitrate_av;
+ ai->bitrate_average_damping=hi->bitrate_av_damp;
+ ai->bitrate_limit_reservoir_bits=hi->bitrate_reservoir;
+ ai->bitrate_limit_reservoir_bias=hi->bitrate_reservoir_bias;
}
- return(0);
+ return (0);
+ case OV_ECTL_RATEMANAGE2_SET:
+ {
+ struct ovectl_ratemanage2_arg *ai=
+ (struct ovectl_ratemanage2_arg *)arg;
+ if(ai==NULL){
+ hi->managed=0;
+ }else{
+ /* sanity check; only catch invariant violations */
+ if(ai->bitrate_limit_min_kbps>0 &&
+ ai->bitrate_average_kbps>0 &&
+ ai->bitrate_limit_min_kbps>ai->bitrate_average_kbps)
+ return OV_EINVAL;
+
+ if(ai->bitrate_limit_max_kbps>0 &&
+ ai->bitrate_average_kbps>0 &&
+ ai->bitrate_limit_max_kbps<ai->bitrate_average_kbps)
+ return OV_EINVAL;
+
+ if(ai->bitrate_limit_min_kbps>0 &&
+ ai->bitrate_limit_max_kbps>0 &&
+ ai->bitrate_limit_min_kbps>ai->bitrate_limit_max_kbps)
+ return OV_EINVAL;
+
+ if(ai->bitrate_average_damping <= 0.)
+ return OV_EINVAL;
+
+ if(ai->bitrate_limit_reservoir_bits < 0)
+ return OV_EINVAL;
+ if(ai->bitrate_limit_reservoir_bias < 0.)
+ return OV_EINVAL;
+
+ if(ai->bitrate_limit_reservoir_bias > 1.)
+ return OV_EINVAL;
+
+ hi->managed=ai->management_active;
+ hi->bitrate_min=ai->bitrate_limit_min_kbps;
+ hi->bitrate_max=ai->bitrate_limit_max_kbps;
+ hi->bitrate_av=ai->bitrate_average_kbps;
+ hi->bitrate_av_damp=ai->bitrate_average_damping;
+ hi->bitrate_reservoir=ai->bitrate_limit_reservoir_bits;
+ hi->bitrate_reservoir_bias=ai->bitrate_limit_reservoir_bias;
+ }
+ }
+ return 0;
+
case OV_ECTL_LOWPASS_GET:
{
double *farg=(double *)arg;