[xiph-commits] r16458 - in trunk/theora: examples include/theora lib

tterribe at svn.xiph.org tterribe at svn.xiph.org
Sat Aug 8 04:46:40 PDT 2009


Author: tterribe
Date: 2009-08-08 04:46:40 -0700 (Sat, 08 Aug 2009)
New Revision: 16458

Modified:
   trunk/theora/examples/encoder_example.c
   trunk/theora/include/theora/theoraenc.h
   trunk/theora/lib/analyze.c
   trunk/theora/lib/encint.h
   trunk/theora/lib/encode.c
Log:
Re-hook up the speed level setting to the encoder internals and add a ctl code
 to retrieve the current setting.
The meaning of the options has changed slightly, and the default is no longer
 zero (the slowest).
The currently defined levels (for all encoding modes):
- 0: Slowest (best)
- 1: Enable early skip (default)
- 2: Disable motion compensation
The difference in quality between 0 and 1 is very small, and quality is only
 likely to improve a very small amount, and only at very low bitrates.
The difference in quality between 1 and 2 is very large, and quality will
 degrade significantly (though speed will vastly improve).


Modified: trunk/theora/examples/encoder_example.c
===================================================================
--- trunk/theora/examples/encoder_example.c	2009-08-08 00:02:01 UTC (rev 16457)
+++ trunk/theora/examples/encoder_example.c	2009-08-08 11:46:40 UTC (rev 16458)
@@ -61,7 +61,7 @@
 }
 #endif
 
-const char *optstring = "b:e:o:a:A:v:V:s:S:f:F:ck:d:\1\2\3\4";
+const char *optstring = "b:e:o:a:A:v:V:s:S:f:F:ck:d:z:\1\2\3\4";
 struct option options [] = {
   {"begin-time",required_argument,NULL,'b'},
   {"end-time",required_argument,NULL,'e'},
@@ -75,6 +75,7 @@
   {"framerate-numerator",required_argument,NULL,'f'},
   {"framerate-denominator",required_argument,NULL,'F'},
   {"vp3-compatible",no_argument,NULL,'c'},
+  {"speed",required_argument,NULL,'z'},
   {"soft-target",no_argument,NULL,'\1'},
   {"keyframe-freq",required_argument,NULL,'k'},
   {"buf-delay",required_argument,NULL,'d'},
@@ -188,6 +189,17 @@
           "                                  The frame rate nominator divided by this\n"
           "                                  determinates the frame rate in units per tick\n"
           "   -k --keyframe-freq <n>         Keyframe frequency\n"
+          "   -z --speed <n>                 Sets the encoder speed level. Higher speed\n"
+          "                                  levels favor quicker encoding over better\n"
+          "                                  quality per bit. Depending on the encoding\n"
+          "                                  mode, and the internal algorithms used,\n"
+          "                                  quality may actually improve with higher\n"
+          "                                  speeds, but in this case bitrate will also\n"
+          "                                  likely increase. The maximum value, and the\n"
+          "                                  meaning of each value, are implementation-\n"
+          "                                  specific and may change depending on the\n"
+          "                                  current encoding mode (rate constrained,\n"
+          "                                  two-pass, etc.).\n"
           "   -d --buf-delay <n>             Buffer delay (in frames). Longer delays\n"
           "                                  allow smoother rate adaptation and provide\n"
           "                                  better overall quality, but require more\n"
@@ -1217,6 +1229,7 @@
   vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
   vorbis_block     vb; /* local working space for packet->PCM decode */
 
+  int speed=-1;
   int audioflag=0;
   int videoflag=0;
   int akbps=0;
@@ -1327,6 +1340,14 @@
       }
       break;
 
+    case 'z':
+      speed=atoi(optarg);
+      if(speed<0){
+        fprintf(stderr,"Illegal speed level\n");
+        exit(1);
+      }
+      break;
+
     case 'b':
       {
         char *pos=strchr(optarg,':');
@@ -1607,6 +1628,32 @@
         fprintf(stderr,"Warning: could not set desired buffer delay.\n");
       }
     }
+    /*Speed should also be set after the current encoder mode is established,
+       since the available speed levels may change depending.*/
+    if(speed>=0){
+      int speed_max;
+      int ret;
+      ret=th_encode_ctl(td,TH_ENCCTL_GET_SPLEVEL_MAX,
+       &speed_max,sizeof(speed_max));
+      if(ret<0){
+        fprintf(stderr,"Warning: could not determine maximum speed level.\n");
+        speed_max=0;
+      }
+      ret=th_encode_ctl(td,TH_ENCCTL_SET_SPLEVEL,&speed,sizeof(speed));
+      if(ret<0){
+        fprintf(stderr,"Warning: could not set speed level to %i of %i\n",
+         speed,speed_max);
+        if(speed>speed_max){
+          fprintf(stderr,"Setting it to %i instead\n",speed_max);
+        }
+        ret=th_encode_ctl(td,TH_ENCCTL_SET_SPLEVEL,
+         &speed_max,sizeof(speed_max));
+        if(ret<0){
+          fprintf(stderr,"Warning: could not set speed level to %i of %i\n",
+           speed_max,speed_max);
+        }
+      }
+    }
     /* write the bitstream header packets with proper page interleave */
     th_comment_init(&tc);
     /* first packet will get its own page automatically */

Modified: trunk/theora/include/theora/theoraenc.h
===================================================================
--- trunk/theora/include/theora/theoraenc.h	2009-08-08 00:02:01 UTC (rev 16457)
+++ trunk/theora/include/theora/theoraenc.h	2009-08-08 11:46:40 UTC (rev 16458)
@@ -120,7 +120,7 @@
  *                    encoding mode.*/
 #define TH_ENCCTL_GET_SPLEVEL_MAX (12)
 /**Sets the speed level.
- * By default, the slowest speed (0) is used.
+ * The current speed level may be retrieved using #TH_ENCCTL_GET_SPLEVEL.
  *
  * \param[in] _buf <tt>int</tt>: The new encoding speed level.
  *                 0 is slowest, larger values use less CPU.
@@ -133,6 +133,20 @@
  * \retval TH_IMPL   Not supported by this implementation in the current
  *                    encoding mode.*/
 #define TH_ENCCTL_SET_SPLEVEL (14)
+/**Gets the current speed level.
+ * The default speed level may vary according to encoder implementation, but if
+ *  this control code is not supported (it returns #TH_IMPL), the default may
+ *  be assumed to be the slowest available speed (0).
+ * The maximum encoding speed level may be implementation- and encoding
+ *  mode-specific, and can be obtained via #TH_ENCCTL_GET_SPLEVEL_MAX.
+ *
+ * \param[out] _buf <tt>int</tt>: The current encoding speed level.
+ *                  0 is slowest, larger values use less CPU.
+ * \retval TH_EFAULT \a _enc_ctx or \a _buf is <tt>NULL</tt>.
+ * \retval TH_EINVAL \a _buf_sz is not <tt>sizeof(int)</tt>.
+ * \retval TH_IMPL   Not supported by this implementation in the current
+ *                    encoding mode.*/
+#define TH_ENCCTL_GET_SPLEVEL (16)
 /**Sets the number of duplicates of the next frame to produce.
  * Although libtheora can encode duplicate frames very cheaply, it costs some
  *  amount of CPU to detect them, and a run of duplicates cannot span a

Modified: trunk/theora/lib/analyze.c
===================================================================
--- trunk/theora/lib/analyze.c	2009-08-08 00:02:01 UTC (rev 16457)
+++ trunk/theora/lib/analyze.c	2009-08-08 11:46:40 UTC (rev 16458)
@@ -675,14 +675,15 @@
   qii=frags[_fragi].qii;
   if(qii&~3){
 #if !defined(OC_COLLECT_METRICS)
-    /*Enable early skip detection.*/
-    frags[_fragi].coded=0;
-    return 0;
-#else
+    if(_enc->sp_level>=OC_SP_LEVEL_EARLY_SKIP){
+      /*Enable early skip detection.*/
+      frags[_fragi].coded=0;
+      return 0;
+    }
+#endif
     /*Try and code this block anyway.*/
     qii&=3;
     frags[_fragi].qii=qii;
-#endif
   }
   mb_mode=frags[_fragi].mb_mode;
   ref=_enc->state.ref_frame_data[
@@ -1937,7 +1938,7 @@
         /*Motion estimation:
           We always do a basic 1MV search for all macroblocks, coded or not,
            keyframe or not.*/
-        if(!_recode)oc_mcenc_search(_enc,mbi);
+        if(!_recode&&_enc->sp_level<OC_SP_LEVEL_NOMC)oc_mcenc_search(_enc,mbi);
         dx=dy=0;
         /*Find the block choice with the lowest estimated coding cost.
           If a Cb or Cr block is coded but no Y' block from a macro block then
@@ -1966,68 +1967,96 @@
         oc_skip_cost(_enc,&pipe,mbi,skip_ssd);
         oc_cost_inter_nomv(_enc,modes+OC_MODE_INTER_NOMV,mbi,
          OC_MODE_INTER_NOMV,pipe.fr+0,pipe.qs+0,skip_ssd);
-        oc_cost_intra(_enc,modes+OC_MODE_INTRA,mbi,
-         pipe.fr+0,pipe.qs+0,intra_satd,skip_ssd);
-        mb_mv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_INTER_MV,mbi,
-         OC_MODE_INTER_MV,embs[mbi].unref_mv[OC_FRAME_PREV],
-         pipe.fr+0,pipe.qs+0,skip_ssd);
-        oc_cost_inter(_enc,modes+OC_MODE_INTER_MV_LAST,mbi,
-         OC_MODE_INTER_MV_LAST,last_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
-        oc_cost_inter(_enc,modes+OC_MODE_INTER_MV_LAST2,mbi,
-         OC_MODE_INTER_MV_LAST2,prior_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
-        oc_cost_inter_nomv(_enc,modes+OC_MODE_GOLDEN_NOMV,mbi,
-         OC_MODE_GOLDEN_NOMV,pipe.fr+0,pipe.qs+0,skip_ssd);
-        mb_gmv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_GOLDEN_MV,mbi,
-         OC_MODE_GOLDEN_MV,embs[mbi].unref_mv[OC_FRAME_GOLD],
-         pipe.fr+0,pipe.qs+0,skip_ssd);
-        oc_cost_inter4mv(_enc,modes+OC_MODE_INTER_MV_FOUR,mbi,
-         embs[mbi].block_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
-        /*The explicit MV modes (2,6,7) have not yet gone through halfpel
-           refinement.
-          We choose the explicit MV mode that's already furthest ahead on bits
-           and refine only that one.
-          We have to be careful to remember which ones we've refined so that
-           we don't refine it again if we re-encode this frame.*/
-        inter_mv_pref=_enc->lambda*3;
-        if(modes[OC_MODE_INTER_MV_FOUR].cost<modes[OC_MODE_INTER_MV].cost&&
-         modes[OC_MODE_INTER_MV_FOUR].cost<modes[OC_MODE_GOLDEN_MV].cost){
-          if(!(embs[mbi].refined&0x80)){
-            oc_mcenc_refine4mv(_enc,mbi);
-            embs[mbi].refined|=0x80;
-          }
+        if(_enc->sp_level<OC_SP_LEVEL_NOMC){
+          oc_cost_intra(_enc,modes+OC_MODE_INTRA,mbi,
+           pipe.fr+0,pipe.qs+0,intra_satd,skip_ssd);
+          mb_mv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_INTER_MV,mbi,
+           OC_MODE_INTER_MV,embs[mbi].unref_mv[OC_FRAME_PREV],
+           pipe.fr+0,pipe.qs+0,skip_ssd);
+          oc_cost_inter(_enc,modes+OC_MODE_INTER_MV_LAST,mbi,
+           OC_MODE_INTER_MV_LAST,last_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
+          oc_cost_inter(_enc,modes+OC_MODE_INTER_MV_LAST2,mbi,
+           OC_MODE_INTER_MV_LAST2,prior_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
           oc_cost_inter4mv(_enc,modes+OC_MODE_INTER_MV_FOUR,mbi,
-           embs[mbi].ref_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
-        }
-        else if(modes[OC_MODE_GOLDEN_MV].cost+inter_mv_pref<
-         modes[OC_MODE_INTER_MV].cost){
-          if(!(embs[mbi].refined&0x40)){
-            oc_mcenc_refine1mv(_enc,mbi,OC_FRAME_GOLD);
-            embs[mbi].refined|=0x40;
-          }
+           embs[mbi].block_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
+          oc_cost_inter_nomv(_enc,modes+OC_MODE_GOLDEN_NOMV,mbi,
+           OC_MODE_GOLDEN_NOMV,pipe.fr+0,pipe.qs+0,skip_ssd);
           mb_gmv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_GOLDEN_MV,mbi,
-           OC_MODE_GOLDEN_MV,embs[mbi].analysis_mv[0][OC_FRAME_GOLD],
+           OC_MODE_GOLDEN_MV,embs[mbi].unref_mv[OC_FRAME_GOLD],
            pipe.fr+0,pipe.qs+0,skip_ssd);
+          /*The explicit MV modes (2,6,7) have not yet gone through halfpel
+             refinement.
+            We choose the explicit MV mode that's already furthest ahead on
+             R-D cost and refine only that one.
+            We have to be careful to remember which ones we've refined so that
+             we don't refine it again if we re-encode this frame.*/
+          inter_mv_pref=_enc->lambda*3;
+          if(modes[OC_MODE_INTER_MV_FOUR].cost<modes[OC_MODE_INTER_MV].cost&&
+           modes[OC_MODE_INTER_MV_FOUR].cost<modes[OC_MODE_GOLDEN_MV].cost){
+            if(!(embs[mbi].refined&0x80)){
+              oc_mcenc_refine4mv(_enc,mbi);
+              embs[mbi].refined|=0x80;
+            }
+            oc_cost_inter4mv(_enc,modes+OC_MODE_INTER_MV_FOUR,mbi,
+             embs[mbi].ref_mv,pipe.fr+0,pipe.qs+0,skip_ssd);
+          }
+          else if(modes[OC_MODE_GOLDEN_MV].cost+inter_mv_pref<
+           modes[OC_MODE_INTER_MV].cost){
+            if(!(embs[mbi].refined&0x40)){
+              oc_mcenc_refine1mv(_enc,mbi,OC_FRAME_GOLD);
+              embs[mbi].refined|=0x40;
+            }
+            mb_gmv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_GOLDEN_MV,mbi,
+             OC_MODE_GOLDEN_MV,embs[mbi].analysis_mv[0][OC_FRAME_GOLD],
+             pipe.fr+0,pipe.qs+0,skip_ssd);
+          }
+          if(!(embs[mbi].refined&0x04)){
+            oc_mcenc_refine1mv(_enc,mbi,OC_FRAME_PREV);
+            embs[mbi].refined|=0x04;
+          }
+          mb_mv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_INTER_MV,mbi,
+           OC_MODE_INTER_MV,embs[mbi].analysis_mv[0][OC_FRAME_PREV],
+           pipe.fr+0,pipe.qs+0,skip_ssd);
+          /*Finally, pick the mode with the cheapest estimated R-D cost.*/
+          mb_mode=OC_MODE_INTER_NOMV;
+          if(modes[OC_MODE_INTRA].cost<modes[OC_MODE_INTER_NOMV].cost){
+            mb_mode=OC_MODE_INTRA;
+          }
+          if(modes[OC_MODE_INTER_MV_LAST].cost<modes[mb_mode].cost){
+            mb_mode=OC_MODE_INTER_MV_LAST;
+          }
+          if(modes[OC_MODE_INTER_MV_LAST2].cost<modes[mb_mode].cost){
+            mb_mode=OC_MODE_INTER_MV_LAST2;
+          }
+          if(modes[OC_MODE_GOLDEN_NOMV].cost<modes[mb_mode].cost){
+            mb_mode=OC_MODE_GOLDEN_NOMV;
+          }
+          if(modes[OC_MODE_GOLDEN_MV].cost<modes[mb_mode].cost){
+            mb_mode=OC_MODE_GOLDEN_MV;
+          }
+          if(modes[OC_MODE_INTER_MV_FOUR].cost<modes[mb_mode].cost){
+            mb_mode=OC_MODE_INTER_MV_FOUR;
+          }
+          /*We prefer OC_MODE_INTER_MV, but not over LAST and LAST2.*/
+          if(mb_mode==OC_MODE_INTER_MV_LAST||mb_mode==OC_MODE_INTER_MV_LAST2){
+            inter_mv_pref=0;
+          }
+          if(modes[OC_MODE_INTER_MV].cost<modes[mb_mode].cost+inter_mv_pref){
+            mb_mode=OC_MODE_INTER_MV;
+          }
         }
-        if(!(embs[mbi].refined&0x04)){
-          oc_mcenc_refine1mv(_enc,mbi,OC_FRAME_PREV);
-          embs[mbi].refined|=0x04;
+        else{
+          oc_cost_inter_nomv(_enc,modes+OC_MODE_GOLDEN_NOMV,mbi,
+           OC_MODE_GOLDEN_NOMV,pipe.fr+0,pipe.qs+0,skip_ssd);
+          mb_mode=OC_MODE_INTER_NOMV;
+          if(modes[OC_MODE_INTRA].cost<modes[OC_MODE_INTER_NOMV].cost){
+            mb_mode=OC_MODE_INTRA;
+          }
+          if(modes[OC_MODE_GOLDEN_NOMV].cost<modes[mb_mode].cost){
+            mb_mode=OC_MODE_GOLDEN_NOMV;
+          }
+          mb_mv_bits_0=mb_gmv_bits_0;
         }
-        mb_mv_bits_0=oc_cost_inter1mv(_enc,modes+OC_MODE_INTER_MV,mbi,
-         OC_MODE_INTER_MV,embs[mbi].analysis_mv[0][OC_FRAME_PREV],
-         pipe.fr+0,pipe.qs+0,skip_ssd);
-        /*Finally, pick the mode with the cheapest estimated R-D cost.*/
-        mb_mode=0;
-        if(modes[1].cost<modes[0].cost)mb_mode=1;
-        if(modes[3].cost<modes[mb_mode].cost)mb_mode=3;
-        if(modes[4].cost<modes[mb_mode].cost)mb_mode=4;
-        if(modes[5].cost<modes[mb_mode].cost)mb_mode=5;
-        if(modes[6].cost<modes[mb_mode].cost)mb_mode=6;
-        if(modes[7].cost<modes[mb_mode].cost)mb_mode=7;
-        /*We prefer OC_MODE_INTER_MV, but not over LAST and LAST2.*/
-        if(mb_mode==OC_MODE_INTER_MV_LAST||mb_mode==OC_MODE_INTER_MV_LAST2){
-          inter_mv_pref=0;
-        }
-        if(modes[2].cost<modes[mb_mode].cost+inter_mv_pref)mb_mode=2;
         mb_modes[mbi]=mb_mode;
         /*Propagate the MVs to the luma blocks.*/
         if(mb_mode!=OC_MODE_INTER_MV_FOUR){

Modified: trunk/theora/lib/encint.h
===================================================================
--- trunk/theora/lib/encint.h	2009-08-08 00:02:01 UTC (rev 16457)
+++ trunk/theora/lib/encint.h	2009-08-08 11:46:40 UTC (rev 16458)
@@ -50,6 +50,14 @@
 /*Next packet to emit: Data packet, and one is ready.*/
 #define OC_PACKET_READY (1)
 
+/*All features enabled.*/
+#define OC_SP_LEVEL_SLOW       (0)
+/*Enable early skip.*/
+#define OC_SP_LEVEL_EARLY_SKIP (1)
+/*Disable motion compensation.*/
+#define OC_SP_LEVEL_NOMC       (2)
+/*Maximum valid speed level.*/
+#define OC_SP_LEVEL_MAX        (2)
 
 
 /*The bits used for each of the MB mode codebooks.*/
@@ -319,6 +327,8 @@
   ogg_uint32_t             nqueued_dups;
   /*The number of duplicates emitted for the last frame.*/
   ogg_uint32_t             prev_dup_count;
+  /*The current speed level.*/
+  int                      sp_level;
   /*Whether or not VP3 compatibility mode has been enabled.*/
   unsigned char            vp3_compatible;
   /*Whether or not any INTER frames have been coded.*/

Modified: trunk/theora/lib/encode.c
===================================================================
--- trunk/theora/lib/encode.c	2009-08-08 00:02:01 UTC (rev 16457)
+++ trunk/theora/lib/encode.c	2009-08-08 11:46:40 UTC (rev 16458)
@@ -1108,6 +1108,8 @@
   _enc->dup_count=0;
   _enc->nqueued_dups=0;
   _enc->prev_dup_count=0;
+  /*Enable speed optimizations up through early skip by default.*/
+  _enc->sp_level=OC_SP_LEVEL_EARLY_SKIP;
   /*Disable VP3 compatibility by default.*/
   _enc->vp3_compatible=0;
   /*No INTER frames coded yet.*/
@@ -1294,7 +1296,7 @@
     case TH_ENCCTL_GET_SPLEVEL_MAX:{
       if(_enc==NULL||_buf==NULL)return TH_EFAULT;
       if(_buf_sz!=sizeof(int))return TH_EINVAL;
-      *(int *)_buf=2;
+      *(int *)_buf=OC_SP_LEVEL_MAX;
       return 0;
     }break;
     case TH_ENCCTL_SET_SPLEVEL:{
@@ -1302,25 +1304,16 @@
       if(_enc==NULL||_buf==NULL)return TH_EFAULT;
       if(_buf_sz!=sizeof(speed))return TH_EINVAL;
       speed=*(int *)_buf;
-      switch(speed){
-        case 0:{
-          /*_enc->MotionCompensation=1;*/
-          /*_enc->info.quick_p=0;*/
-        }break;
-        case 1:{
-          /*_enc->MotionCompensation=1;*/
-          /*_enc->info.quick_p=1;*/
-        }break;
-        case 2:{
-          /*_enc->MotionCompensation=0;*/
-          /*_enc->info.quick_p=1;*/
-        }break;
-        default:{
-          return TH_EINVAL;
-        }
-      }
+      if(speed<0||speed>OC_SP_LEVEL_MAX)return TH_EINVAL;
+      _enc->sp_level=speed;
       return 0;
     }break;
+    case TH_ENCCTL_GET_SPLEVEL:{
+      if(_enc==NULL||_buf==NULL)return TH_EFAULT;
+      if(_buf_sz!=sizeof(int))return TH_EINVAL;
+      *(int *)_buf=_enc->sp_level;
+      return 0;
+    }
     case TH_ENCCTL_SET_DUP_COUNT:{
       int dup_count;
       if(_enc==NULL||_buf==NULL)return TH_EFAULT;



More information about the commits mailing list