]> git.sesse.net Git - x264/commitdiff
Add mb_info API for signalling constant macroblocks
authorFiona Glaser <fiona@x264.com>
Thu, 29 Mar 2012 21:14:07 +0000 (14:14 -0700)
committerFiona Glaser <fiona@x264.com>
Tue, 24 Apr 2012 21:13:17 +0000 (14:13 -0700)
Some use-cases of x264 involve encoding video with large constant areas of the frame.
Sometimes, the caller knows which areas these are, and can tell x264.
This API lets the caller do this and adds internal tracking of modifications to macroblocks to avoid problems.
This is really only suitable without B-frames.
An example use-case would be using x264 for VNC.

common/common.h
common/deblock.c
common/frame.c
common/frame.h
encoder/analyse.c
encoder/encoder.c
x264.h

index a3149c02c7f963ac4bd04630ecfdda12359d5b6f..5e3421291613becbd7e53fc5aa4fc830db5f3419 100644 (file)
@@ -385,6 +385,7 @@ typedef struct
     } ref_pic_list_order[2][X264_REF_MAX];
 
     /* P-frame weighting */
+    int b_weighted_pred;
     x264_weight_t weight[X264_REF_MAX*2][3];
 
     int i_mmco_remove_from_end;
index f8b60b5a144fc390c0205e7e0187e5431ea3c5ad..1f05e831cc7eb948005b0ec3f9bb92a87444fc92 100644 (file)
@@ -393,7 +393,7 @@ void x264_frame_deblock_row( x264_t *h, int mb_y )
         x264_macroblock_cache_load_neighbours_deblock( h, mb_x, mb_y );
 
         int mb_xy = h->mb.i_mb_xy;
-        int transform_8x8 = h->mb.mb_transform_size[h->mb.i_mb_xy];
+        int transform_8x8 = h->mb.mb_transform_size[mb_xy];
         int intra_cur = IS_INTRA( h->mb.type[mb_xy] );
         uint8_t (*bs)[8][4] = h->deblock_strength[mb_y&1][h->param.b_sliced_threads?mb_xy:mb_x];
 
@@ -501,8 +501,19 @@ void x264_frame_deblock_row( x264_t *h, int mb_y )
                 int qp_left = (qp + qpl + 1) >> 1;
                 int qpc_left = (qpc + h->chroma_qp_table[qpl] + 1) >> 1;
                 int intra_left = IS_INTRA( h->mb.type[h->mb.i_mb_xy-1] );
+                int intra_deblock = intra_cur || intra_left;
 
-                if( intra_cur || intra_left )
+                /* Any MB that was coded, or that analysis decided to skip, has quality commensurate with its QP.
+                 * But if deblocking affects neighboring MBs that were force-skipped, blur might accumulate there.
+                 * So reset their effective QP to max, to indicate that lack of guarantee. */
+                if( h->fdec->mb_info && M32( bs[0][0] ) )
+                {
+#define RESET_EFFECTIVE_QP(xy) h->fdec->effective_qp[xy] |= 0xff * !!(h->fdec->mb_info[xy] & X264_MBINFO_CONSTANT);
+                    RESET_EFFECTIVE_QP(mb_xy);
+                    RESET_EFFECTIVE_QP(h->mb.i_mb_left_xy[0]);
+                }
+
+                if( intra_deblock )
                     FILTER( _intra, 0, 0, qp_left, qpc_left );
                 else
                     FILTER(       , 0, 0, qp_left, qpc_left );
@@ -547,15 +558,22 @@ void x264_frame_deblock_row( x264_t *h, int mb_y )
                 int qp_top = (qp + qpt + 1) >> 1;
                 int qpc_top = (qpc + h->chroma_qp_table[qpt] + 1) >> 1;
                 int intra_top = IS_INTRA( h->mb.type[h->mb.i_mb_top_xy] );
+                int intra_deblock = intra_cur || intra_top;
 
-                if( (!b_interlaced || (!MB_INTERLACED && !h->mb.field[h->mb.i_mb_top_xy]))
-                    && (intra_cur || intra_top) )
+                /* This edge has been modified, reset effective qp to max. */
+                if( h->fdec->mb_info && M32( bs[1][0] ) )
+                {
+                    RESET_EFFECTIVE_QP(mb_xy);
+                    RESET_EFFECTIVE_QP(h->mb.i_mb_top_xy);
+                }
+
+                if( (!b_interlaced || (!MB_INTERLACED && !h->mb.field[h->mb.i_mb_top_xy])) && intra_deblock )
                 {
                     FILTER( _intra, 1, 0, qp_top, qpc_top );
                 }
                 else
                 {
-                    if( intra_cur || intra_top )
+                    if( intra_deblock )
                         M32( bs[1][0] ) = 0x03030303;
                     FILTER(       , 1, 0, qp_top, qpc_top );
                 }
index a65610a431df8d72c78b900209f43c5918aec4e9..8b74ce6788711001aef8f709334f39d831dddf46 100644 (file)
@@ -210,6 +210,8 @@ static x264_frame_t *x264_frame_new( x264_t *h, int b_fdec )
         }
         if( PARAM_INTERLACED )
             CHECKED_MALLOC( frame->field, i_mb_count * sizeof(uint8_t) );
+        if( h->param.analyse.b_mb_info )
+            CHECKED_MALLOC( frame->effective_qp, i_mb_count * sizeof(uint8_t) );
     }
     else /* fenc frame */
     {
@@ -289,6 +291,7 @@ void x264_frame_delete( x264_frame_t *frame )
         x264_free( frame->f_row_qp );
         x264_free( frame->f_row_qscale );
         x264_free( frame->field );
+        x264_free( frame->effective_qp );
         x264_free( frame->mb_type );
         x264_free( frame->mb_partition );
         x264_free( frame->mv[0] );
@@ -354,6 +357,8 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src )
     dst->i_pic_struct = src->i_pic_struct;
     dst->extra_sei  = src->extra_sei;
     dst->opaque     = src->opaque;
+    dst->mb_info    = src->prop.mb_info;
+    dst->mb_info_free = src->prop.mb_info_free;
 
     uint8_t *pix[3];
     int stride[3];
index 31f0a3f1febdefadad98593702e8299f75042b88..29ce70937638f5cf38c8c018e8c97cee6459cd2b 100644 (file)
@@ -97,6 +97,7 @@ typedef struct x264_frame
     int16_t (*mv16x16)[2];
     int16_t (*lowres_mvs[2][X264_BFRAME_MAX+1])[2];
     uint8_t *field;
+    uint8_t *effective_qp;
 
     /* Stored as (lists_used << LOWRES_COST_SHIFT) + (cost).
      * Doesn't need special addressing for intra cost because
@@ -165,6 +166,10 @@ typedef struct x264_frame
 
     /* user data */
     void *opaque;
+
+    /* user frame properties */
+    uint8_t *mb_info;
+    void (*mb_info_free)( void* );
 } x264_frame_t;
 
 /* synchronized frame list */
index e47787718dac7d5810fc3a914902c01e8d3ba96a..ee61a0035071a97aeff0e879fc022b6fce826bbd 100644 (file)
@@ -2995,6 +2995,8 @@ void x264_macroblock_analyse( x264_t *h )
     if( h->param.rc.i_aq_mode && h->param.analyse.i_subpel_refine < 10 && abs(h->mb.i_qp - h->mb.i_last_qp) == 1 )
         h->mb.i_qp = h->mb.i_last_qp;
 
+    if( h->param.analyse.b_mb_info )
+        h->fdec->effective_qp[h->mb.i_mb_xy] = h->mb.i_qp; /* Store the real analysis QP. */
     x264_mb_analyse_init( h, &analysis, h->mb.i_qp );
 
     /*--------------------------- Do the analysis ---------------------------*/
@@ -3034,23 +3036,33 @@ intra_analysis:
         }
         else
         {
-            int skip_invalid = h->i_thread_frames > 1 && h->mb.cache.pskip_mv[1] > h->mb.mv_max_spel[1];
-            /* If the current macroblock is off the frame, just skip it. */
-            if( HAVE_INTERLACED && !MB_INTERLACED && h->mb.i_mb_y * 16 >= h->param.i_height && !skip_invalid )
+            /* Special fast-skip logic using information from mb_info. */
+            if( h->fenc->mb_info && !SLICE_MBAFF && (h->fenc->mb_info[h->mb.i_mb_xy]&X264_MBINFO_CONSTANT) &&
+                !M32(h->mb.cache.pskip_mv) && (h->fenc->i_frame - h->fref[0][0]->i_frame) == 1 && !h->sh.b_weighted_pred &&
+                h->fref[0][0]->effective_qp[h->mb.i_mb_xy] <= h->mb.i_qp )
+            {
                 b_skip = 1;
-            /* Fast P_SKIP detection */
-            else if( h->param.analyse.b_fast_pskip )
-            {
-                if( skip_invalid )
-                    // FIXME don't need to check this if the reference frame is done
-                    {}
-                else if( h->param.analyse.i_subpel_refine >= 3 )
-                    analysis.b_try_skip = 1;
-                else if( h->mb.i_mb_type_left[0] == P_SKIP ||
-                         h->mb.i_mb_type_top == P_SKIP ||
-                         h->mb.i_mb_type_topleft == P_SKIP ||
-                         h->mb.i_mb_type_topright == P_SKIP )
-                    b_skip = x264_macroblock_probe_pskip( h );
+            }
+            else
+            {
+                int skip_invalid = h->i_thread_frames > 1 && h->mb.cache.pskip_mv[1] > h->mb.mv_max_spel[1];
+                /* If the current macroblock is off the frame, just skip it. */
+                if( HAVE_INTERLACED && !MB_INTERLACED && h->mb.i_mb_y * 16 >= h->param.i_height && !skip_invalid )
+                    b_skip = 1;
+                /* Fast P_SKIP detection */
+                else if( h->param.analyse.b_fast_pskip )
+                {
+                    if( skip_invalid )
+                        // FIXME don't need to check this if the reference frame is done
+                        {}
+                    else if( h->param.analyse.i_subpel_refine >= 3 )
+                        analysis.b_try_skip = 1;
+                    else if( h->mb.i_mb_type_left[0] == P_SKIP ||
+                             h->mb.i_mb_type_top == P_SKIP ||
+                             h->mb.i_mb_type_topleft == P_SKIP ||
+                             h->mb.i_mb_type_topright == P_SKIP )
+                        b_skip = x264_macroblock_probe_pskip( h );
+                }
             }
         }
 
index 4531ef75ced032060016b3d961e6ea921d77a9a0..97d6562ee8e8ebf9e435df72757250d7ba1f37fe 100644 (file)
@@ -280,8 +280,10 @@ static void x264_slice_header_write( bs_t *s, x264_slice_header_t *sh, int i_nal
         }
     }
 
+    sh->b_weighted_pred = 0;
     if( sh->pps->b_weighted_pred && sh->i_type == SLICE_TYPE_P )
     {
+        sh->b_weighted_pred = sh->weight[0][0].weightfn || sh->weight[0][1].weightfn || sh->weight[0][2].weightfn;
         /* pred_weight_table() */
         bs_write_ue( s, sh->weight[0][0].i_denom );
         bs_write_ue( s, sh->weight[0][1].i_denom );
@@ -3196,6 +3198,10 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,
     }
 
     x264_emms();
+
+    if( h->fdec->mb_info_free )
+        h->fdec->mb_info_free( h->fdec->mb_info );
+
     /* generate buffering period sei and insert it into place */
     if( h->i_thread_frames > 1 && h->fenc->b_keyframe && h->sps->vui.b_nal_hrd_parameters_present )
     {
diff --git a/x264.h b/x264.h
index 0cb1ecfdb83260f20e1e9012797814cc920a76b0..3dcb386d898f5fbfd0f97b3f727cd6f7f9ce7986 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -41,7 +41,7 @@
 
 #include "x264_config.h"
 
-#define X264_BUILD 123
+#define X264_BUILD 124
 
 /* Application developers planning to link against a shared library version of
  * libx264 from a Microsoft Visual Studio or similar development environment
@@ -364,6 +364,8 @@ typedef struct x264_param_t
         float        f_psy_trellis; /* Psy trellis strength */
         int          b_psy; /* Toggle all psy optimizations */
 
+        int          b_mb_info; /* Use input mb_info data in x264_picture_t */
+
         /* the deadzone size that will be used in luma quantization */
         int          i_luma_deadzone[2]; /* {inter, intra} */
 
@@ -689,18 +691,36 @@ typedef struct
 
 typedef struct
 {
+    /* All arrays of data here are ordered as follows:
+     * each array contains one offset per macroblock, in raster scan order.  In interlaced
+     * mode, top-field MBs and bottom-field MBs are interleaved at the row level.
+     * Macroblocks are 16x16 blocks of pixels (with respect to the luma plane).  For the
+     * purposes of calculating the number of macroblocks, width and height are rounded up to
+     * the nearest 16.  If in interlaced mode, height is rounded up to the nearest 32 instead. */
+
     /* In: an array of quantizer offsets to be applied to this image during encoding.
      *     These are added on top of the decisions made by x264.
      *     Offsets can be fractional; they are added before QPs are rounded to integer.
      *     Adaptive quantization must be enabled to use this feature.  Behavior if quant
-     *     offsets differ between encoding passes is undefined.
-     *
-     *     Array contains one offset per macroblock, in raster scan order.  In interlaced
-     *     mode, top-field MBs and bottom-field MBs are interleaved at the row level. */
+     *     offsets differ between encoding passes is undefined. */
     float *quant_offsets;
     /* In: optional callback to free quant_offsets when used.
      *     Useful if one wants to use a different quant_offset array for each frame. */
     void (*quant_offsets_free)( void* );
+
+    /* In: optional array of flags for each macroblock.
+     *     Allows specifying additional information for the encoder such as which macroblocks
+     *     remain unchanged.  Usable flags are listed below.
+     *     x264_param_t.analyse.b_mb_info must be set to use this, since x264 needs to track
+     *     extra data internally to make full use of this information. */
+    uint8_t *mb_info;
+    /* In: optional callback to free mb_info when used. */
+    void (*mb_info_free)( void* );
+
+    /* The macroblock is constant and remains unchanged from the previous frame. */
+    #define X264_MBINFO_CONSTANT   (1<<0)
+    /* More flags may be added in the future. */
+
     /* Out: SSIM of the the frame luma (if x264_param_t.b_ssim is set) */
     double f_ssim;
     /* Out: Average PSNR of the frame (if x264_param_t.b_psnr is set) */