param->b_aud = 0;
param->b_vfr_input = 1;
param->b_dts_compress = 0;
+ param->i_nal_hrd = X264_NAL_HRD_NONE;
+ param->b_tff = 1;
+ param->b_pic_struct = 0;
}
static int x264_param_apply_preset( x264_param_t *param, const char *preset )
param->i_sync_lookahead = 0;
param->i_bframe = 0;
param->b_sliced_threads = 1;
+ param->b_vfr_input = 0;
}
else if( !strncasecmp( s, "touhou", 6 ) )
{
p->i_cabac_init_idc = atoi(value);
OPT("interlaced")
p->b_interlaced = atobool(value);
+ OPT("tff")
+ p->b_interlaced = p->b_tff = atobool(value);
+ OPT("bff")
+ {
+ p->b_interlaced = atobool(value);
+ p->b_tff = !p->b_interlaced;
+ }
OPT("constrained-intra")
p->b_constrained_intra = atobool(value);
OPT("cqm")
p->b_annexb = atobool(value);
OPT("force-cfr")
p->b_vfr_input = !atobool(value);
+ OPT("nal-hrd")
+ b_error |= parse_enum( value, x264_nal_hrd_names, &p->i_nal_hrd );
+ OPT("pic-struct")
+ p->b_pic_struct = atobool(value);
else
return X264_PARAM_BAD_NAME;
#undef OPT
pic->img.i_stride[1] = i_width / 2;
pic->img.i_stride[2] = i_width / 2;
pic->param = NULL;
+ pic->i_pic_struct = PIC_STRUCT_AUTO;
return 0;
}
{
s += sprintf( s, "%dx%d ", p->i_width, p->i_height );
s += sprintf( s, "fps=%d/%d ", p->i_fps_num, p->i_fps_den );
+ s += sprintf( s, "timebase=%d/%d ", p->i_timebase_num, p->i_timebase_den );
}
s += sprintf( s, "cabac=%d", p->b_cabac );
s += sprintf( s, " slice_max_mbs=%d", p->i_slice_max_mbs );
s += sprintf( s, " nr=%d", p->analyse.i_noise_reduction );
s += sprintf( s, " decimate=%d", p->analyse.b_dct_decimate );
- s += sprintf( s, " mbaff=%d", p->b_interlaced );
+ s += sprintf( s, " interlaced=%s", p->b_interlaced ? p->b_tff ? "tff" : "bff" : "0" );
+
s += sprintf( s, " constrained_intra=%d", p->b_constrained_intra );
s += sprintf( s, " bframes=%d", p->i_bframe );
s += sprintf( s, " zones" );
}
+ if( p->rc.i_vbv_buffer_size )
+ s += sprintf( s, " nal_hrd=%s", x264_nal_hrd_names[p->i_nal_hrd] );
return buf;
}
#define X264_WEIGHTP_FAKE (-1)
+#define NALU_OVERHEAD 5 // startcode + NAL type costs 5 bytes per frame
+#define FILLER_OVERHEAD (NALU_OVERHEAD+1)
+
/****************************************************************************
* Includes
****************************************************************************/
static const char slice_type_to_char[] = { 'P', 'B', 'I', 'S', 'S' };
+enum sei_payload_type_e
+{
+ SEI_BUFFERING_PERIOD = 0,
+ SEI_PIC_TIMING = 1,
+ SEI_PAN_SCAN_RECT = 2,
+ SEI_FILLER = 3,
+ SEI_USER_DATA_REGISTERED = 4,
+ SEI_USER_DATA_UNREGISTERED = 5,
+ SEI_RECOVERY_POINT = 6,
+};
+
typedef struct
{
x264_sps_t *sps;
int i_idr_pic_id; /* -1 if nal_type != 5 */
- int i_poc_lsb;
+ int i_poc;
int i_delta_poc_bottom;
int i_delta_poc[2];
int i_nal_type;
int i_nal_ref_idc;
+ int i_disp_fields; /* Number of displayed fields (both coded and implied via pic_struct) */
+ int i_disp_fields_last_frame;
+ int i_prev_duration; /* Duration of previous frame */
+ int i_coded_fields; /* Number of coded fields (both coded and implied via pic_struct) */
+ int i_cpb_delay; /* Equal to number of fields preceding this field
+ * since last buffering_period SEI */
+ int i_coded_fields_lookahead; /* Use separate counters for lookahead */
+ int i_cpb_delay_lookahead;
+
/* We use only one SPS and one PPS */
x264_sps_t sps_array[1];
x264_sps_t *sps;
int64_t i_bframe_delay_time;
int64_t i_init_delta;
int64_t i_prev_reordered_pts[2];
+ int64_t i_largest_pts;
+ int64_t i_second_largest_pts;
int b_have_lowres; /* Whether 1/2 resolution luma planes are being used */
int b_have_sub8x8_esa;
} frames;
x264_frame_t *fref1[16+3]; /* ref list 1 */
int b_ref_reorder[2];
-
+ /* hrd */
+ int initial_cpb_removal_delay;
+ int64_t first_pts;
/* Current MB DCT coeffs */
struct
frame->i_frame_num = -1;
frame->i_lines_completed = -1;
frame->b_fdec = b_fdec;
+ frame->i_pic_struct = PIC_STRUCT_AUTO;
+ frame->i_field_cnt = -1;
+ frame->i_duration =
+ frame->i_cpb_duration =
+ frame->i_dpb_output_delay =
+ frame->i_cpb_delay = 0;
+ frame->i_coded_fields_lookahead =
+ frame->i_cpb_delay_lookahead = -1;
+
frame->orig = frame;
/* all 4 luma planes allocated together, since the cacheline split code
dst->i_qpplus1 = src->i_qpplus1;
dst->i_pts = dst->i_reordered_pts = src->i_pts;
dst->param = src->param;
+ dst->i_pic_struct = src->i_pic_struct;
for( i=0; i<3; i++ )
{
int i_qpplus1;
int64_t i_pts;
int64_t i_reordered_pts;
+ int i_duration; /* in SPS time_scale units (i.e 2 * timebase units) used for vfr */
+ int i_cpb_duration;
+ int i_cpb_delay; /* in SPS time_scale units (i.e 2 * timebase units) */
+ int i_dpb_output_delay;
x264_param_t *param;
int i_frame; /* Presentation frame number */
int i_coded; /* Coded frame number */
+ int i_field_cnt; /* Presentation field count */
int i_frame_num; /* 7.4.3 frame_num */
int b_kept_as_ref;
+ int i_pic_struct;
int b_keyframe;
uint8_t b_fdec;
uint8_t b_last_minigop_bframe; /* this frame is the last b in a sequence of bframes */
uint32_t i_pixel_sum;
uint64_t i_pixel_ssd;
+ /* hrd */
+ x264_hrd_t hrd_timing;
+
/* vbv */
uint8_t i_planned_type[X264_LOOKAHEAD_MAX+1];
int i_planned_satd[X264_LOOKAHEAD_MAX+1];
+ double f_planned_cpb_duration[X264_LOOKAHEAD_MAX+1];
+ int i_coded_fields_lookahead;
+ int i_cpb_delay_lookahead;
/* threading */
int i_lines_completed; /* in pixels */
#if defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ > 3)
#define x264_clz(x) __builtin_clz(x)
+#define x264_ctz(x) __builtin_ctz(x)
#else
static int ALWAYS_INLINE x264_clz( uint32_t x )
{
x >>= y^4;
return z + lut[x];
}
+
+static int ALWAYS_INLINE x264_ctz( uint32_t x )
+{
+ static uint8_t lut[16] = {4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0};
+ int y, z = (((x & 0xffff) - 1) >> 27) & 16;
+ x >>= z;
+ z += y = (((x & 0xff) - 1) >> 28) & 8;
+ x >>= y;
+ z += y = (((x & 0xf) - 1) >> 29) & 4;
+ x >>= y;
+ return z + lut[x&0xf];
+}
#endif
#ifdef USE_REAL_PTHREAD
int i_time_scale;
int b_fixed_frame_rate;
+ int b_nal_hrd_parameters_present;
+ int b_vcl_hrd_parameters_present;
+
+ struct
+ {
+ int i_cpb_cnt;
+ int i_bit_rate_scale;
+ int i_cpb_size_scale;
+ int i_bit_rate_value;
+ int i_cpb_size_value;
+ int i_bit_rate_unscaled;
+ int i_cpb_size_unscaled;
+ int b_cbr_hrd;
+
+ int i_initial_cpb_removal_delay_length;
+ int i_cpb_removal_delay_length;
+ int i_dpb_output_delay_length;
+ int i_time_offset_length;
+ } hrd;
+
+ int b_pic_struct_present;
int b_bitstream_restriction;
int b_motion_vectors_over_pic_boundaries;
int i_max_bytes_per_pic_denom;
//#define DEBUG_MB_TYPE
-#define NALU_OVERHEAD 5 // startcode + NAL type costs 5 bytes per frame
-
#define bs_write_ue bs_write_ue_big
static int x264_encoder_frame_end( x264_t *h, x264_t *thread_current,
sh->i_idr_pic_id = i_idr_pic_id;
/* poc stuff, fixed later */
- sh->i_poc_lsb = 0;
+ sh->i_poc = 0;
sh->i_delta_poc_bottom = 0;
sh->i_delta_poc[0] = 0;
sh->i_delta_poc[1] = 0;
if( sh->sps->i_poc_type == 0 )
{
- bs_write( s, sh->sps->i_log2_max_poc_lsb, sh->i_poc_lsb & ((1<<sh->sps->i_log2_max_poc_lsb)-1) );
+ bs_write( s, sh->sps->i_log2_max_poc_lsb, sh->i_poc & ((1<<sh->sps->i_log2_max_poc_lsb)-1) );
if( sh->pps->b_pic_order && !sh->b_field_pic )
{
bs_write_se( s, sh->i_delta_poc_bottom );
h->param.analyse.b_ssim = 0;
}
+ if( h->param.b_interlaced )
+ h->param.b_pic_struct = 1;
+
+ if( h->param.i_nal_hrd && !h->param.rc.i_vbv_buffer_size )
+ {
+ x264_log( h, X264_LOG_WARNING, "NAL HRD parameters require VBV parameters\n" );
+ h->param.i_nal_hrd = X264_NAL_HRD_NONE;
+ }
+
+ if( h->param.i_nal_hrd == X264_NAL_HRD_CBR &&
+ (h->param.rc.i_bitrate != h->param.rc.i_vbv_max_bitrate || !h->param.rc.i_vbv_max_bitrate) )
+ {
+ x264_log( h, X264_LOG_WARNING, "CBR HRD requires constant bitrate\n" );
+ h->param.i_nal_hrd = X264_NAL_HRD_VBR;
+ }
+
/* ensure the booleans are 0 or 1 so they can be used in math */
#define BOOLIFY(x) h->param.x = !!h->param.x
BOOLIFY( b_cabac );
BOOLIFY( b_aud );
BOOLIFY( b_repeat_headers );
BOOLIFY( b_annexb );
+ BOOLIFY( b_vfr_input );
+ BOOLIFY( b_pic_struct );
BOOLIFY( analyse.b_transform_8x8 );
BOOLIFY( analyse.b_weighted_bipred );
BOOLIFY( analyse.b_chroma_me );
h->frames.i_delay += h->i_thread_frames - 1;
h->frames.i_delay = X264_MIN( h->frames.i_delay, X264_LOOKAHEAD_MAX );
h->frames.i_delay += h->param.i_sync_lookahead;
+ h->frames.i_delay += h->param.b_vfr_input && (h->param.rc.i_rc_method == X264_RC_ABR || h->param.rc.b_stat_write
+ || h->param.rc.i_vbv_buffer_size);
h->frames.i_bframe_delay = h->param.i_bframe ? (h->param.i_bframe_pyramid ? 2 : 1) : 0;
h->frames.i_max_ref0 = h->param.i_frame_reference;
h->frames.i_last_keyframe = - h->param.i_keyint_max;
h->frames.i_input = 0;
+ h->frames.i_largest_pts = h->frames.i_second_largest_pts = -1;
CHECKED_MALLOCZERO( h->frames.unused[0], (h->frames.i_delay + 3) * sizeof(x264_frame_t *) );
/* Allocate room for max refs plus a few extra just in case. */
CHECKED_MALLOCZERO( h->frames.blank_unused, h->i_thread_frames * 4 * sizeof(x264_frame_t *) );
h->i_ref0 = 0;
h->i_ref1 = 0;
-
+ h->i_cpb_delay = h->i_coded_fields = h->i_disp_fields = h->i_prev_duration = 0;
+ h->i_disp_fields_last_frame = -1;
x264_rdo_init();
/* init CPU functions */
if( x264_ratecontrol_new( h ) < 0 )
goto fail;
+ if( h->param.i_nal_hrd )
+ {
+ x264_log( h, X264_LOG_DEBUG, "HRD bitrate: %i bits/sec\n", h->sps->vui.hrd.i_bit_rate_unscaled );
+ x264_log( h, X264_LOG_DEBUG, "CPB size: %i bits\n", h->sps->vui.hrd.i_cpb_size_unscaled );
+ }
+
if( h->param.psz_dump_yuv )
{
/* create or truncate the reconstructed video file */
****************************************************************************/
int x264_encoder_reconfig( x264_t *h, x264_param_t *param )
{
+ int rc_reconfig = 0;
h = h->thread[h->thread[0]->i_thread_phase];
x264_set_aspect_ratio( h, param, 0 );
#define COPY(var) h->param.var = param->var
COPY( i_slice_max_size );
COPY( i_slice_max_mbs );
COPY( i_slice_count );
+
/* VBV can't be turned on if it wasn't on to begin with */
if( h->param.rc.i_vbv_max_bitrate > 0 && h->param.rc.i_vbv_buffer_size > 0 &&
param->rc.i_vbv_max_bitrate > 0 && param->rc.i_vbv_buffer_size > 0 )
COPY( rc.i_vbv_max_bitrate );
COPY( rc.i_vbv_buffer_size );
COPY( rc.i_bitrate );
+ rc_reconfig = 1;
+ }
+ if( h->param.rc.f_rf_constant != param->rc.f_rf_constant )
+ {
+ COPY( rc.f_rf_constant );
+ rc_reconfig = 1;
}
- COPY( rc.f_rf_constant );
+
#undef COPY
mbcmp_init( h );
* vbv-bufsize
* crf
* bitrate (CBR only) */
- if( !ret )
+ if( !ret && rc_reconfig )
x264_ratecontrol_init_reconfigurable( h, 0 );
return ret;
return x264_nal_check_buffer( h );
}
-static int x264_encoder_encapsulate_nals( x264_t *h )
+static int x264_encoder_encapsulate_nals( x264_t *h, int start )
{
- int nal_size = 0, i;
- for( i = 0; i < h->out.i_nal; i++ )
+ int nal_size = 0, previous_nal_size = 0, i;
+
+ for( i = 0; i < start; i++ )
+ previous_nal_size += h->out.nal[i].i_payload;
+
+ for( i = start; i < h->out.i_nal; i++ )
nal_size += h->out.nal[i].i_payload;
/* Worst-case NAL unit escaping: reallocate the buffer if it's too small. */
uint8_t *buf = x264_malloc( nal_size * 2 + h->out.i_nal * 4 );
if( !buf )
return -1;
+ if( previous_nal_size )
+ memcpy( buf, h->nal_buffer, previous_nal_size );
x264_free( h->nal_buffer );
h->nal_buffer = buf;
}
- uint8_t *nal_buffer = h->nal_buffer;
+ uint8_t *nal_buffer = h->nal_buffer + previous_nal_size;
- for( i = 0; i < h->out.i_nal; i++ )
+ for( i = start; i < h->out.i_nal; i++ )
{
int long_startcode = !i || h->out.nal[i].i_type == NAL_SPS || h->out.nal[i].i_type == NAL_PPS;
int size = x264_nal_encode( nal_buffer, &h->out.nal[i], h->param.b_annexb, long_startcode );
nal_buffer += size;
}
- return nal_buffer - h->nal_buffer;
+ return nal_buffer - (h->nal_buffer + previous_nal_size);
}
/****************************************************************************
bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );
/* Write SEI, SPS and PPS. */
- x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
- if( x264_sei_version_write( h, &h->out.bs ) )
- return -1;
- if( x264_nal_end( h ) )
- return -1;
/* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
if( x264_nal_end( h ) )
return -1;
- frame_size = x264_encoder_encapsulate_nals( h );
+ /* identify ourselves */
+ x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+ if( x264_sei_version_write( h, &h->out.bs ) )
+ return -1;
+ if( x264_nal_end( h ) )
+ return -1;
+
+ frame_size = x264_encoder_encapsulate_nals( h, 0 );
/* now set output*/
*pi_nal = h->out.i_nal;
if( h->sps->i_poc_type == 0 )
{
- h->sh.i_poc_lsb = h->fdec->i_poc & ( (1 << h->sps->i_log2_max_poc_lsb) - 1 );
- h->sh.i_delta_poc_bottom = 0;
+ h->sh.i_poc = h->fdec->i_poc;
+ if( h->param.b_interlaced )
+ {
+ h->sh.i_delta_poc_bottom = h->param.b_tff ? 1 : -1;
+ if( h->sh.i_delta_poc_bottom == -1 )
+ h->sh.i_poc = h->fdec->i_poc + 1;
+ }
+ else
+ h->sh.i_delta_poc_bottom = 0;
}
else if( h->sps->i_poc_type == 1 )
{
{
x264_t *thread_current, *thread_prev, *thread_oldest;
int i_nal_type, i_nal_ref_idc, i_global_qp, i;
+ int overhead = NALU_OVERHEAD;
if( h->i_thread_frames > 1 )
{
if( h->frames.i_bframe_delay && fenc->i_frame == h->frames.i_bframe_delay )
h->frames.i_bframe_delay_time = fenc->i_pts;
+ if( h->param.b_vfr_input && fenc->i_pts <= h->frames.i_largest_pts )
+ x264_log( h, X264_LOG_WARNING, "non-strictly-monotonic PTS\n" );
+
+ h->frames.i_second_largest_pts = h->frames.i_largest_pts;
+ h->frames.i_largest_pts = fenc->i_pts;
+
+ if( (fenc->i_pic_struct < PIC_STRUCT_AUTO) || (fenc->i_pic_struct > PIC_STRUCT_TRIPLE) )
+ fenc->i_pic_struct = PIC_STRUCT_AUTO;
+
+ if( fenc->i_pic_struct == PIC_STRUCT_AUTO )
+ {
+ int b_interlaced = fenc->param ? fenc->param->b_interlaced : h->param.b_interlaced;
+ if( b_interlaced )
+ {
+ int b_tff = fenc->param ? fenc->param->b_tff : h->param.b_tff;
+ fenc->i_pic_struct = b_tff ? PIC_STRUCT_TOP_BOTTOM : PIC_STRUCT_BOTTOM_TOP;
+ }
+ else
+ fenc->i_pic_struct = PIC_STRUCT_PROGRESSIVE;
+ }
+
if( h->frames.b_have_lowres )
{
if( h->param.analyse.i_weighted_pred == X264_WEIGHTP_FAKE || h->param.analyse.i_weighted_pred == X264_WEIGHTP_SMART )
/* ------------------- Get frame to be encoded ------------------------- */
/* 4: get picture to encode */
h->fenc = x264_frame_shift( h->frames.current );
+ if( h->i_frame == 0 )
+ h->first_pts = h->fenc->i_reordered_pts;
if( h->fenc->param )
{
x264_encoder_reconfig( h, h->fenc->param );
bs_rbsp_trailing( &h->out.bs );
if( x264_nal_end( h ) )
return -1;
+ overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1);
}
h->i_nal_type = i_nal_type;
h->i_nal_ref_idc = i_nal_ref_idc;
- int overhead = NALU_OVERHEAD;
-
if( h->param.b_intra_refresh && h->fenc->i_type == X264_TYPE_P )
{
int pocdiff = (h->fdec->i_poc - h->fref0[0]->i_poc)/2;
h->fdec->i_pir_end_col = h->fdec->f_pir_position+0.5;
}
- /* Write SPS and PPS */
if( h->fenc->b_keyframe )
{
+ /* Write SPS and PPS */
if( h->param.b_repeat_headers )
{
- if( h->fenc->i_frame == 0 )
- {
- /* identify ourself */
- x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
- if( x264_sei_version_write( h, &h->out.bs ) )
- return -1;
- if( x264_nal_end( h ) )
- return -1;
- overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
- }
-
/* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
x264_sps_write( &h->out.bs, h->sps );
overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
}
+ /* generate sei buffering period */
+ if( h->sps->vui.b_nal_hrd_parameters_present )
+ {
+ h->initial_cpb_removal_delay = x264_hrd_fullness( h, overhead*8 );
+
+ x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+ x264_sei_buffering_period_write( h, &h->out.bs, h->initial_cpb_removal_delay );
+ if( x264_nal_end( h ) )
+ return -1;
+ overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1);
+ }
+
+ if( h->param.b_repeat_headers && h->fenc->i_frame == 0 )
+ {
+ /* identify ourself */
+ x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+ if( x264_sei_version_write( h, &h->out.bs ) )
+ return -1;
+ if( x264_nal_end( h ) )
+ return -1;
+ overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1);
+ }
+
if( h->fenc->i_type != X264_TYPE_IDR )
{
int time_to_recovery = X264_MIN( h->sps->i_mb_width - 1, h->param.i_keyint_max ) + h->param.i_bframe;
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_recovery_point_write( h, &h->out.bs, time_to_recovery );
x264_nal_end( h );
- overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
+ overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1);
}
}
+ /* generate sei pic timing */
+ if( h->sps->vui.b_pic_struct_present || h->sps->vui.b_nal_hrd_parameters_present )
+ {
+ x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
+ x264_sei_pic_timing_write( h, &h->out.bs, h->fenc->i_cpb_delay, h->fenc->i_dpb_output_delay, h->fenc->i_pic_struct );
+ if( x264_nal_end( h ) )
+ return -1;
+ overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD - (h->param.b_annexb && h->out.i_nal-1);
+ }
+
/* Init the rate control */
/* FIXME: Include slice header bit cost. */
x264_ratecontrol_start( h, h->fenc->i_qpplus1, overhead*8 );
x264_frame_push_unused( thread_current, h->fenc );
- /* End bitstream, set output */
- *pi_nal = h->out.i_nal;
- *pp_nal = h->out.nal;
-
- frame_size = x264_encoder_encapsulate_nals( h );
-
- h->out.i_nal = 0;
+ frame_size = x264_encoder_encapsulate_nals( h, 0 );
/* Set output picture properties */
if( h->sh.i_type == SLICE_TYPE_I )
}
else
pic_out->i_dts = h->fenc->i_reordered_pts;
- assert( pic_out->i_pts >= pic_out->i_dts );
+ if( pic_out->i_pts < pic_out->i_dts )
+ x264_log( h, X264_LOG_WARNING, "invalid DTS: PTS is less than DTS\n" );
pic_out->img.i_plane = h->fdec->i_plane;
for(i = 0; i < 3; i++)
/* update rc */
x264_emms();
- if( x264_ratecontrol_end( h, frame_size * 8 ) < 0 )
+ int filler = 0;
+ if( x264_ratecontrol_end( h, frame_size * 8, &filler ) < 0 )
return -1;
+ pic_out->hrd_timing = h->fenc->hrd_timing;
+
+ while( filler > 0 )
+ {
+ int f, overhead;
+ overhead = (FILLER_OVERHEAD - h->param.b_annexb);
+ if( h->param.i_slice_max_size && filler > h->param.i_slice_max_size )
+ {
+ int next_size = filler - h->param.i_slice_max_size;
+ int overflow = X264_MAX( overhead - next_size, 0 );
+ f = h->param.i_slice_max_size - overhead - overflow;
+ }
+ else
+ f = X264_MAX( 0, filler - overhead );
+
+ x264_nal_start( h, NAL_FILLER, NAL_PRIORITY_DISPOSABLE );
+ x264_filler_write( h, &h->out.bs, f );
+ if( x264_nal_end( h ) )
+ return -1;
+ int total_size = x264_encoder_encapsulate_nals( h, h->out.i_nal-1 );
+ frame_size += total_size;
+ filler -= total_size;
+ }
+
+ /* End bitstream, set output */
+ *pi_nal = h->out.i_nal;
+ *pp_nal = h->out.nal;
+
+ h->out.i_nal = 0;
+
x264_noise_reduction_update( thread_current );
/* ---------------------- Compute/Print statistics --------------------- */
h->stat.i_frame_count[SLICE_TYPE_B];
int64_t i_mb_count = i_count * h->mb.i_mb_count;
float fps = (float) h->param.i_fps_num / h->param.i_fps_den;
- float f_bitrate = fps * SUM3(h->stat.i_frame_size) / i_count / 125;
+ float f_bitrate;
+ /* duration algorithm fails with one frame */
+ if( i_count == 1 )
+ f_bitrate = fps * SUM3(h->stat.i_frame_size) / i_count / 125;
+ else
+ {
+ float duration = (float)(2 * h->frames.i_largest_pts - h->frames.i_second_largest_pts) * h->param.i_timebase_num / h->param.i_timebase_den;
+ f_bitrate = SUM3(h->stat.i_frame_size) / duration / 125;
+ }
if( h->pps->b_transform_8x8_mode )
{
int16_t i_weight_denom;
int refcount[16];
int refs;
+ int i_duration;
+ int i_cpb_duration;
} ratecontrol_entry_t;
typedef struct
double buffer_fill_final; /* real buffer as of the last finished frame */
double buffer_fill; /* planned buffer, if all in-progress frames hit their bit budget */
double buffer_rate; /* # of bits added to buffer_fill after each frame */
+ double vbv_max_rate; /* # of bits added to buffer_fill per second */
predictor_t *pred; /* predict frame size from satd */
int single_frame_vbv;
int i_zones;
x264_zone_t *zones;
x264_zone_t *prev_zone;
+
+ /* hrd stuff */
+ int initial_cpb_removal_delay;
+ double nrt_first_access_unit; /* nominal removal time */
+ double previous_cpb_final_arrival_time;
};
static int parse_zones( x264_t *h );
static int init_pass2(x264_t *);
static float rate_estimate_qscale( x264_t *h );
-static void update_vbv( x264_t *h, int bits );
+static int update_vbv( x264_t *h, int bits );
static void update_vbv_plan( x264_t *h, int overhead );
static double predict_size( predictor_t *p, double q, double var );
static void update_predictor( predictor_t *p, double q, double var, double bits );
if( !b_init && rc->b_2pass )
return;
+ if( h->param.rc.i_rc_method == X264_RC_CRF )
+ {
+ /* Arbitrary rescaling to make CRF somewhat similar to QP.
+ * Try to compensate for MB-tree's effects as well. */
+ double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? 120 : 80);
+ double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : 0;
+ rc->rate_factor_constant = pow( base_cplx, 1 - rc->qcompress )
+ / qp2qscale( h->param.rc.f_rf_constant + mbtree_offset );
+ }
+
if( h->param.rc.i_vbv_max_bitrate > 0 && h->param.rc.i_vbv_buffer_size > 0 )
{
if( h->param.rc.i_vbv_buffer_size < (int)(h->param.rc.i_vbv_max_bitrate / rc->fps) )
so if the stream starts as CBR, keep it CBR. */
if( rc->b_vbv_min_rate )
h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate;
- rc->buffer_rate = h->param.rc.i_vbv_max_bitrate * 1000. / rc->fps;
- rc->buffer_size = h->param.rc.i_vbv_buffer_size * 1000.;
+
+ int vbv_buffer_size = h->param.rc.i_vbv_buffer_size * 1000;
+ int vbv_max_bitrate = h->param.rc.i_vbv_max_bitrate * 1000;
+
+ /* Init HRD */
+ if( h->param.i_nal_hrd && b_init )
+ {
+ h->sps->vui.hrd.i_cpb_cnt = 1;
+ h->sps->vui.hrd.b_cbr_hrd = h->param.i_nal_hrd == X264_NAL_HRD_CBR;
+ h->sps->vui.hrd.i_time_offset_length = 0;
+
+ #define BR_SHIFT 6
+ #define CPB_SHIFT 4
+
+ int bitrate = 1000*h->param.rc.i_vbv_max_bitrate;
+ int bufsize = 1000*h->param.rc.i_vbv_buffer_size;
+
+ // normalize HRD size and rate to the value / scale notation
+ h->sps->vui.hrd.i_bit_rate_scale = x264_clip3( x264_ctz( bitrate ) - BR_SHIFT, 0, 15 );
+ h->sps->vui.hrd.i_bit_rate_value = bitrate >> ( h->sps->vui.hrd.i_bit_rate_scale + BR_SHIFT );
+ h->sps->vui.hrd.i_bit_rate_unscaled = h->sps->vui.hrd.i_bit_rate_value << ( h->sps->vui.hrd.i_bit_rate_scale + BR_SHIFT );
+ h->sps->vui.hrd.i_cpb_size_scale = x264_clip3( x264_ctz( bufsize ) - CPB_SHIFT, 0, 15 );
+ h->sps->vui.hrd.i_cpb_size_value = bufsize >> ( h->sps->vui.hrd.i_cpb_size_scale + CPB_SHIFT );
+ h->sps->vui.hrd.i_cpb_size_unscaled = h->sps->vui.hrd.i_cpb_size_value << ( h->sps->vui.hrd.i_cpb_size_scale + CPB_SHIFT );
+
+ #undef CPB_SHIFT
+ #undef BR_SHIFT
+
+ // arbitrary
+ #define MAX_DURATION 0.5
+
+ int max_cpb_output_delay = h->param.i_keyint_max * MAX_DURATION * h->sps->vui.i_time_scale / h->sps->vui.i_num_units_in_tick;
+ int max_dpb_output_delay = h->sps->vui.i_max_dec_frame_buffering * MAX_DURATION * h->sps->vui.i_time_scale / h->sps->vui.i_num_units_in_tick;
+ int max_delay = (int)(90000.0 * (double)h->sps->vui.hrd.i_cpb_size_unscaled / h->sps->vui.hrd.i_bit_rate_unscaled + 0.5);
+
+ h->sps->vui.hrd.i_initial_cpb_removal_delay_length = 2 + x264_clip3( 32 - x264_clz( max_delay ), 4, 22 );
+ h->sps->vui.hrd.i_cpb_removal_delay_length = x264_clip3( 32 - x264_clz( max_cpb_output_delay ), 4, 32 );
+ h->sps->vui.hrd.i_dpb_output_delay_length = x264_clip3( 32 - x264_clz( max_dpb_output_delay ), 4, 32 );
+
+ #undef MAX_DURATION
+
+ vbv_buffer_size = X264_MIN( vbv_buffer_size, h->sps->vui.hrd.i_cpb_size_unscaled );
+ vbv_max_bitrate = X264_MIN( vbv_max_bitrate, h->sps->vui.hrd.i_bit_rate_unscaled );
+ }
+ else if( h->param.i_nal_hrd && !b_init )
+ {
+ x264_log( h, X264_LOG_WARNING, "VBV parameters cannot be changed when NAL HRD is in use\n" );
+ return;
+ }
+
+ rc->buffer_rate = vbv_max_bitrate / rc->fps;
+ rc->vbv_max_rate = vbv_max_bitrate;
+ rc->buffer_size = vbv_buffer_size;
rc->single_frame_vbv = rc->buffer_rate * 1.1 > rc->buffer_size;
rc->cbr_decay = 1.0 - rc->buffer_rate / rc->buffer_size
* 0.5 * X264_MAX(0, 1.5 - rc->buffer_rate * rc->fps / rc->bitrate);
&& h->param.rc.i_vbv_max_bitrate <= h->param.rc.i_bitrate;
}
}
- if( h->param.rc.i_rc_method == X264_RC_CRF )
- {
- /* Arbitrary rescaling to make CRF somewhat similar to QP.
- * Try to compensate for MB-tree's effects as well. */
- double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? 120 : 80);
- double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : 0;
- rc->rate_factor_constant = pow( base_cplx, 1 - rc->qcompress )
- / qp2qscale( h->param.rc.f_rf_constant + mbtree_offset );
- }
}
int x264_ratecontrol_new( x264_t *h )
return -1;
}
+ if( ( p = strstr( opts, "timebase=" ) ) && sscanf( p, "timebase=%d/%d", &i, &j ) != 2 )
+ {
+ x264_log( h, X264_LOG_ERROR, "timebase specified in stats file not valid\n" );
+ return -1;
+ }
+ if( i != h->param.i_timebase_num || j != h->param.i_timebase_den )
+ {
+ x264_log( h, X264_LOG_ERROR, "timebase mismatch with 1st pass (%d/%d vs %d/%d)\n",
+ h->param.i_timebase_num, h->param.i_timebase_den, i, j );
+ return -1;
+ }
+
CMP_OPT_FIRST_PASS( "wpredp", X264_MAX( 0, h->param.analyse.i_weighted_pred ) );
CMP_OPT_FIRST_PASS( "bframes", h->param.i_bframe );
CMP_OPT_FIRST_PASS( "b_pyramid", h->param.i_bframe_pyramid );
rce = &rc->entry[frame_number];
rce->direct_mode = 0;
- e += sscanf(p, " in:%*d out:%*d type:%c q:%f tex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c",
- &pict_type, &qp, &rce->tex_bits,
+ e += sscanf(p, " in:%*d out:%*d type:%c dur:%d cpbdur:%d q:%f tex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c",
+ &pict_type, &rce->i_duration, &rce->i_cpb_duration, &qp, &rce->tex_bits,
&rce->mv_bits, &rce->misc_bits, &rce->i_count, &rce->p_count,
&rce->s_count, &rce->direct_mode);
break;
default: e = -1; break;
}
- if(e < 10)
+ if(e < 12)
{
parse_error:
x264_log(h, X264_LOG_ERROR, "statistics are damaged at line %d, parser out=%d\n", i, e);
{
memset( h->fdec->i_row_bits, 0, h->sps->i_mb_height * sizeof(int) );
rc->row_pred = &rc->row_preds[h->sh.i_type];
+ rc->buffer_rate = h->fenc->i_cpb_duration * rc->vbv_max_rate * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
update_vbv_plan( h, overhead );
const x264_level_t *l = x264_levels;
while( l->level_idc != 0 && l->level_idc != h->param.i_level_idc )
l++;
+ int mincr = l->mincr;
+
+ /* Blu-ray requires this */
+ if( l->level_idc == 41 && h->param.i_nal_hrd )
+ mincr = 4;
+
/* The spec has a bizarre special case for the first frame. */
if( h->i_frame == 0 )
{
//384 * ( Max( PicSizeInMbs, fR * MaxMBPS ) + MaxMBPS * ( tr( 0 ) - tr,n( 0 ) ) ) / MinCR
double fr = 1. / 172;
int pic_size_in_mbs = h->sps->i_mb_width * h->sps->i_mb_height;
- rc->frame_size_maximum = 384 * 8 * X264_MAX( pic_size_in_mbs, fr*l->mbps ) / l->mincr;
+ rc->frame_size_maximum = 384 * 8 * X264_MAX( pic_size_in_mbs, fr*l->mbps ) / mincr;
}
else
{
//384 * MaxMBPS * ( tr( n ) - tr( n - 1 ) ) / MinCR
- rc->frame_size_maximum = 384 * 8 * (1 / rc->fps) * l->mbps / l->mincr;
+ rc->frame_size_maximum = 384 * 8 * ((double)h->fenc->i_cpb_duration * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale) * l->mbps / mincr;
}
}
}
/* After encoding one frame, save stats and update ratecontrol state */
-int x264_ratecontrol_end( x264_t *h, int bits )
+int x264_ratecontrol_end( x264_t *h, int bits, int *filler )
{
x264_ratecontrol_t *rc = h->rc;
const int *mbs = h->stat.frame.i_mb_count;
dir_avg>0 ? 's' : dir_avg<0 ? 't' : '-' )
: '-';
if( fprintf( rc->p_stat_file_out,
- "in:%d out:%d type:%c q:%.2f tex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c ref:",
+ "in:%d out:%d type:%c dur:%d cpbdur:%d q:%.2f tex:%d mv:%d misc:%d imb:%d pmb:%d smb:%d d:%c ref:",
h->fenc->i_frame, h->i_frame,
- c_type, rc->qpa_rc,
+ c_type, h->fenc->i_duration,
+ h->fenc->i_cpb_duration, rc->qpa_rc,
h->stat.frame.i_tex_bits,
h->stat.frame.i_mv_bits,
h->stat.frame.i_misc_bits,
rc->cplxr_sum += bits * qp2qscale(rc->qpa_rc) / (rc->last_rceq * fabs(h->param.rc.f_pb_factor));
}
rc->cplxr_sum *= rc->cbr_decay;
- rc->wanted_bits_window += rc->bitrate / rc->fps;
+ double frame_duration = (double)h->fenc->i_duration * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+
+ rc->wanted_bits_window += frame_duration * rc->bitrate;
rc->wanted_bits_window *= rc->cbr_decay;
}
}
}
- update_vbv( h, bits );
+ *filler = update_vbv( h, bits );
+
+ if( h->sps->vui.b_nal_hrd_parameters_present )
+ {
+ if( h->fenc->i_frame == 0 )
+ {
+ // access unit initialises the HRD
+ h->fenc->hrd_timing.cpb_initial_arrival_time = 0;
+ rc->initial_cpb_removal_delay = h->initial_cpb_removal_delay;
+ h->fenc->hrd_timing.cpb_removal_time = rc->nrt_first_access_unit = (double)rc->initial_cpb_removal_delay / 90000;
+ }
+ else
+ {
+ h->fenc->hrd_timing.cpb_removal_time = rc->nrt_first_access_unit + (double)h->fenc->i_cpb_delay *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ if( h->fenc->b_keyframe )
+ {
+ rc->nrt_first_access_unit = h->fenc->hrd_timing.cpb_removal_time;
+ rc->initial_cpb_removal_delay = h->initial_cpb_removal_delay;
+ }
+
+ if( h->sps->vui.hrd.b_cbr_hrd )
+ h->fenc->hrd_timing.cpb_initial_arrival_time = rc->previous_cpb_final_arrival_time;
+ else
+ {
+ // NOTE: Equation C-4 has initial_cpb_removal_delay_offset which is hardcoded to zero in x264.
+ double cpb_earliest_arrival_time = h->fenc->hrd_timing.cpb_removal_time - (double)rc->initial_cpb_removal_delay / 90000;
+ h->fenc->hrd_timing.cpb_initial_arrival_time = X264_MAX( rc->previous_cpb_final_arrival_time, cpb_earliest_arrival_time );
+ }
+ }
+ int filler_bits = *filler ? X264_MAX( (FILLER_OVERHEAD - h->param.b_annexb), *filler )*8 : 0;
+ // Equation C-6
+ h->fenc->hrd_timing.cpb_final_arrival_time = rc->previous_cpb_final_arrival_time = h->fenc->hrd_timing.cpb_initial_arrival_time +
+ (double)(bits + filler_bits) / h->sps->vui.hrd.i_bit_rate_unscaled;
+
+ h->fenc->hrd_timing.dpb_output_time = (double)h->fenc->i_dpb_output_delay * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale +
+ h->fenc->hrd_timing.cpb_removal_time;
+ }
+
return 0;
fail:
x264_log(h, X264_LOG_ERROR, "ratecontrol_end: stats file could not be written to\n");
}
// update VBV after encoding a frame
-static void update_vbv( x264_t *h, int bits )
+static int update_vbv( x264_t *h, int bits )
{
+ int filler = 0;
+
x264_ratecontrol_t *rcc = h->rc;
x264_ratecontrol_t *rct = h->thread[0]->rc;
update_predictor( &rct->pred[h->sh.i_type], qp2qscale(rcc->qpa_rc), rcc->last_satd, bits );
if( !rcc->b_vbv )
- return;
+ return filler;
rct->buffer_fill_final -= bits;
+
if( rct->buffer_fill_final < 0 )
x264_log( h, X264_LOG_WARNING, "VBV underflow (frame %d, %.0f bits)\n", h->i_frame, rct->buffer_fill_final );
rct->buffer_fill_final = X264_MAX( rct->buffer_fill_final, 0 );
rct->buffer_fill_final += rcc->buffer_rate;
- rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rcc->buffer_size );
+
+ if( h->sps->vui.hrd.b_cbr_hrd && rct->buffer_fill_final > rcc->buffer_size )
+ {
+ filler = ceil( (rct->buffer_fill_final - rcc->buffer_size) / 8 );
+ rct->buffer_fill_final -= X264_MAX( (FILLER_OVERHEAD - h->param.b_annexb), filler ) * 8;
+ }
+ else
+ rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, rcc->buffer_size );
+
+ return filler;
+}
+
+int x264_hrd_fullness( x264_t *h, int overhead )
+{
+ x264_ratecontrol_t *rct = h->thread[0]->rc;
+ double cpb_bits = rct->buffer_fill_final - overhead;
+ double bps = h->sps->vui.hrd.i_bit_rate_unscaled;
+ double cpb_size = h->sps->vui.hrd.i_cpb_size_unscaled;
+ double cpb_fullness = 90000.0*cpb_bits/bps;
+
+ if( cpb_bits < 0 || cpb_bits > cpb_size )
+ {
+ x264_log( h, X264_LOG_WARNING, "CPB %s: %.0lf bits in a %.0lf-bit buffer\n",
+ cpb_bits < 0 ? "underflow" : "overflow", cpb_bits, cpb_size );
+ }
+
+ return x264_clip3f( cpb_fullness + 0.5, 0, 90000.0*cpb_size/bps ); // just lie if we are in a weird state
}
// provisionally update VBV according to the planned size of all frames currently in progress
bits = X264_MAX(bits, t->rc->frame_size_estimated);
rcc->buffer_fill -= bits;
rcc->buffer_fill = X264_MAX( rcc->buffer_fill, 0 );
- rcc->buffer_fill += rcc->buffer_rate;
+ rcc->buffer_fill += t->rc->buffer_rate;
rcc->buffer_fill = X264_MIN( rcc->buffer_fill, rcc->buffer_size );
}
}
double cur_bits = predict_size( &rcc->pred[h->sh.i_type], q, rcc->last_satd );
double buffer_fill_cur = rcc->buffer_fill - cur_bits;
double target_fill;
+ double total_duration = 0;
frame_q[0] = h->sh.i_type == SLICE_TYPE_I ? q * h->param.rc.f_ip_factor : q;
frame_q[1] = frame_q[0] * h->param.rc.f_pb_factor;
frame_q[2] = frame_q[0] / h->param.rc.f_ip_factor;
/* Loop over the planned future frames. */
for( j = 0; buffer_fill_cur >= 0 && buffer_fill_cur <= rcc->buffer_size; j++ )
{
- buffer_fill_cur += rcc->buffer_rate;
+ total_duration += h->fenc->f_planned_cpb_duration[j];
+ buffer_fill_cur += rcc->vbv_max_rate * h->fenc->f_planned_cpb_duration[j];
int i_type = h->fenc->i_planned_type[j];
int i_satd = h->fenc->i_planned_satd[j];
if( i_type == X264_TYPE_AUTO )
buffer_fill_cur -= cur_bits;
}
/* Try to get to get the buffer at least 50% filled, but don't set an impossible goal. */
- target_fill = X264_MIN( rcc->buffer_fill + j * rcc->buffer_rate * 0.5, rcc->buffer_size * 0.5 );
+ target_fill = X264_MIN( rcc->buffer_fill + total_duration * rcc->vbv_max_rate * 0.5, rcc->buffer_size * 0.5 );
if( buffer_fill_cur < target_fill )
{
q *= 1.01;
continue;
}
/* Try to get the buffer no more than 80% filled, but don't set an impossible goal. */
- target_fill = x264_clip3f( rcc->buffer_fill - j * rcc->buffer_rate * 0.5, rcc->buffer_size * 0.8, rcc->buffer_size );
+ target_fill = x264_clip3f( rcc->buffer_fill - total_duration * rcc->vbv_max_rate * 0.5, rcc->buffer_size * 0.8, rcc->buffer_size );
if( rcc->b_vbv_min_rate && buffer_fill_cur > target_fill )
{
q /= 1.01;
double pbbits = bits;
double bbits = predict_size( rcc->pred_b_from_p, q * h->param.rc.f_pb_factor, rcc->last_satd );
double space;
- if( bbits > rcc->buffer_rate )
+ double bframe_cpb_duration = 0;
+ double minigop_cpb_duration;
+ for( int i = 0; i < nb; i++ )
+ bframe_cpb_duration += h->fenc->f_planned_cpb_duration[1+i];
+
+ if( bbits * nb > bframe_cpb_duration * rcc->vbv_max_rate )
nb = 0;
pbbits += nb * bbits;
- space = rcc->buffer_fill + (1+nb)*rcc->buffer_rate - rcc->buffer_size;
+ minigop_cpb_duration = bframe_cpb_duration + h->fenc->f_planned_cpb_duration[0];
+ space = rcc->buffer_fill + minigop_cpb_duration*rcc->vbv_max_rate - rcc->buffer_size;
if( pbbits < space )
{
q *= X264_MAX( pbbits / space, bits / (0.5 * rcc->buffer_size) );
else
{
int i_frame_done = h->fenc->i_frame + 1 - h->i_thread_frames;
+ double i_time_done = i_frame_done / rcc->fps;
+ if( h->param.b_vfr_input )
+ i_time_done = ((double)(h->fenc->i_reordered_pts - h->first_pts)) * h->param.i_timebase_num / h->param.i_timebase_den;
q = get_qscale( h, &rce, rcc->wanted_bits_window / rcc->cplxr_sum, h->fenc->i_frame );
if( !rcc->b_vbv_min_rate && rcc->last_satd )
{
// FIXME is it simpler to keep track of wanted_bits in ratecontrol_end?
- wanted_bits = i_frame_done * rcc->bitrate / rcc->fps;
+ wanted_bits = i_time_done * rcc->bitrate;
if( wanted_bits > 0 )
{
- abr_buffer *= X264_MAX( 1, sqrt(i_frame_done/25) );
+ abr_buffer *= X264_MAX( 1, sqrt(i_time_done) );
overflow = x264_clip3f( 1.0 + (total_bits - wanted_bits) / abr_buffer, .5, 2 );
q *= overflow;
}
COPY(expected_bits_sum);
COPY(wanted_bits_window);
COPY(bframe_bits);
+ COPY(initial_cpb_removal_delay);
+ COPY(nrt_first_access_unit);
+ COPY(previous_cpb_final_arrival_time);
#undef COPY
}
//FIXME row_preds[] (not strictly necessary, but would improve prediction)
int i, start=-1, end=-1;
for(i = *t0; i < rcc->num_entries; i++)
{
- fill += (rcc->buffer_rate - qscale2bits(&rcc->entry[i], rcc->entry[i].new_qscale)) * parity;
+ fill += (rcc->entry[i].i_cpb_duration * rcc->vbv_max_rate * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale -
+ qscale2bits(&rcc->entry[i], rcc->entry[i].new_qscale)) * parity;
fill = x264_clip3f(fill, 0, rcc->buffer_size);
fills[i] = fill;
if(fill <= buffer_min || i == 0)
return expected_bits;
}
-static int vbv_pass2( x264_t *h )
+static int vbv_pass2( x264_t *h, double all_available_bits )
{
/* for each interval of buffer_full .. underflow, uniformly increase the qp of all
* frames in the interval until either buffer is full at some intermediate frame or the
x264_ratecontrol_t *rcc = h->rc;
double *fills;
- double all_available_bits = h->param.rc.i_bitrate * 1000. * rcc->num_entries / rcc->fps;
double expected_bits = 0;
double adjustment;
double prev_bits = 0;
{
x264_ratecontrol_t *rcc = h->rc;
uint64_t all_const_bits = 0;
- uint64_t all_available_bits = (uint64_t)(h->param.rc.i_bitrate * 1000. * rcc->num_entries / rcc->fps);
+ int i;
+ double duration = 0;
+ for( i = 0; i < rcc->num_entries; i++ )
+ duration += rcc->entry[i].i_duration;
+ duration *= (double)h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ uint64_t all_available_bits = h->param.rc.i_bitrate * 1000. * duration;
double rate_factor, step, step_mult;
double qblur = h->param.rc.f_qblur;
double cplxblur = h->param.rc.f_complexity_blur;
const int filter_size = (int)(qblur*4) | 1;
double expected_bits;
double *qscale, *blurred_qscale;
- int i;
/* find total/average complexity & const_bits */
for(i=0; i<rcc->num_entries; i++)
x264_free(blurred_qscale);
if(rcc->b_vbv)
- if( vbv_pass2( h ) )
+ if( vbv_pass2( h, all_available_bits ) )
return -1;
expected_bits = count_expected_bits(h);
void x264_ratecontrol_set_weights( x264_t *h, x264_frame_t *frm );
void x264_ratecontrol_mb( x264_t *, int bits );
int x264_ratecontrol_qp( x264_t * );
-int x264_ratecontrol_end( x264_t *, int bits );
+int x264_ratecontrol_end( x264_t *, int bits, int *filler );
void x264_ratecontrol_summary( x264_t * );
void x264_ratecontrol_set_estimated_size( x264_t *, int bits );
int x264_ratecontrol_get_estimated_size( x264_t const *);
int x264_weighted_reference_duplicate( x264_t *h, int i_ref, const x264_weight_t *w );
void x264_threads_distribute_ratecontrol( x264_t *h );
void x264_threads_merge_ratecontrol( x264_t *h );
-
+int x264_hrd_fullness( x264_t *h, int overhead );
#endif
#define bs_write_ue bs_write_ue_big
+// Indexed by pic_struct values
+static const uint8_t num_clock_ts[10] = { 0, 1, 1, 1, 2, 2, 3, 3, 2, 3 };
+
static void transpose( uint8_t *buf, int w )
{
int i, j;
}
}
+static uint8_t *x264_sei_write_header( bs_t *s, int payload_type )
+{
+ bs_write( s, 8, payload_type );
+
+ bs_flush( s );
+ uint8_t *p_start = s->p;
+ bs_realign( s );
+
+ bs_write( s, 8, 0 );
+ return p_start;
+}
+
+static void x264_sei_write( bs_t *s, uint8_t *p_start )
+{
+ bs_align_10( s );
+ bs_flush( s );
+
+ p_start[0] = s->p - p_start - 1; // -1 for the length byte
+ bs_realign( s );
+
+ bs_rbsp_trailing( s );
+}
+
void x264_sps_init( x264_sps_t *sps, int i_id, x264_param_t *param )
{
sps->i_id = i_id;
sps->vui.i_chroma_loc_bottom = param->vui.i_chroma_loc;
}
- sps->vui.b_timing_info_present = 0;
- if( param->i_timebase_num > 0 && param->i_timebase_den > 0 )
+ sps->vui.b_timing_info_present = param->i_timebase_num > 0 && param->i_timebase_den > 0;
+
+ if( sps->vui.b_timing_info_present )
{
- sps->vui.b_timing_info_present = 1;
sps->vui.i_num_units_in_tick = param->i_timebase_num;
sps->vui.i_time_scale = param->i_timebase_den * 2;
sps->vui.b_fixed_frame_rate = !param->b_vfr_input;
}
+ sps->vui.b_vcl_hrd_parameters_present = 0; // we don't support VCL HRD
+ sps->vui.b_nal_hrd_parameters_present = !!param->i_nal_hrd;
+ sps->vui.b_pic_struct_present = param->b_pic_struct;
+
+ // NOTE: HRD related parts of the SPS are initialised in x264_ratecontrol_init_reconfigurable
+
sps->vui.i_num_reorder_frames = param->i_bframe_pyramid ? 2 : param->i_bframe ? 1 : 0;
/* extra slot with pyramid so that we don't have to override the
* order of forgetting old pictures */
sps->vui.i_max_bytes_per_pic_denom = 0;
sps->vui.i_max_bits_per_mb_denom = 0;
sps->vui.i_log2_max_mv_length_horizontal =
- sps->vui.i_log2_max_mv_length_vertical = (int)(log(param->analyse.i_mv_range*4-1)/log(2)) + 1;
+ sps->vui.i_log2_max_mv_length_vertical = (int)log2f( param->analyse.i_mv_range*4-1 ) + 1;
}
}
-
void x264_sps_write( bs_t *s, x264_sps_t *sps )
{
bs_realign( s );
bs_write1( s, sps->vui.b_fixed_frame_rate );
}
- bs_write1( s, 0 ); /* nal_hrd_parameters_present_flag */
- bs_write1( s, 0 ); /* vcl_hrd_parameters_present_flag */
- bs_write1( s, 0 ); /* pic_struct_present_flag */
+ bs_write1( s, sps->vui.b_nal_hrd_parameters_present );
+ if( sps->vui.b_nal_hrd_parameters_present )
+ {
+ bs_write_ue( s, sps->vui.hrd.i_cpb_cnt - 1 );
+ bs_write( s, 4, sps->vui.hrd.i_bit_rate_scale );
+ bs_write( s, 4, sps->vui.hrd.i_cpb_size_scale );
+
+ bs_write_ue( s, sps->vui.hrd.i_bit_rate_value - 1 );
+ bs_write_ue( s, sps->vui.hrd.i_cpb_size_value - 1 );
+
+ bs_write1( s, sps->vui.hrd.b_cbr_hrd );
+
+ bs_write( s, 5, sps->vui.hrd.i_initial_cpb_removal_delay_length - 1 );
+ bs_write( s, 5, sps->vui.hrd.i_cpb_removal_delay_length - 1 );
+ bs_write( s, 5, sps->vui.hrd.i_dpb_output_delay_length - 1 );
+ bs_write( s, 5, sps->vui.hrd.i_time_offset_length );
+ }
+
+ bs_write1( s, sps->vui.b_vcl_hrd_parameters_present );
+
+ if( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present )
+ bs_write1( s, 0 ); /* low_delay_hrd_flag */
+
+ bs_write1( s, sps->vui.b_pic_struct_present );
bs_write1( s, sps->vui.b_bitstream_restriction );
if( sps->vui.b_bitstream_restriction )
{
void x264_sei_recovery_point_write( x264_t *h, bs_t *s, int recovery_frame_cnt )
{
- int payload_size;
-
bs_realign( s );
- bs_write( s, 8, 0x06 ); // payload_type = Recovery Point
- payload_size = bs_size_ue( recovery_frame_cnt ) + 4;
+ uint8_t *p_start = x264_sei_write_header( s, SEI_RECOVERY_POINT );
- bs_write( s, 8, (payload_size + 7) / 8);
bs_write_ue( s, recovery_frame_cnt ); // recovery_frame_cnt
bs_write( s, 1, 1 ); //exact_match_flag 1
bs_write( s, 1, 0 ); //broken_link_flag 0
bs_write( s, 2, 0 ); //changing_slice_group 0
- bs_align_10( s );
- bs_rbsp_trailing( s );
+ x264_sei_write( s, p_start );
bs_flush( s );
}
length = strlen(version)+1+16;
bs_realign( s );
- bs_write( s, 8, 0x5 ); // payload_type = user_data_unregistered
+ bs_write( s, 8, SEI_USER_DATA_UNREGISTERED );
// payload_size
for( i = 0; i <= length-255; i += 255 )
bs_write( s, 8, 255 );
return -1;
}
+void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_removal_delay )
+{
+ x264_sps_t *sps = h->sps;
+ bs_realign( s );
+ uint8_t *p_start = x264_sei_write_header( s, SEI_BUFFERING_PERIOD );
+
+ bs_write_ue( s, sps->i_id );
+
+ if( sps->vui.b_nal_hrd_parameters_present )
+ {
+ bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, initial_cpb_removal_delay );
+ bs_write( s, sps->vui.hrd.i_initial_cpb_removal_delay_length, 0 ); /* initial_cpb_removal_delay_offset */
+ }
+
+ x264_sei_write( s, p_start );
+ bs_flush( s );
+}
+
+void x264_sei_pic_timing_write( x264_t *h, bs_t *s, int cpb_removal_delay, int dpb_output_delay, int pic_struct )
+{
+ x264_sps_t *sps = h->sps;
+ bs_realign( s );
+ uint8_t *p_start = x264_sei_write_header( s, SEI_PIC_TIMING );
+
+ if( sps->vui.b_nal_hrd_parameters_present || sps->vui.b_vcl_hrd_parameters_present )
+ {
+ bs_write( s, sps->vui.hrd.i_cpb_removal_delay_length, cpb_removal_delay );
+ bs_write( s, sps->vui.hrd.i_dpb_output_delay_length, dpb_output_delay );
+ }
+
+ if( sps->vui.b_pic_struct_present )
+ {
+ bs_write( s, 4, pic_struct-1 ); // We use index 0 for "Auto"
+
+ // These clock timestamps are not standardised so we don't set them
+ // They could be time of origin, capture or alternative ideal display
+ for( int i = 0; i < num_clock_ts[pic_struct]; i++ )
+ bs_write1( s, 0 ); // clock_timestamp_flag
+ }
+
+ x264_sei_write( s, p_start );
+ bs_flush( s );
+}
+
+void x264_filler_write( x264_t *h, bs_t *s, int filler )
+{
+ int i;
+
+ bs_realign( s );
+
+ for( i = 0; i < filler; i++ )
+ bs_write( s, 8, 0xff );
+
+ bs_rbsp_trailing( s );
+ bs_flush( s );
+}
+
const x264_level_t x264_levels[] =
{
{ 10, 1485, 99, 152064, 64, 175, 64, 64, 0, 2, 0, 0, 1 },
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 );
int x264_validate_levels( x264_t *h, int verbose );
+void x264_sei_buffering_period_write( x264_t *h, bs_t *s, int initial_cpb_removal_delay );
+void x264_sei_pic_timing_write( x264_t *h, bs_t *s, int cpb_removal_delay, int dpb_output_delay, int pic_struct );
+void x264_filler_write( x264_t *h, bs_t *s, int filler );
#endif
#include "macroblock.h"
#include "me.h"
+// Indexed by pic_struct values
+static const uint8_t delta_tfi_divisor[10] = { 0, 2, 1, 1, 2, 2, 3, 3, 4, 6 };
+
static int x264_slicetype_frame_cost( x264_t *h, x264_mb_analysis_t *a,
x264_frame_t **frames, int p0, int p1, int b,
int b_intra_penalty );
return cost;
}
+static void x264_calculate_durations( x264_t *h, x264_frame_t *cur_frame, x264_frame_t *prev_frame, int *i_cpb_delay, int *i_coded_fields )
+{
+ cur_frame->i_cpb_delay = *i_cpb_delay;
+ cur_frame->i_dpb_output_delay = cur_frame->i_field_cnt - *i_coded_fields;
+
+ // add a correction term for frame reordering
+ cur_frame->i_dpb_output_delay += h->sps->vui.i_num_reorder_frames*2;
+
+ // fix possible negative dpb_output_delay because of pulldown changes and reordering
+ if( cur_frame->i_dpb_output_delay < 0 )
+ {
+ cur_frame->i_cpb_delay += cur_frame->i_dpb_output_delay;
+ cur_frame->i_dpb_output_delay = 0;
+ if( prev_frame )
+ prev_frame->i_cpb_duration += cur_frame->i_dpb_output_delay;
+ }
+
+ if( cur_frame->b_keyframe )
+ *i_cpb_delay = 0;
+
+ *i_cpb_delay += cur_frame->i_duration;
+ *i_coded_fields += cur_frame->i_duration;
+ cur_frame->i_cpb_duration = cur_frame->i_duration;
+}
+
static void x264_vbv_lookahead( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **frames, int num_frames, int keyframe )
{
int last_nonb = 0, cur_nonb = 1, next_nonb, i, idx = 0;
+ x264_frame_t *prev_frame = NULL;
+ int prev_frame_idx = 0;
while( cur_nonb < num_frames && frames[cur_nonb]->i_type == X264_TYPE_B )
cur_nonb++;
next_nonb = keyframe ? last_nonb : cur_nonb;
- while( cur_nonb <= num_frames )
+ if( frames[0]->i_coded_fields_lookahead >= 0 )
+ {
+ h->i_coded_fields_lookahead = frames[0]->i_coded_fields_lookahead;
+ h->i_cpb_delay_lookahead = frames[0]->i_cpb_delay_lookahead;
+ }
+
+ while( cur_nonb < num_frames )
{
/* P/I cost: This shouldn't include the cost of next_nonb */
if( next_nonb != cur_nonb )
int p0 = IS_X264_TYPE_I( frames[cur_nonb]->i_type ) ? cur_nonb : last_nonb;
frames[next_nonb]->i_planned_satd[idx] = x264_vbv_frame_cost( h, a, frames, p0, cur_nonb, cur_nonb );
frames[next_nonb]->i_planned_type[idx] = frames[cur_nonb]->i_type;
+ frames[cur_nonb]->i_coded_fields_lookahead = h->i_coded_fields_lookahead;
+ frames[cur_nonb]->i_cpb_delay_lookahead = h->i_cpb_delay_lookahead;
+ x264_calculate_durations( h, frames[cur_nonb], prev_frame, &h->i_cpb_delay_lookahead, &h->i_coded_fields_lookahead );
+ if( prev_frame )
+ {
+ frames[next_nonb]->f_planned_cpb_duration[prev_frame_idx] = (double)prev_frame->i_cpb_duration *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ }
+ frames[next_nonb]->f_planned_cpb_duration[idx] = (double)frames[cur_nonb]->i_cpb_duration *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ prev_frame = frames[cur_nonb];
+ prev_frame_idx = idx;
idx++;
}
/* Handle the B-frames: coded order */
{
frames[next_nonb]->i_planned_satd[idx] = x264_vbv_frame_cost( h, a, frames, last_nonb, cur_nonb, i );
frames[next_nonb]->i_planned_type[idx] = X264_TYPE_B;
+ frames[i]->i_coded_fields_lookahead = h->i_coded_fields_lookahead;
+ frames[i]->i_cpb_delay_lookahead = h->i_cpb_delay_lookahead;
+ x264_calculate_durations( h, frames[i], prev_frame, &h->i_cpb_delay_lookahead, &h->i_coded_fields_lookahead );
+ if( prev_frame )
+ {
+ frames[next_nonb]->f_planned_cpb_duration[prev_frame_idx] = (double)prev_frame->i_cpb_duration *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ }
+ frames[next_nonb]->f_planned_cpb_duration[idx] = (double)frames[i]->i_cpb_duration *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ prev_frame = frames[i];
+ prev_frame_idx = idx;
}
last_nonb = cur_nonb;
cur_nonb++;
if( !h->lookahead->next.i_size )
return;
+ int lookahead_size = h->lookahead->next.i_size;
+
+ if( h->param.rc.i_rc_method == X264_RC_ABR || h->param.rc.b_stat_write || h->param.rc.i_vbv_buffer_size )
+ {
+ for( i = 0; i < h->lookahead->next.i_size; i++ )
+ {
+ if( h->param.b_vfr_input )
+ {
+ if( lookahead_size-- > 1 )
+ h->lookahead->next.list[i]->i_duration = 2 * (h->lookahead->next.list[i+1]->i_pts - h->lookahead->next.list[i]->i_pts);
+ else
+ h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
+ }
+ else
+ h->lookahead->next.list[i]->i_duration = delta_tfi_divisor[h->lookahead->next.list[i]->i_pic_struct];
+ h->i_prev_duration = h->lookahead->next.list[i]->i_duration;
+
+ if( h->lookahead->next.list[i]->i_frame > h->i_disp_fields_last_frame && lookahead_size > 0 )
+ {
+ h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields;
+ h->i_disp_fields += h->lookahead->next.list[i]->i_duration;
+ h->i_disp_fields_last_frame = h->lookahead->next.list[i]->i_frame;
+ }
+ else if( lookahead_size == 0 )
+ {
+ h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields;
+ h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
+ }
+ }
+ }
+
if( h->param.rc.b_stat_read )
{
/* Use the frame types from the first pass */
frames[0]->i_reordered_pts = h->lookahead->next.list[0]->i_pts;
memcpy( h->lookahead->next.list, frames, (bframes+1) * sizeof(x264_frame_t*) );
}
+
for( i = 0; i <= bframes; i++ )
- h->lookahead->next.list[i]->i_coded = i_coded++;
+ {
+ h->lookahead->next.list[i]->i_coded = i_coded++;
+ if( h->param.rc.i_rc_method == X264_RC_ABR || h->param.rc.b_stat_write || h->param.rc.i_vbv_buffer_size )
+ {
+ if( i )
+ {
+ x264_calculate_durations( h, h->lookahead->next.list[i], h->lookahead->next.list[i-1], &h->i_cpb_delay, &h->i_coded_fields );
+ h->lookahead->next.list[0]->f_planned_cpb_duration[i-1] = (double)h->lookahead->next.list[i-1]->i_cpb_duration *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ }
+ else
+ x264_calculate_durations( h, h->lookahead->next.list[i], NULL, &h->i_cpb_delay, &h->i_coded_fields );
+
+ h->lookahead->next.list[0]->f_planned_cpb_duration[i] = (double)h->lookahead->next.list[i]->i_cpb_duration *
+ h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
+ }
+ }
}
int x264_rc_analyse_slice( x264_t *h )
pic->img.i_csp = i_csp;
pic->img.i_plane = 3;
pic->param = NULL;
+ pic->i_pic_struct = PIC_STRUCT_AUTO;
return 0;
}
flv_hnd_t *p_flv = handle;
flv_buffer *c = p_flv->c;
- int sei_size = p_nal[0].i_payload;
- int sps_size = p_nal[1].i_payload;
- int pps_size = p_nal[2].i_payload;
+ int sps_size = p_nal[0].i_payload;
+ int pps_size = p_nal[1].i_payload;
+ int sei_size = p_nal[2].i_payload;
// SEI
/* It is within the spec to write this as-is but for
return -1;
p_flv->sei_len = sei_size;
- memcpy( p_flv->sei, p_nal[0].p_payload, sei_size );
+ memcpy( p_flv->sei, p_nal[2].p_payload, sei_size );
// SPS
- uint8_t *sps = p_nal[1].p_payload + 4;
+ uint8_t *sps = p_nal[0].p_payload + 4;
x264_put_byte( c, FLV_TAG_TYPE_VIDEO );
x264_put_be24( c, 0 ); // rewrite later
// PPS
x264_put_byte( c, 1 ); // number of pps
x264_put_be16( c, pps_size - 4 );
- flv_append_data( c, p_nal[2].p_payload + 4, pps_size - 4 );
+ flv_append_data( c, p_nal[1].p_payload + 4, pps_size - 4 );
// rewrite data length info
unsigned length = c->d_cur - p_flv->start;
{
mkv_hnd_t *p_mkv = handle;
- int sei_size = p_nal[0].i_payload;
- int sps_size = p_nal[1].i_payload - 4;
- int pps_size = p_nal[2].i_payload - 4;
+ int sps_size = p_nal[0].i_payload - 4;
+ int pps_size = p_nal[1].i_payload - 4;
+ int sei_size = p_nal[2].i_payload;
- uint8_t *sei = p_nal[0].p_payload;
- uint8_t *sps = p_nal[1].p_payload + 4;
- uint8_t *pps = p_nal[2].p_payload + 4;
+ uint8_t *sps = p_nal[0].p_payload + 4;
+ uint8_t *pps = p_nal[1].p_payload + 4;
+ uint8_t *sei = p_nal[2].p_payload;
int ret;
uint8_t *avcC;
mp4_hnd_t *p_mp4 = handle;
GF_AVCConfigSlot *p_slot;
- int sei_size = p_nal[0].i_payload;
- int sps_size = p_nal[1].i_payload - 4;
- int pps_size = p_nal[2].i_payload - 4;
+ int sps_size = p_nal[0].i_payload - 4;
+ int pps_size = p_nal[1].i_payload - 4;
+ int sei_size = p_nal[2].i_payload;
- uint8_t *sei = p_nal[0].p_payload;
- uint8_t *sps = p_nal[1].p_payload + 4;
- uint8_t *pps = p_nal[2].p_payload + 4;
+ uint8_t *sps = p_nal[0].p_payload + 4;
+ uint8_t *pps = p_nal[1].p_payload + 4;
+ uint8_t *sei = p_nal[2].p_payload;
// SPS
FILE *qpfile;
FILE *tcfile_out;
double timebase_convert_multiplier;
+ int i_pulldown;
} cli_opt_t;
/* i/o file operation function pointer structs */
0
};
+static const char * const pulldown_names[] = { "none", "22", "32", "64", "double", "triple", "euro", 0 };
+
+typedef struct{
+ int mod;
+ uint8_t pattern[24];
+ float fps_factor;
+} cli_pulldown_t;
+
+enum pulldown_type_e
+{
+ X264_PULLDOWN_22 = 1,
+ X264_PULLDOWN_32,
+ X264_PULLDOWN_64,
+ X264_PULLDOWN_DOUBLE,
+ X264_PULLDOWN_TRIPLE,
+ X264_PULLDOWN_EURO
+};
+
+#define TB PIC_STRUCT_TOP_BOTTOM
+#define BT PIC_STRUCT_BOTTOM_TOP
+#define TBT PIC_STRUCT_TOP_BOTTOM_TOP
+#define BTB PIC_STRUCT_BOTTOM_TOP_BOTTOM
+
+static const cli_pulldown_t pulldown_values[] =
+{
+ [X264_PULLDOWN_22] = {1, {TB}, 2.0},
+ [X264_PULLDOWN_32] = {4, {TBT, BT, BTB, TB}, 1.25},
+ [X264_PULLDOWN_64] = {2, {PIC_STRUCT_DOUBLE, PIC_STRUCT_TRIPLE}, 1.0},
+ [X264_PULLDOWN_DOUBLE] = {1, {PIC_STRUCT_DOUBLE}, 2.0},
+ [X264_PULLDOWN_TRIPLE] = {1, {PIC_STRUCT_TRIPLE}, 3.0},
+ [X264_PULLDOWN_EURO] = {24, {TBT, BT, BT, BT, BT, BT, BT, BT, BT, BT, BT, BT,
+ BTB, TB, TB, TB, TB, TB, TB, TB, TB, TB, TB, TB}, 25.0/24.0}
+};
+
+#undef TB
+#undef BT
+#undef TBT
+#undef BTB
+
+// indexed by pic_struct enum
+static const float pulldown_frame_duration[10] = { 0.0, 1, 0.5, 0.5, 1, 1, 1.5, 1.5, 2, 3 };
+
static void Help( x264_param_t *defaults, int longhelp );
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 );
" --no-cabac --no-deblock --no-weightb\n"
" --weightp 0\n"
" - zerolatency:\n"
- " --bframes 0 --rc-lookahead 0\n"
+ " --bframes 0 --force-cfr --rc-lookahead 0\n"
" --sync-lookahead 0 --sliced-threads\n" );
else H0( " - psy tunings: film,animation,grain,psnr,ssim\n"
" - other tunings: fastdecode,zerolatency\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");
- H0( " --interlaced Enable pure-interlaced mode\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" );
H0( "\n" );
H0( "Ratecontrol:\n" );
strtable_lookup( x264_colmatrix_names, defaults->vui.i_colmatrix ) );
H2( " --chromaloc <integer> Specify chroma sample location (0 to 5) [%d]\n",
defaults->vui.i_chroma_loc );
+
+ H2( " --nal-hrd <string> Signal HRD information (requires vbv-bufsize)\n"
+ " - none, vbr, cbr (cbr not allowed in .mp4)\n" );
+ H2( " --pic-struct Force pic_struct in Picture Timing SEI\n" );
+
H0( "\n" );
H0( "Input/Output:\n" );
H0( "\n" );
H2( " --timebase <int/int> Specify timebase numerator and denominator\n"
" <integer> Specify timebase numerator for input timecode file\n"
" or specify timebase denominator for other input\n" );
+ H0( " --pulldown <string> Use soft pulldown to change frame rate\n"
+ " - none, 22, 32, 64, double, triple, euro (requires cfr input)\n" );
H0( "\n" );
}
#define OPT_TCFILE_IN 274
#define OPT_TCFILE_OUT 275
#define OPT_TIMEBASE 276
+#define OPT_PULLDOWN 277
static char short_options[] = "8A:B:b:f:hI:i:m:o:p:q:r:t:Vvw";
static struct option long_options[] =
{ "filter", required_argument, NULL, 0 },
{ "deblock", required_argument, NULL, 'f' },
{ "interlaced", no_argument, NULL, OPT_INTERLACED },
+ { "tff", no_argument, NULL, OPT_INTERLACED },
+ { "bff", no_argument, NULL, OPT_INTERLACED },
{ "no-interlaced", no_argument, NULL, OPT_INTERLACED },
{ "constrained-intra", no_argument, NULL, 0 },
{ "cabac", no_argument, NULL, 0 },
{ "tcfile-in", required_argument, NULL, OPT_TCFILE_IN },
{ "tcfile-out", required_argument, NULL, OPT_TCFILE_OUT },
{ "timebase", required_argument, NULL, OPT_TIMEBASE },
+ { "pic-struct", no_argument, NULL, 0 },
+ { "nal-hrd", required_argument, NULL, 0 },
+ { "pulldown", required_argument, NULL, OPT_PULLDOWN },
{0, 0, 0, 0}
};
#ifdef MP4_OUTPUT
output = mp4_output;
param->b_annexb = 0;
- param->b_aud = 0;
param->b_dts_compress = 0;
param->b_repeat_headers = 0;
+ if( param->i_nal_hrd == X264_NAL_HRD_CBR )
+ {
+ fprintf( stderr, "x264 [warning]: cbr nal-hrd is not compatible with mp4\n" );
+ param->i_nal_hrd = X264_NAL_HRD_VBR;
+ }
#else
fprintf( stderr, "x264 [error]: not compiled with MP4 output support\n" );
return -1;
{
output = mkv_output;
param->b_annexb = 0;
- param->b_aud = 0;
param->b_dts_compress = 0;
param->b_repeat_headers = 0;
}
{
output = flv_output;
param->b_annexb = 0;
- param->b_aud = 0;
param->b_dts_compress = 1;
param->b_repeat_headers = 0;
}
case OPT_TIMEBASE:
input_opt.timebase = optarg;
break;
+ case OPT_PULLDOWN:
+ for( i = 0; pulldown_names[i] && strcasecmp( pulldown_names[i], optarg ); )
+ i++;
+ if( !pulldown_names[i] )
+ {
+ fprintf( stderr, "x264 [error]: invalid pulldown '%s'\n", optarg );
+ return -1;
+ }
+ opt->i_pulldown = i;
+ break;
default:
generic_option:
{
{
x264_t *h;
x264_picture_t pic;
+ const cli_pulldown_t *pulldown = NULL; // shut up gcc
int i_frame, i_frame_total, i_frame_output;
int64_t i_start, i_end;
double duration;
int prev_timebase_den = param->i_timebase_den / gcd( param->i_timebase_num, param->i_timebase_den );
int dts_compress_multiplier;
+ double pulldown_pts = 0;
opt->b_progress &= param->i_log_level < X264_LOG_DEBUG;
i_frame_total = input.get_frame_total( opt->hin );
param->i_frame_total = i_frame_total;
i_update_interval = i_frame_total ? x264_clip3( i_frame_total / 1000, 1, 10 ) : 10;
+ /* set up pulldown */
+ if( opt->i_pulldown && !param->b_vfr_input )
+ {
+ param->b_pic_struct = 1;
+ pulldown = &pulldown_values[opt->i_pulldown];
+ param->i_timebase_num = param->i_fps_den;
+ if( fmod( param->i_fps_num * pulldown->fps_factor, 1 ) )
+ {
+ fprintf( stderr, "x264 [error]: unsupported framerate for chosen pulldown\n" );
+ return -1;
+ }
+ param->i_timebase_den = param->i_fps_num * pulldown->fps_factor;
+ }
+
if( ( h = x264_encoder_open( param ) ) == NULL )
{
fprintf( stderr, "x264 [error]: x264_encoder_open failed\n" );
if( !param->b_vfr_input )
pic.i_pts = i_frame;
- if( opt->timebase_convert_multiplier )
+
+ if( opt->i_pulldown && !param->b_vfr_input )
+ {
+ pic.i_pic_struct = pulldown->pattern[ i_frame % pulldown->mod ];
+ pic.i_pts = (int64_t)( pulldown_pts + 0.5 );
+ pulldown_pts += pulldown_frame_duration[pic.i_pic_struct];
+ }
+ else if( opt->timebase_convert_multiplier )
pic.i_pts = (int64_t)( pic.i_pts * opt->timebase_convert_multiplier + 0.5 );
int64_t output_pts = pic.i_pts * dts_compress_multiplier; /* pts libx264 returns */
pic.i_pts = largest_pts + ticks_per_frame;
output_pts = pic.i_pts * dts_compress_multiplier;
}
+
second_largest_pts = largest_pts;
largest_pts = pic.i_pts;
if( opt->tcfile_out )
duration = (double)param->i_fps_den / param->i_fps_num;
else
duration = (double)(2 * largest_pts - second_largest_pts) * param->i_timebase_num / param->i_timebase_den;
- duration *= dts_compress_multiplier;
+ if( !(opt->i_pulldown && !param->b_vfr_input) )
+ duration *= dts_compress_multiplier;
i_end = x264_mdate();
input.picture_clean( &pic );
#include <stdarg.h>
-#define X264_BUILD 88
+#define X264_BUILD 89
/* x264_t:
* opaque handler for encoder */
static const char * const x264_colorprim_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", 0 };
static const char * const x264_transfer_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100", "log316", 0 };
static const char * const x264_colmatrix_names[] = { "GBR", "bt709", "undef", "", "fcc", "bt470bg", "smpte170m", "smpte240m", "YCgCo", 0 };
+static const char * const x264_nal_hrd_names[] = { "none", "vbr", "cbr", 0 };
/* Colorspace type
* legacy only; nothing other than I420 is really supported. */
#define X264_THREADS_AUTO 0 /* Automatically select optimal number of threads */
#define X264_SYNC_LOOKAHEAD_AUTO (-1) /* Automatically select optimal lookahead thread buffer size */
+/* HRD */
+#define X264_NAL_HRD_NONE 0
+#define X264_NAL_HRD_VBR 1
+#define X264_NAL_HRD_CBR 2
+
/* Zones: override ratecontrol or other options for specific sections of the video.
* See x264_encoder_reconfig() for which options can be changed.
* If zones overlap, whichever comes later in the list takes precedence. */
int i_level_idc;
int i_frame_total; /* number of frames to encode if known, else 0 */
+ /* NAL HRD
+ * Uses Buffering and Picture Timing SEIs to signal HRD
+ * The HRD in H.264 was not designed with VFR in mind.
+ * It is therefore not recommendeded to use NAL HRD with VFR.
+ * Furthermore, reconfiguring the VBV (via x264_encoder_reconfig)
+ * will currently generate invalid HRD. */
+ int i_nal_hrd;
+
struct
{
/* they will be reduced to be 0 < x <= 65535 and prime */
* by compressing them to be less than the second PTS.
* Warning: this will change the timebase! */
+ int b_tff;
+
+ /* Pulldown:
+ * The correct pic_struct must be passed with each input frame.
+ * The input timebase should be the timebase corresponding to the output framerate. This should be constant.
+ * e.g. for 3:2 pulldown timebase should be 1001/30000
+ * The PTS passed with each frame must be the PTS of the frame after pulldown is applied.
+ * Frame doubling and tripling require b_vfr_input set to zero (see H.264 Table D-1)
+ *
+ * Pulldown changes are not clearly defined in H.264. Therefore, it is the calling app's responsibility to manage this.
+ */
+
+ int b_pic_struct;
+
/* 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. */
/****************************************************************************
* Picture structures and functions
****************************************************************************/
+
+enum pic_struct_e
+{
+ PIC_STRUCT_AUTO = 0, // automatically decide (default)
+ PIC_STRUCT_PROGRESSIVE = 1, // progressive frame
+ // "TOP" and "BOTTOM" are not supported in x264 (PAFF only)
+ PIC_STRUCT_TOP_BOTTOM = 4, // top field followed by bottom
+ PIC_STRUCT_BOTTOM_TOP = 5, // bottom field followed by top
+ PIC_STRUCT_TOP_BOTTOM_TOP = 6, // top field, bottom field, top field repeated
+ PIC_STRUCT_BOTTOM_TOP_BOTTOM = 7, // bottom field, top field, bottom field repeated
+ PIC_STRUCT_DOUBLE = 8, // double frame
+ PIC_STRUCT_TRIPLE = 9, // triple frame
+};
+
+typedef struct
+{
+ double cpb_initial_arrival_time;
+ double cpb_final_arrival_time;
+ double cpb_removal_time;
+
+ double dpb_output_time;
+} x264_hrd_t;
+
typedef struct
{
int i_csp; /* Colorspace */
int i_type;
/* In: force quantizer for > 0 */
int i_qpplus1;
+ /* In: pic_struct, for pulldown/doubling/etc...used only if b_pic_timing_sei=1.
+ * use pic_struct_e for pic_struct inputs */
+ int i_pic_struct;
/* Out: whether this frame is a keyframe. Important when using modes that result in
* SEI recovery points being used instead of IDR frames. */
int b_keyframe;
x264_param_t *param;
/* In: raw data */
x264_image_t img;
+ /* Out: HRD timing information. Output only when i_nal_hrd is set. */
+ x264_hrd_t hrd_timing;
/* private user data. libx264 doesn't touch this,
not even copy it from input to output frames. */
void *opaque;
enum nal_unit_type_e
{
- NAL_UNKNOWN = 0,
- NAL_SLICE = 1,
+ NAL_UNKNOWN = 0,
+ NAL_SLICE = 1,
NAL_SLICE_DPA = 2,
NAL_SLICE_DPB = 3,
NAL_SLICE_DPC = 4,
NAL_SPS = 7,
NAL_PPS = 8,
NAL_AUD = 9,
+ NAL_FILLER = 12,
/* ref_idc == 0 for 6,9,10,11,12 */
};
enum nal_priority_e