h->param.i_slice_max_size = X264_MAX( h->param.i_slice_max_size, 0 );
h->param.i_slice_max_mbs = X264_MAX( h->param.i_slice_max_mbs, 0 );
+ h->param.i_slice_min_mbs = X264_MAX( h->param.i_slice_min_mbs, 0 );
+ if( h->param.i_slice_max_mbs )
+ h->param.i_slice_min_mbs = X264_MIN( h->param.i_slice_min_mbs, h->param.i_slice_max_mbs/2 );
+ else if( !h->param.i_slice_max_size )
+ h->param.i_slice_min_mbs = 0;
+ if( PARAM_INTERLACED && h->param.i_slice_min_mbs )
+ {
+ x264_log( h, X264_LOG_WARNING, "interlace + slice-min-mbs is not implemented\n" );
+ h->param.i_slice_min_mbs = 0;
+ }
+ int mb_width = (h->param.i_width+15)/16;
+ if( h->param.i_slice_min_mbs > mb_width )
+ {
+ x264_log( h, X264_LOG_WARNING, "slice-min-mbs > row mb size (%d) not implemented\n", mb_width );
+ h->param.i_slice_min_mbs = mb_width;
+ }
int max_slices = (h->param.i_height+((16<<PARAM_INTERLACED)-1))/(16<<PARAM_INTERLACED);
if( h->param.b_sliced_threads )
COPY( i_bframe_pyramid );
COPY( i_slice_max_size );
COPY( i_slice_max_mbs );
+ COPY( i_slice_min_mbs );
COPY( i_slice_count );
COPY( b_tff );
int b_deblock = h->sh.i_disable_deblocking_filter_idc != 1;
int b_hpel = h->fdec->b_kept_as_ref;
int orig_last_mb = h->sh.i_last_mb;
+ int thread_last_mb = h->i_threadslice_end * h->mb.i_mb_width - 1;
uint8_t *last_emu_check;
- x264_bs_bak_t bs_bak[2];
+#define BS_BAK_SLICE_MAX_SIZE 0
+#define BS_BAK_SLICE_MIN_MBS 1
+#define BS_BAK_ROW_VBV 2
+ x264_bs_bak_t bs_bak[3];
b_deblock &= b_hpel || h->param.b_full_recon || h->param.psz_dump_yuv;
bs_realign( &h->out.bs );
if( x264_bitstream_check_buffer( h ) )
return -1;
if( !(i_mb_y & SLICE_MBAFF) && h->param.rc.i_vbv_buffer_size )
- x264_bitstream_backup( h, &bs_bak[1], i_skip, 1 );
+ x264_bitstream_backup( h, &bs_bak[BS_BAK_ROW_VBV], i_skip, 1 );
if( !h->mb.b_reencode_mb )
x264_fdec_filter_row( h, i_mb_y, 0 );
}
if( !(i_mb_y & SLICE_MBAFF) && back_up_bitstream )
- x264_bitstream_backup( h, &bs_bak[0], i_skip, 0 );
+ {
+ x264_bitstream_backup( h, &bs_bak[BS_BAK_SLICE_MAX_SIZE], i_skip, 0 );
+ if( slice_max_size && (thread_last_mb+1-mb_xy) == h->param.i_slice_min_mbs )
+ x264_bitstream_backup( h, &bs_bak[BS_BAK_SLICE_MIN_MBS], i_skip, 0 );
+ }
if( PARAM_INTERLACED )
{
h->mb.i_skip_intra = 0;
h->mb.b_skip_mc = 0;
h->mb.b_overflow = 0;
- x264_bitstream_restore( h, &bs_bak[0], &i_skip, 0 );
+ x264_bitstream_restore( h, &bs_bak[BS_BAK_SLICE_MAX_SIZE], &i_skip, 0 );
goto reencode;
}
}
/* We'll just re-encode this last macroblock if we go over the max slice size. */
if( total_bits - starting_bits > slice_max_size && !h->mb.b_reencode_mb )
{
+ /* Handle the most obnoxious slice-min-mbs edge case: we need to end the slice
+ * because it's gone over the maximum size, but doing so would violate slice-min-mbs.
+ * If possible, roll back to the last checkpoint and try again.
+ * We could try raising QP, but that would break in the case where a slice spans multiple
+ * rows, which the re-encoding infrastructure can't currently handle. */
+ if( mb_xy < thread_last_mb && (thread_last_mb+1-mb_xy) < h->param.i_slice_min_mbs )
+ {
+ if( thread_last_mb-h->param.i_slice_min_mbs < h->sh.i_first_mb+h->param.i_slice_min_mbs )
+ {
+ x264_log( h, X264_LOG_WARNING, "slice-max-size violated (frame %d, cause: slice-min-mbs)\n", h->i_frame );
+ slice_max_size = 0;
+ goto cont;
+ }
+ x264_bitstream_restore( h, &bs_bak[BS_BAK_SLICE_MIN_MBS], &i_skip, 0 );
+ h->mb.b_reencode_mb = 1;
+ h->sh.i_last_mb = thread_last_mb-h->param.i_slice_min_mbs;
+ break;
+ }
if( mb_xy-SLICE_MBAFF*h->mb.i_mb_stride != h->sh.i_first_mb )
{
- x264_bitstream_restore( h, &bs_bak[0], &i_skip, 0 );
+ x264_bitstream_restore( h, &bs_bak[BS_BAK_SLICE_MAX_SIZE], &i_skip, 0 );
h->mb.b_reencode_mb = 1;
if( SLICE_MBAFF )
{
h->sh.i_last_mb = mb_xy;
}
}
+cont:
h->mb.b_reencode_mb = 0;
#if HAVE_VISUALIZE
if( x264_ratecontrol_mb( h, mb_size ) < 0 )
{
- x264_bitstream_restore( h, &bs_bak[1], &i_skip, 1 );
+ x264_bitstream_restore( h, &bs_bak[BS_BAK_ROW_VBV], &i_skip, 1 );
h->mb.b_reencode_mb = 1;
i_mb_x = 0;
i_mb_y = i_mb_y - SLICE_MBAFF;
i_mb_x = 0;
}
}
+ if( h->sh.i_last_mb < h->sh.i_first_mb )
+ return 0;
+
h->out.nal[h->out.i_nal].i_last_mb = h->sh.i_last_mb;
if( h->param.b_cabac )
h->sh.i_last_mb = last_x + h->mb.i_mb_stride*last_y;
}
else
+ {
h->sh.i_last_mb = h->sh.i_first_mb + h->param.i_slice_max_mbs - 1;
+ if( h->sh.i_last_mb < last_thread_mb && last_thread_mb - h->sh.i_last_mb < h->param.i_slice_min_mbs )
+ h->sh.i_last_mb = last_thread_mb - h->param.i_slice_min_mbs;
+ }
}
else if( h->param.i_slice_count && !h->param.b_sliced_threads )
{
" slices and is overridden by other slicing options\n" );
else H1( " --slices <integer> Number of slices per frame\n" );
H2( " --slice-max-size <integer> Limit the size of each slice in bytes\n");
- H2( " --slice-max-mbs <integer> Limit the size of each slice in macroblocks\n");
+ H2( " --slice-max-mbs <integer> Limit the size of each slice in macroblocks (max)\n");
+ H2( " --slice-min-mbs <integer> Limit the size of each slice in macroblocks (min)\n");
H0( " --tff Enable interlaced mode (top field first)\n" );
H0( " --bff Enable interlaced mode (bottom field first)\n" );
H2( " --constrained-intra Enable constrained intra prediction.\n" );
{ "no-sliced-threads", no_argument, NULL, 0 },
{ "slice-max-size", required_argument, NULL, 0 },
{ "slice-max-mbs", required_argument, NULL, 0 },
+ { "slice-min-mbs", required_argument, NULL, 0 },
{ "slices", required_argument, NULL, 0 },
{ "thread-input", no_argument, NULL, OPT_THREAD_INPUT },
{ "sync-lookahead", required_argument, NULL, 0 },