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.
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 */
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")
}
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 );
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;
/* 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 */
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
{
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) );
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 */
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 */
/* 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 */
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];
/* 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
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 )
}
}
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;
}
}
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 );
/*--------------------------- 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 );
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 );
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 )
}
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 )
{
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" );
}
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;
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 );
BOOLIFY( b_deterministic );
BOOLIFY( b_sliced_threads );
BOOLIFY( b_interlaced );
+ BOOLIFY( b_intra_refresh );
BOOLIFY( b_visualize );
BOOLIFY( b_aud );
BOOLIFY( b_repeat_headers );
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 *) );
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;
}
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 =
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 */
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++)
{
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;
}
}
} 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;
}
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;
};
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]) );
}
}
- 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;
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
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);
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" );
/* 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;
}
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 )
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,
/* 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 );
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;
}
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;
}
}
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;
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 );
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
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;
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
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 );
}
/* 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;
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--;
{
int p0=0, p1, b;
int cost;
+ x264_emms();
if( IS_X264_TYPE_I(h->fenc->i_type) )
p1 = b = 0;
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;
}
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 );
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;
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 );
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"
{ "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 },
#include <stdarg.h>
-#define X264_BUILD 81
+#define X264_BUILD 82
/* x264_t:
* opaque handler for encoder */
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;
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,