[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