From 337ad83e5893e8e6e0bbaf42d1ed6a61399f9ddf Mon Sep 17 00:00:00 2001 From: hui su Date: Fri, 2 Dec 2016 10:11:33 -0800 Subject: [PATCH] Add support for VP9 level targeting Constraints on encoder config: -target_bandwidth is no larger than 80% of level bitrate limit -target_bandwidth * (1 + max_over_shoot_pct) is no larger than 88% of level bitrate limit -min_gf_interval is no smaller than level limit -tile_columns is no larger than level limit Constraints on rate control: -current frame size plus previous three frames' size is no larger than the CPB level limit -current frame size is no larger than 50%/40%/20% of the CPB level limit if it's a key/alt-ref/other frame. Change-Id: I84d1a2d6d6e3c82bfd533b3309ce999cfaba2c8b --- test/level_test.cc | 31 +++++++++ vp9/encoder/vp9_encoder.c | 136 +++++++++++++++++++++++++++++++++++++- vp9/encoder/vp9_encoder.h | 34 +++++++++- vp9/vp9_cx_iface.c | 68 +++++++++++++++++++ 4 files changed, 265 insertions(+), 4 deletions(-) diff --git a/test/level_test.cc b/test/level_test.cc index fbbb539df..67a794e6f 100644 --- a/test/level_test.cc +++ b/test/level_test.cc @@ -66,6 +66,36 @@ class LevelTest int level_; }; +TEST_P(LevelTest, TestTargetLevel11) { + ASSERT_NE(encoding_mode_, ::libvpx_test::kRealTime); + ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0, + 90); + target_level_ = 11; + cfg_.rc_target_bitrate = 150; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_EQ(target_level_, level_); +} + +TEST_P(LevelTest, TestTargetLevel20) { + ASSERT_NE(encoding_mode_, ::libvpx_test::kRealTime); + ::libvpx_test::I420VideoSource video("hantro_collage_w352h288.yuv", 352, 288, + 30, 1, 0, 90); + target_level_ = 20; + cfg_.rc_target_bitrate = 1200; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_EQ(target_level_, level_); +} + +TEST_P(LevelTest, TestTargetLevel31) { + ASSERT_NE(encoding_mode_, ::libvpx_test::kRealTime); + ::libvpx_test::I420VideoSource video("niklas_1280_720_30.y4m", 1280, 720, 30, + 1, 0, 60); + target_level_ = 31; + cfg_.rc_target_bitrate = 8000; + ASSERT_NO_FATAL_FAILURE(RunLoop(&video)); + ASSERT_EQ(target_level_, level_); +} + // Test for keeping level stats only TEST_P(LevelTest, TestTargetLevel0) { ::libvpx_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0, @@ -94,6 +124,7 @@ TEST_P(LevelTest, TestTargetLevelApi) { vpx_codec_ctx_t enc; vpx_codec_enc_cfg_t cfg; EXPECT_EQ(VPX_CODEC_OK, vpx_codec_enc_config_default(codec, &cfg, 0)); + cfg.rc_target_bitrate = 100; EXPECT_EQ(VPX_CODEC_OK, vpx_codec_enc_init(&enc, codec, &cfg, 0)); for (int level = 0; level <= 256; ++level) { if (level == 10 || level == 11 || level == 20 || level == 21 || diff --git a/vp9/encoder/vp9_encoder.c b/vp9/encoder/vp9_encoder.c index 476cb2d69..e123cd22a 100644 --- a/vp9/encoder/vp9_encoder.c +++ b/vp9/encoder/vp9_encoder.c @@ -108,7 +108,7 @@ static int is_psnr_calc_enabled(VP9_COMP *cpi) { } /* clang-format off */ -static const Vp9LevelSpec vp9_level_defs[VP9_LEVELS] = { +const Vp9LevelSpec vp9_level_defs[VP9_LEVELS] = { { LEVEL_1, 829440, 36864, 200, 400, 2, 1, 4, 8 }, { LEVEL_1_1, 2764800, 73728, 800, 1000, 2, 1, 4, 8 }, { LEVEL_2, 4608000, 122880, 1800, 1500, 2, 1, 4, 8 }, @@ -128,6 +128,16 @@ static const Vp9LevelSpec vp9_level_defs[VP9_LEVELS] = { }; /* clang-format on */ +static const char *level_fail_messages[TARGET_LEVEL_FAIL_IDS] = + { "The average bit-rate is too high.", + "The picture size is too large.", + "The luma sample rate is too large.", + "The CPB size is too large.", + "The compression ratio is too small", + "Too many column tiles are used.", + "The alt-ref distance is too small.", + "Too many reference buffers are used." }; + static INLINE void Scale2Ratio(VPX_SCALING mode, int *hr, int *hs) { switch (mode) { case NORMAL: @@ -224,8 +234,9 @@ VP9_LEVEL vp9_get_level(const Vp9LevelSpec *const level_spec) { for (i = 0; i < VP9_LEVELS; ++i) { this_level = &vp9_level_defs[i]; - if ((double)level_spec->max_luma_sample_rate * (1 + SAMPLE_RATE_GRACE_P) > - (double)this_level->max_luma_sample_rate || + if ((double)level_spec->max_luma_sample_rate > + (double)this_level->max_luma_sample_rate * + (1 + SAMPLE_RATE_GRACE_P) || level_spec->max_luma_picture_size > this_level->max_luma_picture_size || level_spec->average_bitrate > this_level->average_bitrate || level_spec->max_cpb_size > this_level->max_cpb_size || @@ -878,6 +889,22 @@ static void init_buffer_indices(VP9_COMP *cpi) { cpi->alt_fb_idx = 2; } +static void init_level_constraint(LevelConstraint *lc) { + lc->level_index = -1; + lc->max_cpb_size = INT_MAX; + lc->max_frame_size = INT_MAX; + lc->rc_config_updated = 0; + lc->fail_flag = 0; +} + +static void set_level_constraint(LevelConstraint *ls, int8_t level_index) { + vpx_clear_system_state(); + ls->level_index = level_index; + if (level_index >= 0) { + ls->max_cpb_size = vp9_level_defs[level_index].max_cpb_size * (double)1000; + } +} + static void init_config(struct VP9_COMP *cpi, VP9EncoderConfig *oxcf) { VP9_COMMON *const cm = &cpi->common; @@ -893,6 +920,8 @@ static void init_config(struct VP9_COMP *cpi, VP9EncoderConfig *oxcf) { cpi->target_level = oxcf->target_level; cpi->keep_level_stats = oxcf->target_level != LEVEL_MAX; + set_level_constraint(&cpi->level_constraint, + get_level_index(cpi->target_level)); cm->width = oxcf->width; cm->height = oxcf->height; @@ -1409,6 +1438,8 @@ void vp9_change_config(struct VP9_COMP *cpi, const VP9EncoderConfig *oxcf) { cpi->target_level = oxcf->target_level; cpi->keep_level_stats = oxcf->target_level != LEVEL_MAX; + set_level_constraint(&cpi->level_constraint, + get_level_index(cpi->target_level)); if (cm->profile <= PROFILE_1) assert(cm->bit_depth == VPX_BITS_8); @@ -1685,6 +1716,7 @@ VP9_COMP *vp9_create_compressor(VP9EncoderConfig *oxcf, cpi->b_calculate_psnr = CONFIG_INTERNAL_STATS; init_level_info(&cpi->level_info); + init_level_constraint(&cpi->level_constraint); #if CONFIG_INTERNAL_STATS cpi->b_calculate_blockiness = 1; @@ -4313,6 +4345,26 @@ static void adjust_image_stat(double y, double u, double v, double all, } #endif // CONFIG_INTERNAL_STATS +// Adjust the maximum allowable frame size for the target level. +static void level_rc_framerate(VP9_COMP *cpi, int arf_src_index) { + RATE_CONTROL *const rc = &cpi->rc; + LevelConstraint *const ls = &cpi->level_constraint; + VP9_COMMON *const cm = &cpi->common; + const double max_cpb_size = ls->max_cpb_size; + vpx_clear_system_state(); + rc->max_frame_bandwidth = VPXMIN(rc->max_frame_bandwidth, ls->max_frame_size); + if (frame_is_intra_only(cm)) { + rc->max_frame_bandwidth = + VPXMIN(rc->max_frame_bandwidth, (int)(max_cpb_size * 0.5)); + } else if (arf_src_index > 0) { + rc->max_frame_bandwidth = + VPXMIN(rc->max_frame_bandwidth, (int)(max_cpb_size * 0.4)); + } else { + rc->max_frame_bandwidth = + VPXMIN(rc->max_frame_bandwidth, (int)(max_cpb_size * 0.2)); + } +} + static void update_level_info(VP9_COMP *cpi, size_t *size, int arf_src_index) { VP9_COMMON *const cm = &cpi->common; Vp9LevelInfo *const level_info = &cpi->level_info; @@ -4321,6 +4373,8 @@ static void update_level_info(VP9_COMP *cpi, size_t *size, int arf_src_index) { int i, idx; uint64_t luma_samples, dur_end; const uint32_t luma_pic_size = cm->width * cm->height; + LevelConstraint *const level_constraint = &cpi->level_constraint; + const int8_t level_index = level_constraint->level_index; double cpb_data_size; vpx_clear_system_state(); @@ -4431,6 +4485,78 @@ static void update_level_info(VP9_COMP *cpi, size_t *size, int arf_src_index) { if (level_spec->max_col_tiles < (1 << cm->log2_tile_cols)) { level_spec->max_col_tiles = (1 << cm->log2_tile_cols); } + + if (level_index >= 0 && level_constraint->fail_flag == 0) { + if (level_spec->max_luma_picture_size > + vp9_level_defs[level_index].max_luma_picture_size) { + level_constraint->fail_flag |= (1 << LUMA_PIC_SIZE_TOO_LARGE); + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "Failed to encode to the target level %d. %s", + vp9_level_defs[level_index].level, + level_fail_messages[LUMA_PIC_SIZE_TOO_LARGE]); + } + + if ((double)level_spec->max_luma_sample_rate > + (double)vp9_level_defs[level_index].max_luma_sample_rate * + (1 + SAMPLE_RATE_GRACE_P)) { + level_constraint->fail_flag |= (1 << LUMA_SAMPLE_RATE_TOO_LARGE); + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "Failed to encode to the target level %d. %s", + vp9_level_defs[level_index].level, + level_fail_messages[LUMA_SAMPLE_RATE_TOO_LARGE]); + } + + if (level_spec->max_col_tiles > vp9_level_defs[level_index].max_col_tiles) { + level_constraint->fail_flag |= (1 << TOO_MANY_COLUMN_TILE); + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "Failed to encode to the target level %d. %s", + vp9_level_defs[level_index].level, + level_fail_messages[TOO_MANY_COLUMN_TILE]); + } + + if (level_spec->min_altref_distance < + vp9_level_defs[level_index].min_altref_distance) { + level_constraint->fail_flag |= (1 << ALTREF_DIST_TOO_SMALL); + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "Failed to encode to the target level %d. %s", + vp9_level_defs[level_index].level, + level_fail_messages[ALTREF_DIST_TOO_SMALL]); + } + + if (level_spec->max_ref_frame_buffers > + vp9_level_defs[level_index].max_ref_frame_buffers) { + level_constraint->fail_flag |= (1 << TOO_MANY_REF_BUFFER); + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "Failed to encode to the target level %d. %s", + vp9_level_defs[level_index].level, + level_fail_messages[TOO_MANY_REF_BUFFER]); + } + + if (level_spec->max_cpb_size > vp9_level_defs[level_index].max_cpb_size) { + level_constraint->fail_flag |= (1 << CPB_TOO_LARGE); + vpx_internal_error(&cm->error, VPX_CODEC_ERROR, + "Failed to encode to the target level %d. %s", + vp9_level_defs[level_index].level, + level_fail_messages[CPB_TOO_LARGE]); + } + + // Set an upper bound for the next frame size. It will be used in + // level_rc_framerate() before encoding the next frame. + cpb_data_size = 0; + for (i = 0; i < CPB_WINDOW_SIZE - 1; ++i) { + if (i >= level_stats->frame_window_buffer.len) break; + idx = (level_stats->frame_window_buffer.start + + level_stats->frame_window_buffer.len - 1 - i) % + FRAME_WINDOW_SIZE; + cpb_data_size += level_stats->frame_window_buffer.buf[idx].size; + } + cpb_data_size = cpb_data_size / 125.0; + level_constraint->max_frame_size = + (int)((vp9_level_defs[level_index].max_cpb_size - cpb_data_size) * + 1000.0); + if (level_stats->frame_window_buffer.len < CPB_WINDOW_SIZE - 1) + level_constraint->max_frame_size >>= 1; + } } int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, @@ -4658,6 +4784,10 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, set_frame_size(cpi); } + if (oxcf->pass != 1 && cpi->level_constraint.level_index >= 0 && + cpi->level_constraint.fail_flag == 0) + level_rc_framerate(cpi, arf_src_index); + if (cpi->oxcf.pass != 0 || cpi->use_svc || frame_is_intra_only(cm) == 1) { for (i = 0; i < MAX_REF_FRAMES; ++i) cpi->scaled_ref_idx[i] = INVALID_IDX; } diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h index 833d6a29e..de324d3aa 100644 --- a/vp9/encoder/vp9_encoder.h +++ b/vp9/encoder/vp9_encoder.h @@ -237,7 +237,7 @@ typedef struct VP9EncoderConfig { int max_threads; - int target_level; + unsigned int target_level; vpx_fixed_buf_t two_pass_stats_in; struct vpx_codec_pkt_list *output_pkt_list; @@ -341,6 +341,8 @@ typedef struct { uint8_t max_ref_frame_buffers; } Vp9LevelSpec; +extern const Vp9LevelSpec vp9_level_defs[VP9_LEVELS]; + typedef struct { int64_t ts; // timestamp uint32_t luma_samples; @@ -368,6 +370,26 @@ typedef struct { Vp9LevelSpec level_spec; } Vp9LevelInfo; +typedef enum { + BITRATE_TOO_LARGE = 0, + LUMA_PIC_SIZE_TOO_LARGE = 1, + LUMA_SAMPLE_RATE_TOO_LARGE = 2, + CPB_TOO_LARGE = 3, + COMPRESSION_RATIO_TOO_SMALL = 4, + TOO_MANY_COLUMN_TILE = 5, + ALTREF_DIST_TOO_SMALL = 6, + TOO_MANY_REF_BUFFER = 7, + TARGET_LEVEL_FAIL_IDS = 8 +} TARGET_LEVEL_FAIL_ID; + +typedef struct { + int8_t level_index; + uint8_t rc_config_updated; + uint8_t fail_flag; + int max_frame_size; // in bits + double max_cpb_size; // in bits +} LevelConstraint; + typedef struct VP9_COMP { QUANTS quants; ThreadData td; @@ -611,6 +633,8 @@ typedef struct VP9_COMP { // Previous Partition Info BLOCK_SIZE *prev_partition; int8_t *prev_segment_id; + + LevelConstraint level_constraint; } VP9_COMP; void vp9_initialize_enc(void); @@ -766,6 +790,14 @@ static INLINE int *cond_cost_list(const struct VP9_COMP *cpi, int *cost_list) { return cpi->sf.mv.subpel_search_method != SUBPEL_TREE ? cost_list : NULL; } +static INLINE int get_level_index(VP9_LEVEL level) { + int i; + for (i = 0; i < VP9_LEVELS; ++i) { + if (level == vp9_level_defs[i].level) return i; + } + return -1; +} + VP9_LEVEL vp9_get_level(const Vp9LevelSpec *const level_spec); void vp9_new_framerate(VP9_COMP *cpi, double framerate); diff --git a/vp9/vp9_cx_iface.c b/vp9/vp9_cx_iface.c index 4e8e40440..e6cea080d 100644 --- a/vp9/vp9_cx_iface.c +++ b/vp9/vp9_cx_iface.c @@ -390,6 +390,50 @@ static int get_image_bps(const vpx_image_t *img) { return 0; } +// Modify the encoder config for the target level. +static void config_target_level(VP9EncoderConfig *oxcf) { + double max_average_bitrate; // in bits per second + int max_over_shoot_pct; + const int target_level_index = get_level_index(oxcf->target_level); + + vpx_clear_system_state(); + assert(target_level_index >= 0); + assert(target_level_index < VP9_LEVELS); + + // Maximum target bit-rate is level_limit * 80%. + max_average_bitrate = + vp9_level_defs[target_level_index].average_bitrate * 800.0; + if ((double)oxcf->target_bandwidth > max_average_bitrate) + oxcf->target_bandwidth = (int64_t)(max_average_bitrate); + if (oxcf->ss_number_layers == 1 && oxcf->pass != 0) + oxcf->ss_target_bitrate[0] = (int)oxcf->target_bandwidth; + + // Adjust max over-shoot percentage. + max_over_shoot_pct = + (int)((max_average_bitrate * 1.10 - (double)oxcf->target_bandwidth) * + 100 / (double)(oxcf->target_bandwidth)); + if (oxcf->over_shoot_pct > max_over_shoot_pct) + oxcf->over_shoot_pct = max_over_shoot_pct; + + // Adjust worst allowed quantizer. + oxcf->worst_allowed_q = vp9_quantizer_to_qindex(63); + + // Adjust minimum art-ref distance. + if (oxcf->min_gf_interval < + (int)vp9_level_defs[target_level_index].min_altref_distance) + oxcf->min_gf_interval = + (int)vp9_level_defs[target_level_index].min_altref_distance; + + // Adjust maximum column tiles. + if (vp9_level_defs[target_level_index].max_col_tiles < + (1 << oxcf->tile_columns)) { + while (oxcf->tile_columns > 0 && + vp9_level_defs[target_level_index].max_col_tiles < + (1 << oxcf->tile_columns)) + --oxcf->tile_columns; + } +} + static vpx_codec_err_t set_encoder_config( VP9EncoderConfig *oxcf, const vpx_codec_enc_cfg_t *cfg, const struct vp9_extracfg *extra_cfg) { @@ -533,6 +577,8 @@ static vpx_codec_err_t set_encoder_config( } else if (oxcf->ts_number_layers == 1) { oxcf->ts_rate_decimator[0] = 1; } + + if (get_level_index(oxcf->target_level) >= 0) config_target_level(oxcf); /* printf("Current VP9 Settings: \n"); printf("target_bandwidth: %d\n", oxcf->target_bandwidth); @@ -1003,6 +1049,28 @@ static vpx_codec_err_t encoder_encode(vpx_codec_alg_priv_t *ctx, if (cpi == NULL) return VPX_CODEC_INVALID_PARAM; + if (cpi->oxcf.pass == 2 && cpi->level_constraint.level_index >= 0 && + !cpi->level_constraint.rc_config_updated) { + SVC *const svc = &cpi->svc; + const int is_two_pass_svc = + (svc->number_spatial_layers > 1) || (svc->number_temporal_layers > 1); + const VP9EncoderConfig *const oxcf = &cpi->oxcf; + TWO_PASS *const twopass = &cpi->twopass; + FIRSTPASS_STATS *stats = &twopass->total_stats; + if (is_two_pass_svc) { + const double frame_rate = 10000000.0 * stats->count / stats->duration; + vp9_update_spatial_layer_framerate(cpi, frame_rate); + twopass->bits_left = + (int64_t)(stats->duration * + svc->layer_context[svc->spatial_layer_id].target_bandwidth / + 10000000.0); + } else { + twopass->bits_left = + (int64_t)(stats->duration * oxcf->target_bandwidth / 10000000.0); + } + cpi->level_constraint.rc_config_updated = 1; + } + if (img != NULL) { res = validate_img(ctx, img); if (res == VPX_CODEC_OK) { -- 2.40.0