]> git.sesse.net Git - x264/commitdiff
Periodic intra refresh
authorFiona Glaser <fiona@x264.com>
Tue, 22 Dec 2009 23:59:29 +0000 (18:59 -0500)
committerFiona Glaser <fiona@x264.com>
Thu, 14 Jan 2010 04:46:56 +0000 (23:46 -0500)
Uses SEI recovery points, a moving vertical "bar" of intra blocks, and motion vector restrictions to eliminate keyframes.
Attempt to hide the visual appearance of the intra bar when --no-psy isn't set.
Enabled with --intra-refresh.
The refresh interval is controlled using keyint, but won't exceed the number of macroblock columns in the frame.
Greatly benefits low-latency streaming by making it possible to achieve constant framesize without intra-only encoding.
Combined with slice-max size for one slice per packet, tests suggest effective resiliance against packet loss as high as 25%.
x264 is now the best free software low-latency video encoder in the world.

Accordingly, change the API to add b_keyframe to the parameters present in output pictures.
Calling applications should check this to see if a frame is seekable, not the frame type.

Also make x264's motion estimation strictly abide by horizontal MV range limits in order for PIR to work.
Also fix a major bug in sliced-threads VBV handling.
Also change "auto" threads for sliced threads to "cores" instead of "1.5*cores" after performance testing.
Also simplify ratecontrol's checking of first pass options.
Also some minor tweaks to row-based VBV that should improve VBV accuracy on small frames.

18 files changed:
common/bs.h
common/common.c
common/common.h
common/frame.c
common/frame.h
encoder/analyse.c
encoder/encoder.c
encoder/lookahead.c
encoder/me.c
encoder/ratecontrol.c
encoder/set.c
encoder/set.h
encoder/slicetype.c
output/flv.c
output/matroska.c
output/mp4.c
x264.c
x264.h

index afe6b5af7bdd7134ea9d2451f9d2d8ce5d8bc4dd..dadd28eb4ddfbde746ca96d9ad0d682e32a8b954 100644 (file)
@@ -160,6 +160,11 @@ static inline void bs_align_1( bs_t *s )
     bs_write( s, s->i_left&7, (1 << (s->i_left&7)) - 1 );
     bs_flush( s );
 }
+static inline void bs_align_10( bs_t *s )
+{
+    if( s->i_left&7 )
+        bs_write( s, s->i_left&7, 1 << ( (s->i_left&7) - 1 ) );
+}
 
 /* golomb functions */
 
index 0833dcf582d0f717e864e1bc9eb02324ff12fd17..9eed5c37e955294471105f79ff66d2680bd615b2 100644 (file)
@@ -356,6 +356,8 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value )
             p->i_scenecut_threshold = atoi(value);
         }
     }
+    OPT("intra-refresh")
+        p->b_intra_refresh = atobool(value);
     OPT("bframes")
         p->i_bframe = atoi(value);
     OPT("b-adapt")
@@ -915,8 +917,8 @@ char *x264_param2string( x264_param_t *p, int b_res )
     }
     s += sprintf( s, " wpredp=%d", p->analyse.i_weighted_pred > 0 ? p->analyse.i_weighted_pred : 0 );
 
-    s += sprintf( s, " keyint=%d keyint_min=%d scenecut=%d",
-                  p->i_keyint_max, p->i_keyint_min, p->i_scenecut_threshold );
+    s += sprintf( s, " keyint=%d keyint_min=%d scenecut=%d intra_refresh=%d",
+                  p->i_keyint_max, p->i_keyint_min, p->i_scenecut_threshold, p->b_intra_refresh );
 
     if( p->rc.b_mb_tree || p->rc.i_vbv_buffer_size )
         s += sprintf( s, " rc_lookahead=%d", p->rc.i_lookahead );
index a1f4573fecc852cadbd81dc64ca05b730d88f168..5c484c49caf2f041fe5cc29240c7885c2eb56fa4 100644 (file)
@@ -285,7 +285,7 @@ typedef struct x264_lookahead_t
     volatile uint8_t              b_exit_thread;
     uint8_t                       b_thread_active;
     uint8_t                       b_analyse_keyframe;
-    int                           i_last_idr;
+    int                           i_last_keyframe;
     int                           i_slicetype_length;
     x264_frame_t                  *last_nonb;
     x264_synch_frame_list_t       ifbuf;
@@ -423,7 +423,7 @@ struct x264_t
         /* frames used for reference + sentinels */
         x264_frame_t *reference[16+2];
 
-        int i_last_idr; /* Frame number of the last IDR */
+        int i_last_keyframe; /* Frame number of the last keyframe */
 
         int i_input;    /* Number of input frames already accepted */
 
@@ -559,6 +559,7 @@ struct x264_t
         int b_skip_mc;
         /* set to true if we are re-encoding a macroblock. */
         int b_reencode_mb;
+        int ip_offset; /* Used by PIR to offset the quantizer of intra-refresh blocks. */
 
         struct
         {
index 259fbf50a63ed47a2737b391057cbe7f73a229d2..0ece66917c9e2d464b01f85c312c729dc63c4f33 100644 (file)
@@ -1005,6 +1005,7 @@ x264_frame_t *x264_frame_pop_unused( x264_t *h, int b_fdec )
     frame->i_reference_count = 1;
     frame->b_intra_calculated = 0;
     frame->b_scenecut = 1;
+    frame->b_keyframe = 0;
 
     memset( frame->weight, 0, sizeof(frame->weight) );
     memset( frame->f_weighted_cost_delta, 0, sizeof(frame->f_weighted_cost_delta) );
index 66196d5cb30e241694aaf6a4accca9081b513e6d..786869e5660edd6dcc3904bc16dbe5ca061f392c 100644 (file)
@@ -38,10 +38,11 @@ typedef struct x264_frame
     int64_t i_dts;
     x264_param_t *param;
 
-    int     i_frame;    /* Presentation frame number */
-    int     i_coded; /* Coded frame number */
+    int     i_frame;     /* Presentation frame number */
+    int     i_coded;     /* Coded frame number */
     int     i_frame_num; /* 7.4.3 frame_num */
     int     b_kept_as_ref;
+    int     b_keyframe;
     uint8_t b_fdec;
     uint8_t b_last_minigop_bframe; /* this frame is the last b in a sequence of bframes */
     uint8_t i_bframes;   /* number of bframes following this nonb in coded order */
@@ -117,6 +118,10 @@ typedef struct x264_frame
     x264_pthread_mutex_t mutex;
     x264_pthread_cond_t  cv;
 
+    /* periodic intra refresh */
+    float   f_pir_position;
+    int     i_pir_start_col;
+    int     i_pir_end_col;
 } x264_frame_t;
 
 /* synchronized frame list */
index 74f77dc0bcb67c794139bbe2674857a397aab967..116e7a9e8518b90c813e4856a3a96d28c29b8fd5 100644 (file)
@@ -84,6 +84,7 @@ typedef struct
     /* I: Intra part */
     /* Take some shortcuts in intra search if intra is deemed unlikely */
     int b_fast_intra;
+    int b_force_intra; /* For Periodic Intra Refresh.  Only supported in P-frames. */
     int b_try_pskip;
 
     /* Luma part */
@@ -325,15 +326,8 @@ static void x264_mb_analyse_load_costs( x264_t *h, x264_mb_analysis_t *a )
     a->p_cost_ref1 = x264_cost_ref[a->i_lambda][x264_clip3(h->sh.i_num_ref_idx_l1_active-1,0,2)];
 }
 
-static void x264_mb_analyse_init( x264_t *h, x264_mb_analysis_t *a, int i_qp )
+static void x264_mb_analyse_init_qp( x264_t *h, x264_mb_analysis_t *a, int i_qp )
 {
-    int i = h->param.analyse.i_subpel_refine - (h->sh.i_type == SLICE_TYPE_B);
-
-    /* mbrd == 1 -> RD mode decision */
-    /* mbrd == 2 -> RD refinement */
-    /* mbrd == 3 -> QPRD */
-    a->i_mbrd = (i>=6) + (i>=8) + (h->param.analyse.i_subpel_refine>=10);
-
     /* conduct the analysis using this lamda and QP */
     a->i_qp = h->mb.i_qp = i_qp;
     h->mb.i_chroma_qp = h->chroma_qp_table[i_qp];
@@ -353,6 +347,19 @@ static void x264_mb_analyse_init( x264_t *h, x264_mb_analysis_t *a, int i_qp )
     /* Adjusting chroma lambda based on QP offset hurts PSNR but improves visual quality. */
     h->mb.i_chroma_lambda2_offset = h->param.analyse.b_psy ? x264_chroma_lambda2_offset_tab[h->mb.i_qp-h->mb.i_chroma_qp+12] : 256;
 
+}
+
+static void x264_mb_analyse_init( x264_t *h, x264_mb_analysis_t *a, int i_qp )
+{
+    int i = h->param.analyse.i_subpel_refine - (h->sh.i_type == SLICE_TYPE_B);
+
+    /* mbrd == 1 -> RD mode decision */
+    /* mbrd == 2 -> RD refinement */
+    /* mbrd == 3 -> QPRD */
+    a->i_mbrd = (i>=6) + (i>=8) + (h->param.analyse.i_subpel_refine>=10);
+
+    x264_mb_analyse_init_qp( h, a, i_qp );
+
     h->mb.i_me_method = h->param.analyse.i_me_method;
     h->mb.i_subpel_refine = h->param.analyse.i_subpel_refine;
     h->mb.b_chroma_me = h->param.analyse.b_chroma_me && h->sh.i_type == SLICE_TYPE_P
@@ -391,6 +398,14 @@ static void x264_mb_analyse_init( x264_t *h, x264_mb_analysis_t *a, int i_qp )
         h->mb.mv_max[0] = 4*( 16*( h->sps->i_mb_width - h->mb.i_mb_x - 1 ) + 24 );
         h->mb.mv_min_spel[0] = CLIP_FMV( h->mb.mv_min[0] );
         h->mb.mv_max_spel[0] = CLIP_FMV( h->mb.mv_max[0] );
+        if( h->param.b_intra_refresh && h->sh.i_type == SLICE_TYPE_P )
+        {
+            int max_x = (h->fref0[0]->i_pir_end_col * 16 - 3)*4; /* 3 pixels of hpel border */
+            int max_mv = max_x - 4*16*h->mb.i_mb_x;
+            /* If we're left of the refresh bar, don't reference right of it. */
+            if( max_mv > 0 && h->mb.i_mb_x < h->fdec->i_pir_start_col )
+                h->mb.mv_max_spel[0] = X264_MIN( h->mb.mv_max_spel[0], max_mv );
+        }
         h->mb.mv_min_fpel[0] = (h->mb.mv_min_spel[0]>>2) + i_fpel_border;
         h->mb.mv_max_fpel[0] = (h->mb.mv_max_spel[0]>>2) - i_fpel_border;
         if( h->mb.i_mb_x == 0 )
@@ -489,6 +504,14 @@ static void x264_mb_analyse_init( x264_t *h, x264_mb_analysis_t *a, int i_qp )
             }
         }
         h->mb.b_skip_mc = 0;
+        if( h->param.b_intra_refresh && h->sh.i_type == SLICE_TYPE_P &&
+            h->mb.i_mb_x >= h->fdec->i_pir_start_col && h->mb.i_mb_x <= h->fdec->i_pir_end_col )
+        {
+            a->b_force_intra = 1;
+            a->b_fast_intra = 0;
+        }
+        else
+            a->b_force_intra = 0;
     }
 }
 
@@ -1275,7 +1298,7 @@ static void x264_mb_analyse_inter_p16x16( x264_t *h, x264_mb_analysis_t *a )
     if( a->i_mbrd )
     {
         x264_mb_cache_fenc_satd( h );
-        if( a->l0.me16x16.i_ref == 0 && M32( a->l0.me16x16.mv ) == M32( h->mb.cache.pskip_mv ) )
+        if( a->l0.me16x16.i_ref == 0 && M32( a->l0.me16x16.mv ) == M32( h->mb.cache.pskip_mv ) && !a->b_force_intra )
         {
             h->mb.i_partition = D_16x16;
             x264_macroblock_cache_mv_ptr( h, 0, 0, 4, 4, 0, a->l0.me16x16.mv );
@@ -2399,6 +2422,7 @@ void x264_macroblock_analyse( x264_t *h )
     /*--------------------------- Do the analysis ---------------------------*/
     if( h->sh.i_type == SLICE_TYPE_I )
     {
+intra_analysis:
         if( analysis.i_mbrd )
             x264_mb_cache_fenc_satd( h );
         x264_mb_analyse_intra( h, &analysis, COST_MAX );
@@ -2421,20 +2445,31 @@ void x264_macroblock_analyse( x264_t *h )
 
         h->mc.prefetch_ref( h->mb.pic.p_fref[0][0][h->mb.i_mb_x&3], h->mb.pic.i_stride[0], 0 );
 
-        /* Fast P_SKIP detection */
         analysis.b_try_pskip = 0;
-        if( h->param.analyse.b_fast_pskip )
+        if( analysis.b_force_intra )
         {
-            if( h->param.i_threads > 1 && !h->param.b_sliced_threads && h->mb.cache.pskip_mv[1] > h->mb.mv_max_spel[1] )
-                // 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_pskip = 1;
-            else if( h->mb.i_mb_type_left == 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 );
+            if( !h->param.analyse.b_psy )
+            {
+                x264_mb_analyse_init_qp( h, &analysis, X264_MAX( h->mb.i_qp - h->mb.ip_offset, h->param.rc.i_qp_min ) );
+                goto intra_analysis;
+            }
+        }
+        else
+        {
+            /* Fast P_SKIP detection */
+            if( h->param.analyse.b_fast_pskip )
+            {
+                if( h->param.i_threads > 1 && !h->param.b_sliced_threads && h->mb.cache.pskip_mv[1] > h->mb.mv_max_spel[1] )
+                    // 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_pskip = 1;
+                else if( h->mb.i_mb_type_left == 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 );
+            }
         }
 
         h->mc.prefetch_ref( h->mb.pic.p_fref[0][0][h->mb.i_mb_x&3], h->mb.pic.i_stride[0], 1 );
@@ -2623,6 +2658,17 @@ void x264_macroblock_analyse( x264_t *h )
             COPY2_IF_LT( i_cost, analysis.i_satd_i4x4, i_type, I_4x4 );
             COPY2_IF_LT( i_cost, analysis.i_satd_pcm, i_type, I_PCM );
 
+            if( analysis.b_force_intra && !IS_INTRA(i_type) )
+            {
+                /* Intra masking: copy fdec to fenc and re-encode the block as intra in order to make it appear as if
+                 * it was an inter block. */
+                h->mc.copy[PIXEL_16x16]( h->mb.pic.p_fenc[0], FENC_STRIDE, h->mb.pic.p_fdec[0], FDEC_STRIDE, 16 );
+                h->mc.copy[PIXEL_8x8]  ( h->mb.pic.p_fenc[1], FENC_STRIDE, h->mb.pic.p_fdec[1], FDEC_STRIDE, 8 );
+                h->mc.copy[PIXEL_8x8]  ( h->mb.pic.p_fenc[2], FENC_STRIDE, h->mb.pic.p_fdec[2], FDEC_STRIDE, 8 );
+                x264_mb_analyse_init_qp( h, &analysis, X264_MAX( h->mb.i_qp - h->mb.ip_offset, h->param.rc.i_qp_min ) );
+                goto intra_analysis;
+            }
+
             h->mb.i_type = i_type;
 
             if( analysis.i_mbrd >= 2 && h->mb.i_type != I_PCM )
index 89824f6aef3a145083df577fc7d424b7d374cffb..2e839e4cb14e7d5fe658163664ef75fe82bf50f1 100644 (file)
@@ -403,7 +403,7 @@ static int x264_validate_parameters( x264_t *h )
     }
 
     if( h->param.i_threads == X264_THREADS_AUTO )
-        h->param.i_threads = x264_cpu_num_processors() * 3/2;
+        h->param.i_threads = x264_cpu_num_processors() * (h->param.b_sliced_threads?2:3)/2;
     h->param.i_threads = x264_clip3( h->param.i_threads, 1, X264_THREAD_MAX );
     if( h->param.i_threads > 1 )
     {
@@ -534,7 +534,6 @@ static int x264_validate_parameters( x264_t *h )
         h->param.i_keyint_max = 1;
     if( h->param.i_scenecut_threshold < 0 )
         h->param.i_scenecut_threshold = 0;
-    h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
     if( !h->param.analyse.i_subpel_refine && h->param.analyse.i_direct_mv_pred > X264_DIRECT_PRED_SPATIAL )
     {
         x264_log( h, X264_LOG_WARNING, "subme=0 + direct=temporal is not supported\n" );
@@ -542,7 +541,10 @@ static int x264_validate_parameters( x264_t *h )
     }
     h->param.i_bframe = x264_clip3( h->param.i_bframe, 0, X264_BFRAME_MAX );
     if( h->param.i_keyint_max == 1 )
+    {
         h->param.i_bframe = 0;
+        h->param.b_intra_refresh = 0;
+    }
     h->param.i_bframe_bias = x264_clip3( h->param.i_bframe_bias, -90, 100 );
     if( h->param.i_bframe <= 1 )
         h->param.i_bframe_pyramid = X264_B_PYRAMID_NONE;
@@ -553,6 +555,19 @@ static int x264_validate_parameters( x264_t *h )
         h->param.analyse.i_direct_mv_pred = 0;
         h->param.analyse.b_weighted_bipred = 0;
     }
+    if( h->param.b_intra_refresh && h->param.i_bframe_pyramid == X264_B_PYRAMID_NORMAL )
+    {
+        x264_log( h, X264_LOG_WARNING, "b-pyramid normal + intra-refresh is not supported\n" );
+        h->param.i_bframe_pyramid = X264_B_PYRAMID_STRICT;
+    }
+    if( h->param.b_intra_refresh && h->param.i_frame_reference > 1 )
+    {
+        x264_log( h, X264_LOG_WARNING, "ref > 1 + intra-refresh is not supported\n" );
+        h->param.i_frame_reference = 1;
+    }
+    if( h->param.b_intra_refresh )
+        h->param.i_keyint_max = X264_MIN( h->param.i_keyint_max, (h->param.i_width+15)/16 - 1 );
+    h->param.i_keyint_min = x264_clip3( h->param.i_keyint_min, 1, h->param.i_keyint_max/2+1 );
     h->param.rc.i_lookahead = x264_clip3( h->param.rc.i_lookahead, 0, X264_LOOKAHEAD_MAX );
     {
         int maxrate = X264_MAX( h->param.rc.i_vbv_max_bitrate, h->param.rc.i_bitrate );
@@ -737,6 +752,7 @@ static int x264_validate_parameters( x264_t *h )
     BOOLIFY( b_deterministic );
     BOOLIFY( b_sliced_threads );
     BOOLIFY( b_interlaced );
+    BOOLIFY( b_intra_refresh );
     BOOLIFY( b_visualize );
     BOOLIFY( b_aud );
     BOOLIFY( b_repeat_headers );
@@ -889,7 +905,7 @@ x264_t *x264_encoder_open( x264_param_t *param )
     h->frames.b_have_lowres |= h->param.rc.b_stat_read && h->param.rc.i_vbv_buffer_size > 0;
     h->frames.b_have_sub8x8_esa = !!(h->param.analyse.inter & X264_ANALYSE_PSUB8x8);
 
-    h->frames.i_last_idr = - h->param.i_keyint_max;
+    h->frames.i_last_keyframe = - h->param.i_keyint_max;
     h->frames.i_input    = 0;
 
     CHECKED_MALLOCZERO( h->frames.unused[0], (h->frames.i_delay + 3) * sizeof(x264_frame_t *) );
@@ -2104,12 +2120,13 @@ int     x264_encoder_encode( x264_t *h,
             h->fenc->param->param_free( h->fenc->param );
     }
 
-    if( h->fenc->i_type == X264_TYPE_IDR )
+    if( h->fenc->b_keyframe )
     {
-        h->frames.i_last_idr = h->fenc->i_frame;
-        h->i_frame_num = 0;
+        h->frames.i_last_keyframe = h->fenc->i_frame;
+        if( h->fenc->i_type == X264_TYPE_IDR )
+            h->i_frame_num = 0;
     }
-    h->sh.i_mmco_command_count = 0;
+    h->sh.i_mmco_command_count =
     h->sh.i_mmco_remove_from_end = 0;
     h->b_ref_reorder[0] =
     h->b_ref_reorder[1] = 0;
@@ -2153,7 +2170,7 @@ int     x264_encoder_encode( x264_t *h,
     }
 
     h->fdec->i_poc =
-    h->fenc->i_poc = 2 * (h->fenc->i_frame - h->frames.i_last_idr);
+    h->fenc->i_poc = 2 * (h->fenc->i_frame - h->frames.i_last_keyframe);
     h->fdec->i_type = h->fenc->i_type;
     h->fdec->i_frame = h->fenc->i_frame;
     h->fenc->b_kept_as_ref =
@@ -2206,33 +2223,65 @@ int     x264_encoder_encode( x264_t *h,
 
     int overhead = NALU_OVERHEAD;
 
+    if( h->param.b_intra_refresh && h->fenc->i_type == X264_TYPE_P )
+    {
+        int pocdiff = (h->fdec->i_poc - h->fref0[0]->i_poc)/2;
+        float increment = ((float)h->sps->i_mb_width-1) / h->param.i_keyint_max;
+        if( IS_X264_TYPE_I( h->fref0[0]->i_type ) )
+            h->fdec->f_pir_position = 0;
+        else
+        {
+            if( h->fref0[0]->i_pir_end_col == h->sps->i_mb_width - 1 )
+            {
+                h->fdec->f_pir_position = 0;
+                h->fenc->b_keyframe = 1;
+            }
+            else
+                h->fdec->f_pir_position = h->fref0[0]->f_pir_position;
+        }
+        h->fdec->i_pir_start_col = h->fdec->f_pir_position+0.5;
+        h->fdec->f_pir_position += increment * pocdiff;
+        h->fdec->i_pir_end_col = X264_MIN( h->fdec->f_pir_position+0.5, h->sps->i_mb_width-1 );
+    }
+
     /* Write SPS and PPS */
-    if( i_nal_type == NAL_SLICE_IDR && h->param.b_repeat_headers )
+    if( h->fenc->b_keyframe )
     {
-        if( h->fenc->i_frame == 0 )
+        if( h->param.b_repeat_headers )
         {
-            /* identify ourself */
-            x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
-            if( x264_sei_version_write( h, &h->out.bs ) )
+            if( h->fenc->i_frame == 0 )
+            {
+                /* identify ourself */
+                x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+                if( x264_sei_version_write( h, &h->out.bs ) )
+                    return -1;
+                if( x264_nal_end( h ) )
+                    return -1;
+                overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
+            }
+
+            /* generate sequence parameters */
+            x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
+            x264_sps_write( &h->out.bs, h->sps );
+            if( x264_nal_end( h ) )
                 return -1;
+            overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
+
+            /* generate picture parameters */
+            x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
+            x264_pps_write( &h->out.bs, h->pps );
             if( x264_nal_end( h ) )
                 return -1;
             overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
         }
 
-        /* generate sequence parameters */
-        x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
-        x264_sps_write( &h->out.bs, h->sps );
-        if( x264_nal_end( h ) )
-            return -1;
-        overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
-
-        /* generate picture parameters */
-        x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
-        x264_pps_write( &h->out.bs, h->pps );
-        if( x264_nal_end( h ) )
-            return -1;
-        overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
+        if( h->fenc->i_type != X264_TYPE_IDR )
+        {
+            x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+            x264_sei_recovery_point_write( h, &h->out.bs, h->param.i_keyint_max );
+            x264_nal_end( h );
+            overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
+        }
     }
 
     /* Init the rate control */
@@ -2321,9 +2370,9 @@ static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,
     else
         pic_out->i_type = X264_TYPE_B;
 
+    pic_out->b_keyframe = h->fenc->b_keyframe;
     pic_out->i_pts = h->fenc->i_pts;
     pic_out->i_dts = h->fenc->i_dts - h->frames.i_bframe_delay_time;
-
     pic_out->img.i_plane = h->fdec->i_plane;
     for(i = 0; i < 3; i++)
     {
index f33b167fed58f386de80db58cb22a3dd380e3daa..b66eedc576f636d101ced75232bccf45f5cac066 100644 (file)
@@ -133,7 +133,7 @@ int x264_lookahead_init( x264_t *h, int i_slicetype_length )
     for( i = 0; i < h->param.i_threads; i++ )
         h->thread[i]->lookahead = look;
 
-    look->i_last_idr = - h->param.i_keyint_max;
+    look->i_last_keyframe = - h->param.i_keyint_max;
     look->b_analyse_keyframe = (h->param.rc.b_mb_tree || (h->param.rc.i_vbv_buffer_size && h->param.rc.i_lookahead))
                                && !h->param.rc.b_stat_read;
     look->i_slicetype_length = i_slicetype_length;
index af9abcd46354a038b18e90f83d7b163c87c5169b..908cb414d3fb1744bb13f9657e055fd1b146b4cc 100644 (file)
@@ -536,7 +536,7 @@ me_hex2:
                     }
                 }
             } while( ++i <= i_me_range/4 );
-            if( bmy <= mv_y_max && bmy >= mv_y_min )
+            if( bmy <= mv_y_max && bmy >= mv_y_min && bmx <= mv_x_max && bmx >= mv_x_min )
                 goto me_hex2;
             break;
         }
@@ -843,7 +843,7 @@ static void refine_subpel( x264_t *h, x264_me_t *m, int hpel_iters, int qpel_ite
     bdir = -1;
     for( i = qpel_iters; i > 0; i-- )
     {
-        if( bmy <= h->mb.mv_min_spel[1] || bmy >= h->mb.mv_max_spel[1] )
+        if( bmy <= h->mb.mv_min_spel[1] || bmy >= h->mb.mv_max_spel[1] || bmx <= h->mb.mv_min_spel[0] || bmx >= h->mb.mv_max_spel[0] )
             break;
         odir = bdir;
         omx = bmx;
@@ -936,7 +936,9 @@ static void ALWAYS_INLINE x264_me_refine_bidir( x264_t *h, x264_me_t *m0, x264_m
     };
 
     if( bm0y < h->mb.mv_min_spel[1] + 8 || bm1y < h->mb.mv_min_spel[1] + 8 ||
-        bm0y > h->mb.mv_max_spel[1] - 8 || bm1y > h->mb.mv_max_spel[1] - 8 )
+        bm0y > h->mb.mv_max_spel[1] - 8 || bm1y > h->mb.mv_max_spel[1] - 8 ||
+        bm0x < h->mb.mv_min_spel[0] + 8 || bm1x < h->mb.mv_min_spel[0] + 8 ||
+        bm0x > h->mb.mv_max_spel[0] - 8 || bm1x > h->mb.mv_max_spel[0] - 8 )
         return;
 
     h->mc.memzero_aligned( visited, sizeof(uint8_t[8][8][8]) );
@@ -1106,8 +1108,8 @@ void x264_me_refine_qpel_rd( x264_t *h, x264_me_t *m, int i_lambda2, int i4, int
         }
     }
 
-    if( bmy < h->mb.mv_min_spel[1] + 3 ||
-        bmy > h->mb.mv_max_spel[1] - 3 )
+    if( bmy < h->mb.mv_min_spel[1] + 3 || bmy > h->mb.mv_max_spel[1] - 3 ||
+        bmx < h->mb.mv_min_spel[0] + 3 || bmx > h->mb.mv_max_spel[0] - 3 )
     {
         h->mb.b_skip_mc = 0;
         return;
index b52de4ff5cdd47c9013739e6b2570ba0e74bc41d..5044329e824e822f90ba58a0a3ccd4663428fc38 100644 (file)
@@ -157,6 +157,15 @@ static void update_vbv_plan( x264_t *h, int overhead );
 static double predict_size( predictor_t *p, double q, double var );
 static void update_predictor( predictor_t *p, double q, double var, double bits );
 
+#define CMP_OPT_FIRST_PASS( opt, param_val )\
+{\
+    if( ( p = strstr( opts, opt "=" ) ) && sscanf( p, opt "=%d" , &i ) && param_val != i )\
+    {\
+        x264_log( h, X264_LOG_ERROR, "different " opt " setting than first pass (%d vs %d)\n", param_val, i );\
+        return -1;\
+    }\
+}
+
 /* Terminology:
  * qp = h.264's quantizer
  * qscale = linearized quantizer = Lagrange multiplier
@@ -497,6 +506,7 @@ int x264_ratecontrol_new( x264_t *h )
     rc->qp_constant[SLICE_TYPE_P] = h->param.rc.i_qp_constant;
     rc->qp_constant[SLICE_TYPE_I] = x264_clip3( h->param.rc.i_qp_constant - rc->ip_offset + 0.5, 0, 51 );
     rc->qp_constant[SLICE_TYPE_B] = x264_clip3( h->param.rc.i_qp_constant + rc->pb_offset + 0.5, 0, 51 );
+    h->mb.ip_offset = rc->ip_offset + 0.5;
 
     rc->lstep = pow( 2, h->param.rc.i_qp_step / 6.0 );
     rc->last_qscale = qp2qscale(26);
@@ -576,35 +586,11 @@ int x264_ratecontrol_new( x264_t *h )
                 return -1;
             }
 
-            if( ( p = strstr( opts, "bframes=" ) ) && sscanf( p, "bframes=%d", &i )
-                && h->param.i_bframe != i )
-            {
-                x264_log( h, X264_LOG_ERROR, "different number of B-frames than 1st pass (%d vs %d)\n",
-                          h->param.i_bframe, i );
-                return -1;
-            }
-
-            if( ( p = strstr( opts, "wpredp=" ) ) && sscanf( p, "wpredp=%d", &i ) &&
-                X264_MAX( 0, h->param.analyse.i_weighted_pred ) != i )
-            {
-                x264_log( h, X264_LOG_ERROR, "different weightp option than 1st pass (had weightp=%d)\n", i );
-                return -1;
-            }
-
-            if( h->param.i_bframe && ( p = strstr( opts, "b_pyramid=" ) ) &&
-                sscanf( p, "b_pyramid=%d", &i ) && h->param.i_bframe_pyramid != i )
-            {
-                x264_log( h, X264_LOG_ERROR, "different B-pyramid setting than 1st pass (%d vs %d)\n", h->param.i_bframe_pyramid, i );
-                return -1;
-            }
-
-            if( ( p = strstr( opts, "keyint=" ) ) && sscanf( p, "keyint=%d", &i )
-                && h->param.i_keyint_max != i )
-            {
-                x264_log( h, X264_LOG_ERROR, "different keyint than 1st pass (%d vs %d)\n",
-                          h->param.i_keyint_max, i );
-                return -1;
-            }
+            CMP_OPT_FIRST_PASS( "wpredp", X264_MAX( 0, h->param.analyse.i_weighted_pred ) );
+            CMP_OPT_FIRST_PASS( "bframes", h->param.i_bframe );
+            CMP_OPT_FIRST_PASS( "b_pyramid", h->param.i_bframe_pyramid );
+            CMP_OPT_FIRST_PASS( "intra_refresh", h->param.b_intra_refresh );
+            CMP_OPT_FIRST_PASS( "keyint", h->param.i_keyint_max );
 
             if( strstr( opts, "qp=0" ) && h->param.rc.i_rc_method == X264_RC_ABR )
                 x264_log( h, X264_LOG_WARNING, "1st pass was lossless, bitrate prediction will be inaccurate\n" );
@@ -1144,9 +1130,7 @@ static double predict_row_size( x264_t *h, int y, int qp )
     /* Our QP is lower than the reference! */
     else
     {
-        double newq = qp2qscale(qp);
-        double oldq = qp2qscale(h->fref0[0]->i_row_qp[y]);
-        double pred_intra = predict_size( rc->row_pred[1], (1 - newq / oldq) * newq, h->fdec->i_row_satds[0][0][y] );
+        double pred_intra = predict_size( rc->row_pred[1], qp2qscale(qp), h->fdec->i_row_satds[0][0][y] );
         /* Sum: better to overestimate than underestimate by using only one of the two predictors. */
         return pred_intra + pred_s;
     }
@@ -1189,11 +1173,7 @@ void x264_ratecontrol_mb( x264_t *h, int bits )
 
     update_predictor( rc->row_pred[0], qp2qscale(rc->qpm), h->fdec->i_row_satd[y], h->fdec->i_row_bits[y] );
     if( h->sh.i_type == SLICE_TYPE_P && rc->qpm < h->fref0[0]->i_row_qp[y] )
-    {
-        double newq = qp2qscale(rc->qpm);
-        double oldq = qp2qscale(h->fref0[0]->i_row_qp[y]);
-        update_predictor( rc->row_pred[1], (1 - newq / oldq) * newq, h->fdec->i_row_satds[0][0][y], h->fdec->i_row_bits[y] );
-    }
+        update_predictor( rc->row_pred[1], qp2qscale(rc->qpm), h->fdec->i_row_satds[0][0][y], h->fdec->i_row_bits[y] );
 
     /* tweak quality based on difference from predicted size */
     if( y < h->i_threadslice_end-1 )
@@ -1214,6 +1194,7 @@ void x264_ratecontrol_mb( x264_t *h, int bits )
         float size_of_other_slices = rc->frame_size_planned - slice_size_planned;
         /* More threads means we have to be more cautious in letting ratecontrol use up extra bits. */
         float rc_tol = buffer_left_planned / h->param.i_threads * rc->rate_tolerance;
+        float max_frame_error = X264_MAX( 0.05, 1.0 / h->sps->i_mb_height );
         int b1 = predict_row_size_sum( h, y, rc->qpm );
 
         /* Assume that if this slice has become larger than expected,
@@ -1253,7 +1234,7 @@ void x264_ratecontrol_mb( x264_t *h, int bits )
 
         /* avoid VBV underflow */
         while( (rc->qpm < h->param.rc.i_qp_max)
-               && (rc->buffer_fill - b1 < rc->buffer_rate * 0.05 ) )
+               && (rc->buffer_fill - b1 < rc->buffer_rate * max_frame_error) )
         {
             rc->qpm ++;
             b1 = predict_row_size_sum( h, y, rc->qpm );
@@ -1970,6 +1951,10 @@ static float rate_estimate_qscale( x264_t *h )
             rcc->frame_size_planned = qscale2bits(&rce, q);
         else
             rcc->frame_size_planned = predict_size( &rcc->pred[h->sh.i_type], q, rcc->last_satd );
+
+        /* Always use up the whole VBV in this case. */
+        if( rcc->single_frame_vbv )
+            rcc->frame_size_planned = rcc->buffer_rate;
         x264_ratecontrol_set_estimated_size(h, rcc->frame_size_planned);
         return q;
     }
@@ -1983,19 +1968,19 @@ void x264_threads_distribute_ratecontrol( x264_t *h )
             totalsize += h->fdec->i_row_satd[row];
     for( i = 0; i < h->param.i_threads; i++ )
     {
-        x264_ratecontrol_t *t = h->thread[i]->rc;
+        x264_t *t = h->thread[i];
         x264_ratecontrol_t *rc = h->rc;
-        memcpy( t, rc, sizeof( x264_ratecontrol_t ) );
+        memcpy( t->rc, rc, sizeof(x264_ratecontrol_t) );
         /* Calculate the planned slice size. */
         if( h->rc->b_vbv && rc->frame_size_planned )
         {
             int size = 0;
-            for( row = h->i_threadslice_start; row < h->i_threadslice_end; row++ )
+            for( row = t->i_threadslice_start; row < t->i_threadslice_end; row++ )
                 size += h->fdec->i_row_satd[row];
-            t->slice_size_planned = size * rc->frame_size_planned / totalsize;
+            t->rc->slice_size_planned = size * rc->frame_size_planned / totalsize;
         }
         else
-            t->slice_size_planned = 0;
+            t->rc->slice_size_planned = 0;
     }
 }
 
index 1b86ab31c4a2a4b5aafab5dcd1331f58bfed53cf..641eae981a6ed29362c43d02102166f270697d2a 100644 (file)
@@ -467,6 +467,23 @@ void x264_pps_write( bs_t *s, x264_pps_t *pps )
     bs_rbsp_trailing( s );
 }
 
+void x264_sei_recovery_point_write( x264_t *h, bs_t *s, int recovery_frame_cnt )
+{
+    int payload_size;
+
+    bs_write( s, 8, 0x06 ); // payload_type = Recovery Point
+    payload_size = bs_size_ue( recovery_frame_cnt ) + 4;
+
+    bs_write( s, 8, (payload_size + 7) / 8);
+    bs_write_ue( s, recovery_frame_cnt ); // recovery_frame_cnt
+    bs_write( s, 1, 1 ); //exact_match_flag 1
+    bs_write( s, 1, 0 ); //broken_link_flag 0
+    bs_write( s, 2, 0 ); //changing_slice_group 0
+
+    bs_align_10( s );
+    bs_rbsp_trailing( s );
+}
+
 int x264_sei_version_write( x264_t *h, bs_t *s )
 {
     int i;
index e76e65117728d85451972dc08d333109d5dbbe4b..125f7e14f3cd767c501c815beecc2aea076b3532 100644 (file)
@@ -28,6 +28,7 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param );
 void x264_sps_write( bs_t *s, x264_sps_t *sps );
 void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t *sps );
 void x264_pps_write( bs_t *s, x264_pps_t *pps );
+void x264_sei_recovery_point_write( x264_t *h, bs_t *s, int recovery_frame_cnt );
 int  x264_sei_version_write( x264_t *h, bs_t *s );
 int  x264_validate_levels( x264_t *h, int verbose );
 
index 1ffac0a202cc17076c51afbed0335d0ae6025508..512bd36411fca9037ebc721c565972be3f82c4b3 100644 (file)
@@ -937,7 +937,7 @@ static int scenecut_internal( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **f
     int icost = frame->i_cost_est[0][0];
     int pcost = frame->i_cost_est[p1-p0][0];
     float f_bias;
-    int i_gop_size = frame->i_frame - h->lookahead->i_last_idr;
+    int i_gop_size = frame->i_frame - h->lookahead->i_last_keyframe;
     float f_thresh_max = h->param.i_scenecut_threshold / 100.0;
     /* magic numbers pulled out of thin air */
     float f_thresh_min = f_thresh_max * h->param.i_keyint_min
@@ -946,7 +946,7 @@ static int scenecut_internal( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **f
 
     if( h->param.i_keyint_min == h->param.i_keyint_max )
         f_thresh_min= f_thresh_max;
-    if( i_gop_size < h->param.i_keyint_min / 4 )
+    if( i_gop_size < h->param.i_keyint_min / 4 || h->param.b_intra_refresh )
         f_bias = f_thresh_min / 4;
     else if( i_gop_size <= h->param.i_keyint_min )
         f_bias = f_thresh_min * i_gop_size / h->param.i_keyint_min;
@@ -1033,11 +1033,11 @@ void x264_slicetype_analyse( x264_t *h, int keyframe )
     if( !j )
         return;
 
-    keyint_limit = h->param.i_keyint_max - frames[0]->i_frame + h->lookahead->i_last_idr - 1;
-    orig_num_frames = num_frames = X264_MIN( j, keyint_limit );
+    keyint_limit = h->param.i_keyint_max - frames[0]->i_frame + h->lookahead->i_last_keyframe - 1;
+    orig_num_frames = num_frames = h->param.b_intra_refresh ? j : X264_MIN( j, keyint_limit );
 
     x264_lowres_context_init( h, &a );
-    idr_frame_type = frames[1]->i_frame - h->lookahead->i_last_idr >= h->param.i_keyint_min ? X264_TYPE_IDR : X264_TYPE_I;
+    idr_frame_type = frames[1]->i_frame - h->lookahead->i_last_keyframe >= h->param.i_keyint_min ? X264_TYPE_IDR : X264_TYPE_I;
 
     /* This is important psy-wise: if we have a non-scenecut keyframe,
      * there will be significant visual artifacts if the frames just before
@@ -1166,16 +1166,17 @@ void x264_slicetype_analyse( x264_t *h, int keyframe )
         x264_macroblock_tree( h, &a, frames, X264_MIN(num_frames, h->param.i_keyint_max), keyframe );
 
     /* Enforce keyframe limit. */
-    for( j = 0; j < num_frames; j++ )
-    {
-        if( ((j-keyint_limit) % h->param.i_keyint_max) == 0 )
+    if( !h->param.b_intra_refresh )
+        for( j = 0; j < num_frames; j++ )
         {
-            if( j && h->param.i_keyint_max > 1 )
-                frames[j]->i_type = X264_TYPE_P;
-            frames[j+1]->i_type = X264_TYPE_IDR;
-            reset_start = X264_MIN( reset_start, j+2 );
+            if( ((j-keyint_limit) % h->param.i_keyint_max) == 0 )
+            {
+                if( j && h->param.i_keyint_max > 1 )
+                    frames[j]->i_type = X264_TYPE_P;
+                frames[j+1]->i_type = X264_TYPE_IDR;
+                reset_start = X264_MIN( reset_start, j+2 );
+            }
         }
-    }
 
     if( h->param.rc.i_vbv_buffer_size )
         x264_vbv_lookahead( h, &a, frames, num_frames, keyframe );
@@ -1230,7 +1231,7 @@ void x264_slicetype_decide( x264_t *h )
         }
 
         /* Limit GOP size */
-        if( frm->i_frame - h->lookahead->i_last_idr >= h->param.i_keyint_max )
+        if( (!h->param.b_intra_refresh || frm->i_frame == 0) && frm->i_frame - h->lookahead->i_last_keyframe >= h->param.i_keyint_max )
         {
             if( frm->i_type == X264_TYPE_AUTO )
                 frm->i_type = X264_TYPE_IDR;
@@ -1240,7 +1241,8 @@ void x264_slicetype_decide( x264_t *h )
         if( frm->i_type == X264_TYPE_IDR )
         {
             /* Close GOP */
-            h->lookahead->i_last_idr = frm->i_frame;
+            h->lookahead->i_last_keyframe = frm->i_frame;
+            frm->b_keyframe = 1;
             if( bframes > 0 )
             {
                 bframes--;
@@ -1349,6 +1351,7 @@ int x264_rc_analyse_slice( x264_t *h )
 {
     int p0=0, p1, b;
     int cost;
+    x264_emms();
 
     if( IS_X264_TYPE_I(h->fenc->i_type) )
         p1 = b = 0;
@@ -1382,5 +1385,24 @@ int x264_rc_analyse_slice( x264_t *h )
     memcpy( h->fdec->i_row_satd, h->fenc->i_row_satd, h->sps->i_mb_height * sizeof(int) );
     if( !IS_X264_TYPE_I(h->fenc->i_type) )
         memcpy( h->fdec->i_row_satds[0][0], h->fenc->i_row_satds[0][0], h->sps->i_mb_height * sizeof(int) );
+
+    if( h->param.b_intra_refresh && h->param.rc.i_vbv_buffer_size && h->fenc->i_type == X264_TYPE_P )
+    {
+        int x, y;
+        int ip_factor = 256 * h->param.rc.f_ip_factor; /* fix8 */
+        for( y = 0; y < h->sps->i_mb_height; y++ )
+        {
+            int mb_xy = y * h->mb.i_mb_stride;
+            for( x = h->fdec->i_pir_start_col; x <= h->fdec->i_pir_end_col; x++, mb_xy++ )
+            {
+                int intra_cost = (h->fenc->i_intra_cost[mb_xy] * ip_factor) >> 8;
+                int inter_cost = h->fenc->lowres_costs[b-p0][p1-b][mb_xy];
+                int diff = intra_cost - inter_cost;
+                h->fdec->i_row_satd[y] += diff;
+                cost += diff;
+            }
+        }
+    }
+
     return cost;
 }
index 7e4b63c0e3322cd4b7f589d93a004d9915eaad56..8a937cf1018aaf6efac9de37375cf8b058f9b10b 100644 (file)
@@ -264,7 +264,7 @@ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_
     x264_put_be24( c, 0 );
 
     p_flv->start = c->d_cur;
-    x264_put_byte( c, p_picture->i_type == X264_TYPE_IDR ? FLV_FRAME_KEY : FLV_FRAME_INTER );
+    x264_put_byte( c, p_picture->b_keyframe ? FLV_FRAME_KEY : FLV_FRAME_INTER );
     x264_put_byte( c, 1 ); // AVC NALU
     x264_put_be24( c, offset );
 
index 5d9346b8926008231cfcb0a20aaf2e15f6e90c98..8e84f52a115bac7c872ff4c3e031ec4a2a404746 100644 (file)
@@ -185,7 +185,7 @@ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_
 
     p_mkv->b_writing_frame = 0;
 
-    if( mk_set_frame_flags( p_mkv->w, i_stamp, p_picture->i_type == X264_TYPE_IDR ) < 0 )
+    if( mk_set_frame_flags( p_mkv->w, i_stamp, p_picture->b_keyframe ) < 0 )
         return -1;
 
     return i_size;
index 261b8178227a465c7e85f8a2367636e104dd4e60..7889e4fe25fbd42ae54c337b47a8c7e365f3cba2 100644 (file)
@@ -308,7 +308,7 @@ static int write_frame( hnd_t handle, uint8_t *p_nalu, int i_size, x264_picture_
         offset = cts - dts;
     }
 
-    p_mp4->p_sample->IsRAP = p_picture->i_type == X264_TYPE_IDR ? 1 : 0;
+    p_mp4->p_sample->IsRAP = p_picture->b_keyframe;
     p_mp4->p_sample->DTS = dts;
     p_mp4->p_sample->CTS_Offset = offset;
     gf_isom_add_sample( p_mp4->p_file, p_mp4->i_track, p_mp4->i_descidx, p_mp4->p_sample );
diff --git a/x264.c b/x264.c
index 3726141e17b64cbccd2b05af102695f6746b1316..4834961ebd75aa5ae56fedd7fc223ea419cd4907 100644 (file)
--- a/x264.c
+++ b/x264.c
@@ -313,6 +313,7 @@ static void Help( x264_param_t *defaults, int longhelp )
     H2( "  -i, --min-keyint <integer>  Minimum GOP size [%d]\n", defaults->i_keyint_min );
     H2( "      --no-scenecut           Disable adaptive I-frame decision\n" );
     H2( "      --scenecut <integer>    How aggressively to insert extra I-frames [%d]\n", defaults->i_scenecut_threshold );
+    H2( "      --intra-refresh         Use Periodic Intra Refresh instead of IDR frames\n" );
     H1( "  -b, --bframes <integer>     Number of B-frames between I and P [%d]\n", defaults->i_bframe );
     H1( "      --b-adapt <integer>     Adaptive B-frame decision method [%d]\n"
         "                                  Higher values may lower threading efficiency.\n"
@@ -550,6 +551,7 @@ static struct option long_options[] =
     { "b-pyramid",   required_argument, NULL, 0 },
     { "min-keyint",  required_argument, NULL, 'i' },
     { "keyint",      required_argument, NULL, 'I' },
+    { "intra-refresh",     no_argument, NULL, 0 },
     { "scenecut",    required_argument, NULL, 0 },
     { "no-scenecut",       no_argument, NULL, 0 },
     { "nf",                no_argument, NULL, 0 },
diff --git a/x264.h b/x264.h
index 62aecc137b57c2b4eaaada7ca1dd42ae0c4f9a4a..f760e72e18c5b321c83536474828e0b035addbd8 100644 (file)
--- a/x264.h
+++ b/x264.h
@@ -35,7 +35,7 @@
 
 #include <stdarg.h>
 
-#define X264_BUILD 81
+#define X264_BUILD 82
 
 /* x264_t:
  *      opaque handler for encoder */
@@ -201,6 +201,8 @@ typedef struct x264_param_t
     int         i_keyint_max;       /* Force an IDR keyframe at this interval */
     int         i_keyint_min;       /* Scenecuts closer together than this are coded as I, not IDR. */
     int         i_scenecut_threshold; /* how aggressively to insert extra I frames */
+    int         b_intra_refresh;    /* Whether or not to use periodic intra refresh instead of IDR frames. */
+
     int         i_bframe;   /* how many b-frame between 2 references pictures */
     int         i_bframe_adaptive;
     int         i_bframe_bias;
@@ -383,6 +385,9 @@ typedef struct
     int     i_type;
     /* In: force quantizer for > 0 */
     int     i_qpplus1;
+    /* Out: whether this frame is a keyframe.  Important when using modes that result in
+     * SEI recovery points being used instead of IDR frames. */
+    int     b_keyframe;
     /* In: user pts, Out: pts of encoded picture (user)*/
     int64_t i_pts;
     /* Out: frame dts. Since the pts of the first frame is always zero,