hopefully better bitrate controll
authorMichael Niedermayer <michaelni@gmx.at>
Sun, 17 Mar 2002 16:31:38 +0000 (16:31 +0000)
committerMichael Niedermayer <michaelni@gmx.at>
Sun, 17 Mar 2002 16:31:38 +0000 (16:31 +0000)
Originally committed as revision 334 to svn://svn.ffmpeg.org/ffmpeg/trunk

ffmpeg.c
libavcodec/avcodec.h
libavcodec/motion_est.c
libavcodec/mpegvideo.c
libavcodec/mpegvideo.h

index b885066..23145ce 100644 (file)
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -75,7 +75,13 @@ static int frame_width  = 160;
 static int frame_height = 128;
 static int frame_rate = 25 * FRAME_RATE_BASE;
 static int video_bit_rate = 200000;
+static int video_bit_rate_tolerance = 200000;
 static int video_qscale = 0;
+static int video_qmin = 3;
+static int video_qmax = 15;
+static int video_qdiff = 3;
+static float video_qblur = 0.5;
+static float video_qcomp = 0.5;
 static int video_disable = 0;
 static int video_codec_id = CODEC_ID_NONE;
 static int same_quality = 0;
@@ -1149,6 +1155,11 @@ void opt_video_bitrate(const char *arg)
     video_bit_rate = atoi(arg) * 1000;
 }
 
+void opt_video_bitrate_tolerance(const char *arg)
+{
+    video_bit_rate_tolerance = atoi(arg) * 1000;
+}
+
 void opt_frame_rate(const char *arg)
 {
     frame_rate = (int)(strtod(arg, 0) * FRAME_RATE_BASE);
@@ -1182,6 +1193,45 @@ void opt_qscale(const char *arg)
     }
 }
 
+void opt_qmin(const char *arg)
+{
+    video_qmin = atoi(arg);
+    if (video_qmin < 0 ||
+        video_qmin > 31) {
+        fprintf(stderr, "qmin must be >= 1 and <= 31\n");
+        exit(1);
+    }
+}
+
+void opt_qmax(const char *arg)
+{
+    video_qmax = atoi(arg);
+    if (video_qmax < 0 ||
+        video_qmax > 31) {
+        fprintf(stderr, "qmax must be >= 1 and <= 31\n");
+        exit(1);
+    }
+}
+
+void opt_qdiff(const char *arg)
+{
+    video_qdiff = atoi(arg);
+    if (video_qdiff < 0 ||
+        video_qdiff > 31) {
+        fprintf(stderr, "qdiff must be >= 1 and <= 31\n");
+        exit(1);
+    }
+}
+
+void opt_qblur(const char *arg)
+{
+    video_qblur = atof(arg);
+}
+
+void opt_qcomp(const char *arg)
+{
+    video_qcomp = atof(arg);
+}
 
 void opt_audio_bitrate(const char *arg)
 {
@@ -1611,6 +1661,7 @@ void opt_output_file(const char *filename)
             video_enc->codec_type = CODEC_TYPE_VIDEO;
             
             video_enc->bit_rate = video_bit_rate;
+            video_enc->bit_rate_tolerance = video_bit_rate_tolerance;
             video_enc->frame_rate = frame_rate; 
             
             video_enc->width = frame_width;
@@ -1623,6 +1674,13 @@ void opt_output_file(const char *filename)
                 video_enc->flags |= CODEC_FLAG_QSCALE;
                 video_enc->quality = video_qscale;
             }
+            
+            video_enc->qmin= video_qmin;
+            video_enc->qmax= video_qmax;
+            video_enc->max_qdiff= video_qdiff;
+            video_enc->qblur= video_qblur;
+            video_enc->qcompress= video_qcomp;
+            
             if (do_psnr)
                 video_enc->get_psnr = 1;
             else
@@ -1948,6 +2006,12 @@ const OptionDef options[] = {
     { "intra", OPT_BOOL | OPT_EXPERT, {(void*)&intra_only}, "use only intra frames"},
     { "vn", OPT_BOOL, {(void*)&video_disable}, "disable video" },
     { "qscale", HAS_ARG | OPT_EXPERT, {(void*)opt_qscale}, "use fixed video quantiser scale (VBR)", "q" },
+    { "qmin", HAS_ARG | OPT_EXPERT, {(void*)opt_qmin}, "min video quantiser scale (VBR)", "q" },
+    { "qmax", HAS_ARG | OPT_EXPERT, {(void*)opt_qmax}, "max video quantiser scale (VBR)", "q" },
+    { "qdiff", HAS_ARG | OPT_EXPERT, {(void*)opt_qdiff}, "max difference between the quantiser scale (VBR)", "q" },
+    { "qblur", HAS_ARG | OPT_EXPERT, {(void*)opt_qblur}, "video quantiser scale blur (VBR)", "blur" },
+    { "qcomp", HAS_ARG | OPT_EXPERT, {(void*)opt_qcomp}, "video quantiser scale compression (VBR)", "compression" },
+    { "bt", HAS_ARG, {(void*)opt_video_bitrate_tolerance}, "set video bitrate tolerance (in kbit/s)", "tolerance" },
 #ifdef CONFIG_GRAB
     { "vd", HAS_ARG | OPT_EXPERT, {(void*)opt_video_device}, "set video device", "device" },
 #endif
index cc16cc8..7245851 100644 (file)
@@ -72,6 +72,7 @@ extern int motion_estimation_method;
 
 typedef struct AVCodecContext {
     int bit_rate;
+    int bit_rate_tolerance; /* amount of +- bits (>0)*/
     int flags;
     int sub_id;    /* some codecs needs additionnal format info. It is
                       stored there */
@@ -101,6 +102,12 @@ typedef struct AVCodecContext {
                          a key frame (intra, or seekable) */
     int quality;      /* quality of the previous encoded frame 
                          (between 1 (good) and 31 (bad)) */
+    float qcompress;  /* amount of qscale change between easy & hard scenes (0.0-1.0)*/
+    float qblur;      /* amount of qscale smoothing over time (0.0-1.0) */
+    int qmin;         /* min qscale */
+    int qmax;         /* max qscale */
+    int max_qdiff;    /* max qscale difference between frames */
+
     struct AVCodec *codec;
     void *priv_data;
 
index 0cdb370..e924969 100644 (file)
@@ -460,6 +460,7 @@ int estimate_motion(MpegEncContext * s,
     varc = (varc >> 8) - (sum * sum);
     s->mb_var[s->mb_width * mb_y + mb_x] = varc;
     s->avg_mb_var += varc;
+    s->mc_mb_var += vard;
      
 #if 0
     printf("varc=%4d avg_var=%4d (sum=%4d) vard=%4d mx=%2d my=%2d\n",
index 070058d..15af25d 100644 (file)
@@ -276,6 +276,7 @@ int MPV_encode_init(AVCodecContext *avctx)
     int i;
 
     s->bit_rate = avctx->bit_rate;
+    s->bit_rate_tolerance = avctx->bit_rate_tolerance;
     s->frame_rate = avctx->frame_rate;
     s->width = avctx->width;
     s->height = avctx->height;
@@ -284,6 +285,11 @@ int MPV_encode_init(AVCodecContext *avctx)
     s->rtp_payload_size = avctx->rtp_payload_size;
     if (avctx->rtp_callback)
         s->rtp_callback = avctx->rtp_callback;
+    s->qmin= avctx->qmin;
+    s->qmax= avctx->qmax;
+    s->max_qdiff= avctx->max_qdiff;
+    s->qcompress= avctx->qcompress;
+    s->qblur= avctx->qblur;
     s->avctx = avctx;
     
     if (s->gop_size <= 1) {
@@ -520,7 +526,9 @@ int MPV_encode_picture(AVCodecContext *avctx,
         mjpeg_picture_trailer(s);
 
     flush_put_bits(&s->pb);
-    s->total_bits += (pbBufPtr(&s->pb) - s->pb.buf) * 8;
+    s->last_frame_bits= s->frame_bits;
+    s->frame_bits  = (pbBufPtr(&s->pb) - s->pb.buf) * 8;
+    s->total_bits += s->frame_bits;
 
     avctx->quality = s->qscale;
     if (avctx->get_psnr) {
@@ -1040,6 +1048,36 @@ static void encode_picture(MpegEncContext *s, int picture_number)
     int i, motion_x, motion_y;
 
     s->picture_number = picture_number;
+
+    s->last_mc_mb_var = s->mc_mb_var;
+    /* Reset the average MB variance */
+    s->avg_mb_var = 0;
+    s->mc_mb_var = 0;
+
+    /* Estimate motion for every MB */
+    for(mb_y=0; mb_y < s->mb_height; mb_y++) {
+        for(mb_x=0; mb_x < s->mb_width; mb_x++) {
+            int xy= mb_y * s->mb_width + mb_x;
+            s->mb_x = mb_x;
+            s->mb_y = mb_y;
+
+            /* compute motion vector and macro block type (intra or non intra) */
+            motion_x = 0;
+            motion_y = 0;
+            if (s->pict_type == P_TYPE) {
+                s->mb_intra = estimate_motion(s, mb_x, mb_y,
+                                              &motion_x,
+                                              &motion_y);
+            } else {
+                s->mb_intra = 1;
+            }
+            /* Store MB type and MV */
+            s->mb_type[xy] = s->mb_intra;
+            s->mv_table[0][xy] = motion_x;
+            s->mv_table[1][xy] = motion_y;
+        }
+    }
+
     if (!s->fixed_qscale) 
         s->qscale = rate_estimate_qscale(s);
 
@@ -1094,33 +1132,7 @@ static void encode_picture(MpegEncContext *s, int picture_number)
         else
             s->gob_index = 4;
     }
-    
-    /* Reset the average MB variance */
-    s->avg_mb_var = 0;
-    
-    /* Estimate motion for every MB */
-    for(mb_y=0; mb_y < s->mb_height; mb_y++) {
-        for(mb_x=0; mb_x < s->mb_width; mb_x++) {
-            s->mb_x = mb_x;
-            s->mb_y = mb_y;
-
-            /* compute motion vector and macro block type (intra or non intra) */
-            motion_x = 0;
-            motion_y = 0;
-            if (s->pict_type == P_TYPE) {
-                s->mb_intra = estimate_motion(s, mb_x, mb_y,
-                                              &motion_x,
-                                              &motion_y);
-            } else {
-                s->mb_intra = 1;
-            }
-            /* Store MB type and MV */
-            s->mb_type[mb_y * s->mb_width + mb_x] = s->mb_intra;
-            s->mv_table[0][mb_y * s->mb_width + mb_x] = motion_x;
-            s->mv_table[1][mb_y * s->mb_width + mb_x] = motion_y;
-        }
-    }
-    
+        
     s->avg_mb_var = s->avg_mb_var / s->mb_num;        
     
     s->block_wrap[0]=
@@ -1542,6 +1554,22 @@ static void dct_unquantize_h263_c(MpegEncContext *s,
 
 static void rate_control_init(MpegEncContext *s)
 {
+#if 1
+    emms_c();
+
+    //initial values, they dont really matter as they will be totally different within a few frames
+    s->i_pred.coeff= s->p_pred.coeff= 7.0;
+    s->i_pred.count= s->p_pred.count= 1.0;
+    
+    s->i_pred.decay= s->p_pred.decay= 0.4;
+    
+    // use more bits at the beginning, otherwise high motion at the begin will look like shit
+    s->qsum=100;
+    s->qcount=100;
+
+    s->short_term_qsum=0.001;
+    s->short_term_qcount=0.001;
+#else
     s->wanted_bits = 0;
 
     if (s->intra_only) {
@@ -1552,24 +1580,122 @@ static void rate_control_init(MpegEncContext *s)
                                  (float)((float)s->frame_rate / FRAME_RATE_BASE * (I_FRAME_SIZE_RATIO + s->gop_size - 1)));
         s->I_frame_bits = (int)(s->P_frame_bits * I_FRAME_SIZE_RATIO);
     }
-    
+
 #if defined(DEBUG)
     printf("I_frame_size=%d P_frame_size=%d\n",
            s->I_frame_bits, s->P_frame_bits);
 #endif
+#endif
 }
 
+static double predict(Predictor *p, double q, double var)
+{
+    return p->coeff*var / (q*p->count);
+}
+
+static void update_predictor(Predictor *p, double q, double var, double size)
+{
+    double new_coeff= size*q / (var + 1);
+    if(var<1000) return;
+/*{
+int pred= predict(p, q, var);
+int error= abs(pred-size);
+static double sum=0;
+static int count=0;
+if(count>5) sum+=error;
+count++;
+if(256*256*256*64%count==0){
+    printf("%d %f %f\n", count, sum/count, p->coeff);
+}
+}*/
+    p->count*= p->decay;
+    p->coeff*= p->decay;
+    p->count++;
+    p->coeff+= new_coeff;
+}
 
-/*
- * This heuristic is rather poor, but at least we do not have to
- * change the qscale at every macroblock.
- */
 static int rate_estimate_qscale(MpegEncContext *s)
 {
-    INT64 diff, total_bits = s->total_bits;
+#if 1
+    int qmin= s->qmin;
+    int qmax= s->qmax;
+    int rate_q=5;
     float q;
-    int qscale, qmin;
+    int qscale;
+    float br_compensation;
+    double diff;
+    double short_term_q;
+    double long_term_q;
+    int last_qscale= s->qscale;
+    double fps;
+    INT64 wanted_bits;
+    emms_c();
+    
+    fps= (double)s->frame_rate / FRAME_RATE_BASE;
+    wanted_bits= s->bit_rate*(double)s->picture_number/fps;
+
+    
+    if(s->picture_number>2){
+        /* update predictors */
+        if(s->last_pict_type == I_TYPE){
+        //FIXME
+        }else{ //P Frame
+//printf("%d %d %d %f\n", s->qscale, s->last_mc_mb_var, s->frame_bits, s->p_pred.coeff);
+            update_predictor(&s->p_pred, s->qscale, s->last_mc_mb_var, s->frame_bits);
+        }
+    }
+
+    if(s->pict_type == I_TYPE){
+        //FIXME
+        rate_q= s->qsum/s->qcount;
+    }else{ //P Frame
+        int i;
+        int diff, best_diff=1000000000;
+        for(i=1; i<=31; i++){
+            diff= predict(&s->p_pred, i, s->mc_mb_var) - (double)s->bit_rate/fps;
+            if(diff<0) diff= -diff;
+            if(diff<best_diff){
+                best_diff= diff;
+                rate_q= i;
+            }
+        }
+    }
+
+    s->short_term_qsum*=s->qblur;
+    s->short_term_qcount*=s->qblur;
+
+    s->short_term_qsum+= rate_q;
+    s->short_term_qcount++;
+    short_term_q= s->short_term_qsum/s->short_term_qcount;
+    
+    long_term_q= s->qsum/s->qcount*s->total_bits/wanted_bits;
+
+//    q= (long_term_q - short_term_q)*s->qcompress + short_term_q;
+    q= 1/((1/long_term_q - 1/short_term_q)*s->qcompress + 1/short_term_q);
+
+    diff= s->total_bits - wanted_bits;
+    br_compensation= (s->bit_rate_tolerance - diff)/s->bit_rate_tolerance;
+    q/=br_compensation;
+
+    qscale= (int)(q + 0.5);
+    if     (qscale<qmin) qscale=qmin;
+    else if(qscale>qmax) qscale=qmax;
+    
+    if     (qscale<last_qscale-s->max_qdiff) qscale=last_qscale-s->max_qdiff;
+    else if(qscale>last_qscale+s->max_qdiff) qscale=last_qscale+s->max_qdiff;
+
+    s->qsum+= qscale;
+    s->qcount++;
 
+    s->last_pict_type= s->pict_type;
+//printf("q:%d diff:%d comp:%f rate_q:%d st_q:%d fvar:%d last_size:%d\n", qscale, (int)diff, br_compensation, 
+//       rate_q, (int)short_term_q, s->mc_mb_var, s->frame_bits);
+//printf("%d %d\n", s->bit_rate, (int)fps);
+    return qscale;
+#else
+    INT64 diff, total_bits = s->total_bits;
+    float q;
+    int qscale;
     if (s->pict_type == I_TYPE) {
         s->wanted_bits += s->I_frame_bits;
     } else {
@@ -1600,6 +1726,7 @@ static int rate_estimate_qscale(MpegEncContext *s)
            (int)diff, q);
 #endif
     return qscale;
+#endif
 }
 
 AVCodec mpeg1video_encoder = {
index da70ed6..8bccde6 100644 (file)
@@ -34,6 +34,12 @@ enum OutputFormat {
 #define QMAT_SHIFT_MMX 19
 #define QMAT_SHIFT 25
 
+typedef struct Predictor{
+    double coeff;
+    double count;
+    double decay;
+} Predictor;
+
 typedef struct MpegEncContext {
     struct AVCodecContext *avctx;
     /* the following parameters must be initialized before encoding */
@@ -42,6 +48,7 @@ typedef struct MpegEncContext {
     int frame_rate; /* number of frames per second */
     int intra_only; /* if true, only intra pictures are generated */
     int bit_rate;        /* wanted bit rate */
+    int bit_rate_tolerance; /* amount of +- bits (>0)*/
     enum OutputFormat out_format; /* output format */
     int h263_plus; /* h263 plus headers */
     int h263_rv10; /* use RV10 variation for H263 */
@@ -49,6 +56,11 @@ typedef struct MpegEncContext {
     int h263_msmpeg4; /* generate MSMPEG4 compatible stream */
     int h263_intel; /* use I263 intel h263 header */
     int fixed_qscale; /* fixed qscale if non zero */
+    float qcompress;  /* amount of qscale change between easy & hard scenes (0.0-1.0) */
+    float qblur;      /* amount of qscale smoothing over time (0.0-1.0) */
+    int qmin;         /* min qscale */
+    int qmax;         /* max qscale */
+    int max_qdiff;    /* max qscale difference between frames */
     int encoding;     /* true if we are encoding (vs decoding) */
     /* the following fields are managed internally by the encoder */
 
@@ -85,6 +97,7 @@ typedef struct MpegEncContext {
     int qscale;
     int pict_type;
     int last_non_b_pict_type; /* used for mpeg4 gmc b-frames */
+    int last_pict_type; /* used for bit rate stuff (needs that to update the right predictor) */
     int frame_rate_index;
     /* motion compensation */
     int unrestricted_mv;
@@ -146,9 +159,19 @@ typedef struct MpegEncContext {
     int I_frame_bits;    /* wanted number of bits per I frame */
     int P_frame_bits;    /* same for P frame */
     int avg_mb_var;        /* average MB variance for current frame */
+    int mc_mb_var;     /* motion compensated MB variance for current frame */
+    int last_mc_mb_var;     /* motion compensated MB variance for last frame */
     INT64 wanted_bits;
     INT64 total_bits;
+    int frame_bits;      /* bits used for the current frame */
+    int last_frame_bits; /* bits used for the last frame */
+    Predictor i_pred;
+    Predictor p_pred;
+    double qsum;         /* sum of qscales */
+    double qcount;       /* count of qscales */
+    double short_term_qsum;   /* sum of recent qscales */
+    double short_term_qcount; /* count of recent qscales */
+
     /* H.263 specific */
     int gob_number;
     int gob_index;