[x264-devel] Add slice-min-mbs feature
Jason Garrett-Glaser
git at videolan.org
Tue Apr 23 23:03:04 CEST 2013
x264 | branch: master | Jason Garrett-Glaser <jason at x264.com> | Thu Feb 14 17:22:02 2013 -0800| [f546e98eb8f9afd15fb7e8f95ec02fcf65155079] | committer: Jason Garrett-Glaser
Add slice-min-mbs feature
Works in conjunction with slice-max-mbs and/or slice-max-size to avoid overly
small slices.
Useful with certain decoders that barf on extremely small slices.
If slice-min-mbs would be violated as a result of slice-max-size, x264 will
exceed slice-max-size and print a warning.
> http://git.videolan.org/gitweb.cgi/x264.git/?a=commit;h=f546e98eb8f9afd15fb7e8f95ec02fcf65155079
---
common/common.c | 4 ++++
encoder/encoder.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++-----
x264.c | 4 +++-
x264.h | 3 ++-
4 files changed, 66 insertions(+), 8 deletions(-)
diff --git a/common/common.c b/common/common.c
index ffd3142..43fd537 100644
--- a/common/common.c
+++ b/common/common.c
@@ -778,6 +778,8 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value )
p->i_slice_max_size = atoi(value);
OPT("slice-max-mbs")
p->i_slice_max_mbs = atoi(value);
+ OPT("slice-min-mbs")
+ p->i_slice_min_mbs = atoi(value);
OPT("slices")
p->i_slice_count = atoi(value);
OPT("cabac")
@@ -1309,6 +1311,8 @@ char *x264_param2string( x264_param_t *p, int b_res )
s += sprintf( s, " slice_max_size=%d", p->i_slice_max_size );
if( p->i_slice_max_mbs )
s += sprintf( s, " slice_max_mbs=%d", p->i_slice_max_mbs );
+ if( p->i_slice_min_mbs )
+ s += sprintf( s, " slice_min_mbs=%d", p->i_slice_min_mbs );
s += sprintf( s, " nr=%d", p->analyse.i_noise_reduction );
s += sprintf( s, " decimate=%d", p->analyse.b_dct_decimate );
s += sprintf( s, " interlaced=%s", p->b_interlaced ? p->b_tff ? "tff" : "bff" : p->b_fake_interlaced ? "fake" : "0" );
diff --git a/encoder/encoder.c b/encoder/encoder.c
index e5c7c3f..75d7aa1 100644
--- a/encoder/encoder.c
+++ b/encoder/encoder.c
@@ -670,6 +670,22 @@ static int x264_validate_parameters( x264_t *h, int b_open )
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 )
@@ -1497,6 +1513,7 @@ int x264_encoder_reconfig( x264_t *h, x264_param_t *param )
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 );
@@ -2269,8 +2286,12 @@ static int x264_slice_write( x264_t *h )
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 );
@@ -2318,13 +2339,17 @@ static int x264_slice_write( x264_t *h )
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 )
{
@@ -2387,7 +2412,7 @@ reencode:
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;
}
}
@@ -2412,9 +2437,27 @@ 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 )
{
@@ -2432,6 +2475,7 @@ reencode:
h->sh.i_last_mb = mb_xy;
}
}
+cont:
h->mb.b_reencode_mb = 0;
#if HAVE_VISUALIZE
@@ -2444,7 +2488,7 @@ reencode:
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;
@@ -2543,6 +2587,9 @@ reencode:
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 )
@@ -2654,7 +2701,11 @@ static void *x264_slices_write( x264_t *h )
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 )
{
diff --git a/x264.c b/x264.c
index 7565a1b..c8b30af 100644
--- a/x264.c
+++ b/x264.c
@@ -597,7 +597,8 @@ static void help( x264_param_t *defaults, int longhelp )
" 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" );
@@ -971,6 +972,7 @@ static struct option long_options[] =
{ "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 },
diff --git a/x264.h b/x264.h
index e5a1600..95911b8 100644
--- a/x264.h
+++ b/x264.h
@@ -41,7 +41,7 @@
#include "x264_config.h"
-#define X264_BUILD 130
+#define X264_BUILD 131
/* Application developers planning to link against a shared library version of
* libx264 from a Microsoft Visual Studio or similar development environment
@@ -477,6 +477,7 @@ typedef struct x264_param_t
/* Slicing parameters */
int i_slice_max_size; /* Max size per slice in bytes; includes estimated NAL overhead. */
int i_slice_max_mbs; /* Max number of MBs per slice; overrides i_slice_count. */
+ int i_slice_min_mbs; /* Min number of MBs per slice */
int i_slice_count; /* Number of slices per frame: forces rectangular slices. */
/* Optional callback for freeing this x264_param_t when it is done being used.
More information about the x264-devel
mailing list