From 4c2aafd864dd201832ec2be0fef4484925146650 Mon Sep 17 00:00:00 2001 From: Anton Mitrofanov Date: Thu, 2 Jul 2020 22:23:50 +0200 Subject: [PATCH] Add new API function: x264_param_cleanup Should be called to free struct members allocated internally by libx264, e.g. by x264_param_parse. Partially based on videolan/x264!18 by Derek Buitenhuis. --- common/base.c | 87 +++++++++++++++++++++++++++++++++++++++---- common/base.h | 12 +++++- common/frame.c | 3 ++ encoder/encoder.c | 30 ++++++++++----- encoder/ratecontrol.c | 6 +++ x264.c | 26 +++++++------ x264.h | 16 +++++++- 7 files changed, 148 insertions(+), 32 deletions(-) diff --git a/common/base.c b/common/base.c index abca9144..5d7da403 100644 --- a/common/base.c +++ b/common/base.c @@ -198,6 +198,66 @@ error: return NULL; } +/**************************************************************************** + * x264_strdup: + ****************************************************************************/ +typedef struct { + int size; + int count; + void *ptr[]; +} strdup_buffer; + +#define BUFFER_OFFSET offsetof(strdup_buffer, ptr) +#define BUFFER_DEFAULT_SIZE 16 + +char *x264_strdup( x264_param_t *param, const char *src ) +{ + strdup_buffer *buf = param->opaque; + if( !buf ) + { + buf = malloc( BUFFER_OFFSET + BUFFER_DEFAULT_SIZE * sizeof(void *) ); + if( !buf ) + goto fail; + buf->size = BUFFER_DEFAULT_SIZE; + buf->count = 0; + param->opaque = buf; + } + else if( buf->count == buf->size ) + { + if( buf->size > (INT_MAX - BUFFER_OFFSET) / 2 / (int)sizeof(void *) ) + goto fail; + int new_size = buf->size * 2; + buf = realloc( buf, BUFFER_OFFSET + new_size * sizeof(void *) ); + if( !buf ) + goto fail; + buf->size = new_size; + param->opaque = buf; + } + char *res = strdup( src ); + if( !res ) + goto fail; + buf->ptr[buf->count++] = res; + return res; +fail: + x264_log_internal( X264_LOG_ERROR, "strdup failed\n" ); + return NULL; +} + +/**************************************************************************** + * x264_param_cleanup: + ****************************************************************************/ +REALIGN_STACK void x264_param_cleanup( x264_param_t *param ) +{ + strdup_buffer *buf = param->opaque; + if( buf ) + { + for( int i = 0; i < buf->count; i++ ) + free( buf->ptr[i] ); + free( buf ); + param->opaque = NULL; + } +} + /**************************************************************************** * x264_picture_init: ****************************************************************************/ @@ -811,6 +871,15 @@ static double atof_internal( const char *str, int *b_error ) #undef atof #define atoi(str) atoi_internal( str, &b_error ) #define atof(str) atof_internal( str, &b_error ) +#define CHECKED_ERROR_STRDUP( var, param, src )\ +do {\ + var = x264_strdup( param, src );\ + if( !var )\ + {\ + b_error = 1;\ + errortype = X264_PARAM_ALLOC_FAILED;\ + }\ +} while( 0 ) REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const char *value ) { @@ -833,7 +902,7 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha char *c; name_buf = strdup(name); if( !name_buf ) - return X264_PARAM_BAD_NAME; + return X264_PARAM_ALLOC_FAILED; while( (c = strchr( name_buf, '_' )) ) *c = '-'; name = name_buf; @@ -876,6 +945,8 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha if( (p->cpu&X264_CPU_SSSE3) && !(p->cpu&X264_CPU_SSE2_IS_SLOW) ) p->cpu |= X264_CPU_SSE2_IS_FAST; } + else + errortype = X264_PARAM_ALLOC_FAILED; } } OPT("threads") @@ -1062,10 +1133,10 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha else if( strstr( value, "jvt" ) ) p->i_cqm_preset = X264_CQM_JVT; else - p->psz_cqm_file = strdup(value); + CHECKED_ERROR_STRDUP( p->psz_cqm_file, p, value ); } OPT("cqmfile") - p->psz_cqm_file = strdup(value); + CHECKED_ERROR_STRDUP( p->psz_cqm_file, p, value ); OPT("cqm4") { p->i_cqm_preset = X264_CQM_CUSTOM; @@ -1129,7 +1200,7 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha OPT("log") p->i_log_level = atoi(value); OPT("dump-yuv") - p->psz_dump_yuv = strdup(value); + CHECKED_ERROR_STRDUP( p->psz_dump_yuv, p, value ); OPT2("analyse", "partitions") { p->analyse.inter = 0; @@ -1245,8 +1316,8 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha } OPT("stats") { - p->rc.psz_stat_in = strdup(value); - p->rc.psz_stat_out = strdup(value); + CHECKED_ERROR_STRDUP( p->rc.psz_stat_in, p, value ); + CHECKED_ERROR_STRDUP( p->rc.psz_stat_out, p, value ); } OPT("qcomp") p->rc.f_qcompress = atof(value); @@ -1257,7 +1328,7 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha OPT2("cplxblur", "cplx-blur") p->rc.f_complexity_blur = atof(value); OPT("zones") - p->rc.psz_zones = strdup(value); + CHECKED_ERROR_STRDUP( p->rc.psz_zones, p, value ); OPT("crop-rect") b_error |= sscanf( value, "%d,%d,%d,%d", &p->crop_rect.i_left, &p->crop_rect.i_top, &p->crop_rect.i_right, &p->crop_rect.i_bottom ) != 4; @@ -1292,7 +1363,7 @@ REALIGN_STACK int x264_param_parse( x264_param_t *p, const char *name, const cha OPT("opencl") p->b_opencl = atobool( value ); OPT("opencl-clbin") - p->psz_clbin_file = strdup( value ); + CHECKED_ERROR_STRDUP( p->psz_clbin_file, p, value ); OPT("opencl-device") p->i_opencl_device = atoi( value ); else diff --git a/common/base.h b/common/base.h index 7159faa4..354973be 100644 --- a/common/base.h +++ b/common/base.h @@ -261,7 +261,7 @@ X264_API void x264_reduce_fraction64( uint64_t *n, uint64_t *d ); X264_API void x264_log_default( void *p_unused, int i_level, const char *psz_fmt, va_list arg ); X264_API void x264_log_internal( int i_level, const char *psz_fmt, ... ); -/* x264_malloc : will do or emulate a memalign +/* x264_malloc: will do or emulate a memalign * you have to use x264_free for buffers allocated with x264_malloc */ X264_API void *x264_malloc( int64_t ); X264_API void x264_free( void * ); @@ -269,6 +269,10 @@ X264_API void x264_free( void * ); /* x264_slurp_file: malloc space for the whole file and read it */ X264_API char *x264_slurp_file( const char *filename ); +/* x264_strdup: will do strdup and save returned pointer inside + * x264_param_t for later freeing during x264_param_cleanup */ +char *x264_strdup( x264_param_t *param, const char *src ); + /* x264_param2string: return a (malloced) string containing most of * the encoding options */ X264_API char *x264_param2string( x264_param_t *p, int b_res ); @@ -287,6 +291,12 @@ do {\ CHECKED_MALLOC( var, size );\ memset( var, 0, size );\ } while( 0 ) +#define CHECKED_STRDUP( var, param, src )\ +do {\ + var = x264_strdup( param, src );\ + if( !var )\ + goto fail;\ +} while( 0 ) /* Macros for merging multiple allocations into a single large malloc, for improved * use with huge pages. */ diff --git a/common/frame.c b/common/frame.c index 9e6fbc0d..8437193a 100644 --- a/common/frame.c +++ b/common/frame.c @@ -318,7 +318,10 @@ void x264_frame_delete( x264_frame_t *frame ) x264_free( frame->base ); if( frame->param && frame->param->param_free ) + { + x264_param_cleanup( frame->param ); frame->param->param_free( frame->param ); + } if( frame->mb_info_free ) frame->mb_info_free( frame->mb_info ); if( frame->extra_sei.sei_free ) diff --git a/encoder/encoder.c b/encoder/encoder.c index 968e2735..c96e3f24 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -1460,9 +1460,27 @@ x264_t *x264_encoder_open( x264_param_t *param ) /* Create a copy of param */ memcpy( &h->param, param, sizeof(x264_param_t) ); + h->param.opaque = NULL; + h->param.param_free = NULL; + + if( h->param.psz_cqm_file ) + CHECKED_STRDUP( h->param.psz_cqm_file, &h->param, h->param.psz_cqm_file ); + if( h->param.psz_dump_yuv ) + CHECKED_STRDUP( h->param.psz_dump_yuv, &h->param, h->param.psz_dump_yuv ); + if( h->param.rc.psz_stat_out ) + CHECKED_STRDUP( h->param.rc.psz_stat_out, &h->param, h->param.rc.psz_stat_out ); + if( h->param.rc.psz_stat_in ) + CHECKED_STRDUP( h->param.rc.psz_stat_in, &h->param, h->param.rc.psz_stat_in ); + if( h->param.rc.psz_zones ) + CHECKED_STRDUP( h->param.rc.psz_zones, &h->param, h->param.rc.psz_zones ); + if( h->param.psz_clbin_file ) + CHECKED_STRDUP( h->param.psz_clbin_file, &h->param, h->param.psz_clbin_file ); if( param->param_free ) + { + x264_param_cleanup( param ); param->param_free( param ); + } #if HAVE_INTEL_DISPATCHER x264_intel_dispatcher_override(); @@ -1481,11 +1499,6 @@ x264_t *x264_encoder_open( x264_param_t *param ) if( x264_cqm_parse_file( h, h->param.psz_cqm_file ) < 0 ) goto fail; - if( h->param.rc.psz_stat_out ) - h->param.rc.psz_stat_out = strdup( h->param.rc.psz_stat_out ); - if( h->param.rc.psz_stat_in ) - h->param.rc.psz_stat_in = strdup( h->param.rc.psz_stat_in ); - x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); x264_reduce_fraction( &h->param.i_timebase_num, &h->param.i_timebase_den ); @@ -1900,6 +1913,7 @@ int x264_encoder_reconfig( x264_t *h, x264_param_t *param ) void x264_encoder_parameters( x264_t *h, x264_param_t *param ) { memcpy( param, &h->thread[h->i_thread_phase]->param, sizeof(x264_param_t) ); + param->opaque = NULL; } /* internal usage */ @@ -3395,6 +3409,7 @@ int x264_encoder_encode( x264_t *h, x264_encoder_reconfig_apply( h, h->fenc->param ); if( h->fenc->param->param_free ) { + x264_param_cleanup( h->fenc->param ); h->fenc->param->param_free( h->fenc->param ); h->fenc->param = NULL; } @@ -4418,10 +4433,7 @@ void x264_encoder_close ( x264_t *h ) x264_ratecontrol_delete( h ); /* param */ - if( h->param.rc.psz_stat_out ) - free( h->param.rc.psz_stat_out ); - if( h->param.rc.psz_stat_in ) - free( h->param.rc.psz_stat_in ); + x264_param_cleanup( &h->param ); x264_cqm_delete( h ); x264_free( h->nal_buffer ); diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 008fbfe5..03d5e818 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -1237,6 +1237,7 @@ static int parse_zone( x264_t *h, x264_zone_t *z, char *p ) return 0; CHECKED_MALLOC( z->param, sizeof(x264_param_t) ); memcpy( z->param, &h->param, sizeof(x264_param_t) ); + z->param->opaque = NULL; z->param->param_free = x264_free; while( (tok = strtok_r( p, ",", &saveptr )) ) { @@ -1315,6 +1316,7 @@ static int parse_zones( x264_t *h ) rc->zones[0].f_bitrate_factor = 1; CHECKED_MALLOC( rc->zones[0].param, sizeof(x264_param_t) ); memcpy( rc->zones[0].param, &h->param, sizeof(x264_param_t) ); + rc->zones[0].param->opaque = NULL; for( int i = 1; i < rc->i_zones; i++ ) { if( !rc->zones[i].param ) @@ -1391,10 +1393,14 @@ void x264_ratecontrol_delete( x264_t *h ) macroblock_tree_rescale_destroy( rc ); if( rc->zones ) { + x264_param_cleanup( rc->zones[0].param ); x264_free( rc->zones[0].param ); for( int i = 1; i < rc->i_zones; i++ ) if( rc->zones[i].param != rc->zones[0].param && rc->zones[i].param->param_free ) + { + x264_param_cleanup( rc->zones[i].param ); rc->zones[i].param->param_free( rc->zones[i].param ); + } x264_free( rc->zones ); } x264_free( rc ); diff --git a/x264.c b/x264.c index 1d6334ee..d60cb929 100644 --- a/x264.c +++ b/x264.c @@ -288,7 +288,7 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ); static int encode( x264_param_t *param, cli_opt_t *opt ); /* logging and printing for within the cli system */ -static int cli_log_level; +static int cli_log_level = X264_LOG_INFO; void x264_cli_log( const char *name, int i_level, const char *fmt, ... ) { if( i_level > cli_log_level ) @@ -393,6 +393,7 @@ REALIGN_STACK int main( int argc, char **argv ) _setmode( _fileno( stderr ), _O_BINARY ); #endif + x264_param_default( ¶m ); /* Parse command line */ if( parse( argc, argv, ¶m, &opt ) < 0 ) ret = -1; @@ -419,6 +420,7 @@ REALIGN_STACK int main( int argc, char **argv ) fclose( opt.tcfile_out ); if( opt.qpfile ) fclose( opt.qpfile ); + x264_param_cleanup( ¶m ); #ifdef _WIN32 SetConsoleTitleW( org_console_title ); @@ -1412,16 +1414,6 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) char *preset = NULL; char *tune = NULL; - x264_param_default( &defaults ); - cli_log_level = defaults.i_log_level; - - memset( &input_opt, 0, sizeof(cli_input_opt_t) ); - memset( &output_opt, 0, sizeof(cli_output_opt_t) ); - input_opt.bit_depth = 8; - input_opt.input_range = input_opt.output_range = param->vui.b_fullrange = RANGE_AUTO; - int output_csp = defaults.i_csp; - opt->b_progress = 1; - /* Presets are applied before all other options. */ for( optind = 0;; ) { @@ -1439,9 +1431,19 @@ static int parse( int argc, char **argv, x264_param_t *param, cli_opt_t *opt ) if( preset && !strcasecmp( preset, "placebo" ) ) b_turbo = 0; - if( x264_param_default_preset( param, preset, tune ) < 0 ) + if( (preset || tune) && x264_param_default_preset( param, preset, tune ) < 0 ) return -1; + x264_param_default( &defaults ); + cli_log_level = defaults.i_log_level; + + memset( &input_opt, 0, sizeof(cli_input_opt_t) ); + memset( &output_opt, 0, sizeof(cli_output_opt_t) ); + input_opt.bit_depth = 8; + input_opt.input_range = input_opt.output_range = param->vui.b_fullrange = RANGE_AUTO; + int output_csp = defaults.i_csp; + opt->b_progress = 1; + /* Parse command line options */ for( optind = 0;; ) { diff --git a/x264.h b/x264.h index 3a50f1c8..dec9331e 100644 --- a/x264.h +++ b/x264.h @@ -45,7 +45,7 @@ extern "C" { #include "x264_config.h" -#define X264_BUILD 160 +#define X264_BUILD 161 #ifdef _WIN32 # define X264_DLL_IMPORT __declspec(dllimport) @@ -583,6 +583,9 @@ typedef struct x264_param_t * e.g. if doing multiple encodes in one process. */ void (*nalu_process)( x264_t *h, x264_nal_t *nal, void *opaque ); + + /* For internal use only */ + void *opaque; } x264_param_t; X264_API void x264_nal_encode( x264_t *h, uint8_t *dst, x264_nal_t *nal ); @@ -625,11 +628,20 @@ X264_API void x264_param_default( x264_param_t * ); * note: BAD_VALUE occurs only if it can't even parse the value, * numerical range is not checked until x264_encoder_open() or * x264_encoder_reconfig(). - * value=NULL means "true" for boolean options, but is a BAD_VALUE for non-booleans. */ + * value=NULL means "true" for boolean options, but is a BAD_VALUE for non-booleans. + * can allocate memory which should be freed by call of x264_param_cleanup. */ #define X264_PARAM_BAD_NAME (-1) #define X264_PARAM_BAD_VALUE (-2) +#define X264_PARAM_ALLOC_FAILED (-3) X264_API int x264_param_parse( x264_param_t *, const char *name, const char *value ); +/* x264_param_cleanup: + * Cleans up and frees allocated members of x264_param_t. + * This *does not* free the x264_param_t itself, as it may exist on the + * stack. It only frees any members of the struct that were allocated by + * x264 itself, in e.g. x264_param_parse(). */ +X264_API void x264_param_cleanup( x264_param_t *param ); + /**************************************************************************** * Advanced parameter handling functions ****************************************************************************/ -- 2.40.0