Add encoder API for minimum compression ratio
If the compression ratio is smaller than the specified limit, encoder
would re-encode the frame with higher QP values until the minimum
compression ratio is met, or QP is equal to "worst_quality".
BUG=aomedia:2391
BUG=b/124327886
Change-Id: Ibef279cc669e459e613f8a0c8e77f5a0112c5d03
diff --git a/aom/aomcx.h b/aom/aomcx.h
index 7593354..77d68b0 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -1100,6 +1100,13 @@
* Bit value 0: Main Tier; 1: High Tier.
*/
AV1E_SET_TIER_MASK,
+
+ /*!\brief Control to set minimum compression ratio.
+ * Take integer values. If non-zero, encoder will try to keep the compression
+ * ratio of each frame to be higher than the given value divided by 100.
+ * E.g. 850 means minimum compression ratio of 8.5.
+ */
+ AV1E_SET_MIN_CR
};
/*!\brief aom 1-D scaling mode
@@ -1521,6 +1528,9 @@
AOM_CTRL_USE_TYPE(AV1E_SET_TIER_MASK, unsigned int)
#define AOM_CTRL_AV1E_SET_TIER_MASK
+AOM_CTRL_USE_TYPE(AV1E_SET_MIN_CR, unsigned int)
+#define AOM_CTRL_AV1E_SET_MIN_CR
+
/*!\endcond */
/*! @} - end defgroup aom_encoder */
#ifdef __cplusplus
diff --git a/apps/aomenc.c b/apps/aomenc.c
index 0ce8b79..81321f3 100644
--- a/apps/aomenc.c
+++ b/apps/aomenc.c
@@ -668,6 +668,11 @@
"xy: Target level index for the OP. "
"E.g. \"0\" means target level index 0 for the 0th OP; "
"\"1021\" means target level index 21 for the 10th OP.");
+static const arg_def_t set_min_cr =
+ ARG_DEF(NULL, "min-cr", 1,
+ "Set minimum compression ratio. Take integer values. Default is 0. "
+ "If non-zero, encoder will try to keep the compression ratio of "
+ "each frame to be higher than the given value divided by 100.");
static const struct arg_enum_list color_primaries_enum[] = {
{ "bt709", AOM_CICP_CP_BT_709 },
@@ -871,6 +876,7 @@
&enable_ref_frame_mvs,
&target_seq_level_idx,
&set_tier_mask,
+ &set_min_cr,
&bitdeptharg,
&inbitdeptharg,
&input_chroma_subsampling_x,
@@ -971,6 +977,7 @@
AV1E_SET_ENABLE_REF_FRAME_MVS,
AV1E_SET_TARGET_SEQ_LEVEL_IDX,
AV1E_SET_TIER_MASK,
+ AV1E_SET_MIN_CR,
0 };
#endif // CONFIG_AV1_ENCODER
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 74b7e39..80ff7d8 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -141,6 +141,8 @@
// Bit mask to specify which tier each of the 32 possible operating points
// conforms to.
unsigned int tier_mask;
+ // min_cr / 100 is the target minimum compression ratio for each frame.
+ unsigned int min_cr;
COST_UPDATE_TYPE coeff_cost_upd_freq;
COST_UPDATE_TYPE mode_cost_upd_freq;
};
@@ -255,6 +257,7 @@
31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
}, // target_seq_level_idx
0, // tier_mask
+ 0, // min_cr
COST_UPD_SB, // coeff_cost_upd_freq
COST_UPD_SB, // mode_cost_upd_freq
};
@@ -885,6 +888,7 @@
memcpy(oxcf->target_seq_level_idx, extra_cfg->target_seq_level_idx,
sizeof(oxcf->target_seq_level_idx));
oxcf->tier_mask = extra_cfg->tier_mask;
+ oxcf->min_cr = extra_cfg->min_cr;
return AOM_CODEC_OK;
}
@@ -1616,6 +1620,13 @@
return update_extra_cfg(ctx, &extra_cfg);
}
+static aom_codec_err_t ctrl_set_min_cr(aom_codec_alg_priv_t *ctx,
+ va_list args) {
+ struct av1_extracfg extra_cfg = ctx->extra_cfg;
+ extra_cfg.min_cr = CAST(AV1E_SET_MIN_CR, args);
+ return update_extra_cfg(ctx, &extra_cfg);
+}
+
static aom_codec_err_t encoder_init(aom_codec_ctx_t *ctx,
aom_codec_priv_enc_mr_cfg_t *data) {
aom_codec_err_t res = AOM_CODEC_OK;
@@ -2321,6 +2332,7 @@
{ AV1E_ENABLE_MOTION_VECTOR_UNIT_TEST, ctrl_enable_motion_vector_unit_test },
{ AV1E_SET_TARGET_SEQ_LEVEL_IDX, ctrl_set_target_seq_level_idx },
{ AV1E_SET_TIER_MASK, ctrl_set_tier_mask },
+ { AV1E_SET_MIN_CR, ctrl_set_min_cr },
// Getters
{ AOME_GET_LAST_QUANTIZER, ctrl_get_quantizer },
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 8bc0124..4ec05ee 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -878,6 +878,22 @@
av1_rc_update_framerate(cpi, cpi->common.width, cpi->common.height);
}
+double av1_get_compression_ratio(const AV1_COMMON *const cm,
+ size_t encoded_frame_size) {
+ const int upscaled_width = cm->superres_upscaled_width;
+ const int height = cm->height;
+ const int luma_pic_size = upscaled_width * height;
+ const SequenceHeader *const seq_params = &cm->seq_params;
+ const BITSTREAM_PROFILE profile = seq_params->profile;
+ const int pic_size_profile_factor =
+ profile == PROFILE_0 ? 15 : (profile == PROFILE_1 ? 30 : 36);
+ encoded_frame_size =
+ (encoded_frame_size > 129 ? encoded_frame_size - 128 : 1);
+ const size_t uncompressed_frame_size =
+ (luma_pic_size * pic_size_profile_factor) >> 3;
+ return uncompressed_frame_size / (double)encoded_frame_size;
+}
+
static void set_tile_info(AV1_COMP *cpi) {
AV1_COMMON *const cm = &cpi->common;
int i, start_sb;
@@ -4225,16 +4241,36 @@
// its bitstream. This function works out whether we under- or over-shot
// our bitrate target and adjusts q as appropriate. Also decides whether
// or not we should do another recode loop, indicated by *loop
-static void recode_loop_update_q(AV1_COMP *const cpi, int *const loop,
- int *const q, int *const q_low,
- int *const q_high, const int top_index,
- const int bottom_index,
- int *const undershoot_seen,
- int *const overshoot_seen,
- const int loop_at_this_size) {
+static void recode_loop_update_q(
+ AV1_COMP *const cpi, int *const loop, int *const q, int *const q_low,
+ int *const q_high, const int top_index, const int bottom_index,
+ int *const undershoot_seen, int *const overshoot_seen,
+ int *const low_cr_seen, const int loop_at_this_size) {
AV1_COMMON *const cm = &cpi->common;
RATE_CONTROL *const rc = &cpi->rc;
+ const int min_cr = cpi->oxcf.min_cr;
+ if (min_cr > 0) {
+ aom_clear_system_state();
+ const double compression_ratio =
+ av1_get_compression_ratio(cm, rc->projected_frame_size >> 3);
+ const double target_cr = min_cr / 100.0;
+ if (compression_ratio < target_cr) {
+ *low_cr_seen = 1;
+ if (*q < rc->worst_quality) {
+ const double cr_ratio = target_cr / compression_ratio;
+ const int projected_q = AOMMAX(*q + 1, (int)(*q * cr_ratio * cr_ratio));
+ *q = AOMMIN(AOMMIN(projected_q, *q + 32), rc->worst_quality);
+ *q_low = AOMMAX(*q, *q_low);
+ *q_high = AOMMAX(*q, *q_high);
+ *loop = 1;
+ }
+ }
+ if (*low_cr_seen) return;
+ }
+
+ if (cpi->oxcf.rc_mode == AOM_Q) return;
+
int frame_over_shoot_limit = 0, frame_under_shoot_limit = 0;
av1_rc_compute_frame_size_bounds(cpi, rc->this_frame_target,
&frame_under_shoot_limit,
@@ -4391,6 +4427,7 @@
int loop = 0;
int overshoot_seen = 0;
int undershoot_seen = 0;
+ int low_cr_seen = 0;
#if CONFIG_COLLECT_COMPONENT_TIMING
printf("\n Encoding a frame:");
@@ -4486,11 +4523,11 @@
restore_coding_context(cpi);
}
- if (allow_recode && cpi->oxcf.rc_mode != AOM_Q) {
+ if (allow_recode) {
// Update q and decide whether to do a recode loop
recode_loop_update_q(cpi, &loop, &q, &q_low, &q_high, top_index,
bottom_index, &undershoot_seen, &overshoot_seen,
- loop_at_this_size);
+ &low_cr_seen, loop_at_this_size);
}
// Special case for overlay frame.
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 2b6ff18..91739ad 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -409,6 +409,8 @@
// Bit mask to specify which tier each of the 32 possible operating points
// conforms to.
unsigned int tier_mask;
+ // min_cr / 100 is the target minimum compression ratio for each frame.
+ unsigned int min_cr;
} AV1EncoderConfig;
static INLINE int is_lossless_requested(const AV1EncoderConfig *cfg) {
@@ -1238,6 +1240,10 @@
return cpi->sf.mv.subpel_search_method != SUBPEL_TREE ? cost_list : NULL;
}
+// Compression ratio of current frame.
+double av1_get_compression_ratio(const AV1_COMMON *const cm,
+ size_t encoded_frame_size);
+
void av1_new_framerate(AV1_COMP *cpi, double framerate);
void av1_setup_frame_size(AV1_COMP *cpi);
diff --git a/av1/encoder/level.c b/av1/encoder/level.c
index 13e14e2..b0bc36e 100644
--- a/av1/encoder/level.c
+++ b/av1/encoder/level.c
@@ -618,23 +618,16 @@
&min_cropped_tile_width, &min_cropped_tile_height,
&tile_width_is_valid);
- const SequenceHeader *const seq_params = &cm->seq_params;
- const BITSTREAM_PROFILE profile = seq_params->profile;
- const int pic_size_profile_factor =
- profile == PROFILE_0 ? 15 : (profile == PROFILE_1 ? 30 : 36);
- const size_t frame_compressed_size = (size > 129 ? size - 128 : 1);
- const size_t frame_uncompressed_size =
- (luma_pic_size * pic_size_profile_factor) >> 3;
-
aom_clear_system_state();
- const double compression_ratio =
- frame_uncompressed_size / (double)frame_compressed_size;
+ const double compression_ratio = av1_get_compression_ratio(cm, size);
const double total_time_encoded =
(cpi->last_end_time_stamp_seen - cpi->first_time_stamp_ever) /
(double)TICKS_PER_SEC;
const int temporal_layer_id = cm->temporal_layer_id;
const int spatial_layer_id = cm->spatial_layer_id;
+ const SequenceHeader *const seq_params = &cm->seq_params;
+ const BITSTREAM_PROFILE profile = seq_params->profile;
const int is_still_picture = seq_params->still_picture;
// update level_stats
// TODO(kyslov@) fix the implementation according to buffer model