Reorganize frame type selection: No longer produces consecutive I-frames when B-frame...
authorLoren Merritt <pengvado@videolan.org>
Wed, 5 Jan 2005 06:59:29 +0000 (06:59 +0000)
committerLoren Merritt <pengvado@videolan.org>
Wed, 5 Jan 2005 06:59:29 +0000 (06:59 +0000)
Fix scenecut detection when B-frames are present: Can now produce IDR, but is slower since it re-encodes more frames. This might reduce compression ratio in the presence of quick fade-ins.
2pass ratecontrol deals more gracefully with completely skipped frames.

git-svn-id: svn://svn.videolan.org/x264/trunk@83 df754926-b1dd-0310-bc7b-ec298dee348c

encoder/encoder.c
encoder/ratecontrol.c

index 2be43d5..cc6eab7 100644 (file)
@@ -561,6 +561,25 @@ static x264_frame_t *x264_frame_get( x264_frame_t *list[X264_BFRAME_MAX+1] )
     return frame;
 }
 
+/* Sort queued frames into input order */
+static void x264_frame_sort( x264_frame_t *list[X264_BFRAME_MAX+1] )
+{
+    int i, b_ok;
+    do {
+        b_ok = 1;
+        for( i = 0; i < X264_BFRAME_MAX && list[i+1]; i++ )
+        {
+            if( list[i]->i_frame > list[i+1]->i_frame )
+            {
+                x264_frame_t *tmp = list[i+1];
+                list[i+1] = list[i];
+                list[i] = tmp;
+                b_ok = 0;
+            }
+        }
+    } while( !b_ok );
+}
+
 static inline void x264_reference_build_list( x264_t *h, int i_poc )
 {
     int i;
@@ -896,118 +915,99 @@ int     x264_encoder_encode( x264_t *h,
     TIMER_START( i_mtime_encode_frame );
     if( pic != NULL )
     {
-        /* Copy the picture to a frame, init the frame and move it to a buffer */
-        /* 1: get a frame */
+        /* 1: Copy the picture to a frame and move it to a buffer */
         x264_frame_t *fenc = x264_frame_get( h->frames.unused );
 
         x264_frame_copy_picture( h, fenc, pic );
 
         fenc->i_frame = h->frames.i_input++;
 
-        /* 2: get its type */
-        if( h->param.rc.b_stat_read )
+        x264_frame_put( h->frames.next, fenc );
+
+        if( h->frames.i_input <= h->param.i_bframe )
         {
-            /* XXX: trusts that the first pass used compatible B and IDR frequencies */
-            fenc->i_type = x264_ratecontrol_slice_type( h, fenc->i_frame );
-            if( fenc->i_type == X264_TYPE_I && h->frames.next[0] == NULL &&
-                h->frames.i_last_idr + 1 >= h->param.i_idrframe )
-            {
-                fenc->i_type = X264_TYPE_IDR;
-                h->i_poc       = 0;
-                h->i_frame_num = 0;
-            }
+            /* Nothing yet to encode */
+            /* waiting for filling bframe buffer */
+            pic->i_type = X264_TYPE_AUTO;
+            return 0;
         }
-        else if( ( h->frames.i_last_i + 1 >= h->param.i_iframe && h->frames.i_last_idr + 1 >= h->param.i_idrframe ) ||
-            pic->i_type == X264_TYPE_IDR )
-        {
-            /* IDR */
-            fenc->i_type = X264_TYPE_IDR;
-
-            h->i_poc       = 0;
-            h->i_frame_num = 0;
-
-            /* Last schedule B frames need to be encoded as P */
-            if( h->frames.next[0] != NULL )
-            {
-                x264_frame_t *tmp;
-                int i = 0;
+    }
 
-                while( h->frames.next[i+1] != NULL ) i++;
-                h->frames.next[i]->i_type = X264_TYPE_P;
+    if( h->frames.current[0] == NULL )
+    {
+        /* 2: Select frame types */
+        x264_frame_t *frm;
+        int bframes;
 
-                /* remove this P from next */
-                tmp = h->frames.next[i];
-                h->frames.next[i] = NULL;
+        if( h->frames.next[0] == NULL )
+            return 0;
 
-                /* move this P + Bs to current */
-                x264_frame_put( h->frames.current, tmp );
-                while( ( tmp = x264_frame_get( h->frames.next ) ) )
-                {
-                    x264_frame_put( h->frames.current, tmp );
-                }
-            }
-        }
-        else if( h->param.i_bframe > 0 )
+        if( h->param.rc.b_stat_read )
         {
-            if( h->frames.i_last_i  + 1 >= h->param.i_iframe )
-                fenc->i_type = X264_TYPE_I;
-            else if( h->frames.next[h->param.i_bframe-1] != NULL )
-                fenc->i_type = X264_TYPE_P;
-            else if( pic->i_type == X264_TYPE_AUTO )
-                fenc->i_type = X264_TYPE_B;
-            else
-                fenc->i_type = pic->i_type;
+            /* Use the frame types from the first pass */
+            for( i = 0; h->frames.next[i] != NULL; i++ )
+                h->frames.next[i]->i_type =
+                    x264_ratecontrol_slice_type( h, h->frames.next[i]->i_frame );
         }
-        else
+
+        for( bframes = 0;; bframes++ )
         {
-            if( pic->i_type == X264_TYPE_AUTO )
-            {
-                if( h->frames.i_last_i + 1 >= h->param.i_iframe )
-                    fenc->i_type = X264_TYPE_I;
-                else
-                    fenc->i_type = X264_TYPE_P;
-            }
-            else
+            frm = h->frames.next[bframes];
+
+            if( h->frames.i_last_i + bframes + 1 >= h->param.i_iframe
+                && frm->i_type != X264_TYPE_IDR )
             {
-                fenc->i_type = pic->i_type;
-            }
-        }
+                fprintf(stderr,"exceeded I-int: i_last_i(%d) + bframes(%d) + 1 >= i_iframe(%d) \n",
+                        h->frames.i_last_i, bframes, h->param.i_iframe);
+                if(    frm->i_type == X264_TYPE_P
+                    || frm->i_type == X264_TYPE_B )
+                    x264_log( h, X264_LOG_ERROR, "specified frame type is not compatible with keyframe interval\n" );
 
-        fenc->i_poc = h->i_poc;
+                frm->i_type = X264_TYPE_I;
+            }
 
-        /* 3: Update current/next */
-        if( fenc->i_type == X264_TYPE_B )
-        {
-            x264_frame_put( h->frames.next, fenc );
-        }
-        else
-        {
-            x264_frame_put( h->frames.current, fenc );
-            while( ( fenc = x264_frame_get( h->frames.next ) ) )
+            if( frm->i_type == X264_TYPE_IDR
+                || ( frm->i_type == X264_TYPE_I &&
+                   ( h->frames.i_last_idr + 1 >= h->param.i_idrframe
+                     && (bframes == 0 || !h->param.rc.b_stat_read ))))
             {
-                x264_frame_put( h->frames.current, fenc );
+                fprintf(stderr,"exceeded IDR-int: i_last_idr(%d) + 1 >= i_idrframe(%d) \n",
+                        h->frames.i_last_idr, h->param.i_idrframe);;
+                frm->i_type = X264_TYPE_IDR;
+                if( bframes > 0 )
+                {
+                    bframes--;
+                    h->frames.next[bframes]->i_type = X264_TYPE_P;
+                }
+
+                h->i_poc = 0;
+                h->i_frame_num = 0;
             }
-        }
-        h->i_poc += 2;
-    }
-    else    /* No more picture, begin encoding of last frames */
-    {
-        /* Move all next frames to current and mark the last one as a P */
-        x264_frame_t *tmp;
-        int i = -1;
-        while( h->frames.next[i+1] != NULL ) i++;
-        if( i >= 0 )
-        {
-            h->frames.next[i]->i_type = X264_TYPE_P;
-            tmp = h->frames.next[i];
-            h->frames.next[i] = NULL;
 
-            x264_frame_put( h->frames.current, tmp );
-            while( ( tmp = x264_frame_get( h->frames.next ) ) )
+            if( bframes == h->param.i_bframe
+                || h->frames.next[bframes+1] == NULL )
             {
-                x264_frame_put( h->frames.current, tmp );
+                if( frm->i_type == X264_TYPE_B )
+                    x264_log( h, X264_LOG_ERROR, "specified frame type is not compatible with max B-frames\n" );
+                if(    frm->i_type == X264_TYPE_AUTO
+                    || frm->i_type == X264_TYPE_B )
+                    frm->i_type = X264_TYPE_P;
             }
+
+            frm->i_poc = h->i_poc;
+            h->i_poc += 2;
+
+            if( frm->i_type != X264_TYPE_AUTO && frm->i_type != X264_TYPE_B )
+                break;
+
+            frm->i_type = X264_TYPE_B;
         }
+
+        /* 3: move some B-frames and 1 non-B to encode queue */
+        x264_frame_put( h->frames.current, h->frames.next[bframes] );
+        while( bframes-- )
+            x264_frame_put( h->frames.current, x264_frame_get( h->frames.next ) );
+        x264_frame_get( h->frames.next );
     }
     TIMER_STOP( i_mtime_encode_frame );
 
@@ -1021,7 +1021,6 @@ int     x264_encoder_encode( x264_t *h,
         pic->i_type = X264_TYPE_AUTO;
         return 0;
     }
-    x264_frame_put( h->frames.unused, h->fenc );  /* Safe to do it now, we don't use frames.unused for the rest */
 
 do_encode:
 
@@ -1122,7 +1121,7 @@ do_encode:
     x264_slice_write( h, i_nal_type, i_nal_ref_idc );
 
     /* XXX: this scene cut won't work with B frame (it may never create IDR -> bad) */
-    if( i_slice_type != SLICE_TYPE_I && !h->param.rc.b_stat_read 
+    if( i_slice_type == SLICE_TYPE_P && !h->param.rc.b_stat_read 
         && h->param.i_scenecut_threshold >= 0 )
     {
         int i_bias;
@@ -1146,8 +1145,7 @@ do_encode:
         i_bias = X264_MIN( i_bias, 100 );
 
         /* Bad P will be reencoded as I */
-        if( i_slice_type == SLICE_TYPE_P &&
-            i_mb_s < i_mb &&
+        if( i_mb_s < i_mb &&
             100 * i_inter_cost >= (100 - i_bias) * i_intra_cost )
             /* 100 * i_mb_i >= (100 - i_bias) * i_mb ) */
             /*
@@ -1159,7 +1157,7 @@ do_encode:
         {
 
             x264_log( h, X264_LOG_DEBUG, "scene cut at %d size=%d last I:%d last P:%d Intra:%lld Inter:%lld Ratio:%lld Bias=%d (I:%d P:%d Skip:%d)\n",
-                      h->i_frame - 1,
+                      h->fenc->i_frame,
                       h->out.nal[h->out.i_nal-1].i_payload,
                       h->i_last_intra_size, h->i_last_inter_size,
                       i_intra_cost, i_inter_cost,
@@ -1169,20 +1167,41 @@ do_encode:
             /* Restore frame num */
             h->i_frame_num--;
 
-            /* Do IDR if needed and if we can (won't work with B frames) */
-            if( h->frames.current[0] == NULL &&
-                h->frames.i_last_idr + 1 >= h->param.i_idrframe )
+            /* Do IDR if needed */
+            if( h->frames.i_last_idr + 1 >= h->param.i_idrframe )
             {
-                /* Reset */
-                h->i_poc       = 0;
-                h->i_frame_num = 0;
+                /* If using B-frames, make sure GOP is closed */
+                for( i = 0; h->frames.current[i] && h->frames.current[i]->i_type == X264_TYPE_B; i++ );
+                if( i > 0 )
+                {
+                    /* We don't know which frame is the scene cut, so we can't
+                     * assign an I-frame yet. Instead, change the previous
+                     * B-frame to P, and rearrange coding order. */
+                    x264_frame_t *tmp = h->frames.current[i-1];
+                    h->frames.current[i-1] = h->fenc;
+                    h->fenc = tmp;
+                    h->fenc->i_type = X264_TYPE_P;
+                }
+                else
+                {
+                    x264_frame_t *tmp;
+
+                    /* Reset */
+                    h->i_poc       = 0;
+                    h->i_frame_num = 0;
 
-                /* Reinit field of fenc */
-                h->fenc->i_type = X264_TYPE_IDR;
-                h->fenc->i_poc = h->i_poc;
+                    /* Reinit field of fenc */
+                    h->fenc->i_type = X264_TYPE_IDR;
+                    h->fenc->i_poc = h->i_poc;
 
-                /* Next Poc */
-                h->i_poc += 2;
+                    /* Next Poc */
+                    h->i_poc += 2;
+
+                    /* Put enqueued frames back in the pool */
+                    while( (tmp = x264_frame_get( h->frames.current ) ) != NULL )
+                        x264_frame_put( h->frames.next, tmp );
+                    x264_frame_sort( h->frames.next );
+                }
             }
             else
             {
@@ -1233,6 +1252,8 @@ do_encode:
     /* update rc */
     x264_ratecontrol_end( h, h->out.nal[h->out.i_nal-1].i_payload * 8 );
 
+    x264_frame_put( h->frames.unused, h->fenc );
+
     TIMER_STOP( i_mtime_encode_frame );
 
     /* ---------------------- Compute/Print statistics --------------------- */
index 0fa92a3..e73c9e3 100644 (file)
@@ -144,7 +144,7 @@ static inline double bits2qscale(ratecontrol_entry_t *rce, double bits)
 {
     if(bits<0.9)
     {
-        fprintf(stderr, "bits<0.9\n");
+//      fprintf(stderr, "bits<0.9\n");
         bits = 1.0;
     }
     return rce->qscale * (double)(rce->i_tex_bits + rce->p_tex_bits + .1) / bits;
@@ -710,9 +710,12 @@ static double get_diff_limited_q(x264_t *h, ratecontrol_entry_t *rce, double q)
     const double last_p_q    = rcc->last_qscale_for[SLICE_TYPE_P];
     const double last_non_b_q= rcc->last_qscale_for[rcc->last_non_b_pict_type];
     if( pict_type == SLICE_TYPE_I && h->param.rc.f_ip_factor < 0 )
-        q = last_p_q     / -h->param.rc.f_ip_factor;
-    else if(pict_type==SLICE_TYPE_B && h->param.rc.f_pb_factor < 0)
-        q = last_non_b_q * -h->param.rc.f_pb_factor;
+        q = last_p_q / fabs( h->param.rc.f_ip_factor );
+    else if( pict_type == SLICE_TYPE_B
+             && ( h->param.rc.f_pb_factor < 0 || rce->i_tex_bits + rce->p_tex_bits == 0 ) )
+        q = last_non_b_q * fabs( h->param.rc.f_pb_factor );
+    else if( pict_type == SLICE_TYPE_P && rce->i_tex_bits + rce->p_tex_bits == 0 )
+        q = last_p_q;
 
     /* last qscale / qdiff stuff */
     if(rcc->last_non_b_pict_type==pict_type || pict_type!=SLICE_TYPE_I)