Add FindBetterGopCut()
Add a sub-function FindBetterGopCut() to PartitionGopIntervals.
The FindBetterGopCut is still giant, but at least the input/output
is clear.
FindBetterGopCut's goal is to modify cur_last or in the other words
find a better gop cut.
Bug: b/221916304
Change-Id: I2e60c61fcae315f4b5ee5eff819944f5d6811b66
diff --git a/av1/ratectrl_qmode.cc b/av1/ratectrl_qmode.cc
index 1bb1393..666a5e3 100644
--- a/av1/ratectrl_qmode.cc
+++ b/av1/ratectrl_qmode.cc
@@ -424,6 +424,164 @@
#define MIN_SHRINK_LEN 6
+// This function takes in a suggesting gop interval from cur_start to cur_last,
+// analyzes firstpass stats and region stats and then return a better gop cut
+// location.
+// TODO(b/231517281): Simplify the indices once we have an unit test.
+// We are using four indices here, order_index, cur_start, cur_last, and
+// frames_since_key. Ideally, only three indices are needed.
+// 1) start_index = order_index + cur_start
+// 2) end_index = order_index + cur_end
+// 3) key_index
+int FindBetterGopCut(const std::vector<FIRSTPASS_STATS> &stats_list,
+ const std::vector<REGIONS> ®ions_list,
+ int min_gop_show_frame_count, int max_gop_show_frame_count,
+ int order_index, int cur_start, int cur_last,
+ int frames_since_key) {
+ // only try shrinking if interval smaller than active_max_gf_interval
+ if (cur_last - cur_start > max_gop_show_frame_count ||
+ cur_start >= cur_last) {
+ return cur_last;
+ }
+ int num_regions = static_cast<int>(regions_list.size());
+ int num_stats = static_cast<int>(stats_list.size());
+ const int min_shrink_int = std::max(MIN_SHRINK_LEN, min_gop_show_frame_count);
+
+ // find the region indices of where the first and last frame belong.
+ int k_start = FindRegionIndex(regions_list, cur_start + frames_since_key);
+ int k_last = FindRegionIndex(regions_list, cur_last + frames_since_key);
+ if (cur_start + frames_since_key == 0) k_start = 0;
+
+ int scenecut_idx = -1;
+ // See if we have a scenecut in between
+ for (int r = k_start + 1; r <= k_last; r++) {
+ if (regions_list[r].type == SCENECUT_REGION &&
+ regions_list[r].last - frames_since_key - cur_start >
+ min_gop_show_frame_count) {
+ scenecut_idx = r;
+ break;
+ }
+ }
+
+ // if the found scenecut is very close to the end, ignore it.
+ if (regions_list[num_regions - 1].last - regions_list[scenecut_idx].last <
+ 4) {
+ scenecut_idx = -1;
+ }
+
+ if (scenecut_idx != -1) {
+ // If we have a scenecut, then stop at it.
+ // TODO(bohanli): add logic here to stop before the scenecut and for
+ // the next gop start from the scenecut with GF
+ int is_minor_sc =
+ (regions_list[scenecut_idx].avg_cor_coeff *
+ (1 - stats_list[order_index + regions_list[scenecut_idx].start -
+ frames_since_key]
+ .noise_var /
+ regions_list[scenecut_idx].avg_intra_err) >
+ 0.6);
+ cur_last =
+ regions_list[scenecut_idx].last - frames_since_key - !is_minor_sc;
+ } else {
+ int is_last_analysed =
+ (k_last == num_regions - 1) &&
+ (cur_last + frames_since_key == regions_list[k_last].last);
+ int not_enough_regions =
+ k_last - k_start <= 1 + (regions_list[k_start].type == SCENECUT_REGION);
+ // if we are very close to the end, then do not shrink since it may
+ // introduce intervals that are too short
+ if (!(is_last_analysed && not_enough_regions)) {
+ const double arf_length_factor = 0.1;
+ double best_score = 0;
+ int best_j = -1;
+ const int first_frame = regions_list[0].start - frames_since_key;
+ const int last_frame =
+ regions_list[num_regions - 1].last - frames_since_key;
+ // score of how much the arf helps the whole GOP
+ double base_score = 0.0;
+ // Accumulate base_score in
+ for (int j = cur_start + 1; j < cur_start + min_shrink_int; j++) {
+ if (order_index + j >= num_stats) break;
+ base_score = (base_score + 1.0) * stats_list[order_index + j].cor_coeff;
+ }
+ int met_blending = 0; // Whether we have met blending areas before
+ int last_blending = 0; // Whether the previous frame if blending
+ for (int j = cur_start + min_shrink_int; j <= cur_last; j++) {
+ if (order_index + j >= num_stats) break;
+ base_score = (base_score + 1.0) * stats_list[order_index + j].cor_coeff;
+ int this_reg = FindRegionIndex(regions_list, j + frames_since_key);
+ if (this_reg < 0) continue;
+ // A GOP should include at most 1 blending region.
+ if (regions_list[this_reg].type == BLENDING_REGION) {
+ last_blending = 1;
+ if (met_blending) {
+ break;
+ } else {
+ base_score = 0;
+ continue;
+ }
+ } else {
+ if (last_blending) met_blending = 1;
+ last_blending = 0;
+ }
+
+ // Add the factor of how good the neighborhood is for this
+ // candidate arf.
+ double this_score = arf_length_factor * base_score;
+ double temp_accu_coeff = 1.0;
+ // following frames
+ int count_f = 0;
+ for (int n = j + 1; n <= j + 3 && n <= last_frame; n++) {
+ if (order_index + n >= num_stats) break;
+ temp_accu_coeff *= stats_list[order_index + n].cor_coeff;
+ this_score +=
+ temp_accu_coeff *
+ (1 - stats_list[order_index + n].noise_var /
+ AOMMAX(regions_list[this_reg].avg_intra_err, 0.001));
+ count_f++;
+ }
+ // preceding frames
+ temp_accu_coeff = 1.0;
+ for (int n = j; n > j - 3 * 2 + count_f && n > first_frame; n--) {
+ if (order_index + n < num_stats) break;
+ temp_accu_coeff *= stats_list[order_index + n].cor_coeff;
+ this_score +=
+ temp_accu_coeff *
+ (1 - stats_list[order_index + n].noise_var /
+ AOMMAX(regions_list[this_reg].avg_intra_err, 0.001));
+ }
+
+ if (this_score > best_score) {
+ best_score = this_score;
+ best_j = j;
+ }
+ }
+
+ // For blending areas, move one more frame in case we missed the
+ // first blending frame.
+ int best_reg = FindRegionIndex(regions_list, best_j + frames_since_key);
+ if (best_reg < num_regions - 1 && best_reg > 0) {
+ if (regions_list[best_reg - 1].type == BLENDING_REGION &&
+ regions_list[best_reg + 1].type == BLENDING_REGION) {
+ if (best_j + frames_since_key == regions_list[best_reg].start &&
+ best_j + frames_since_key < regions_list[best_reg].last) {
+ best_j += 1;
+ } else if (best_j + frames_since_key == regions_list[best_reg].last &&
+ best_j + frames_since_key > regions_list[best_reg].start) {
+ best_j -= 1;
+ }
+ }
+ }
+
+ if (cur_last - best_j < 2) best_j = cur_last;
+ if (best_j > 0 && best_score > 0.1) cur_last = best_j;
+ // if cannot find anything, just cut at the original place.
+ }
+ }
+
+ return cur_last;
+}
+
/*!\brief Determine the length of future GF groups.
*
* \ingroup gf_group_algo
@@ -443,11 +601,9 @@
const std::vector<FIRSTPASS_STATS> &stats_list,
const std::vector<REGIONS> ®ions_list, int order_index,
int frames_since_key, int frames_to_key) {
- const int min_shrink_int =
- std::max(MIN_SHRINK_LEN, rc_param.min_gop_show_frame_count);
int i = (frames_since_key == 0) ? 1 : 0;
// If cpi->gf_state.arf_gf_boost_lst is 0, we are starting with a KF or GF.
- int cur_start = 0, cur_last;
+ int cur_start = 0;
// Each element is the last frame of the previous GOP. If there are n GOPs,
// you need n + 1 cuts to find the durations. So cut_pos starts out with -1,
// which is the last frame of the previous GOP.
@@ -455,7 +611,6 @@
int cut_here = 0;
GF_GROUP_STATS gf_stats;
InitGFStats(&gf_stats);
- int num_regions = static_cast<int>(regions_list.size());
int num_stats = static_cast<int>(stats_list.size());
int stats_in_loop_index = order_index;
while (i + order_index < num_stats) {
@@ -475,150 +630,12 @@
++i;
continue;
}
- cur_last = i - 1; // the current last frame in the gf group
- int ori_last = cur_last;
- int scenecut_idx = -1;
+ int original_last = i - 1; // the current last frame in the gf group
+ int cur_last = FindBetterGopCut(
+ stats_list, regions_list, rc_param.min_gop_show_frame_count,
+ rc_param.max_gop_show_frame_count, order_index, cur_start,
+ original_last, frames_since_key);
// only try shrinking if interval smaller than active_max_gf_interval
- if (cur_last - cur_start <= rc_param.max_gop_show_frame_count &&
- cur_last > cur_start) {
- // find the region indices of where the first and last frame belong.
- int k_start = FindRegionIndex(regions_list, cur_start + frames_since_key);
- int k_last = FindRegionIndex(regions_list, cur_last + frames_since_key);
- if (cur_start + frames_since_key == 0) k_start = 0;
-
- // See if we have a scenecut in between
- for (int r = k_start + 1; r <= k_last; r++) {
- if (regions_list[r].type == SCENECUT_REGION &&
- regions_list[r].last - frames_since_key - cur_start >
- rc_param.min_gop_show_frame_count) {
- scenecut_idx = r;
- break;
- }
- }
-
- // if the found scenecut is very close to the end, ignore it.
- if (regions_list[num_regions - 1].last - regions_list[scenecut_idx].last <
- 4) {
- scenecut_idx = -1;
- }
-
- if (scenecut_idx != -1) {
- // If we have a scenecut, then stop at it.
- // TODO(bohanli): add logic here to stop before the scenecut and for
- // the next gop start from the scenecut with GF
- int is_minor_sc =
- (regions_list[scenecut_idx].avg_cor_coeff *
- (1 -
- stats_list[order_index + regions_list[scenecut_idx].start -
- frames_since_key]
- .noise_var /
- regions_list[scenecut_idx].avg_intra_err) >
- 0.6);
- cur_last =
- regions_list[scenecut_idx].last - frames_since_key - !is_minor_sc;
- } else {
- int is_last_analysed =
- (k_last == num_regions - 1) &&
- (cur_last + frames_since_key == regions_list[k_last].last);
- int not_enough_regions =
- k_last - k_start <=
- 1 + (regions_list[k_start].type == SCENECUT_REGION);
- // if we are very close to the end, then do not shrink since it may
- // introduce intervals that are too short
- if (!(is_last_analysed && not_enough_regions)) {
- const double arf_length_factor = 0.1;
- double best_score = 0;
- int best_j = -1;
- const int first_frame = regions_list[0].start - frames_since_key;
- const int last_frame =
- regions_list[num_regions - 1].last - frames_since_key;
- // score of how much the arf helps the whole GOP
- double base_score = 0.0;
- // Accumulate base_score in
- for (int j = cur_start + 1; j < cur_start + min_shrink_int; j++) {
- if (order_index + j >= num_stats) break;
- base_score =
- (base_score + 1.0) * stats_list[order_index + j].cor_coeff;
- }
- int met_blending = 0; // Whether we have met blending areas before
- int last_blending = 0; // Whether the previous frame if blending
- for (int j = cur_start + min_shrink_int; j <= cur_last; j++) {
- if (order_index + j >= num_stats) break;
- base_score =
- (base_score + 1.0) * stats_list[order_index + j].cor_coeff;
- int this_reg = FindRegionIndex(regions_list, j + frames_since_key);
- if (this_reg < 0) continue;
- // A GOP should include at most 1 blending region.
- if (regions_list[this_reg].type == BLENDING_REGION) {
- last_blending = 1;
- if (met_blending) {
- break;
- } else {
- base_score = 0;
- continue;
- }
- } else {
- if (last_blending) met_blending = 1;
- last_blending = 0;
- }
-
- // Add the factor of how good the neighborhood is for this
- // candidate arf.
- double this_score = arf_length_factor * base_score;
- double temp_accu_coeff = 1.0;
- // following frames
- int count_f = 0;
- for (int n = j + 1; n <= j + 3 && n <= last_frame; n++) {
- if (order_index + n >= num_stats) break;
- temp_accu_coeff *= stats_list[order_index + n].cor_coeff;
- this_score +=
- temp_accu_coeff *
- (1 - stats_list[order_index + n].noise_var /
- AOMMAX(regions_list[this_reg].avg_intra_err, 0.001));
- count_f++;
- }
- // preceding frames
- temp_accu_coeff = 1.0;
- for (int n = j; n > j - 3 * 2 + count_f && n > first_frame; n--) {
- if (order_index + n < num_stats) break;
- temp_accu_coeff *= stats_list[order_index + n].cor_coeff;
- this_score +=
- temp_accu_coeff *
- (1 - stats_list[order_index + n].noise_var /
- AOMMAX(regions_list[this_reg].avg_intra_err, 0.001));
- }
-
- if (this_score > best_score) {
- best_score = this_score;
- best_j = j;
- }
- }
-
- // For blending areas, move one more frame in case we missed the
- // first blending frame.
- int best_reg =
- FindRegionIndex(regions_list, best_j + frames_since_key);
- if (best_reg < num_regions - 1 && best_reg > 0) {
- if (regions_list[best_reg - 1].type == BLENDING_REGION &&
- regions_list[best_reg + 1].type == BLENDING_REGION) {
- if (best_j + frames_since_key == regions_list[best_reg].start &&
- best_j + frames_since_key < regions_list[best_reg].last) {
- best_j += 1;
- } else if (best_j + frames_since_key ==
- regions_list[best_reg].last &&
- best_j + frames_since_key >
- regions_list[best_reg].start) {
- best_j -= 1;
- }
- }
- }
-
- if (cur_last - best_j < 2) best_j = cur_last;
- if (best_j > 0 && best_score > 0.1) cur_last = best_j;
- // if cannot find anything, just cut at the original place.
- }
- }
- }
cut_pos.push_back(cur_last);
// reset pointers to the shrunken location
@@ -629,7 +646,7 @@
if (cur_region_idx >= 0)
if (regions_list[cur_region_idx].type == SCENECUT_REGION) cur_start++;
- if (cut_here > 1 && cur_last == ori_last) break;
+ if (cut_here > 1 && cur_last == original_last) break;
// reset accumulators
InitGFStats(&gf_stats);
i = cur_last + 1;