[xiph-commits] r15826 - in branches/theora-thusnelda: examples lib/enc
tterribe at svn.xiph.org
tterribe at svn.xiph.org
Sat Mar 21 15:52:25 PDT 2009
Author: tterribe
Date: 2009-03-21 15:52:25 -0700 (Sat, 21 Mar 2009)
New Revision: 15826
Modified:
branches/theora-thusnelda/examples/encoder_example.c
branches/theora-thusnelda/lib/enc/codec_internal.h
branches/theora-thusnelda/lib/enc/encoder_quant.c
branches/theora-thusnelda/lib/enc/encoder_toplevel.c
branches/theora-thusnelda/lib/enc/mathops.c
branches/theora-thusnelda/lib/enc/mathops.h
Log:
More rate control clean-up.
Account for Ogg page overhead when initializing the codecs.
Set lambda using the _target_ quantizer, not the one we actually selected.
This gives us finer control at low rates, and slightly lowers the minimum
possible bitrate.
Modified: branches/theora-thusnelda/examples/encoder_example.c
===================================================================
--- branches/theora-thusnelda/examples/encoder_example.c 2009-03-21 21:11:55 UTC (rev 15825)
+++ branches/theora-thusnelda/examples/encoder_example.c 2009-03-21 22:52:25 UTC (rev 15826)
@@ -1217,7 +1217,11 @@
ti.aspect_numerator=video_par_n;
ti.aspect_denominator=video_par_d;
ti.colorspace=TH_CS_UNSPECIFIED;
- ti.target_bitrate=video_r;
+ /*Account for the Ogg page overhead.
+ This is 1 byte per 255 for lacing values, plus 26 bytes per 4096 bytes for
+ the page header, plus approximately 1/2 byte per packet (not accounted for
+ here).*/
+ ti.target_bitrate=(int)(64870*(ogg_int64_t)video_r>>16);
ti.quality=video_q;
ti.keyframe_granule_shift=6;
@@ -1249,7 +1253,8 @@
if(audio_q>-99)
ret = vorbis_encode_init_vbr(&vi,audio_ch,audio_hz,audio_q);
else
- ret = vorbis_encode_init(&vi,audio_ch,audio_hz,-1,audio_r,-1);
+ ret = vorbis_encode_init(&vi,audio_ch,audio_hz,-1,
+ (int)(64870*(ogg_int64_t)audio_r>>16),-1);
if(ret){
fprintf(stderr,"The Vorbis encoder could not set up a mode according to\n"
"the requested quality or bitrate.\n\n");
Modified: branches/theora-thusnelda/lib/enc/codec_internal.h
===================================================================
--- branches/theora-thusnelda/lib/enc/codec_internal.h 2009-03-21 21:11:55 UTC (rev 15825)
+++ branches/theora-thusnelda/lib/enc/codec_internal.h 2009-03-21 22:52:25 UTC (rev 15826)
@@ -194,9 +194,11 @@
ogg_int64_t fullness;
ogg_int64_t target;
ogg_int64_t max;
+ ogg_int64_t log_npixels;
unsigned exp[2];
unsigned scale[2];
int buf_delay;
+ int qtarget;
};
/* Encoder (Compressor) instance -- installed in a theora_state */
Modified: branches/theora-thusnelda/lib/enc/encoder_quant.c
===================================================================
--- branches/theora-thusnelda/lib/enc/encoder_quant.c 2009-03-21 21:11:55 UTC (rev 15825)
+++ branches/theora-thusnelda/lib/enc/encoder_quant.c 2009-03-21 22:52:25 UTC (rev 15826)
@@ -19,33 +19,19 @@
#include <string.h>
#include "codec_internal.h"
#include "quant_lookup.h"
+#include "mathops.h"
#define OC_QUANT_MAX (1024<<2)
static const unsigned OC_DC_QUANT_MIN[2]={4<<2,8<<2};
static const unsigned OC_AC_QUANT_MIN[2]={2<<2,4<<2};
-static int ilog(unsigned _v){
- int ret;
- for(ret=0;_v;ret++)_v>>=1;
- return ret;
-}
-
/*Reciprocal square root.
- Adapted from libcelt, which is (C) 2002-2008 Jean-Marc Valin.
- Return: A 4th-order polynomial approximation of 2**15/sqrt(_x).*/
+ Return: 2**15/sqrt(_x).*/
static ogg_uint16_t oc_rsqrt(ogg_uint32_t _x){
- ogg_int32_t n;
- int k;
- k=ilog(_x)-1>>1;
- if(k>7)_x>>=k-7<<1;
- else _x<<=7-k<<1;
- n=_x-32768;
- /*These can be implemented as 16x16->high 16 bit muls, but this is currently
- not performance critical code.
- Note the reliance on the arithmetic right shift, which is not guaranteed by
- ANSI.*/
- return (ogg_uint16_t)(
- (n*((n*((n*((n*4100>>15)-9097)>>15)+9812)>>15)-11496)>>15)+23126>>k);
+ /*A simple polynomial approximation of this would be fine, but since we have
+ the routines and this is not performance critical, let's do it
+ accurately.*/
+ return (ogg_uint16_t)oc_bexp64(OC_Q57(15)-(oc_blog64(_x)>>1));
}
void oc_quant_params_pack(oggpack_buffer *_opb,const th_quant_info *_qinfo){
@@ -67,17 +53,17 @@
index 0, so search for it here.*/
i=_qinfo->loop_filter_limits[0];
for(qi=1;qi<64;qi++)i=OC_MAXI(i,_qinfo->loop_filter_limits[qi]);
- nbits=ilog(i);
+ nbits=OC_ILOG_32(i);
oggpackB_write(_opb,nbits,3);
for(qi=0;qi<64;qi++){
oggpackB_write(_opb,_qinfo->loop_filter_limits[qi],nbits);
}
/*580 bits for VP3.*/
- nbits=OC_MAXI(ilog(_qinfo->ac_scale[0]),1);
+ nbits=OC_MAXI(OC_ILOG_32(_qinfo->ac_scale[0]),1);
oggpackB_write(_opb,nbits-1,4);
for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->ac_scale[qi],nbits);
/*516 bits for VP3.*/
- nbits=OC_MAXI(ilog(_qinfo->dc_scale[0]),1);
+ nbits=OC_MAXI(OC_ILOG_32(_qinfo->dc_scale[0]),1);
oggpackB_write(_opb,nbits-1,4);
for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->dc_scale[qi],nbits);
/*Consolidate any duplicate base matrices.*/
@@ -108,7 +94,7 @@
/*Now store quant ranges and their associated indices into the base matrix
list.
46 bits for VP3 matrices.*/
- nbits=ilog(nbase_mats-1);
+ nbits=OC_ILOG_32(nbase_mats-1);
for(i=0;i<6;i++){
qti=i/3;
pli=i%3;
@@ -138,7 +124,7 @@
}
oggpackB_write(_opb,indices[qti][pli][0],nbits);
for(qi=qri=0;qi<63;qri++){
- oggpackB_write(_opb,qranges->sizes[qri]-1,ilog(62-qi));
+ oggpackB_write(_opb,qranges->sizes[qri]-1,OC_ILOG_32(62-qi));
qi+=qranges->sizes[qri];
oggpackB_write(_opb,indices[qti][pli][qri+1],nbits);
}
@@ -149,7 +135,7 @@
ogg_uint32_t t;
int l;
_d<<=1;
- l=ilog(_d)-1;
+ l=OC_ILOG_32(_d)-1;
t=1+((ogg_uint32_t)1<<16+l)/_d;
_this->m=(ogg_int16_t)(t-0x10000);
_this->l=l;
Modified: branches/theora-thusnelda/lib/enc/encoder_toplevel.c
===================================================================
--- branches/theora-thusnelda/lib/enc/encoder_toplevel.c 2009-03-21 21:11:55 UTC (rev 15825)
+++ branches/theora-thusnelda/lib/enc/encoder_toplevel.c 2009-03-21 22:52:25 UTC (rev 15826)
@@ -31,12 +31,18 @@
static void oc_enc_calc_lambda(CP_INSTANCE *cpi){
ogg_int64_t l;
+ int q;
/*For now, lambda is fixed depending on the qi value and frame type:
lambda=1.125*(qavg[qti][qi]**1.5)
A more adaptive scheme might perform better, but Theora's behavior does not
seem to conform to existing models in the literature.*/
- l=oc_blog64(cpi->qavg[cpi->FrameType!=KEY_FRAME][cpi->BaseQ]);
- l-=(ogg_int64_t)3<<57;
+ /*If rate control is active, use the lambda for the _target_ quantizer.
+ This allows us to scale to rates slightly lower than we'd normally be able
+ to reach, and give the rate control a semblance of "fractional QI"
+ precision.*/
+ if(cpi->info.target_bitrate>0)q=cpi->rc.qtarget;
+ else q=cpi->qavg[cpi->FrameType!=KEY_FRAME][cpi->BaseQ];
+ l=oc_blog64(q)-OC_Q57(3);
/*Raise to the 1.5 power.*/
l+=(l>>1);
/*Multiply by 1.125.*/
@@ -48,12 +54,11 @@
static void oc_rc_state_init(oc_rc_state *_rc,const theora_info *_info){
- unsigned long npixels;
- unsigned long ibpp;
+ ogg_int64_t npixels;
+ ogg_int64_t ibpp;
/*TODO: These parameters should be exposed in a th_enc_ctl() API.*/
_rc->bits_per_frame=(_info->target_bitrate*
- (ogg_int64_t)_info->fps_denominator+(_info->fps_numerator>>1))/
- _info->fps_numerator;
+ (ogg_int64_t)_info->fps_denominator)/_info->fps_numerator;
/*Insane framerates or frame sizes mean insane bitrates.
Let's not get carried away.*/
if(_rc->bits_per_frame>0x40000000000000LL){
@@ -77,7 +82,8 @@
_rc->target=_rc->fullness=(_rc->max+1>>1)+(_rc->max+2>>2);
/*Pick exponents and initial scales for quantizer selection.
TODO: These still need to be tuned.*/
- npixels=_info->width*(unsigned long)_info->height;
+ npixels=_info->width*(ogg_int64_t)_info->height;
+ _rc->log_npixels=oc_blog64(npixels);
ibpp=(npixels+(_rc->bits_per_frame>>1))/_rc->bits_per_frame;
if(ibpp<10){
_rc->exp[0]=48;
@@ -106,17 +112,15 @@
static void oc_enc_update_rc_state(CP_INSTANCE *cpi,
long _bits,int _qti,int _qi,int _trial){
ogg_int64_t log_scale;
- ogg_int64_t log_npixels;
ogg_int64_t log_bits;
ogg_int64_t log_qexp;
ogg_uint32_t scale;
/*Compute the estimated scale factor for this frame type.*/
log_bits=oc_blog64(_bits);
- log_npixels=oc_blog64(cpi->info.width*(ogg_int64_t)cpi->info.height);
- log_qexp=oc_blog64(cpi->qavg[_qti][_qi])-((ogg_int64_t)5<<57);
+ log_qexp=oc_blog64(cpi->qavg[_qti][_qi])-OC_Q57(5);
log_qexp=(log_qexp>>6)*(cpi->rc.exp[_qti]);
- log_scale=((ogg_int64_t)8<<57)+log_bits-log_npixels+log_qexp;
- scale=(ogg_uint32_t)oc_bexp64(OC_MINI(log_scale,(ogg_int64_t)16<<57));
+ log_scale=OC_Q57(8)+log_bits-cpi->rc.log_npixels+log_qexp;
+ scale=(ogg_uint32_t)oc_bexp64(OC_MINI(log_scale,OC_Q57(16)));
/*Use it to set that factor directly if this was a trial.*/
if(_trial)cpi->rc.scale[_qti]=scale;
/*Otherwise update an exponential moving average.*/
@@ -160,7 +164,6 @@
if(rate_total<=0)qtarget=OC_QUANT_MAX<<3;
else{
static const unsigned char KEY_RATIO[2]={29,32};
- ogg_int64_t log_npixels;
ogg_int64_t log_scale0;
ogg_int64_t log_scale1;
ogg_int64_t prevr;
@@ -168,10 +171,8 @@
ogg_int64_t realr;
ogg_int64_t log_qtarget;
int i;
- log_npixels=oc_blog64(cpi->info.width*(ogg_int64_t)cpi->info.height);
- log_scale0=oc_blog64(cpi->rc.scale[_qti])-((ogg_int64_t)8<<57)+log_npixels;
- log_scale1=oc_blog64(cpi->rc.scale[1-_qti])-((ogg_int64_t)8<<57)
- +log_npixels;
+ log_scale0=oc_blog64(cpi->rc.scale[_qti])-OC_Q57(8)+cpi->rc.log_npixels;
+ log_scale1=oc_blog64(cpi->rc.scale[1-_qti])-OC_Q57(8)+cpi->rc.log_npixels;
curr=(rate_total+(buf_delay>>1))/buf_delay;
realr=curr*KEY_RATIO[_qti]+16>>5;
for(i=0;i<10;i++){
@@ -199,10 +200,9 @@
realr=curr*KEY_RATIO[_qti]+16>>5;
if(curr<=0||realr>rate_total||prevr==curr)break;
}
- log_qtarget=((ogg_int64_t)5<<57)-
- ((oc_blog64(realr)-log_scale0+(cpi->rc.exp[_qti]>>1))/
+ log_qtarget=OC_Q57(5)-((oc_blog64(realr)-log_scale0+(cpi->rc.exp[_qti]>>1))/
cpi->rc.exp[_qti]<<6);
- qtarget=(int)oc_bexp64(OC_MINI(log_qtarget,(ogg_int64_t)15<<57));
+ qtarget=(int)oc_bexp64(OC_MINI(log_qtarget,OC_Q57(15)));
}
/*If this was not one of the initial frames, limit a change in quality.*/
if(!_trial){
@@ -228,6 +228,8 @@
best_qdiff=qdiff;
}
}
+ /*Save these parameters for lambda calculations.*/
+ cpi->rc.qtarget=qtarget;
return best_qi;
}
@@ -289,8 +291,10 @@
if(cpi->first_inter_frame == 0){
cpi->first_inter_frame = 1;
EncodeData(cpi);
- oc_enc_update_rc_state(cpi,oggpackB_bytes(cpi->oggbuffer)<<3,
- 1,cpi->BaseQ,1);
+ if(cpi->info.target_bitrate>0){
+ oc_enc_update_rc_state(cpi,oggpackB_bytes(cpi->oggbuffer)<<3,
+ 1,cpi->BaseQ,1);
+ }
CompressFrame(cpi,1);
return 0;
}
@@ -434,8 +438,10 @@
cpi->info.keyframe_frequency_force){
CompressKeyFrame(cpi,0);
- oc_enc_update_rc_state(cpi,oggpackB_bytes(cpi->oggbuffer)<<3,
- 0,cpi->BaseQ,1);
+ if(cpi->info.target_bitrate>0){
+ oc_enc_update_rc_state(cpi,oggpackB_bytes(cpi->oggbuffer)<<3,
+ 0,cpi->BaseQ,1);
+ }
/* On first frame, the previous was a initial dry-run to prime
feed-forward statistics */
Modified: branches/theora-thusnelda/lib/enc/mathops.c
===================================================================
--- branches/theora-thusnelda/lib/enc/mathops.c 2009-03-21 21:11:55 UTC (rev 15825)
+++ branches/theora-thusnelda/lib/enc/mathops.c 2009-03-21 22:52:25 UTC (rev 15826)
@@ -155,7 +155,7 @@
ipart=(int)(_z>>57);
if(ipart<0)return 0;
if(ipart>=63)return 0x7FFFFFFFFFFFFFFFLL;
- z=_z-((ogg_int64_t)ipart<<57);
+ z=_z-OC_Q57(ipart);
if(z){
ogg_int64_t mask;
long wlo;
@@ -292,5 +292,5 @@
}
z=z+8>>4;
}
- return ((ogg_int64_t)ipart<<57)+z;
+ return OC_Q57(ipart)+z;
}
Modified: branches/theora-thusnelda/lib/enc/mathops.h
===================================================================
--- branches/theora-thusnelda/lib/enc/mathops.h 2009-03-21 21:11:55 UTC (rev 15825)
+++ branches/theora-thusnelda/lib/enc/mathops.h 2009-03-21 22:52:25 UTC (rev 15826)
@@ -133,6 +133,8 @@
*/
# define OC_STATIC_ILOG_64(_v) (OC_STATIC_ILOG6((ogg_int64_t)(_v)))
+#define OC_Q57(_v) ((ogg_int64_t)(_v)<<57)
+
ogg_int64_t oc_bexp64(ogg_int64_t _z);
ogg_int64_t oc_blog64(ogg_int64_t _w);
More information about the commits
mailing list