Refactor q mode to compute quality along with structure Now constant quality mode will compute the q values for each frame in a gf group directly after the group structure is determined. No change in performance. Change-Id: Ieb7c04ba3626f3916572fa54fb58163cbdb173b6
diff --git a/av1/encoder/firstpass.h b/av1/encoder/firstpass.h index 7897015..f10ce3f 100644 --- a/av1/encoder/firstpass.h +++ b/av1/encoder/firstpass.h
@@ -125,6 +125,10 @@ int ref_frame_gop_idx[MAX_STATIC_GF_GROUP_LENGTH + 1][REF_FRAMES]; unsigned char pyramid_height; unsigned char pyramid_lvl_nodes[MAX_PYRAMID_LVL]; + // These are currently only populated for AOM_Q mode + unsigned char q_val[MAX_STATIC_GF_GROUP_LENGTH + 1]; + unsigned char q_upper[MAX_STATIC_GF_GROUP_LENGTH + 1]; + unsigned char q_lower[MAX_STATIC_GF_GROUP_LENGTH + 1]; int bit_allocation[MAX_STATIC_GF_GROUP_LENGTH + 1]; int size; } GF_GROUP;
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c index ac22b68..a0e3548 100644 --- a/av1/encoder/pass2_strategy.c +++ b/av1/encoder/pass2_strategy.c
@@ -24,6 +24,7 @@ #include "av1/encoder/encoder.h" #include "av1/encoder/firstpass.h" #include "av1/encoder/gop_structure.h" +#include "av1/encoder/ratectrl.h" // Calculate an active area of the image that discounts formatting // bars and partially discounts other 0 energy areas. @@ -729,6 +730,52 @@ #endif // GROUP_ADAPTIVE_MAXQ #define MIN_FWD_KF_INTERVAL 8 +static void assign_q_and_bounds_q_mode(AV1_COMP *cpi) { + AV1_COMMON *const cm = &cpi->common; + GF_GROUP *const gf_group = &cpi->twopass.gf_group; + const int width = cm->width; + const int height = cm->height; + RATE_CONTROL *const rc = &cpi->rc; + const int last_boosted_q = rc->last_boosted_qindex; + const int last_kf_q = rc->last_kf_qindex; + const int avg_frame_qindex = rc->avg_frame_qindex[INTER_FRAME]; + int bottom_index, top_index; + int q; + + for (int cur_index = 0; cur_index <= gf_group->size; ++cur_index) { + const FRAME_UPDATE_TYPE cur_update_type = gf_group->update_type[cur_index]; + int arf_q = -1; // Initialize to invalid value, for sanity check later. + + q = av1_get_q_and_bounds_constant_quality_two_pass( + cpi, width, height, &bottom_index, &top_index, &arf_q, cur_index); + if (cur_update_type == ARF_UPDATE) { + cpi->rc.arf_q = arf_q; + } + gf_group->q_val[cur_index] = q; + gf_group->q_upper[cur_index] = top_index; + gf_group->q_lower[cur_index] = bottom_index; + + // Update the rate control state necessary to accuratly compute q for + // the next frames. + // This is used to help set quality in forced key frames to reduce popping + if ((q < rc->last_boosted_qindex) || (cur_update_type == KF_UPDATE) || + (!rc->constrained_gf_group && (cur_update_type == ARF_UPDATE || + cur_update_type == INTNL_ARF_UPDATE || + cur_update_type == GF_UPDATE))) { + rc->last_boosted_qindex = q; + } + // TODO(sarahparker) Investigate whether or not this is actually needed + if (cur_update_type == LF_UPDATE) + rc->avg_frame_qindex[INTER_FRAME] = + ROUND_POWER_OF_TWO(3 * rc->avg_frame_qindex[INTER_FRAME] + q, 2); + if (cur_update_type == KF_UPDATE) rc->last_kf_qindex = q; + } + // Reset all of the modified state to the original values. + rc->last_boosted_qindex = last_boosted_q; + rc->last_kf_qindex = last_kf_q; + rc->avg_frame_qindex[INTER_FRAME] = avg_frame_qindex; +} + // Analyse and define a gf/arf group. static void define_gf_group(AV1_COMP *cpi, FIRSTPASS_STATS *this_frame, const EncodeFrameParams *const frame_params) { @@ -1071,6 +1118,8 @@ // Set up the structure of this Group-Of-Pictures (same as GF_GROUP) av1_gop_setup_structure(cpi, frame_params); + if (cpi->oxcf.rc_mode == AOM_Q) assign_q_and_bounds_q_mode(cpi); + // Allocate bits to each of the frames in the GF group. allocate_gf_group_bits(cpi, gf_group_bits, gf_group_error_left, gf_arf_bits, frame_params);
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c index 9e527d4..e075dc3 100644 --- a/av1/encoder/ratectrl.c +++ b/av1/encoder/ratectrl.c
@@ -1029,7 +1029,7 @@ static void get_intra_q_and_bounds_two_pass(const AV1_COMP *cpi, int width, int height, int *active_best, int *active_worst, int *arf_q, - int cq_level) { + int cq_level, int is_fwd_kf) { const AV1_COMMON *const cm = &cpi->common; const RATE_CONTROL *const rc = &cpi->rc; const AV1EncoderConfig *const oxcf = &cpi->oxcf; @@ -1043,7 +1043,7 @@ // as q. active_best_quality = cq_level; active_worst_quality = cq_level; - } else if (cm->current_frame.frame_type == KEY_FRAME && cm->show_frame == 0) { + } else if (is_fwd_kf) { // Handle the special case for forward reference key frames. // Increase the boost because this keyframe is used as a forward and // backward reference. @@ -1116,12 +1116,12 @@ 0); } } - *active_best = active_best_quality; *active_worst = active_worst_quality; } -// Does some final adjustments to the q value and bounds. +// Does some final adjustments to the q value and bounds. This does not apply to +// AOM_Q mode unless it is an INTRA_ONLY_FRAME. static void postprocess_q_and_bounds(const AV1_COMP *cpi, int width, int height, int *active_worst, int *active_best, int *q_out, int is_intrl_arf_boost) { @@ -1132,6 +1132,8 @@ int active_best_quality = *active_best; int active_worst_quality = *active_worst; int q; + assert(IMPLIES(cpi->oxcf.rc_mode == AOM_Q, + cm->current_frame.frame_type == INTRA_ONLY_FRAME)); // Extension to max or min Q if undershoot or overshoot is outside // the permitted range. @@ -1204,6 +1206,106 @@ *q_out = q; } +// Get the value for q and its upper and lower bounds for consant quality mode. +// This is called for every frame in a gf group right after the gf structure is +// determined. +int av1_get_q_and_bounds_constant_quality_two_pass(const AV1_COMP *cpi, + int width, int height, + int *bottom_index, + int *top_index, int *arf_q, + int gf_index) { + const AV1_COMMON *const cm = &cpi->common; + const RATE_CONTROL *const rc = &cpi->rc; + const GF_GROUP *gf_group = &cpi->twopass.gf_group; + 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); + int active_best_quality = 0; + int active_worst_quality = cpi->twopass.active_worst_quality; + int q; + const int bit_depth = cm->seq_params.bit_depth; + + const int is_intrl_arf_boost = + gf_group->update_type[gf_index] == INTNL_ARF_UPDATE; + + const int is_boosted_frame = gf_group->update_type[gf_index] == GF_UPDATE || + gf_group->update_type[gf_index] == ARF_UPDATE || + is_intrl_arf_boost; + + int is_fwd_kf = 0; + if (gf_group->update_type[gf_index] == ARF_UPDATE) { + assert(is_altref_enabled(cpi)); + const int arf_src_index = gf_group->arf_src_offset[gf_index]; + is_fwd_kf = arf_src_index == (rc->frames_to_key - 1); + } + + if (gf_group->update_type[gf_index] == KF_UPDATE || is_fwd_kf) { + get_intra_q_and_bounds_two_pass(cpi, width, height, &active_best_quality, + &active_worst_quality, arf_q, cq_level, + is_fwd_kf); + } else if (is_boosted_frame) { + // Use the lower of active_worst_quality and recent + // average Q as basis for GF/ARF best Q limit unless last frame was + // a key frame. + if (gf_group->update_type[0] != KF_UPDATE && + rc->avg_frame_qindex[INTER_FRAME] < active_worst_quality) { + q = rc->avg_frame_qindex[INTER_FRAME]; + } else { + q = active_worst_quality; + } + if (gf_group->update_type[gf_index] != ARF_UPDATE && !is_intrl_arf_boost) { + active_best_quality = cq_level; + } else { + if (gf_group->update_type[gf_index] == ARF_UPDATE) { + active_best_quality = get_gf_active_quality(rc, q, bit_depth); + const int min_boost = get_gf_high_motion_quality(q, bit_depth); + const int boost = min_boost - active_best_quality; + + active_best_quality = min_boost - (int)(boost * rc->arf_boost_factor); + *arf_q = active_best_quality; + } else { + assert(rc->arf_q >= 0); // Ensure it is set to a valid value. + assert(is_intrl_arf_boost); + active_best_quality = rc->arf_q; + int this_height = gf_group->pyramid_level[gf_index]; + while (this_height < gf_group->pyramid_height) { + active_best_quality = (active_best_quality + cq_level + 1) / 2; + ++this_height; + } + } + } + } else { + active_best_quality = cq_level; + } + + aom_clear_system_state(); + // Static forced key frames Q restrictions dealt with elsewhere. + if ((gf_group->update_type[gf_index] != KF_UPDATE) || + !rc->this_key_frame_forced || + (cpi->twopass.last_kfgroup_zeromotion_pct < STATIC_MOTION_THRESH)) { + const int qdelta = av1_frame_type_qdelta(cpi, active_worst_quality); + active_worst_quality = + AOMMAX(active_worst_quality + qdelta, active_best_quality); + } + + active_best_quality = + clamp(active_best_quality, rc->best_quality, rc->worst_quality); + active_worst_quality = + clamp(active_worst_quality, active_best_quality, rc->worst_quality); + + q = active_best_quality; + clamp(q, active_best_quality, active_worst_quality); + + *top_index = active_worst_quality; + *bottom_index = active_best_quality; + + assert(*top_index <= rc->worst_quality && *top_index >= rc->best_quality); + assert(*bottom_index <= rc->worst_quality && + *bottom_index >= rc->best_quality); + assert(q <= rc->worst_quality && q >= rc->best_quality); + return q; +} + static int rc_pick_q_and_bounds_two_pass(const AV1_COMP *cpi, int width, int height, int *bottom_index, int *top_index, int *arf_q) { @@ -1213,7 +1315,7 @@ const GF_GROUP *gf_group = &cpi->twopass.gf_group; const int cq_level = get_active_cq_level(rc, oxcf, frame_is_intra_only(cm), cm->superres_scale_denominator); - int active_best_quality; + int active_best_quality = 0; int active_worst_quality = cpi->twopass.active_worst_quality; int q; int *inter_minq; @@ -1222,9 +1324,38 @@ const int is_intrl_arf_boost = gf_group->update_type[gf_group->index] == INTNL_ARF_UPDATE; + + // AOM_Q mode determines quality when the gf structure is created. The only + // case in which the q values from this process are unreliable is for an + // INTRA_ONLY frame or superres scaled frame. + if (oxcf->rc_mode == AOM_Q && + cm->current_frame.frame_type != INTRA_ONLY_FRAME) { + // Modify active_best_quality and q for downscaled normal frames. + if (av1_frame_scaled(cm) && !frame_is_kf_gf_arf(cpi)) { + const int qdelta = + av1_compute_qdelta_by_rate(rc, cm->current_frame.frame_type, + active_best_quality, 2.0, bit_depth); + active_best_quality = + AOMMAX(active_best_quality + qdelta, rc->best_quality); + active_best_quality = + clamp(active_best_quality, rc->best_quality, rc->worst_quality); + + *top_index = gf_group->q_upper[gf_group->index]; + *bottom_index = active_best_quality; + // The q value is set to active_best_quality in AOM_Q mode + return active_best_quality; + } + *top_index = gf_group->q_upper[gf_group->index]; + *bottom_index = gf_group->q_lower[gf_group->index]; + return gf_group->q_val[gf_group->index]; + } + if (frame_is_intra_only(cm)) { + const int is_fwd_kf = + cm->current_frame.frame_type == KEY_FRAME && cm->show_frame == 0; get_intra_q_and_bounds_two_pass(cpi, width, height, &active_best_quality, - &active_worst_quality, arf_q, cq_level); + &active_worst_quality, arf_q, cq_level, + is_fwd_kf); } else if (!rc->is_src_frame_alt_ref && (cpi->refresh_golden_frame || is_intrl_arf_boost || cpi->refresh_alt_ref_frame)) { @@ -1261,28 +1392,6 @@ ++this_height; } } - } else if (oxcf->rc_mode == AOM_Q) { - if (!cpi->refresh_alt_ref_frame && !is_intrl_arf_boost) { - active_best_quality = cq_level; - } else { - if (gf_group->update_type[gf_group->index] == ARF_UPDATE) { - active_best_quality = get_gf_active_quality(rc, q, bit_depth); - const int min_boost = get_gf_high_motion_quality(q, bit_depth); - const int boost = min_boost - active_best_quality; - - active_best_quality = min_boost - (int)(boost * rc->arf_boost_factor); - *arf_q = active_best_quality; - } else { - assert(rc->arf_q >= 0); // Ensure it is set to a valid value. - assert(is_intrl_arf_boost); - active_best_quality = rc->arf_q; - int this_height = gf_group_pyramid_level(cpi); - while (this_height < gf_group->pyramid_height) { - active_best_quality = (active_best_quality + cq_level + 1) / 2; - ++this_height; - } - } - } } else { active_best_quality = get_gf_active_quality(rc, q, bit_depth); const int min_boost = get_gf_high_motion_quality(q, bit_depth); @@ -1299,16 +1408,12 @@ } } } else { - if (oxcf->rc_mode == AOM_Q) { - active_best_quality = cq_level; - } else { - active_best_quality = inter_minq[active_worst_quality]; + active_best_quality = inter_minq[active_worst_quality]; - // For the constrained quality mode we don't want - // q to fall below the cq level. - if ((oxcf->rc_mode == AOM_CQ) && (active_best_quality < cq_level)) { - active_best_quality = cq_level; - } + // For the constrained quality mode we don't want + // q to fall below the cq level. + if ((oxcf->rc_mode == AOM_CQ) && (active_best_quality < cq_level)) { + active_best_quality = cq_level; } }
diff --git a/av1/encoder/ratectrl.h b/av1/encoder/ratectrl.h index 1cd5994..39c13cb 100644 --- a/av1/encoder/ratectrl.h +++ b/av1/encoder/ratectrl.h
@@ -263,6 +263,12 @@ int av1_resize_one_pass_cbr(struct AV1_COMP *cpi); +int av1_get_q_and_bounds_constant_quality_two_pass(const struct AV1_COMP *cpi, + int width, int height, + int *bottom_index, + int *top_index, int *arf_q, + int gf_index); + #ifdef __cplusplus } // extern "C" #endif