From c83699f10f252998a42471294a8d97bb20f94296 Mon Sep 17 00:00:00 2001 From: Fiona Glaser Date: Tue, 18 Aug 2009 18:37:26 -0700 Subject: [PATCH] Add support for frame-accurate parameter changes Parameter structs can now be passed with individual frames. The previous method would only change the parameter of what was currently being encoded, which due to delay might be very far from an intended exact frame. Also add support for changing aspect ratio. Only works in a stream with repeating headers and requires the caller to force an IDR to ensure instant effect. --- common/common.c | 1 + common/frame.c | 1 + common/frame.h | 2 ++ encoder/encoder.c | 73 +++++++++++++++++++++++++++---------------- encoder/ratecontrol.c | 8 ++--- x264.h | 21 +++++++++++-- 6 files changed, 72 insertions(+), 34 deletions(-) diff --git a/common/common.c b/common/common.c index 84bf49f7..1f010306 100644 --- a/common/common.c +++ b/common/common.c @@ -660,6 +660,7 @@ int x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_heigh pic->img.i_stride[0] = i_width; pic->img.i_stride[1] = i_width / 2; pic->img.i_stride[2] = i_width / 2; + pic->param = NULL; return 0; } diff --git a/common/frame.c b/common/frame.c index 0d37c7e5..d13cc742 100644 --- a/common/frame.c +++ b/common/frame.c @@ -211,6 +211,7 @@ int x264_frame_copy_picture( x264_t *h, x264_frame_t *dst, x264_picture_t *src ) dst->i_type = src->i_type; dst->i_qpplus1 = src->i_qpplus1; dst->i_pts = src->i_pts; + dst->param = src->param; for( i=0; i<3; i++ ) { diff --git a/common/frame.h b/common/frame.h index 5fd49d54..3459117f 100644 --- a/common/frame.h +++ b/common/frame.h @@ -35,6 +35,8 @@ typedef struct int i_type; int i_qpplus1; int64_t i_pts; + x264_param_t *param; + int i_frame; /* Presentation frame number */ int i_frame_num; /* Coded frame number */ int b_kept_as_ref; diff --git a/encoder/encoder.c b/encoder/encoder.c index 3ce8d80c..03faab47 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -680,6 +680,40 @@ static void mbcmp_init( x264_t *h ) memcpy( h->pixf.fpelcmp_x4, satd ? h->pixf.satd_x4 : h->pixf.sad_x4, sizeof(h->pixf.fpelcmp_x4) ); } +static void x264_set_aspect_ratio( x264_t *h, x264_param_t *param, int initial ) +{ + /* VUI */ + if( param->vui.i_sar_width > 0 && param->vui.i_sar_height > 0 ) + { + int i_w = param->vui.i_sar_width; + int i_h = param->vui.i_sar_height; + int old_w = h->param.vui.i_sar_width; + int old_h = h->param.vui.i_sar_height; + + x264_reduce_fraction( &i_w, &i_h ); + + while( i_w > 65535 || i_h > 65535 ) + { + i_w /= 2; + i_h /= 2; + } + + if( i_w != old_w || i_h != old_h || initial ) + { + h->param.vui.i_sar_width = 0; + h->param.vui.i_sar_height = 0; + if( i_w == 0 || i_h == 0 ) + x264_log( h, X264_LOG_WARNING, "cannot create valid sample aspect ratio\n" ); + else + { + x264_log( h, initial?X264_LOG_INFO:X264_LOG_DEBUG, "using SAR=%d/%d\n", i_w, i_h ); + h->param.vui.i_sar_width = i_w; + h->param.vui.i_sar_height = i_h; + } + } + } +} + /**************************************************************************** * x264_encoder_open: ****************************************************************************/ @@ -694,6 +728,9 @@ x264_t *x264_encoder_open ( x264_param_t *param ) /* Create a copy of param */ memcpy( &h->param, param, sizeof(x264_param_t) ); + if( param->param_free ) + param->param_free( param ); + if( x264_validate_parameters( h ) < 0 ) goto fail; @@ -706,33 +743,7 @@ x264_t *x264_encoder_open ( x264_param_t *param ) if( h->param.rc.psz_stat_in ) h->param.rc.psz_stat_in = strdup( h->param.rc.psz_stat_in ); - /* VUI */ - if( h->param.vui.i_sar_width > 0 && h->param.vui.i_sar_height > 0 ) - { - int i_w = param->vui.i_sar_width; - int i_h = param->vui.i_sar_height; - - x264_reduce_fraction( &i_w, &i_h ); - - while( i_w > 65535 || i_h > 65535 ) - { - i_w /= 2; - i_h /= 2; - } - - h->param.vui.i_sar_width = 0; - h->param.vui.i_sar_height = 0; - if( i_w == 0 || i_h == 0 ) - { - x264_log( h, X264_LOG_WARNING, "cannot create valid sample aspect ratio\n" ); - } - else - { - x264_log( h, X264_LOG_INFO, "using SAR=%d/%d\n", i_w, i_h ); - h->param.vui.i_sar_width = i_w; - h->param.vui.i_sar_height = i_h; - } - } + x264_set_aspect_ratio( h, param, 1 ); x264_reduce_fraction( &h->param.i_fps_num, &h->param.i_fps_den ); @@ -883,6 +894,7 @@ fail: int x264_encoder_reconfig( x264_t *h, x264_param_t *param ) { h = h->thread[h->i_thread_phase%h->param.i_threads]; + x264_set_aspect_ratio( h, param, 0 ); #define COPY(var) h->param.var = param->var COPY( i_frame_reference ); // but never uses more refs than initially specified COPY( i_bframe_bias ); @@ -1565,6 +1577,13 @@ int x264_encoder_encode( x264_t *h, return 0; } + if( h->fenc->param ) + { + x264_encoder_reconfig( h, h->fenc->param ); + if( h->fenc->param->param_free ) + h->fenc->param->param_free( h->fenc->param ); + } + if( h->fenc->i_type == X264_TYPE_IDR ) { h->frames.i_last_idr = h->fenc->i_frame; diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index 65314655..f37747be 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -703,6 +703,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->param_free = x264_free; while( (tok = strtok_r( p, ",", &saveptr )) ) { char *val = strchr( tok, '=' ); @@ -849,10 +850,9 @@ void x264_ratecontrol_delete( x264_t *h ) if( rc->zones ) { x264_free( rc->zones[0].param ); - if( h->param.rc.psz_zones ) - for( i=1; ii_zones; i++ ) - if( rc->zones[i].param != rc->zones[0].param ) - x264_free( rc->zones[i].param ); + for( i=1; ii_zones; i++ ) + if( rc->zones[i].param != rc->zones[0].param && rc->zones[i].param->param_free ) + rc->zones[i].param->param_free( rc->zones[i].param ); x264_free( rc->zones ); } x264_free( rc ); diff --git a/x264.h b/x264.h index 964e0930..37a643cb 100644 --- a/x264.h +++ b/x264.h @@ -35,7 +35,7 @@ #include -#define X264_BUILD 71 +#define X264_BUILD 72 /* x264_t: * opaque handler for encoder */ @@ -294,6 +294,12 @@ typedef struct x264_param_t int b_aud; /* generate access unit delimiters */ int b_repeat_headers; /* put SPS/PPS before each keyframe */ int i_sps_id; /* SPS and PPS id number */ + + /* Optional callback for freeing this x264_param_t when it is done being used. + * Only used when the x264_param_t sits in memory for an indefinite period of time, + * i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones. + * Not used when x264_encoder_reconfig is called directly. */ + void (*param_free)( void* ); } x264_param_t; typedef struct { @@ -354,6 +360,13 @@ typedef struct int i_qpplus1; /* In: user pts, Out: pts of encoded picture (user)*/ int64_t i_pts; + /* In: custom encoding parameters to be set from this frame forwards + (in coded order, not display order). If NULL, continue using + parameters from the previous frame. Some parameters, such as + aspect ratio, can only be changed per-GOP due to the limitations + of H.264 itself; in this case, the caller must force an IDR frame + if it needs the changed parameter to apply immediately. */ + x264_param_t *param; /* In: raw data */ x264_image_t img; @@ -419,8 +432,10 @@ int x264_nal_encode( void *, int *, int b_annexeb, x264_nal_t *nal ); * create a new encoder handler, all parameters from x264_param_t are copied */ x264_t *x264_encoder_open ( x264_param_t * ); /* x264_encoder_reconfig: - * change encoder options while encoding, - * analysis-related parameters from x264_param_t are copied */ + * analysis-related parameters from x264_param_t are copied. + * this takes effect immediately, on whichever frame is encoded next; + * due to delay, this may not be the next frame passed to encoder_encode. + * if the change should apply to some particular frame, use x264_picture_t->param instead. */ int x264_encoder_reconfig( x264_t *, x264_param_t * ); /* x264_encoder_headers: * return the SPS and PPS that will be used for the whole stream */ -- 2.40.0