ref: 337ad83e5893e8e6e0bbaf42d1ed6a61399f9ddf
parent: 2d12a52ff09ff47cd69659b6d2f862af1913c779
author: hui su <[email protected]>
date: Fri Dec 2 05:11:33 EST 2016
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
--- a/test/level_test.cc
+++ b/test/level_test.cc
@@ -66,6 +66,36 @@
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 @@
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 ||
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -108,7 +108,7 @@
}
/* 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 @@
};
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
#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 @@
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 @@
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,
@@ -4657,6 +4783,10 @@
} else if (oxcf->pass == 1) {
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;
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -237,7 +237,7 @@
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 @@
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 @@
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 @@
// Previous Partition Info
BLOCK_SIZE *prev_partition;
int8_t *prev_segment_id;
+
+ LevelConstraint level_constraint;
} VP9_COMP;
void vp9_initialize_enc(void);
@@ -764,6 +788,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);
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -390,6 +390,50 @@
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 @@
} 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);
@@ -1002,6 +1048,28 @@
size_t data_sz;
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);