rendered paste bodydiff --git a/common/common.c b/common/common.cindex 6c88556..90307f6 100644--- a/common/common.c+++ b/common/common.c@@ -25,6 +25,7 @@ *****************************************************************************/ #include "common.h"+#include "encoder/set.h" #include <stdarg.h> #include <ctype.h>@@ -66,7 +67,7 @@ void x264_param_default( x264_param_t *param ) param->vui.i_chroma_loc= 0; /* left center */ param->i_fps_num = 25; param->i_fps_den = 1;- param->i_level_idc = -1;+ param->i_level_idc = X264_LEVEL_IDC_AUTO; param->i_slice_max_size = 0; param->i_slice_max_mbs = 0; param->i_slice_count = 0;@@ -428,15 +429,15 @@ int x264_param_apply_profile( x264_param_t *param, const char *profile ) return 0; #if BIT_DEPTH > 8- if( !strcasecmp( profile, "baseline" ) || !strcasecmp( profile, "main" ) ||- !strcasecmp( profile, "high" ) )+ if( x264_get_profile( profile ) == PROFILE_BASELINE || x264_get_profile( profile == PROFILE_MAIN ) ||+ x264_get_profile( profile ) == PROFILE_HIGH ) { x264_log( NULL, X264_LOG_ERROR, "%s profile doesn't support a bit depth of %d.\n", profile, BIT_DEPTH ); return -1; } #endif - if( !strcasecmp( profile, "baseline" ) )+ if( x264_get_profile( profile ) == PROFILE_BASELINE ) { param->analyse.b_transform_8x8 = 0; param->b_cabac = 0;@@ -454,12 +455,13 @@ int x264_param_apply_profile( x264_param_t *param, const char *profile ) return -1; } }- else if( !strcasecmp( profile, "main" ) )+ else if( x264_get_profile( profile ) == PROFILE_MAIN ) { param->analyse.b_transform_8x8 = 0; param->i_cqm_preset = X264_CQM_FLAT; }- else if( !strcasecmp( profile, "high" ) || !strcasecmp( profile, "high10" ) )+ else if( x264_get_profile( profile ) == PROFILE_HIGH || + x264_get_profile( profile ) == PROFILE_HIGH10 ) { /* Default */ }@@ -477,6 +479,66 @@ int x264_param_apply_profile( x264_param_t *param, const char *profile ) return 0; } +int x264_param_apply_level( x264_param_t *param, const char *profile )+{+ int level_idc = param->i_level_idc;+ if( level_idc == X264_LEVEL_IDC_AUTO )+ return 0;++ int prof = BIT_DEPTH == 8 ? PROFILE_HIGH : PROFILE_HIGH10;+ prof = x264_get_profile( profile );++ const x264_level_t *l = x264_get_level( level_idc );+ if( !l )+ {+ x264_log( NULL, X264_LOG_ERROR, "Invalid level_idc: %d\n", level_idc );+ return -1;+ }++ char level_name[5];+ snprintf( level_name, sizeof(level_name), "%d.%d", level_idc/10, level_idc%10 );+ if( level_idc == 9 )+ strcpy( level_name, "1b" );+ int cbp_factor = prof==PROFILE_HIGH10 ? 12 :+ prof==PROFILE_HIGH ? 5 : 4;+ int mb_size = 384*BIT_DEPTH/8;+ int mbs = ((param->i_width+15)/16) * ((param->i_height+15)/16);+ while( 1 )+ {+ int num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;+ int max_dec_frame_buffering = X264_MIN(X264_REF_MAX, X264_MAX4(param->i_frame_reference, 1 + num_reorder_frames, + param->i_bframe_pyramid ? 4 : 1, param->i_dpb_size));+ int dpb = max_dec_frame_buffering * mbs * mb_size;++ if( dpb <= l->dpb )+ break;++ if( max_dec_frame_buffering == 4 && param->i_bframe_pyramid )+ param->i_bframe_pyramid = 0;+ else if( max_dec_frame_buffering == 2 && param->i_bframe )+ param->i_bframe = 0;+ else if( max_dec_frame_buffering == 1 )+ {+ x264_log( NULL, X264_LOG_ERROR, "Impossible DPB size %d required by level %s. Lowest\n", l->dpb, level_name );+ x264_log( NULL, X264_LOG_ERROR, "possible is %d; try a higher level or lower resolution.\n", dpb );+ return -1;+ }+ else if( param->i_dpb_size > param->i_frame_reference )+ param->i_dpb_size--;+ else+ param->i_frame_reference--;+ }++#define LIMIT( name, limit, val ) \+ if( (val) > (limit) || (val) <= 0 ) \+ val = limit;++ LIMIT( "VBV bitrate", l->bitrate * cbp_factor / 4, param->rc.i_vbv_max_bitrate );+ LIMIT( "VBV buffer", l->cpb * cbp_factor / 4, param->rc.i_vbv_buffer_size );+ LIMIT( "MV range", l->mv_range, param->analyse.i_mv_range );+ return 0;+}+ static int parse_enum( const char *arg, const char * const *names, int *dst ) { for( int i = 0; names[i]; i++ )diff --git a/encoder/encoder.c b/encoder/encoder.cindex 8b14b41..51617d0 100644--- a/encoder/encoder.c+++ b/encoder/encoder.c@@ -742,7 +742,7 @@ static int x264_validate_parameters( x264_t *h ) { const x264_level_t *l = x264_levels;- if( h->param.i_level_idc < 0 )+ if( h->param.i_level_idc == X264_LEVEL_IDC_AUTO ) { int maxrate_bak = h->param.rc.i_vbv_max_bitrate; if( h->param.rc.i_rc_method == X264_RC_ABR && h->param.rc.i_vbv_buffer_size <= 0 )diff --git a/encoder/set.c b/encoder/set.cindex 6cf0c6a..0a8a110 100644--- a/encoder/set.c+++ b/encoder/set.c@@ -115,6 +115,18 @@ void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param ) else sps->i_profile_idc = PROFILE_BASELINE; + /* Hack: if the user specifies a High-compatible VBV, but not a Main-compatible one,+ * switch to High profile. Prevents spurious errors on the first pass. */+ const x264_level_t *l = x264_get_level( param->i_level_idc );+ if( l && sps->i_profile_idc <= PROFILE_MAIN &&+ l->bitrate * 5/4 >= param->rc.i_vbv_max_bitrate &&+ l->cpb * 5/4 >= param->rc.i_vbv_buffer_size &&+ (l->bitrate < param->rc.i_vbv_max_bitrate || l->cpb < param->rc.i_vbv_buffer_size) )+ {+ sps->i_profile_idc = PROFILE_HIGH;+ }+ + sps->b_constraint_set0 = sps->i_profile_idc == PROFILE_BASELINE; /* x264 doesn't support the features that are in Baseline and not in Main, * namely arbitrary_slice_order and slice_groups. */@@ -670,6 +682,16 @@ const x264_level_t x264_levels[] = { 0 } }; +const x264_level_t *x264_get_level( int level_idc )+{+ const x264_level_t *l = x264_levels;+ while( l->level_idc != 0 && l->level_idc != level_idc )+ l++;+ if( l->level_idc )+ return l;+ return NULL;+}+ #define ERROR(...)\ {\ if( verbose )\@@ -681,7 +703,8 @@ int x264_validate_levels( x264_t *h, int verbose ) { int ret = 0; int mbs = h->sps->i_mb_width * h->sps->i_mb_height;- int dpb = mbs * 384 * h->sps->vui.i_max_dec_frame_buffering;+ int mb_size = 384*BIT_DEPTH/8;+ int dpb = mbs * mb_size * h->sps->vui.i_max_dec_frame_buffering; int cbp_factor = h->sps->i_profile_idc==PROFILE_HIGH10 ? 12 : h->sps->i_profile_idc==PROFILE_HIGH ? 5 : 4; @@ -696,7 +719,7 @@ int x264_validate_levels( x264_t *h, int verbose ) h->sps->i_mb_width, h->sps->i_mb_height, l->frame_size ); if( dpb > l->dpb ) ERROR( "DPB size (%d frames, %d bytes) > level limit (%d frames, %d bytes)\n",- h->sps->vui.i_max_dec_frame_buffering, dpb, (int)(l->dpb / (384*mbs)), l->dpb );+ h->sps->vui.i_max_dec_frame_buffering, dpb, (int)(l->dpb / (mb_size*mbs)), l->dpb ); #define CHECK( name, limit, val ) \ if( (val) > (limit) ) \diff --git a/encoder/set.h b/encoder/set.hindex 6d70e96..d034a69 100644--- a/encoder/set.h+++ b/encoder/set.h@@ -33,6 +33,7 @@ void x264_pps_init( x264_pps_t *pps, int i_id, x264_param_t *param, x264_sps_t * 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 );+const x264_level_t *x264_get_level( int level_idc ); int x264_validate_levels( x264_t *h, int verbose ); void x264_sei_buffering_period_write( x264_t *h, bs_t *s ); void x264_sei_pic_timing_write( x264_t *h, bs_t *s );diff --git a/x264.c b/x264.cindex bba17b8..5cc72d1 100644--- a/x264.c+++ b/x264.c@@ -770,7 +770,8 @@ enum OPT_INPUT_RES, OPT_INPUT_CSP, OPT_INPUT_DEPTH,- OPT_DTS_COMPRESSION+ OPT_DTS_COMPRESSION,+ OPT_DEVICE } OptionsOPT; static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw";@@ -781,6 +782,7 @@ static struct option long_options[] = { "fullhelp", no_argument, NULL, OPT_FULLHELP }, { "version", no_argument, NULL, 'V' }, { "profile", required_argument, NULL, OPT_PROFILE },+ { "device", required_argument, NULL, OPT_DEVICE }, { "preset", required_argument, NULL, OPT_PRESET }, { "tune", required_argument, NULL, OPT_TUNE }, { "slow-firstpass", no_argument, NULL, OPT_SLOWFIRSTPASS },@@ -928,6 +930,140 @@ static struct option long_options[] = {0, 0, 0, 0} }; +const x264_device_t x264_device_psp = {480, 272, 8, "main"};+const x264_device_t x264_device_ipod = {320, 240, 8, "baseline"};+const x264_device_t x264_device_ps3 = {1920, 1080, 12, "high"};+const x264_device_t x264_device_iphone = {480, 320, 8, "baseline"};+const x264_device_t x264_device_iphone4 = {960, 640, 8, "main"};+const x264_device_t x264_device_xbox360 = {1920, 1080, 12, "high"};+const x264_device_t x264_device_ipad = {1024, 768, 9, "main"};+const x264_device_t x264_device_wdtv = {1920, 1080, 12, "high"};++const x264_device_t *x264_get_device( const char *device )+{+ if( !strcasecmp( device, "psp" ) ) return &x264_device_psp;+ else if( !strcasecmp( device, "ipod" ) ) return &x264_device_ipod;+ else if( !strcasecmp( device, "ps3" ) ) return &x264_device_ps3;+ else if( !strcasecmp( device, "iphone" ) ) return &x264_device_iphone;+ else if( !strcasecmp( device, "iphone4" ) ) return &x264_device_iphone4;+ else if( !strcasecmp( device, "xbox360" ) ) return &x264_device_xbox360;+ else if( !strcasecmp( device, "ipad" ) ) return &x264_device_ipad;+ else if( !strcasecmp( device, "wdtv" ) ) return &x264_device_wdtv;+ else+ {+ x264_log( NULL, X264_LOG_ERROR, "invalid device: %s\n", device );+ return NULL;+ }+}++const int x264_get_profile( const char *p_profile )+{+ if( !strcmp( p_profile, "baseline" ) ) return PROFILE_BASELINE;+ else if( !strcmp( p_profile, "main" ) ) return PROFILE_MAIN;+ else if( !strcmp( p_profile, "high" ) ) return PROFILE_HIGH;+ else if( !strcmp( p_profile, "high10" ) ) return PROFILE_HIGH10;+ else x264_log( NULL, X264_LOG_ERROR, "invalid profile: %s\n", p_profile );+ return 0;+}++int x264_param_apply_device_restrictions( x264_param_t *param, const char *device )+{+ x264_param_t orig;+ memcpy( &orig, param, sizeof( x264_param_t ) );+ if( !strcmp( device, "psp" ) )+ {+ if( param->i_frame_reference > 3 )+ {+ param->i_frame_reference = 3;+ x264_log( NULL, X264_LOG_WARNING, "Reference frames set to 3\n" );+ }+ if ( param->i_bframe_pyramid > 0 )+ {+ param->i_bframe_pyramid = 0;+ x264_log( NULL, X264_LOG_WARNING, "B-Pyramids disabled\n" );+ }+ + }+ if( !memcmp( &orig, param, sizeof( x264_param_t ) ) ) return 0;+ return 1;+}++x264_param_t *x264_param_apply_device( x264_param_t *param, const char *device )+{ + const x264_device_t *_device;+ int height = param->i_height;+ int width = param->i_width;+ if( !( _device = x264_get_device( device ) ) ) return NULL;+ if( _device->i_level_idc < param->i_level_idc )+ {+ x264_level_t level = x264_levels[ _device->i_level_idc ];+ double ratio = ( double )param->i_width / ( double )param->i_height;+ double max_area = level.frame_size * 256;+ width = sqrt( max_area * ratio );+ height = width / ratio;+ }+ if ( width % 2 ) width--;+ if ( height % 2 ) height--;+ param->i_width = width;+ param->i_height = height;+ param->i_level_idc = x264_levels[ _device->i_level_idc ].level_idc;+ for( int i = 0;; i++ )+ {+ if( _device->confstring[i][0] == 0 ) break;+ x264_param_parse( param, _device->confstring[i][0], _device->confstring[i][1] );+ }+ x264_param_apply_profile( param, _device->p_profile );+ x264_param_apply_level( param, _device->p_profile );+ x264_param_apply_device_restrictions( param, device );+ return param;+}++int x264_parse_device( x264_param_t *param, const char *device )+{+ if( !device ) return -1;+ char *tmp = x264_malloc( strlen( device ) + 1 );+ tmp = strcpy( tmp, device );+ char *s = strtok( tmp, ",./-+" );+ while( s )+ {+ if( !strcasecmp( s, "psp" ) )+ {+ if( !x264_param_apply_device( param, "psp" ) ) return -1;+ }+ else if( !strcasecmp( s, "ipod" ) )+ {+ if( !x264_param_apply_device( param, "ipod" ) ) return -1;+ }+ else if( !strcasecmp( s, "ps3" ) )+ {+ if( !x264_param_apply_device( param, "ps3" ) ) return -1;+ }+ else if( !strcasecmp( s, "iphone" ) )+ {+ if( !x264_param_apply_device( param, "iphone" ) ) return -1;+ }+ else if ( !strcasecmp( s, "iphone4" ) )+ {+ if( !x264_param_apply_device( param, "iphone4" ) ) return -1;+ }+ else if( !strcasecmp( s, "xbox360" ) )+ {+ if( !x264_param_apply_device( param, "xbox360" ) ) return -1;+ }+ else if( !strcasecmp( s, "ipad" ) )+ {+ if( !x264_param_apply_device( param, "ipad" ) ) return -1;;+ }+ else if( !strcasecmp( s, "wdtv" ) )+ {+ if( !x264_param_apply_device( param, "wdtv" ) ) return -1;;+ }+ s = strtok( NULL, ",./-+" );+ }+ x264_free( tmp );+ return 0;+}+ static int select_output( const char *muxer, char *filename, x264_param_t *param ) { const char *ext = get_filename_extension( filename );@@ -975,6 +1111,7 @@ static int select_input( const char *demuxer, char *used_demuxer, char *filename int b_regular = strcmp( filename, "-" ); if( !b_regular && b_auto ) ext = "raw";+ b_regular = b_regular && x264_is_regular_file_path( filename ); if( b_regular ) {@@ -1127,6 +1264,7 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) char *tcfile_name = NULL; x264_param_t defaults; char *profile = NULL;+ char *devices = NULL; char *vid_filters = NULL; int b_thread_input = 0; int b_turbo = 1;@@ -1296,6 +1434,9 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) case OPT_DTS_COMPRESSION: output_opt.use_dts_compress = 1; break;+ case OPT_DEVICE:+ devices = optarg;+ break; default: generic_option: {@@ -1458,23 +1599,10 @@ generic_option: param->b_tff = !!info.tff; } - /* Automatically reduce reference frame count to match the user's target level- * if the user didn't explicitly set a reference frame count. */- if( !b_user_ref )- {- int mbs = (((param->i_width)+15)>>4) * (((param->i_height)+15)>>4);- for( int i = 0; x264_levels[i].level_idc != 0; i++ )- if( param->i_level_idc == x264_levels[i].level_idc )- {- while( mbs * 384 * param->i_frame_reference > x264_levels[i].dpb &&- param->i_frame_reference > 1 )- {- param->i_frame_reference--;- }- break;- }- }-+ if( x264_param_apply_level( param, profile ) < 0 )+ return -1;+ + x264_parse_device( param, devices ); return 0; }@@ -1653,7 +1781,6 @@ static int encode( x264_param_t *param, cli_opt_t *opt ) break; x264_picture_init( &pic ); convert_cli_to_lib_pic( &pic, &cli_pic );- if( !param->b_vfr_input ) pic.i_pts = i_frame; diff --git a/x264.h b/x264.hindex ce79d40..034d077 100644--- a/x264.h+++ b/x264.h@@ -161,6 +161,7 @@ typedef struct #define X264_OPEN_GOP_NONE 0 #define X264_OPEN_GOP_NORMAL 1 #define X264_OPEN_GOP_BLURAY 2+#define X264_LEVEL_IDC_AUTO (-1) static const char * const x264_direct_pred_names[] = { "none", "spatial", "temporal", "auto", 0 }; static const char * const x264_motion_est_names[] = { "dia", "hex", "umh", "esa", "tesa", 0 };@@ -223,6 +224,15 @@ typedef struct struct x264_param_t *param; } x264_zone_t; +typedef struct+{+ int i_screen_width;+ int i_screen_height;+ int i_level_idc;+ char *p_profile;+ char *confstring[5][3];+} x264_device_t;+ typedef struct x264_param_t { /* CPU flags */@@ -524,6 +534,7 @@ int x264_param_parse( x264_param_t *, const char *name, const char *value ); * 2) Custom user options (via param_parse or directly assigned variables) * 3) x264_param_apply_fastfirstpass * 4) x264_param_apply_profile+ * 5) x264_param_apply_level * * Additionally, x264CLI does not apply step 3 if the preset chosen is "placebo" * or --slow-firstpass is set. */@@ -576,6 +587,51 @@ static const char * const x264_profile_names[] = { "baseline", "main", "high", " * returns 0 on success, negative on failure (e.g. invalid profile name). */ int x264_param_apply_profile( x264_param_t *, const char *profile ); +/* x264_param_apply_level:+ * Applies the restrictions of the level set in the passed x264_param_t to+ * the passed x264_param_t, using the passed profile.+ * Uses the level_idc values from x264_levels[].+ *+ * Does nothing (but is considered success) if the level is set to+ * X264_LEVEL_IDC_AUTO (default).+ *+ * x264_param_apply_level does not check or modify parameters related+ * to properties of the source video that may violate levels, e.g.+ * resolution, framerate, interlacing. x264_param_apply_level is not+ * intended as a compatibility check; this is done inside x264 at the+ * start of encoding.+ *+ * i_width and i_height must be initialized for x264_param_apply_level+ * to work properly.+ *+ * returns 0 on success, negative on failure (e.g. invalid level). */+int x264_param_apply_level( x264_param_t *, const char *profile );++/* x264_get_device:+ * Gets the x264_device_t that matches the passed device name. Returns a NULL+ * pointer and logs an error if one does not exist.+*/+const x264_device_t *x264_get_device( const char *device );++/* x264_get_profile:+ * Returns the #defined PROFILE_* relevant to the profile name in p_profile.+ * If one does not exist, it returns 0 and logs an error.+*/+const int x264_get_profile( const char *p_profile );++/* x264_param_apply_devices:+ * Applies the device constraints listed in the passed x264_device_t to the+ * passed x264_param_t. Returns the passed x264_param_t if successful or a+ * NULL pointer if the device could not be found.+*/+x264_param_t *x264_param_apply_devices( x264_param_t *param, const char *device );++/* x264_parse_device:+ * Parses the comma-seperated devices string supplied with the commandline+ * arguments. Returns 0 if successful.+*/+int x264_parse_device( x264_param_t *param, const char *device );+ /**************************************************************************** * Picture structures and functions ****************************************************************************/