Add command-line option for fixed QP offsets.
Cherry-picked from experimental
Change-Id: I54db1ec34dc6667e6617d79212361032abd97f08
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index a4e5f70..f36f066 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -865,6 +865,28 @@
*/
int tile_heights[MAX_TILE_HEIGHTS];
+/*!\brief Number of fixed QP offsets
+ *
+ * This defines the number of elements in the fixed_qp_offsets array.
+ */
+#define FIXED_QP_OFFSET_COUNT 5
+
+ /*!\brief Array of fixed QP offsets
+ *
+ * This array specifies fixed QP offsets (range: 0 to 63) for frames at
+ * different levels of the pyramid. It is a comma-separated list of 5 values:
+ * - QP offset for keyframe
+ * - QP offset for ALTREF frame
+ * - QP offset for 1st level internal ARF
+ * - QP offset for 2nd level internal ARF
+ * - QP offset for 3rd level internal ARF
+ * Notes:
+ * - QP offset for leaf level frames is not explicitly specified. These frames
+ * use the worst quality allowed (--cq-level).
+ * - This option is only relevant for --end-usage=q.
+ */
+ int fixed_qp_offsets[FIXED_QP_OFFSET_COUNT];
+
/*!\brief Options defined per config file
*
*/
diff --git a/apps/aomenc.c b/apps/aomenc.c
index 6b76146..fb2ff6e 100644
--- a/apps/aomenc.c
+++ b/apps/aomenc.c
@@ -802,6 +802,14 @@
"operating points conforms to. "
"Bit value 0(defualt): Main Tier; 1: High Tier.");
+static const arg_def_t fixed_qp_offsets =
+ ARG_DEF(NULL, "fixed-qp-offsets", 1,
+ "Set fixed QP offsets for frames at different levels of the "
+ "pyramid. Comma-separated list of 5 offsets for keyframe, ALTREF, "
+ "and 3 levels of internal alt-refs. If this option is not "
+ "specified (default), offsets are adaptively chosen by the "
+ "encoder.");
+
static const arg_def_t *av1_args[] = { &cpu_used_av1,
&auto_altref,
&sharpness,
@@ -1583,6 +1591,14 @@
} else if (arg_match(&arg, &vmaf_model_path, argi)) {
config->vmaf_model_path = arg.val;
#endif
+ } else if (arg_match(&arg, &fixed_qp_offsets, argi)) {
+ const int fixed_qp_offset_count = arg_parse_list(
+ &arg, config->cfg.fixed_qp_offsets, FIXED_QP_OFFSET_COUNT);
+ if (fixed_qp_offset_count < FIXED_QP_OFFSET_COUNT) {
+ die("Option --fixed_qp_offsets requires %d comma-separated values, but "
+ "only %d values were provided.\n",
+ FIXED_QP_OFFSET_COUNT, fixed_qp_offset_count);
+ }
} else if (global->usage == AOM_USAGE_REALTIME &&
arg_match(&arg, &enable_restoration, argi)) {
if (arg_parse_uint(&arg) == 1) {
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 4c6f067..d3cba5e 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -485,6 +485,18 @@
ERROR("Source bit-depth 12 not supported in profile < 2");
}
+ if (cfg->rc_end_usage == AOM_Q) {
+ for (int i = 0; i < FIXED_QP_OFFSET_COUNT; ++i) {
+ RANGE_CHECK_HI(cfg, fixed_qp_offsets[i], 63);
+ }
+ } else {
+ for (int i = 0; i < FIXED_QP_OFFSET_COUNT; ++i) {
+ if (cfg->fixed_qp_offsets[i] >= 0) {
+ ERROR("--fixed_qp_offsets can only be used with --end-usage=q");
+ }
+ }
+ }
+
RANGE_CHECK(extra_cfg, color_primaries, AOM_CICP_CP_BT_709,
AOM_CICP_CP_EBU_3213); // Need to check range more precisely to
// check for reserved values?
@@ -987,6 +999,18 @@
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->use_fixed_qp_offsets = (oxcf->rc_mode == AOM_Q);
+ for (int i = 0; i < FIXED_QP_OFFSET_COUNT; ++i) {
+ if (cfg->fixed_qp_offsets[i] >= 0) {
+ oxcf->fixed_qp_offsets[i] =
+ av1_quantizer_to_qindex(cfg->fixed_qp_offsets[i]);
+ } else {
+ oxcf->fixed_qp_offsets[i] = -1;
+ oxcf->use_fixed_qp_offsets = 0;
+ }
+ }
+
oxcf->min_cr = extra_cfg->min_cr;
return AOM_CODEC_OK;
}
@@ -2770,20 +2794,21 @@
2000, // rc_two_pass_vbrmax_section
// keyframing settings (kf)
- 0, // fwd_kf_enabled
- AOM_KF_AUTO, // g_kfmode
- 0, // kf_min_dist
- 9999, // kf_max_dist
- 0, // sframe_dist
- 1, // sframe_mode
- 0, // large_scale_tile
- 0, // monochrome
- 0, // full_still_picture_hdr
- 0, // save_as_annexb
- 0, // tile_width_count
- 0, // tile_height_count
- { 0 }, // tile_widths
- { 0 }, // tile_heights
+ 0, // fwd_kf_enabled
+ AOM_KF_AUTO, // g_kfmode
+ 0, // kf_min_dist
+ 9999, // kf_max_dist
+ 0, // sframe_dist
+ 1, // sframe_mode
+ 0, // large_scale_tile
+ 0, // monochrome
+ 0, // full_still_picture_hdr
+ 0, // save_as_annexb
+ 0, // tile_width_count
+ 0, // tile_height_count
+ { 0 }, // tile_widths
+ { 0 }, // tile_heights
+ { -1, -1, -1, -1, -1 }, // fixed_qp_offsets
{ 0, 128, 128, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // cfg
} },
@@ -2839,20 +2864,21 @@
2000, // rc_two_pass_vbrmax_section
// keyframing settings (kf)
- 0, // fwd_kf_enabled
- AOM_KF_AUTO, // g_kfmode
- 0, // kf_min_dist
- 9999, // kf_max_dist
- 0, // sframe_dist
- 1, // sframe_mode
- 0, // large_scale_tile
- 0, // monochrome
- 0, // full_still_picture_hdr
- 0, // save_as_annexb
- 0, // tile_width_count
- 0, // tile_height_count
- { 0 }, // tile_widths
- { 0 }, // tile_heights
+ 0, // fwd_kf_enabled
+ AOM_KF_AUTO, // g_kfmode
+ 0, // kf_min_dist
+ 9999, // kf_max_dist
+ 0, // sframe_dist
+ 1, // sframe_mode
+ 0, // large_scale_tile
+ 0, // monochrome
+ 0, // full_still_picture_hdr
+ 0, // save_as_annexb
+ 0, // tile_width_count
+ 0, // tile_height_count
+ { 0 }, // tile_widths
+ { 0 }, // tile_heights
+ { -1, -1, -1, -1, -1 }, // fixed_qp_offsets
{ 0, 128, 128, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // cfg
} },
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 3513103..274c700 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -431,6 +431,13 @@
// Bit mask to specify which tier each of the 32 possible operating points
// conforms to.
unsigned int tier_mask;
+ // Derived from 'fixed_qp_offsets' by setting it to true iff all values in
+ // that array are valid (>=0).
+ int use_fixed_qp_offsets;
+ // List of QP offsets for: keyframe, ALTREF, and 3 levels of internal ARFs.
+ // If any of these values are negative, fixed offsets are disabled.
+ // Uses internal qindex range: 0 to 255.
+ int fixed_qp_offsets[FIXED_QP_OFFSET_COUNT];
// min_cr / 100 is the target minimum compression ratio for each frame.
unsigned int min_cr;
const cfg_options_t *encoder_cfg;
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index f43279f..7772c68 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -887,6 +887,36 @@
return active_cq_level;
}
+static int get_q_using_fixed_offsets(const AV1EncoderConfig *const oxcf,
+ const GF_GROUP *const gf_group,
+ int cq_level, int is_keyframe,
+ int frames_to_key) {
+ assert(oxcf->use_fixed_qp_offsets);
+ assert(oxcf->rc_mode == AOM_Q);
+ const int frame_index = gf_group->index;
+ const FRAME_UPDATE_TYPE update_type = gf_group->update_type[frame_index];
+
+ int offset_idx = -1;
+ if (is_keyframe) {
+ // Ignore offsets for image coding.
+ if (frames_to_key == 1) return cq_level;
+ offset_idx = 0;
+ } else if (update_type == ARF_UPDATE || update_type == GF_UPDATE) {
+ offset_idx = 1;
+ } else if (update_type == INTNL_ARF_UPDATE) {
+ offset_idx =
+ AOMMIN(gf_group->layer_depth[frame_index], FIXED_QP_OFFSET_COUNT - 1);
+ } else { // Leaf level / overlay frame.
+ assert(update_type == LF_UPDATE || update_type == OVERLAY_UPDATE ||
+ update_type == INTNL_OVERLAY_UPDATE);
+ return cq_level; // Directly Return worst quality allowed.
+ }
+ assert(offset_idx >= 0 && offset_idx < FIXED_QP_OFFSET_COUNT);
+ assert(oxcf->fixed_qp_offsets[offset_idx] >= 0);
+
+ return AOMMAX(cq_level - oxcf->fixed_qp_offsets[offset_idx], 0);
+}
+
static int rc_pick_q_and_bounds_one_pass_vbr(const AV1_COMP *cpi, int width,
int height, int *bottom_index,
int *top_index) {
@@ -896,6 +926,13 @@
const AV1EncoderConfig *const oxcf = &cpi->oxcf;
const int cq_level = get_active_cq_level(rc, oxcf, frame_is_intra_only(cm),
cm->superres_scale_denominator);
+
+ if (oxcf->use_fixed_qp_offsets) {
+ return get_q_using_fixed_offsets(oxcf, &cpi->gf_group, cq_level,
+ frame_is_intra_only(cm),
+ rc->frames_to_key);
+ }
+
int active_best_quality;
int active_worst_quality = calc_active_worst_quality_one_pass_vbr(cpi);
int q;
@@ -1341,6 +1378,12 @@
const GF_GROUP *gf_group = &cpi->gf_group;
const int cq_level = get_active_cq_level(rc, oxcf, frame_is_intra_only(cm),
cm->superres_scale_denominator);
+
+ if (oxcf->use_fixed_qp_offsets) {
+ return get_q_using_fixed_offsets(
+ oxcf, gf_group, cq_level, frame_is_intra_only(cm), rc->frames_to_key);
+ }
+
int active_best_quality = 0;
int active_worst_quality = rc->active_worst_quality;
int q;