Merge "Refactor decode_api_test and realtime_test"
diff --git a/test/simple_encode_test.cc b/test/simple_encode_test.cc
index 3dfc812..cbcae46 100644
--- a/test/simple_encode_test.cc
+++ b/test/simple_encode_test.cc
@@ -106,6 +106,34 @@
   simple_encode.EndEncode();
 }
 
+TEST(SimpleEncode, ObserveKeyFrameMap) {
+  SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den,
+                             target_bitrate, num_frames, infile_path);
+  simple_encode.ComputeFirstPassStats();
+  std::vector<int> key_frame_map = simple_encode.ObserveKeyFrameMap();
+  EXPECT_EQ(key_frame_map.size(), static_cast<size_t>(num_frames));
+  simple_encode.StartEncode();
+  int coded_show_frame_count = 0;
+  while (coded_show_frame_count < num_frames) {
+    const GroupOfPicture group_of_picture =
+        simple_encode.ObserveGroupOfPicture();
+    const std::vector<EncodeFrameInfo> &encode_frame_list =
+        group_of_picture.encode_frame_list;
+    for (size_t group_index = 0; group_index < encode_frame_list.size();
+         ++group_index) {
+      EncodeFrameResult encode_frame_result;
+      simple_encode.EncodeFrame(&encode_frame_result);
+      if (encode_frame_result.frame_type == kFrameTypeKey) {
+        EXPECT_EQ(key_frame_map[encode_frame_result.show_idx], 1);
+      } else {
+        EXPECT_EQ(key_frame_map[encode_frame_result.show_idx], 0);
+      }
+    }
+    coded_show_frame_count += group_of_picture.show_frame_count;
+  }
+  simple_encode.EndEncode();
+}
+
 TEST(SimpleEncode, EncodeFrameWithQuantizeIndex) {
   SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den,
                              target_bitrate, num_frames, infile_path);
@@ -291,35 +319,39 @@
 // Get QPs and arf locations from the first encode.
 // Set external arfs and QPs for the second encode.
 // Expect to get matched results.
-TEST(SimpleEncode, EncodeConsistencyTestUseExternalArfs) {
+TEST(SimpleEncode, EncodeConsistencySetExternalGroupOfPicture) {
   std::vector<int> quantize_index_list;
   std::vector<uint64_t> ref_sse_list;
   std::vector<double> ref_psnr_list;
   std::vector<size_t> ref_bit_size_list;
-  std::vector<int> external_arf_indexes(num_frames, 0);
+  std::vector<int> gop_map(num_frames, 0);
   {
     // The first encode.
     SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den,
                                target_bitrate, num_frames, infile_path);
     simple_encode.ComputeFirstPassStats();
-    const int num_coding_frames = simple_encode.GetCodingFrameNum();
     simple_encode.StartEncode();
-    for (int i = 0; i < num_coding_frames; ++i) {
-      EncodeFrameResult encode_frame_result;
-      simple_encode.EncodeFrame(&encode_frame_result);
-      quantize_index_list.push_back(encode_frame_result.quantize_index);
-      ref_sse_list.push_back(encode_frame_result.sse);
-      ref_psnr_list.push_back(encode_frame_result.psnr);
-      ref_bit_size_list.push_back(encode_frame_result.coding_data_bit_size);
-      if (encode_frame_result.frame_type == kFrameTypeKey) {
-        external_arf_indexes[encode_frame_result.show_idx] = 0;
-      } else if (encode_frame_result.frame_type == kFrameTypeAltRef) {
-        external_arf_indexes[encode_frame_result.show_idx] = 1;
-      } else {
-        // This has to be |= because we can't let overlay overwrites the
-        // arf type for the same frame.
-        external_arf_indexes[encode_frame_result.show_idx] |= 0;
+
+    int coded_show_frame_count = 0;
+    while (coded_show_frame_count < num_frames) {
+      const GroupOfPicture group_of_picture =
+          simple_encode.ObserveGroupOfPicture();
+      gop_map[coded_show_frame_count] |= kGopMapFlagStart;
+      if (group_of_picture.use_alt_ref) {
+        gop_map[coded_show_frame_count] |= kGopMapFlagUseAltRef;
       }
+      const std::vector<EncodeFrameInfo> &encode_frame_list =
+          group_of_picture.encode_frame_list;
+      for (size_t group_index = 0; group_index < encode_frame_list.size();
+           ++group_index) {
+        EncodeFrameResult encode_frame_result;
+        simple_encode.EncodeFrame(&encode_frame_result);
+        quantize_index_list.push_back(encode_frame_result.quantize_index);
+        ref_sse_list.push_back(encode_frame_result.sse);
+        ref_psnr_list.push_back(encode_frame_result.psnr);
+        ref_bit_size_list.push_back(encode_frame_result.coding_data_bit_size);
+      }
+      coded_show_frame_count += group_of_picture.show_frame_count;
     }
     simple_encode.EndEncode();
   }
@@ -329,7 +361,7 @@
     SimpleEncode simple_encode(w, h, frame_rate_num, frame_rate_den,
                                target_bitrate, num_frames, infile_path);
     simple_encode.ComputeFirstPassStats();
-    simple_encode.SetExternalGroupOfPicture(external_arf_indexes);
+    simple_encode.SetExternalGroupOfPicture(gop_map);
     const int num_coding_frames = simple_encode.GetCodingFrameNum();
     EXPECT_EQ(static_cast<size_t>(num_coding_frames),
               quantize_index_list.size());
diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h
index 1a25a496..7aa4bf7 100644
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -532,24 +532,50 @@
   int_mv mv[2];
 } MOTION_VECTOR_INFO;
 
+typedef struct GOP_COMMAND {
+  int use;  // use this command to set gop or not. If not, use vp9's decision.
+  int show_frame_count;
+  int use_alt_ref;
+} GOP_COMMAND;
+
+static INLINE void gop_command_on(GOP_COMMAND *gop_command,
+                                  int show_frame_count, int use_alt_ref) {
+  gop_command->use = 1;
+  gop_command->show_frame_count = show_frame_count;
+  gop_command->use_alt_ref = use_alt_ref;
+}
+
+static INLINE void gop_command_off(GOP_COMMAND *gop_command) {
+  gop_command->use = 0;
+  gop_command->show_frame_count = 0;
+  gop_command->use_alt_ref = 0;
+}
+
+static INLINE int gop_command_coding_frame_count(
+    const GOP_COMMAND *gop_command) {
+  if (gop_command->use == 0) {
+    assert(0);
+    return -1;
+  }
+  return gop_command->show_frame_count + gop_command->use_alt_ref;
+}
+
 typedef struct ENCODE_COMMAND {
   int use_external_quantize_index;
   int external_quantize_index;
-  // A list of binary flags set from the external controller.
-  // Each binary flag indicates whether the frame is an arf or not.
-  const int *external_arf_indexes;
+  GOP_COMMAND gop_command;
 } ENCODE_COMMAND;
 
 static INLINE void encode_command_init(ENCODE_COMMAND *encode_command) {
   vp9_zero(*encode_command);
   encode_command->use_external_quantize_index = 0;
   encode_command->external_quantize_index = -1;
-  encode_command->external_arf_indexes = NULL;
+  gop_command_off(&encode_command->gop_command);
 }
 
-static INLINE void encode_command_set_external_arf_indexes(
-    ENCODE_COMMAND *encode_command, const int *external_arf_indexes) {
-  encode_command->external_arf_indexes = external_arf_indexes;
+static INLINE void encode_command_set_gop_command(
+    ENCODE_COMMAND *encode_command, GOP_COMMAND gop_command) {
+  encode_command->gop_command = gop_command;
 }
 
 static INLINE void encode_command_set_external_quantize_index(
diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c
index 3a48174..45b003e 100644
--- a/vp9/encoder/vp9_firstpass.c
+++ b/vp9/encoder/vp9_firstpass.c
@@ -2508,9 +2508,6 @@
  * structs.
  */
 static int get_gop_coding_frame_num(
-#if CONFIG_RATE_CTRL
-    const int *external_arf_indexes,
-#endif
     int *use_alt_ref, const FRAME_INFO *frame_info,
     const FIRST_PASS_INFO *first_pass_info, const RATE_CONTROL *rc,
     int gf_start_show_idx, const RANGE *active_gf_interval,
@@ -2526,24 +2523,6 @@
       (frame_info->frame_height + frame_info->frame_width) / 4.0;
   double zero_motion_accumulator = 1.0;
   int gop_coding_frames;
-#if CONFIG_RATE_CTRL
-  (void)mv_ratio_accumulator_thresh;
-  (void)active_gf_interval;
-  (void)gop_intra_factor;
-
-  if (external_arf_indexes != NULL && rc->frames_to_key > 1) {
-    // gop_coding_frames = 1 is necessary to filter out the overlay frame,
-    // since the arf is in this group of picture and its overlay is in the next.
-    gop_coding_frames = 1;
-    *use_alt_ref = 1;
-    while (gop_coding_frames < rc->frames_to_key) {
-      const int frame_index = gf_start_show_idx + gop_coding_frames;
-      ++gop_coding_frames;
-      if (external_arf_indexes[frame_index] == 1) break;
-    }
-    return gop_coding_frames;
-  }
-#endif  // CONFIG_RATE_CTRL
 
   *use_alt_ref = 1;
   gop_coding_frames = 0;
@@ -2770,15 +2749,26 @@
     gop_intra_factor = 1.0;
   }
 
-  {
-    gop_coding_frames = get_gop_coding_frame_num(
 #if CONFIG_RATE_CTRL
-        cpi->encode_command.external_arf_indexes,
-#endif
-        &use_alt_ref, frame_info, first_pass_info, rc, gf_start_show_idx,
-        &active_gf_interval, gop_intra_factor, cpi->oxcf.lag_in_frames);
-    use_alt_ref &= allow_alt_ref;
+  {
+    const GOP_COMMAND *gop_command = &cpi->encode_command.gop_command;
+    assert(allow_alt_ref == 1);
+    if (gop_command->use) {
+      gop_coding_frames = gop_command_coding_frame_count(gop_command);
+      use_alt_ref = gop_command->use_alt_ref;
+    } else {
+      gop_coding_frames = get_gop_coding_frame_num(
+          &use_alt_ref, frame_info, first_pass_info, rc, gf_start_show_idx,
+          &active_gf_interval, gop_intra_factor, cpi->oxcf.lag_in_frames);
+      use_alt_ref &= allow_alt_ref;
+    }
   }
+#else
+  gop_coding_frames = get_gop_coding_frame_num(
+      &use_alt_ref, frame_info, first_pass_info, rc, gf_start_show_idx,
+      &active_gf_interval, gop_intra_factor, cpi->oxcf.lag_in_frames);
+  use_alt_ref &= allow_alt_ref;
+#endif
 
   // Was the group length constrained by the requirement for a new KF?
   rc->constrained_gf_group = (gop_coding_frames >= rc->frames_to_key) ? 1 : 0;
@@ -3704,6 +3694,7 @@
                                    int *use_alt_ref, int *coding_frame_count,
                                    int *first_show_idx,
                                    int *last_gop_use_alt_ref) {
+  const GOP_COMMAND *gop_command = &cpi->encode_command.gop_command;
   // We make a copy of rc here because we want to get information from the
   // encoder without changing its state.
   // TODO(angiebird): Avoid copying rc here.
@@ -3726,14 +3717,19 @@
     *first_is_key_frame = 1;
   }
 
-  *coding_frame_count = vp9_get_gop_coding_frame_count(
-      cpi->encode_command.external_arf_indexes, &cpi->oxcf, &cpi->frame_info,
-      &cpi->twopass.first_pass_info, &rc, *first_show_idx, multi_layer_arf,
-      allow_alt_ref, *first_is_key_frame, *last_gop_use_alt_ref, use_alt_ref);
+  if (gop_command->use) {
+    *coding_frame_count = gop_command_coding_frame_count(gop_command);
+    *use_alt_ref = gop_command->use_alt_ref;
+    assert(*coding_frame_count < rc.frames_to_key);
+  } else {
+    *coding_frame_count = vp9_get_gop_coding_frame_count(
+        &cpi->oxcf, &cpi->frame_info, &cpi->twopass.first_pass_info, &rc,
+        *first_show_idx, multi_layer_arf, allow_alt_ref, *first_is_key_frame,
+        *last_gop_use_alt_ref, use_alt_ref);
+  }
 }
 
-int vp9_get_gop_coding_frame_count(const int *external_arf_indexes,
-                                   const VP9EncoderConfig *oxcf,
+int vp9_get_gop_coding_frame_count(const VP9EncoderConfig *oxcf,
                                    const FRAME_INFO *frame_info,
                                    const FIRST_PASS_INFO *first_pass_info,
                                    const RATE_CONTROL *rc, int show_idx,
@@ -3756,9 +3752,6 @@
   }
 
   frame_count = get_gop_coding_frame_num(
-#if CONFIG_RATE_CTRL
-      external_arf_indexes,
-#endif
       use_alt_ref, frame_info, first_pass_info, rc, show_idx,
       &active_gf_interval, gop_intra_factor, oxcf->lag_in_frames);
   *use_alt_ref &= allow_alt_ref;
@@ -3767,8 +3760,7 @@
 
 // Under CONFIG_RATE_CTRL, once the first_pass_info is ready, the number of
 // coding frames (including show frame and alt ref) can be determined.
-int vp9_get_coding_frame_num(const int *external_arf_indexes,
-                             const VP9EncoderConfig *oxcf,
+int vp9_get_coding_frame_num(const VP9EncoderConfig *oxcf,
                              const FRAME_INFO *frame_info,
                              const FIRST_PASS_INFO *first_pass_info,
                              int multi_layer_arf, int allow_alt_ref) {
@@ -3779,7 +3771,6 @@
   int show_idx = 0;
   int last_gop_use_alt_ref = 0;
   vp9_rc_init(oxcf, 1, &rc);
-  rc.static_scene_max_gf_interval = 250;
 
   while (show_idx < first_pass_info->num_frames) {
     int use_alt_ref;
@@ -3792,9 +3783,8 @@
     }
 
     gop_coding_frame_count = vp9_get_gop_coding_frame_count(
-        external_arf_indexes, oxcf, frame_info, first_pass_info, &rc, show_idx,
-        multi_layer_arf, allow_alt_ref, first_is_key_frame,
-        last_gop_use_alt_ref, &use_alt_ref);
+        oxcf, frame_info, first_pass_info, &rc, show_idx, multi_layer_arf,
+        allow_alt_ref, first_is_key_frame, last_gop_use_alt_ref, &use_alt_ref);
 
     rc.source_alt_ref_active = use_alt_ref;
     last_gop_use_alt_ref = use_alt_ref;
@@ -3806,6 +3796,30 @@
   }
   return coding_frame_num;
 }
+
+void vp9_get_key_frame_map(const VP9EncoderConfig *oxcf,
+                           const FRAME_INFO *frame_info,
+                           const FIRST_PASS_INFO *first_pass_info,
+                           int *key_frame_map) {
+  int show_idx = 0;
+  RATE_CONTROL rc;
+  vp9_rc_init(oxcf, 1, &rc);
+
+  // key_frame_map points to an int array with size equal to
+  // first_pass_info->num_frames, which is also the number of show frames in the
+  // video.
+  memset(key_frame_map, 0,
+         sizeof(*key_frame_map) * first_pass_info->num_frames);
+  while (show_idx < first_pass_info->num_frames) {
+    int key_frame_group_size;
+    key_frame_map[show_idx] = 1;
+    key_frame_group_size = vp9_get_frames_to_next_key(
+        oxcf, frame_info, first_pass_info, show_idx, rc.min_gf_interval);
+    assert(key_frame_group_size > 0);
+    show_idx += key_frame_group_size;
+  }
+  assert(show_idx == first_pass_info->num_frames);
+}
 #endif  // CONFIG_RATE_CTRL
 
 FIRSTPASS_STATS vp9_get_frame_stats(const TWO_PASS *twopass) {
diff --git a/vp9/encoder/vp9_firstpass.h b/vp9/encoder/vp9_firstpass.h
index dcaf2ee..b1047ea 100644
--- a/vp9/encoder/vp9_firstpass.h
+++ b/vp9/encoder/vp9_firstpass.h
@@ -264,7 +264,6 @@
 
 /*!\brief Call this function before coding a new group of pictures to get
  * information about it.
- * \param[in] external_arf_indexes External arf indexs passed in
  * \param[in] oxcf                 Encoder config
  * \param[in] frame_info           Frame info
  * \param[in] first_pass_info      First pass stats
@@ -279,8 +278,7 @@
  *
  * \return Returns coding frame count
  */
-int vp9_get_gop_coding_frame_count(const int *external_arf_indexes,
-                                   const struct VP9EncoderConfig *oxcf,
+int vp9_get_gop_coding_frame_count(const struct VP9EncoderConfig *oxcf,
                                    const FRAME_INFO *frame_info,
                                    const FIRST_PASS_INFO *first_pass_info,
                                    const RATE_CONTROL *rc, int show_idx,
@@ -288,11 +286,20 @@
                                    int first_is_key_frame,
                                    int last_gop_use_alt_ref, int *use_alt_ref);
 
-int vp9_get_coding_frame_num(const int *external_arf_indexes,
-                             const struct VP9EncoderConfig *oxcf,
+int vp9_get_coding_frame_num(const struct VP9EncoderConfig *oxcf,
                              const FRAME_INFO *frame_info,
                              const FIRST_PASS_INFO *first_pass_info,
                              int multi_layer_arf, int allow_alt_ref);
+
+/*!\brief Compute a key frame binary map indicates whether key frames appear
+ * in the corresponding positions. The passed in key_frame_map must point to an
+ * integer array with length equal to first_pass_info->num_frames, which is the
+ * number of show frames in the video.
+ */
+void vp9_get_key_frame_map(const struct VP9EncoderConfig *oxcf,
+                           const FRAME_INFO *frame_info,
+                           const FIRST_PASS_INFO *first_pass_info,
+                           int *key_frame_map);
 #endif  // CONFIG_RATE_CTRL
 
 FIRSTPASS_STATS vp9_get_frame_stats(const TWO_PASS *twopass);
diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c
index 4da5037..2d03bad 100644
--- a/vp9/encoder/vp9_ratectrl.c
+++ b/vp9/encoder/vp9_ratectrl.c
@@ -431,6 +431,11 @@
     rc->max_gf_interval = vp9_rc_get_default_max_gf_interval(
         oxcf->init_framerate, rc->min_gf_interval);
   rc->baseline_gf_interval = (rc->min_gf_interval + rc->max_gf_interval) / 2;
+  if ((oxcf->pass == 0) && (oxcf->rc_mode == VPX_Q)) {
+    rc->static_scene_max_gf_interval = FIXED_GF_INTERVAL;
+  } else {
+    rc->static_scene_max_gf_interval = MAX_STATIC_GF_GROUP_LENGTH;
+  }
 
   rc->force_max_q = 0;
   rc->last_post_encode_dropped_scene_change = 0;
diff --git a/vp9/simple_encode.cc b/vp9/simple_encode.cc
index c417a25..f52c180 100644
--- a/vp9/simple_encode.cc
+++ b/vp9/simple_encode.cc
@@ -612,6 +612,9 @@
   group_of_picture->show_frame_count = coding_frame_count - use_alt_ref;
   group_of_picture->start_show_index = first_show_idx;
   group_of_picture->start_coding_index = start_coding_index;
+  group_of_picture->first_is_key_frame = first_is_key_frame;
+  group_of_picture->use_alt_ref = use_alt_ref;
+  group_of_picture->last_gop_use_alt_ref = last_gop_use_alt_ref;
 
   // We need to make a copy of start reference frame info because we
   // use it to simulate the ref frame update.
@@ -776,6 +779,9 @@
   free_encoder(cpi);
   rewind(in_file_);
   vpx_img_free(&img);
+
+  // Generate key_frame_map based on impl_ptr_->first_pass_stats.
+  key_frame_map_ = ComputeKeyFrameMap();
 }
 
 std::vector<std::vector<double>> SimpleEncode::ObserveFirstPassStats() {
@@ -800,9 +806,8 @@
   return output_stats;
 }
 
-void SimpleEncode::SetExternalGroupOfPicture(
-    std::vector<int> external_arf_indexes) {
-  external_arf_indexes_ = external_arf_indexes;
+void SimpleEncode::SetExternalGroupOfPicture(std::vector<int> gop_map) {
+  gop_map_ = gop_map;
 }
 
 template <typename T>
@@ -813,6 +818,32 @@
   return const_cast<T *>(v.data());
 }
 
+static GOP_COMMAND GetGopCommand(const std::vector<int> &gop_map,
+                                 int start_show_index) {
+  assert(static_cast<size_t>(start_show_index) < gop_map.size());
+  assert((gop_map[start_show_index] & kGopMapFlagStart) != 0);
+  GOP_COMMAND gop_command;
+  if (gop_map.size() > 0) {
+    int end_show_index = start_show_index + 1;
+    // gop_map[end_show_index] & kGopMapFlagStart == 0 means this is
+    // the start of a gop.
+    while (static_cast<size_t>(end_show_index) < gop_map.size() &&
+           (gop_map[end_show_index] & kGopMapFlagStart) == 0) {
+      ++end_show_index;
+    }
+    const int show_frame_count = end_show_index - start_show_index;
+    int use_alt_ref = (gop_map[start_show_index] & kGopMapFlagUseAltRef) != 0;
+    if (static_cast<size_t>(end_show_index) == gop_map.size()) {
+      // This is the last gop group, there must be no altref.
+      use_alt_ref = 0;
+    }
+    gop_command_on(&gop_command, show_frame_count, use_alt_ref);
+  } else {
+    gop_command_off(&gop_command);
+  }
+  return gop_command;
+}
+
 void SimpleEncode::StartEncode() {
   assert(impl_ptr_->first_pass_stats.size() > 0);
   vpx_rational_t frame_rate =
@@ -834,9 +865,6 @@
   frame_coding_index_ = 0;
   show_frame_count_ = 0;
 
-  encode_command_set_external_arf_indexes(&impl_ptr_->cpi->encode_command,
-                                          GetVectorData(external_arf_indexes_));
-
   UpdateKeyFrameGroup(show_frame_count_);
 
   UpdateGroupOfPicture(impl_ptr_->cpi, frame_coding_index_, ref_frame_info_,
@@ -914,6 +942,9 @@
 
   IncreaseGroupOfPictureIndex(&group_of_picture_);
   if (IsGroupOfPictureFinished(group_of_picture_)) {
+    const GOP_COMMAND gop_command = GetGopCommand(gop_map_, show_frame_count_);
+    encode_command_set_gop_command(&impl_ptr_->cpi->encode_command,
+                                   gop_command);
     // This function needs to be called after ref_frame_info_ is updated
     // properly in PostUpdateRefFrameInfo() and UpdateKeyFrameGroup().
     UpdateGroupOfPicture(impl_ptr_->cpi, frame_coding_index_, ref_frame_info_,
@@ -1002,8 +1033,24 @@
   encode_command_reset_external_quantize_index(&impl_ptr_->cpi->encode_command);
 }
 
+static int GetCodingFrameNumFromGopMap(const std::vector<int> &gop_map) {
+  int start_show_index = 0;
+  int coding_frame_count = 0;
+  while (static_cast<size_t>(start_show_index) < gop_map.size()) {
+    const GOP_COMMAND gop_command = GetGopCommand(gop_map, start_show_index);
+    start_show_index += gop_command.show_frame_count;
+    coding_frame_count += gop_command_coding_frame_count(&gop_command);
+  }
+  assert(start_show_index == gop_map.size());
+  return coding_frame_count;
+}
+
 int SimpleEncode::GetCodingFrameNum() const {
-  assert(impl_ptr_->first_pass_stats.size() - 1 > 0);
+  assert(impl_ptr_->first_pass_stats.size() > 0);
+  if (gop_map_.size() > 0) {
+    return GetCodingFrameNumFromGopMap(gop_map_);
+  }
+
   // These are the default settings for now.
   const int multi_layer_arf = 0;
   const int allow_alt_ref = 1;
@@ -1017,11 +1064,32 @@
   fps_init_first_pass_info(&first_pass_info,
                            GetVectorData(impl_ptr_->first_pass_stats),
                            num_frames_);
-  return vp9_get_coding_frame_num(external_arf_indexes_.data(), &oxcf,
-                                  &frame_info, &first_pass_info,
+  return vp9_get_coding_frame_num(&oxcf, &frame_info, &first_pass_info,
                                   multi_layer_arf, allow_alt_ref);
 }
 
+std::vector<int> SimpleEncode::ComputeKeyFrameMap() const {
+  assert(impl_ptr_->first_pass_stats.size() == num_frames_);
+  vpx_rational_t frame_rate =
+      make_vpx_rational(frame_rate_num_, frame_rate_den_);
+  const VP9EncoderConfig oxcf =
+      vp9_get_encoder_config(frame_width_, frame_height_, frame_rate,
+                             target_bitrate_, VPX_RC_LAST_PASS);
+  FRAME_INFO frame_info = vp9_get_frame_info(&oxcf);
+  FIRST_PASS_INFO first_pass_info;
+  fps_init_first_pass_info(&first_pass_info,
+                           GetVectorData(impl_ptr_->first_pass_stats),
+                           num_frames_);
+  std::vector<int> key_frame_map(num_frames_, 0);
+  vp9_get_key_frame_map(&oxcf, &frame_info, &first_pass_info,
+                        GetVectorData(key_frame_map));
+  return key_frame_map;
+}
+
+std::vector<int> SimpleEncode::ObserveKeyFrameMap() const {
+  return key_frame_map_;
+}
+
 uint64_t SimpleEncode::GetFramePixelCount() const {
   assert(frame_width_ % 2 == 0);
   assert(frame_height_ % 2 == 0);
diff --git a/vp9/simple_encode.h b/vp9/simple_encode.h
index 4221a70..80ecafd 100644
--- a/vp9/simple_encode.h
+++ b/vp9/simple_encode.h
@@ -39,6 +39,14 @@
   kRefFrameTypeNone = -1,
 };
 
+enum GopMapFlag {
+  kGopMapFlagStart =
+      1 << 0,  // Indicate this location is the start of a group of pictures.
+  kGopMapFlagUseAltRef =
+      1 << 1,  // Indicate this group of pictures will use an alt ref. Only set
+               // this flag when kGopMapFlagStart is set.
+};
+
 // The frame is split to 4x4 blocks.
 // This structure contains the information of each 4x4 block.
 struct PartitionInfo {
@@ -255,6 +263,7 @@
   // triggered when the coded frame is the last one in the previous group of
   // pictures.
   std::vector<EncodeFrameInfo> encode_frame_list;
+
   // Indicates the index of the next coding frame in encode_frame_list.
   // In other words, EncodeFrameInfo of the next coding frame can be
   // obtained with encode_frame_list[next_encode_frame_index].
@@ -263,13 +272,25 @@
   // will be increased after each EncodeFrame()/EncodeFrameWithQuantizeIndex()
   // call.
   int next_encode_frame_index;
+
   // Number of show frames in this group of pictures.
   int show_frame_count;
+
   // The show index/timestamp of the earliest show frame in the group of
   // pictures.
   int start_show_index;
-  // The coding index of the first coding frame in the group of picture.
+
+  // The coding index of the first coding frame in the group of pictures.
   int start_coding_index;
+
+  // Indicates whether this group of pictures starts with a key frame.
+  int first_is_key_frame;
+
+  // Indicates whether this group of pictures uses an alt ref.
+  int use_alt_ref;
+
+  // Indicates whether previous group of pictures used an alt ref.
+  int last_gop_use_alt_ref;
 };
 
 class SimpleEncode {
@@ -283,8 +304,9 @@
   SimpleEncode(SimpleEncode &) = delete;
   SimpleEncode &operator=(const SimpleEncode &) = delete;
 
-  // Makes encoder compute the first pass stats and store it internally for
-  // future encode.
+  // Makes encoder compute the first pass stats and store it at
+  // impl_ptr_->first_pass_stats. key_frame_map_ is also computed based on the
+  // first pass stats.
   void ComputeFirstPassStats();
 
   // Outputs the first pass stats represented by a 2-D vector.
@@ -293,13 +315,23 @@
   // values. For details, please check FIRSTPASS_STATS in vp9_firstpass.h
   std::vector<std::vector<double>> ObserveFirstPassStats();
 
-  // Sets arf indexes for the video from external input.
-  // The arf index determines whether a frame is arf or not.
-  // Therefore it also determines the group of picture size.
-  // If set, VP9 will use the external arf index to make decision.
+  // Ouputs a copy of key_frame_map_, a binary vector with size equal to the
+  // number of show frames in the video. For each entry in the vector, 1
+  // indicates the position is a key frame and 0 indicates it's not a key frame.
+  // This function should be called after ComputeFirstPassStats()
+  std::vector<int> ObserveKeyFrameMap() const;
+
+  // Sets group of pictures map for coding the entire video.
+  // Each entry in the gop_map is corresponding to a show frame in the video.
+  // Therefore, the size of gop_map should equal to the number of show frames in
+  // the entire video.
+  // If a given entry's kGopMapFlagStart is set, it means this is the start of a
+  // gop. Once kGopMapFlagStart is set, one can set kGopMapFlagUseAltRef to
+  // indicate whether this gop use altref.
+  // If a given entry is zero, it means it's in the middle of a gop.
   // This function should be called only once after ComputeFirstPassStats(),
   // before StartEncode().
-  void SetExternalGroupOfPicture(std::vector<int> external_arf_indexes);
+  void SetExternalGroupOfPicture(std::vector<int> gop_map);
 
   // Initializes the encoder for actual encoding.
   // This function should be called after ComputeFirstPassStats().
@@ -341,6 +373,12 @@
   uint64_t GetFramePixelCount() const;
 
  private:
+  // Compute the key frame locations of the video based on first pass stats.
+  // The results are returned as a binary vector with 1s indicating keyframes
+  // and 0s indicating non keyframes.
+  // It has to be called after impl_ptr_->first_pass_stats is computed.
+  std::vector<int> ComputeKeyFrameMap() const;
+
   // Updates key_frame_group_size_, reset key_frame_group_index_ and init
   // ref_frame_info_.
   void UpdateKeyFrameGroup(int key_frame_show_index);
@@ -363,7 +401,8 @@
   std::FILE *out_file_;
   std::unique_ptr<EncodeImpl> impl_ptr_;
 
-  std::vector<int> external_arf_indexes_;
+  std::vector<int> key_frame_map_;
+  std::vector<int> gop_map_;
   GroupOfPicture group_of_picture_;
 
   // The key frame group size includes one key frame plus the number of