Refactor define_gf_group

Move various accumulators and features into a structure. Added and
Changed related functions. This shortens the define_gf_group
function, and is the first step to unify the accumulators in both
calculate_gf_length and define_gf_group.

Change-Id: I3ebb684b491517cec112a50cba951a5c1529d783
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index 24e64bd..794e877 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -32,6 +32,8 @@
 
 #define DEFAULT_KF_BOOST 2300
 #define DEFAULT_GF_BOOST 2000
+#define GROUP_ADAPTIVE_MAXQ 1
+static void init_gf_stats(GF_GROUP_STATS *gf_stats);
 
 // Calculate an active area of the image that discounts formatting
 // bars and partially discounts other 0 energy areas.
@@ -305,20 +307,18 @@
 // Function to test for a condition where a complex transition is followed
 // by a static section. For example in slide shows where there is a fade
 // between slides. This is to help with more optimal kf and gf positioning.
-static int detect_transition_to_still(AV1_COMP *cpi, int frame_interval,
-                                      int still_interval,
-                                      double loop_decay_rate,
-                                      double last_decay_rate) {
-  TWO_PASS *const twopass = &cpi->twopass;
-  RATE_CONTROL *const rc = &cpi->rc;
-
+static int detect_transition_to_still(TWO_PASS *const twopass,
+                                      const int min_gf_interval,
+                                      const int frame_interval,
+                                      const int still_interval,
+                                      const double loop_decay_rate,
+                                      const double last_decay_rate) {
   // Break clause to detect very still sections after motion
   // For example a static image after a fade or other transition
   // instead of a clean scene cut.
-  if (frame_interval > rc->min_gf_interval && loop_decay_rate >= 0.999 &&
+  if (frame_interval > min_gf_interval && loop_decay_rate >= 0.999 &&
       last_decay_rate < 0.9) {
     int j;
-
     // Look ahead a few frames to see if static condition persists...
     for (j = 0; j < still_interval; ++j) {
       const FIRSTPASS_STATS *stats = &twopass->stats_in[j];
@@ -326,18 +326,16 @@
 
       if (stats->pcnt_inter - stats->pcnt_motion < 0.999) break;
     }
-
     // Only if it does do we signal a transition to still.
     return j == still_interval;
   }
-
   return 0;
 }
 
 // This function detects a flash through the high relative pcnt_second_ref
 // score in the frame following a flash frame. The offset passed in should
 // reflect this.
-static int detect_flash(const TWO_PASS *twopass, int offset) {
+static int detect_flash(const TWO_PASS *twopass, const int offset) {
   const FIRSTPASS_STATS *const next_frame = read_frame_stats(twopass, offset);
 
   // What we are looking for here is a situation where there is a
@@ -352,16 +350,13 @@
 
 // Update the motion related elements to the GF arf boost calculation.
 static void accumulate_frame_motion_stats(const FIRSTPASS_STATS *stats,
-                                          double *mv_in_out,
-                                          double *mv_in_out_accumulator,
-                                          double *abs_mv_in_out_accumulator,
-                                          double *mv_ratio_accumulator) {
+                                          GF_GROUP_STATS *gf_stats) {
   const double pct = stats->pcnt_motion;
 
   // Accumulate Motion In/Out of frame stats.
-  *mv_in_out = stats->mv_in_out_count * pct;
-  *mv_in_out_accumulator += *mv_in_out;
-  *abs_mv_in_out_accumulator += fabs(*mv_in_out);
+  gf_stats->this_frame_mv_in_out = stats->mv_in_out_count * pct;
+  gf_stats->mv_in_out_accumulator += gf_stats->this_frame_mv_in_out;
+  gf_stats->abs_mv_in_out_accumulator += fabs(gf_stats->this_frame_mv_in_out);
 
   // Accumulate a measure of how uniform (or conversely how random) the motion
   // field is (a ratio of abs(mv) / mv).
@@ -371,13 +366,122 @@
     const double mvc_ratio =
         fabs(stats->mvc_abs) / DOUBLE_DIVIDE_CHECK(fabs(stats->MVc));
 
-    *mv_ratio_accumulator +=
+    gf_stats->mv_ratio_accumulator +=
         pct * (mvr_ratio < stats->mvr_abs ? mvr_ratio : stats->mvr_abs);
-    *mv_ratio_accumulator +=
+    gf_stats->mv_ratio_accumulator +=
         pct * (mvc_ratio < stats->mvc_abs ? mvc_ratio : stats->mvc_abs);
   }
 }
 
+static void accumulate_this_frame_stats(const FIRSTPASS_STATS *stats,
+                                        const double mod_frame_err,
+                                        GF_GROUP_STATS *gf_stats) {
+  gf_stats->gf_group_err += mod_frame_err;
+#if GROUP_ADAPTIVE_MAXQ
+  gf_stats->gf_group_raw_error += stats->coded_error;
+#endif
+  gf_stats->gf_group_skip_pct += stats->intra_skip_pct;
+  gf_stats->gf_group_inactive_zone_rows += stats->inactive_zone_rows;
+}
+
+static void accumulate_next_frame_stats(
+    const FIRSTPASS_STATS *stats, const FRAME_INFO *frame_info,
+    TWO_PASS *const twopass, const int flash_detected,
+    const int frames_since_key, const int cur_idx, const int can_disable_arf,
+    const int min_gf_interval, GF_GROUP_STATS *gf_stats) {
+  accumulate_frame_motion_stats(stats, gf_stats);
+  // sum up the metric values of current gf group
+  gf_stats->avg_sr_coded_error += stats->sr_coded_error;
+  gf_stats->avg_tr_coded_error += stats->tr_coded_error;
+  gf_stats->avg_pcnt_second_ref += stats->pcnt_second_ref;
+  gf_stats->avg_pcnt_third_ref += stats->pcnt_third_ref;
+  gf_stats->avg_new_mv_count += stats->new_mv_count;
+  gf_stats->avg_wavelet_energy += stats->frame_avg_wavelet_energy;
+  if (fabs(stats->raw_error_stdev) > 0.000001) {
+    gf_stats->non_zero_stdev_count++;
+    gf_stats->avg_raw_err_stdev += stats->raw_error_stdev;
+  }
+
+  // Accumulate the effect of prediction quality decay
+  if (!flash_detected) {
+    gf_stats->last_loop_decay_rate = gf_stats->loop_decay_rate;
+    gf_stats->loop_decay_rate = get_prediction_decay_rate(frame_info, stats);
+
+    gf_stats->decay_accumulator =
+        gf_stats->decay_accumulator * gf_stats->loop_decay_rate;
+
+    // Monitor for static sections.
+    if ((frames_since_key + cur_idx - 1) > 1) {
+      gf_stats->zero_motion_accumulator =
+          AOMMIN(gf_stats->zero_motion_accumulator,
+                 get_zero_motion_factor(frame_info, stats));
+    }
+
+    // Break clause to detect very still sections after motion. For example,
+    // a static image after a fade or other transition.
+    if (can_disable_arf &&
+        detect_transition_to_still(twopass, min_gf_interval, cur_idx, 5,
+                                   gf_stats->loop_decay_rate,
+                                   gf_stats->last_loop_decay_rate)) {
+      gf_stats->allow_alt_ref = 0;
+    }
+  }
+}
+
+static void average_gf_stats(const int total_frame,
+                             const FIRSTPASS_STATS *last_stat,
+                             GF_GROUP_STATS *gf_stats) {
+  if (total_frame) {
+    gf_stats->avg_sr_coded_error /= total_frame;
+    gf_stats->avg_tr_coded_error /= total_frame;
+    gf_stats->avg_pcnt_second_ref /= total_frame;
+    if (total_frame - 1) {
+      gf_stats->avg_pcnt_third_ref_nolast =
+          (gf_stats->avg_pcnt_third_ref - last_stat->pcnt_third_ref) /
+          (total_frame - 1);
+    } else {
+      gf_stats->avg_pcnt_third_ref_nolast =
+          gf_stats->avg_pcnt_third_ref / total_frame;
+    }
+    gf_stats->avg_pcnt_third_ref /= total_frame;
+    gf_stats->avg_new_mv_count /= total_frame;
+    gf_stats->avg_wavelet_energy /= total_frame;
+  }
+
+  if (gf_stats->non_zero_stdev_count)
+    gf_stats->avg_raw_err_stdev /= gf_stats->non_zero_stdev_count;
+}
+
+static void get_features_from_gf_stats(const GF_GROUP_STATS *gf_stats,
+                                       const GF_FRAME_STATS *first_frame,
+                                       const GF_FRAME_STATS *last_frame,
+                                       const int num_mbs,
+                                       const int constrained_gf_group,
+                                       const int kf_zeromotion_pct,
+                                       const int num_frames, float *features) {
+  *features++ = (float)gf_stats->abs_mv_in_out_accumulator;
+  *features++ = (float)(gf_stats->avg_new_mv_count / num_mbs);
+  *features++ = (float)gf_stats->avg_pcnt_second_ref;
+  *features++ = (float)gf_stats->avg_pcnt_third_ref;
+  *features++ = (float)gf_stats->avg_pcnt_third_ref_nolast;
+  *features++ = (float)(gf_stats->avg_sr_coded_error / num_mbs);
+  *features++ = (float)(gf_stats->avg_tr_coded_error / num_mbs);
+  *features++ = (float)(gf_stats->avg_wavelet_energy / num_mbs);
+  *features++ = (float)(constrained_gf_group);
+  *features++ = (float)gf_stats->decay_accumulator;
+  *features++ = (float)(first_frame->frame_coded_error / num_mbs);
+  *features++ = (float)(first_frame->frame_sr_coded_error / num_mbs);
+  *features++ = (float)(first_frame->frame_tr_coded_error / num_mbs);
+  *features++ = (float)(first_frame->frame_err / num_mbs);
+  *features++ = (float)(kf_zeromotion_pct);
+  *features++ = (float)(last_frame->frame_coded_error / num_mbs);
+  *features++ = (float)(last_frame->frame_sr_coded_error / num_mbs);
+  *features++ = (float)(last_frame->frame_tr_coded_error / num_mbs);
+  *features++ = (float)num_frames;
+  *features++ = (float)gf_stats->mv_ratio_accumulator;
+  *features++ = (float)gf_stats->non_zero_stdev_count;
+}
+
 #define BOOST_FACTOR 12.5
 static double baseline_err_per_mb(const FRAME_INFO *frame_info) {
   unsigned int screen_area = frame_info->frame_height * frame_info->frame_width;
@@ -488,12 +592,9 @@
                        int b_frames, int *num_fpstats_used,
                        int *num_fpstats_required) {
   int i;
+  GF_GROUP_STATS gf_stats;
+  init_gf_stats(&gf_stats);
   double boost_score = (double)NORMAL_BOOST;
-  double mv_ratio_accumulator = 0.0;
-  double decay_accumulator = 1.0;
-  double this_frame_mv_in_out = 0.0;
-  double mv_in_out_accumulator = 0.0;
-  double abs_mv_in_out_accumulator = 0.0;
   int arf_boost;
   int flash_detected = 0;
   if (num_fpstats_used) *num_fpstats_used = 0;
@@ -504,9 +605,7 @@
     if (this_frame == NULL) break;
 
     // Update the motion related elements to the boost calculation.
-    accumulate_frame_motion_stats(
-        this_frame, &this_frame_mv_in_out, &mv_in_out_accumulator,
-        &abs_mv_in_out_accumulator, &mv_ratio_accumulator);
+    accumulate_frame_motion_stats(this_frame, &gf_stats);
 
     // We want to discount the flash frame itself and the recovery
     // frame that follows as both will have poor scores.
@@ -515,15 +614,17 @@
 
     // Accumulate the effect of prediction quality decay.
     if (!flash_detected) {
-      decay_accumulator *= get_prediction_decay_rate(frame_info, this_frame);
-      decay_accumulator = decay_accumulator < MIN_DECAY_FACTOR
-                              ? MIN_DECAY_FACTOR
-                              : decay_accumulator;
+      gf_stats.decay_accumulator *=
+          get_prediction_decay_rate(frame_info, this_frame);
+      gf_stats.decay_accumulator = gf_stats.decay_accumulator < MIN_DECAY_FACTOR
+                                       ? MIN_DECAY_FACTOR
+                                       : gf_stats.decay_accumulator;
     }
 
-    boost_score += decay_accumulator *
-                   calc_frame_boost(rc, frame_info, this_frame,
-                                    this_frame_mv_in_out, GF_MAX_BOOST);
+    boost_score +=
+        gf_stats.decay_accumulator *
+        calc_frame_boost(rc, frame_info, this_frame,
+                         gf_stats.this_frame_mv_in_out, GF_MAX_BOOST);
     if (num_fpstats_used) (*num_fpstats_used)++;
   }
 
@@ -531,21 +632,14 @@
 
   // Reset for backward looking loop.
   boost_score = 0.0;
-  mv_ratio_accumulator = 0.0;
-  decay_accumulator = 1.0;
-  this_frame_mv_in_out = 0.0;
-  mv_in_out_accumulator = 0.0;
-  abs_mv_in_out_accumulator = 0.0;
-
+  init_gf_stats(&gf_stats);
   // Search backward towards last gf position.
   for (i = -1; i >= -b_frames; --i) {
     const FIRSTPASS_STATS *this_frame = read_frame_stats(twopass, i + offset);
     if (this_frame == NULL) break;
 
     // Update the motion related elements to the boost calculation.
-    accumulate_frame_motion_stats(
-        this_frame, &this_frame_mv_in_out, &mv_in_out_accumulator,
-        &abs_mv_in_out_accumulator, &mv_ratio_accumulator);
+    accumulate_frame_motion_stats(this_frame, &gf_stats);
 
     // We want to discount the the flash frame itself and the recovery
     // frame that follows as both will have poor scores.
@@ -554,15 +648,17 @@
 
     // Cumulative effect of prediction quality decay.
     if (!flash_detected) {
-      decay_accumulator *= get_prediction_decay_rate(frame_info, this_frame);
-      decay_accumulator = decay_accumulator < MIN_DECAY_FACTOR
-                              ? MIN_DECAY_FACTOR
-                              : decay_accumulator;
+      gf_stats.decay_accumulator *=
+          get_prediction_decay_rate(frame_info, this_frame);
+      gf_stats.decay_accumulator = gf_stats.decay_accumulator < MIN_DECAY_FACTOR
+                                       ? MIN_DECAY_FACTOR
+                                       : gf_stats.decay_accumulator;
     }
 
-    boost_score += decay_accumulator *
-                   calc_frame_boost(rc, frame_info, this_frame,
-                                    this_frame_mv_in_out, GF_MAX_BOOST);
+    boost_score +=
+        gf_stats.decay_accumulator *
+        calc_frame_boost(rc, frame_info, this_frame,
+                         gf_stats.this_frame_mv_in_out, GF_MAX_BOOST);
     if (num_fpstats_used) (*num_fpstats_used)++;
   }
   arf_boost += (int)boost_score;
@@ -882,13 +978,9 @@
 
 #define ARF_ABS_ZOOM_THRESH 4.4
 static INLINE int detect_gf_cut(AV1_COMP *cpi, int frame_index, int cur_start,
-                                int flash_detected, double loop_decay_rate,
-                                double last_loop_decay_rate,
-                                int active_max_gf_interval,
+                                int flash_detected, int active_max_gf_interval,
                                 int active_min_gf_interval,
-                                double zero_motion_accumulator,
-                                double mv_ratio_accumulator,
-                                double abs_mv_in_out_accumulator) {
+                                GF_GROUP_STATS *gf_stats) {
   RATE_CONTROL *const rc = &cpi->rc;
   TWO_PASS *const twopass = &cpi->twopass;
   // Motion breakout threshold for loop below depends on image size.
@@ -898,8 +990,9 @@
   if (!flash_detected) {
     // Break clause to detect very still sections after motion. For example,
     // a static image after a fade or other transition.
-    if (detect_transition_to_still(cpi, frame_index - cur_start, 5,
-                                   loop_decay_rate, last_loop_decay_rate)) {
+    if (detect_transition_to_still(
+            twopass, rc->min_gf_interval, frame_index - cur_start, 5,
+            gf_stats->loop_decay_rate, gf_stats->last_loop_decay_rate)) {
       return 1;
     }
   }
@@ -909,15 +1002,16 @@
       // If possible don't break very close to a kf
       (rc->frames_to_key - frame_index >= rc->min_gf_interval) &&
       ((frame_index - cur_start) & 0x01) && !flash_detected &&
-      (mv_ratio_accumulator > mv_ratio_accumulator_thresh ||
-       abs_mv_in_out_accumulator > ARF_ABS_ZOOM_THRESH)) {
+      (gf_stats->mv_ratio_accumulator > mv_ratio_accumulator_thresh ||
+       gf_stats->abs_mv_in_out_accumulator > ARF_ABS_ZOOM_THRESH)) {
     return 1;
   }
 
   // If almost totally static, we will not use the the max GF length later,
   // so we can continue for more frames.
   if (((frame_index - cur_start) >= active_max_gf_interval + 1) &&
-      !is_almost_static(zero_motion_accumulator, twopass->kf_zeromotion_pct)) {
+      !is_almost_static(gf_stats->zero_motion_accumulator,
+                        twopass->kf_zeromotion_pct)) {
     return 1;
   }
   return 0;
@@ -1071,7 +1165,6 @@
   return reset;
 }
 
-#define GROUP_ADAPTIVE_MAXQ 1
 #if GROUP_ADAPTIVE_MAXQ
 #define RC_FACTOR_MIN 0.75
 #define RC_FACTOR_MAX 1.25
@@ -1173,16 +1266,7 @@
   FRAME_INFO *frame_info = &cpi->frame_info;
   int i;
 
-  double mv_ratio_accumulator = 0.0;
-  double zero_motion_accumulator = 1.0;
-
-  double this_frame_mv_in_out = 0.0;
-  double mv_in_out_accumulator = 0.0;
-  double abs_mv_in_out_accumulator = 0.0;
-
   int flash_detected;
-  double loop_decay_rate = 1.00;
-  double last_loop_decay_rate = 1.00;
 
   aom_clear_system_state();
   av1_zero(next_frame);
@@ -1208,6 +1292,8 @@
   int cur_start = 0, cur_last;
   int cut_here;
   int prev_lows = 0;
+  GF_GROUP_STATS gf_stats;
+  init_gf_stats(&gf_stats);
   while (count_cuts < max_intervals + 1) {
     ++i;
 
@@ -1229,30 +1315,18 @@
         count_cuts++;
         break;
       }
-      // Update the motion related elements to the boost calculation.
-      accumulate_frame_motion_stats(
-          &next_frame, &this_frame_mv_in_out, &mv_in_out_accumulator,
-          &abs_mv_in_out_accumulator, &mv_ratio_accumulator);
-
       // Test for the case where there is a brief flash but the prediction
       // quality back to an earlier frame is then restored.
       flash_detected = detect_flash(twopass, 0);
-      // Monitor for static sections.
-      if (!flash_detected) {
-        last_loop_decay_rate = loop_decay_rate;
-        loop_decay_rate = get_prediction_decay_rate(frame_info, &next_frame);
+      // TODO(bohanli): remove redundant accumulations here, or unify
+      // this and the ones in define_gf_group
+      accumulate_next_frame_stats(&next_frame, frame_info, twopass,
+                                  flash_detected, rc->frames_since_key, i, 0,
+                                  rc->min_gf_interval, &gf_stats);
 
-        if ((rc->frames_since_key + i - 1) > 1) {
-          zero_motion_accumulator =
-              AOMMIN(zero_motion_accumulator,
-                     get_zero_motion_factor(frame_info, &next_frame));
-        }
-      }
-      cut_here =
-          detect_gf_cut(cpi, i, cur_start, flash_detected, loop_decay_rate,
-                        last_loop_decay_rate, active_max_gf_interval,
-                        active_min_gf_interval, zero_motion_accumulator,
-                        mv_ratio_accumulator, abs_mv_in_out_accumulator);
+      cut_here = detect_gf_cut(cpi, i, cur_start, flash_detected,
+                               active_max_gf_interval, active_min_gf_interval,
+                               &gf_stats);
     }
     if (cut_here) {
       cur_last = i - 1;  // the current last frame in the gf group
@@ -1275,6 +1349,10 @@
             after_pad = n - cur_last - 1;
             assert(after_pad >= 0);
             break;
+          } else if (start_pos + n - 1 <
+                     twopass->stats_buf_ctx->stats_in_start) {
+            before_pad = cur_start - n - 1;
+            continue;
           }
           errs[n + before_pad - cur_start] = (start_pos + n - 1)->coded_error;
         }
@@ -1310,15 +1388,7 @@
       i = cur_last;
 
       // reset accumulators
-      mv_ratio_accumulator = 0.0;
-      zero_motion_accumulator = 1.0;
-
-      this_frame_mv_in_out = 0.0;
-      mv_in_out_accumulator = 0.0;
-      abs_mv_in_out_accumulator = 0.0;
-
-      loop_decay_rate = 1.00;
-      last_loop_decay_rate = 1.00;
+      init_gf_stats(&gf_stats);
     }
   }
 
@@ -1446,6 +1516,35 @@
   }
 }
 
+// initialize GF_GROUP_STATS
+static void init_gf_stats(GF_GROUP_STATS *gf_stats) {
+  gf_stats->gf_group_err = 0.0;
+  gf_stats->gf_group_raw_error = 0.0;
+  gf_stats->gf_group_skip_pct = 0.0;
+  gf_stats->gf_group_inactive_zone_rows = 0.0;
+
+  gf_stats->mv_ratio_accumulator = 0.0;
+  gf_stats->decay_accumulator = 1.0;
+  gf_stats->zero_motion_accumulator = 1.0;
+  gf_stats->loop_decay_rate = 1.0;
+  gf_stats->last_loop_decay_rate = 1.0;
+  gf_stats->this_frame_mv_in_out = 0.0;
+  gf_stats->mv_in_out_accumulator = 0.0;
+  gf_stats->abs_mv_in_out_accumulator = 0.0;
+
+  gf_stats->avg_sr_coded_error = 0.0;
+  gf_stats->avg_tr_coded_error = 0.0;
+  gf_stats->avg_pcnt_second_ref = 0.0;
+  gf_stats->avg_pcnt_third_ref = 0.0;
+  gf_stats->avg_pcnt_third_ref_nolast = 0.0;
+  gf_stats->avg_new_mv_count = 0.0;
+  gf_stats->avg_wavelet_energy = 0.0;
+  gf_stats->avg_raw_err_stdev = 0.0;
+  gf_stats->non_zero_stdev_count = 0;
+
+  gf_stats->allow_alt_ref = 0;
+}
+
 // Analyse and define a gf/arf group.
 #define MAX_GF_BOOST 5400
 static void define_gf_group(AV1_COMP *cpi, FIRSTPASS_STATS *this_frame,
@@ -1461,29 +1560,6 @@
   FRAME_INFO *frame_info = &cpi->frame_info;
   int i;
 
-  double gf_group_err = 0.0;
-#if GROUP_ADAPTIVE_MAXQ
-  double gf_group_raw_error = 0.0;
-#endif
-  double gf_group_skip_pct = 0.0;
-  double gf_group_inactive_zone_rows = 0.0;
-  double gf_first_frame_err = 0.0;
-  double mod_frame_err = 0.0;
-
-  double mv_ratio_accumulator = 0.0;
-  double decay_accumulator = 1.0;
-  double zero_motion_accumulator = 1.0;
-
-  double loop_decay_rate = 1.00;
-  double last_loop_decay_rate = 1.00;
-
-  double this_frame_mv_in_out = 0.0;
-  double mv_in_out_accumulator = 0.0;
-  double abs_mv_in_out_accumulator = 0.0;
-
-  unsigned int allow_alt_ref = is_altref_enabled(cpi);
-  const int can_disable_arf = (oxcf->gf_min_pyr_height == MIN_PYRAMID_LVL);
-
   int flash_detected;
   int64_t gf_group_bits;
   const int is_intra_only = frame_params->frame_type == KEY_FRAME ||
@@ -1511,26 +1587,33 @@
     correct_frames_to_key(cpi);
   }
 
+  GF_GROUP_STATS gf_stats;
+  init_gf_stats(&gf_stats);
+  GF_FRAME_STATS first_frame_stats, last_frame_stats;
+
+  gf_stats.allow_alt_ref = is_altref_enabled(cpi);
+  const int can_disable_arf = (oxcf->gf_min_pyr_height == MIN_PYRAMID_LVL);
+
   // Load stats for the current frame.
-  mod_frame_err = calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+  double mod_frame_err =
+      calculate_modified_err(frame_info, twopass, oxcf, this_frame);
 
   // Note the error of the frame at the start of the group. This will be
   // the GF frame error if we code a normal gf.
-  gf_first_frame_err = mod_frame_err;
-
-  const double first_frame_coded_error = this_frame->coded_error;
-  const double first_frame_sr_coded_error = this_frame->sr_coded_error;
-  const double first_frame_tr_coded_error = this_frame->tr_coded_error;
+  first_frame_stats.frame_err = mod_frame_err;
+  first_frame_stats.frame_coded_error = this_frame->coded_error;
+  first_frame_stats.frame_sr_coded_error = this_frame->sr_coded_error;
+  first_frame_stats.frame_tr_coded_error = this_frame->tr_coded_error;
 
   // If this is a key frame or the overlay from a previous arf then
   // the error score / cost of this frame has already been accounted for.
   if (arf_active_or_kf) {
-    gf_group_err -= gf_first_frame_err;
+    gf_stats.gf_group_err -= first_frame_stats.frame_err;
 #if GROUP_ADAPTIVE_MAXQ
-    gf_group_raw_error -= this_frame->coded_error;
+    gf_stats.gf_group_raw_error -= this_frame->coded_error;
 #endif
-    gf_group_skip_pct -= this_frame->intra_skip_pct;
-    gf_group_inactive_zone_rows -= this_frame->inactive_zone_rows;
+    gf_stats.gf_group_skip_pct -= this_frame->intra_skip_pct;
+    gf_stats.gf_group_inactive_zone_rows -= this_frame->inactive_zone_rows;
   }
 
   // TODO(urvang): Try logic to vary min and max interval based on q.
@@ -1538,80 +1621,35 @@
   const int active_max_gf_interval =
       AOMMIN(rc->max_gf_interval, max_gop_length);
 
-  double avg_sr_coded_error = 0;
-  double avg_tr_coded_error = 0;
-
-  double avg_pcnt_second_ref = 0;
-  double avg_pcnt_third_ref = 0;
-
-  double avg_new_mv_count = 0;
-
-  double avg_wavelet_energy = 0;
-
-  double avg_raw_err_stdev = 0;
-  int non_zero_stdev_count = 0;
-
   i = 0;
   // get the determined gf group length from rc->gf_intervals
   while (i < rc->gf_intervals[rc->cur_gf_index]) {
     ++i;
-
     // Accumulate error score of frames in this gf group.
     mod_frame_err =
         calculate_modified_err(frame_info, twopass, oxcf, this_frame);
-    gf_group_err += mod_frame_err;
-#if GROUP_ADAPTIVE_MAXQ
-    gf_group_raw_error += this_frame->coded_error;
-#endif
-    gf_group_skip_pct += this_frame->intra_skip_pct;
-    gf_group_inactive_zone_rows += this_frame->inactive_zone_rows;
+    // accumulate stats for this frame
+    accumulate_this_frame_stats(this_frame, mod_frame_err, &gf_stats);
 
+    // read in the next frame
     if (EOF == input_stats(twopass, &next_frame)) break;
 
     // Test for the case where there is a brief flash but the prediction
     // quality back to an earlier frame is then restored.
     flash_detected = detect_flash(twopass, 0);
 
-    // Update the motion related elements to the boost calculation.
-    accumulate_frame_motion_stats(
-        &next_frame, &this_frame_mv_in_out, &mv_in_out_accumulator,
-        &abs_mv_in_out_accumulator, &mv_ratio_accumulator);
-    // sum up the metric values of current gf group
-    avg_sr_coded_error += next_frame.sr_coded_error;
-    avg_tr_coded_error += next_frame.tr_coded_error;
-    avg_pcnt_second_ref += next_frame.pcnt_second_ref;
-    avg_pcnt_third_ref += next_frame.pcnt_third_ref;
-    avg_new_mv_count += next_frame.new_mv_count;
-    avg_wavelet_energy += next_frame.frame_avg_wavelet_energy;
-    if (fabs(next_frame.raw_error_stdev) > 0.000001) {
-      non_zero_stdev_count++;
-      avg_raw_err_stdev += next_frame.raw_error_stdev;
-    }
+    // accumulate stats for next frame
+    accumulate_next_frame_stats(
+        &next_frame, frame_info, twopass, flash_detected, rc->frames_since_key,
+        i, can_disable_arf, rc->min_gf_interval, &gf_stats);
 
-    // Accumulate the effect of prediction quality decay.
-    if (!flash_detected) {
-      last_loop_decay_rate = loop_decay_rate;
-      loop_decay_rate = get_prediction_decay_rate(frame_info, &next_frame);
-
-      decay_accumulator = decay_accumulator * loop_decay_rate;
-
-      // Monitor for static sections.
-      if ((rc->frames_since_key + i - 1) > 1) {
-        zero_motion_accumulator =
-            AOMMIN(zero_motion_accumulator,
-                   get_zero_motion_factor(frame_info, &next_frame));
-      }
-
-      // Break clause to detect very still sections after motion. For example,
-      // a static image after a fade or other transition.
-      if (can_disable_arf &&
-          detect_transition_to_still(cpi, i, 5, loop_decay_rate,
-                                     last_loop_decay_rate)) {
-        allow_alt_ref = 0;
-      }
-    }
     *this_frame = next_frame;
   }
+  // save the errs for the last frame
+  last_frame_stats.frame_coded_error = next_frame.coded_error;
+  last_frame_stats.frame_sr_coded_error = next_frame.sr_coded_error;
+  last_frame_stats.frame_tr_coded_error = next_frame.tr_coded_error;
+
   if (is_final_pass) {
     rc->intervals_till_gf_calculate_due--;
     rc->cur_gf_index++;
@@ -1623,26 +1661,8 @@
   const int num_mbs = (cpi->oxcf.resize_mode != RESIZE_NONE) ? cpi->initial_mbs
                                                              : cpi->common.MBs;
   assert(num_mbs > 0);
-  const double last_frame_coded_error = next_frame.coded_error;
-  const double last_frame_sr_coded_error = next_frame.sr_coded_error;
-  const double last_frame_tr_coded_error = next_frame.tr_coded_error;
-  double avg_pcnt_third_ref_nolast = avg_pcnt_third_ref;
-  if (i) {
-    avg_sr_coded_error /= i;
-    avg_tr_coded_error /= i;
-    avg_pcnt_second_ref /= i;
-    if (i - 1) {
-      avg_pcnt_third_ref_nolast =
-          (avg_pcnt_third_ref - next_frame.pcnt_third_ref) / (i - 1);
-    } else {
-      avg_pcnt_third_ref_nolast = avg_pcnt_third_ref / i;
-    }
-    avg_pcnt_third_ref /= i;
-    avg_new_mv_count /= i;
-    avg_wavelet_energy /= i;
-  }
 
-  if (non_zero_stdev_count) avg_raw_err_stdev /= non_zero_stdev_count;
+  average_gf_stats(i, &next_frame, &gf_stats);
 
   // Disable internal ARFs for "still" gf groups.
   //   zero_motion_accumulator: minimum percentage of (0,0) motion;
@@ -1651,17 +1671,18 @@
   //                            motion error per block of each frame.
   const int can_disable_internal_arfs =
       (oxcf->gf_min_pyr_height <= MIN_PYRAMID_LVL + 1);
-  if (can_disable_internal_arfs && zero_motion_accumulator > MIN_ZERO_MOTION &&
-      avg_sr_coded_error / num_mbs < MAX_SR_CODED_ERROR &&
-      avg_raw_err_stdev < MAX_RAW_ERR_VAR) {
+  if (can_disable_internal_arfs &&
+      gf_stats.zero_motion_accumulator > MIN_ZERO_MOTION &&
+      gf_stats.avg_sr_coded_error / num_mbs < MAX_SR_CODED_ERROR &&
+      gf_stats.avg_raw_err_stdev < MAX_RAW_ERR_VAR) {
     cpi->internal_altref_allowed = 0;
   }
 
   int use_alt_ref;
   if (can_disable_arf) {
-    use_alt_ref = !is_almost_static(zero_motion_accumulator,
+    use_alt_ref = !is_almost_static(gf_stats.zero_motion_accumulator,
                                     twopass->kf_zeromotion_pct) &&
-                  allow_alt_ref && (i < cpi->oxcf.lag_in_frames) &&
+                  gf_stats.allow_alt_ref && (i < cpi->oxcf.lag_in_frames) &&
                   (i >= MIN_GF_INTERVAL) &&
                   (cpi->oxcf.gf_max_pyr_height > MIN_PYRAMID_LVL);
 
@@ -1669,33 +1690,10 @@
     if (use_alt_ref && cpi->oxcf.rc_mode == AOM_Q &&
         cpi->oxcf.cq_level <= 200) {
       aom_clear_system_state();
-
-      /* clang-format off */
-    // Generate features.
-    const float features[] = {
-      (float)abs_mv_in_out_accumulator,
-      (float)(avg_new_mv_count / num_mbs),
-      (float)avg_pcnt_second_ref,
-      (float)avg_pcnt_third_ref,
-      (float)avg_pcnt_third_ref_nolast,
-      (float)(avg_sr_coded_error / num_mbs),
-      (float)(avg_tr_coded_error / num_mbs),
-      (float)(avg_wavelet_energy / num_mbs),
-      (float)(rc->constrained_gf_group),
-      (float)decay_accumulator,
-      (float)(first_frame_coded_error / num_mbs),
-      (float)(first_frame_sr_coded_error / num_mbs),
-      (float)(first_frame_tr_coded_error / num_mbs),
-      (float)(gf_first_frame_err / num_mbs),
-      (float)(twopass->kf_zeromotion_pct),
-      (float)(last_frame_coded_error / num_mbs),
-      (float)(last_frame_sr_coded_error / num_mbs),
-      (float)(last_frame_tr_coded_error / num_mbs),
-      (float)i,
-      (float)mv_ratio_accumulator,
-      (float)non_zero_stdev_count
-    };
-      /* clang-format on */
+      float features[21];
+      get_features_from_gf_stats(
+          &gf_stats, &first_frame_stats, &last_frame_stats, num_mbs,
+          rc->constrained_gf_group, twopass->kf_zeromotion_pct, i, features);
       // Infer using ML model.
       float score;
       av1_nn_predict(features, &av1_use_flat_gop_nn_config, 1, &score);
@@ -1703,7 +1701,8 @@
     }
   } else {
     assert(cpi->oxcf.gf_max_pyr_height > MIN_PYRAMID_LVL);
-    use_alt_ref = allow_alt_ref && (i < cpi->oxcf.lag_in_frames) && (i > 2);
+    use_alt_ref =
+        gf_stats.allow_alt_ref && (i < cpi->oxcf.lag_in_frames) && (i > 2);
   }
 
 #define REDUCE_GF_LENGTH_THRESH 4
@@ -1801,7 +1800,7 @@
   reset_fpf_position(twopass, start_pos);
 
   // Calculate the bits to be allocated to the gf/arf group as a whole
-  gf_group_bits = calculate_total_gf_group_bits(cpi, gf_group_err);
+  gf_group_bits = calculate_total_gf_group_bits(cpi, gf_stats.gf_group_err);
   rc->gf_group_bits = gf_group_bits;
 
 #if GROUP_ADAPTIVE_MAXQ
@@ -1813,11 +1812,12 @@
   if ((cpi->oxcf.rc_mode != AOM_Q) && (rc->baseline_gf_interval > 1)) {
     const int vbr_group_bits_per_frame =
         (int)(gf_group_bits / rc->baseline_gf_interval);
-    const double group_av_err = gf_group_raw_error / rc->baseline_gf_interval;
+    const double group_av_err =
+        gf_stats.gf_group_raw_error / rc->baseline_gf_interval;
     const double group_av_skip_pct =
-        gf_group_skip_pct / rc->baseline_gf_interval;
+        gf_stats.gf_group_skip_pct / rc->baseline_gf_interval;
     const double group_av_inactive_zone =
-        ((gf_group_inactive_zone_rows * 2) /
+        ((gf_stats.gf_group_inactive_zone_rows * 2) /
          (rc->baseline_gf_interval * (double)cm->mb_rows));
 
     int tmp_q;
@@ -1845,7 +1845,8 @@
 #endif
 
   // Adjust KF group bits and error remaining.
-  if (is_final_pass) twopass->kf_group_error_left -= (int64_t)gf_group_err;
+  if (is_final_pass)
+    twopass->kf_group_error_left -= (int64_t)gf_stats.gf_group_err;
 
   // Set up the structure of this Group-Of-Pictures (same as GF_GROUP)
   av1_gop_setup_structure(cpi, frame_params);
@@ -2124,8 +2125,9 @@
 
       // Special check for transition or high motion followed by a
       // static scene.
-      if (detect_transition_to_still(cpi, i, cpi->oxcf.key_freq - i,
-                                     loop_decay_rate, decay_accumulator)) {
+      if (detect_transition_to_still(twopass, rc->min_gf_interval, i,
+                                     cpi->oxcf.key_freq - i, loop_decay_rate,
+                                     decay_accumulator)) {
         scenecut_detected = 1;
         break;
       }
diff --git a/av1/encoder/pass2_strategy.h b/av1/encoder/pass2_strategy.h
index 6f15c01..437fb8f 100644
--- a/av1/encoder/pass2_strategy.h
+++ b/av1/encoder/pass2_strategy.h
@@ -18,6 +18,41 @@
 
 struct AV1_COMP;
 struct EncodeFrameParams;
+// structure of accumulated stats and features in a gf group
+typedef struct {
+  double gf_group_err;
+  double gf_group_raw_error;
+  double gf_group_skip_pct;
+  double gf_group_inactive_zone_rows;
+
+  double mv_ratio_accumulator;
+  double decay_accumulator;
+  double zero_motion_accumulator;
+  double loop_decay_rate;
+  double last_loop_decay_rate;
+  double this_frame_mv_in_out;
+  double mv_in_out_accumulator;
+  double abs_mv_in_out_accumulator;
+
+  double avg_sr_coded_error;
+  double avg_tr_coded_error;
+  double avg_pcnt_second_ref;
+  double avg_pcnt_third_ref;
+  double avg_pcnt_third_ref_nolast;
+  double avg_new_mv_count;
+  double avg_wavelet_energy;
+  double avg_raw_err_stdev;
+  int non_zero_stdev_count;
+
+  unsigned int allow_alt_ref;
+} GF_GROUP_STATS;
+
+typedef struct {
+  double frame_err;
+  double frame_coded_error;
+  double frame_sr_coded_error;
+  double frame_tr_coded_error;
+} GF_FRAME_STATS;
 
 void av1_init_second_pass(struct AV1_COMP *cpi);