Commit 337ad83e authored by hui su's avatar hui su

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
parent 2d12a52f
......@@ -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 ||
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment