Initial encoder implementation for fwd kf

Add encoder implementation to code forward reference
keyframes as a unfiltered altrefs. In this case, the altref source
is placed on the keyframe location determined in firstpass.c.
This frame is first coded as an invisible keyframe which only updates
the altref buffer. All reference buffers are cleared when the keyframe
is finally displayed using cm->show_existing_frame.

This is a preliminary implementation and has not yet made appropriate
changes to the rate control. There also seems to be a bug in some
instances when the keyframes are spread far apart, and seems to be
an issue with the way the postencode_update is being handled. I am
currently working on a fix as a followup.

This patch does not impact results if --enable-fwd-kf is set to 0.

Preliminary Results:
20 encoded frames with kf-max-dist set to 5: -9.47% gain
20 encoded frames with kf-max-dist set to 8: -11.02% gain
20 encoded frames with kf-max-dist set to 10: 1.60%  loss

STATS_CHANGED

Change-Id: I598038162756a473a033e8d1c26715b0429844a3
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 922ce2a..2caf0f5 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -2239,7 +2239,8 @@
 #endif  // USE_GF16_MULTI_LAYER
 
 static int get_refresh_mask(AV1_COMP *cpi) {
-  if (cpi->common.frame_type == KEY_FRAME || frame_is_sframe(&cpi->common))
+  if ((cpi->common.frame_type == KEY_FRAME && cpi->common.show_frame) ||
+      frame_is_sframe(&cpi->common))
     return 0xFF;
 
   int refresh_mask = 0;
@@ -3959,7 +3960,7 @@
   // The TD is now written outside the frame encode loop
 
   // write sequence header obu if KEY_FRAME, preceded by 4-byte size
-  if (cm->frame_type == KEY_FRAME) {
+  if (cm->frame_type == KEY_FRAME && cm->show_frame) {
     obu_header_size = write_obu_header(OBU_SEQUENCE_HEADER, 0, data);
 
     obu_payload_size = write_sequence_header_obu(cpi, data + obu_header_size);
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 1f99bf1..e713276 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -290,7 +290,8 @@
       cm->fb_of_context_type[i] = -1;
     }
     cm->fb_of_context_type[REGULAR_FRAME] =
-        get_ref_frame_map_idx(cpi, GOLDEN_FRAME);
+        cm->show_frame ? get_ref_frame_map_idx(cpi, GOLDEN_FRAME)
+                       : get_ref_frame_map_idx(cpi, ALTREF_FRAME);
     cm->frame_context_idx = REGULAR_FRAME;
   } else {
     const GF_GROUP *gf_group = &cpi->twopass.gf_group;
@@ -315,7 +316,7 @@
     }
   }
 
-  if (cm->frame_type == KEY_FRAME) {
+  if (cm->frame_type == KEY_FRAME && cm->show_frame) {
     cpi->refresh_golden_frame = 1;
     cpi->refresh_alt_ref_frame = 1;
     av1_zero(cpi->interp_filter_selected);
@@ -3343,7 +3344,9 @@
   // At this point the new frame has been encoded.
   // If any buffer copy / swapping is signaled it should be done here.
 
-  if (cm->frame_type == KEY_FRAME || frame_is_sframe(cm)) {
+  // Only update all of the reference buffers if a KEY_FRAME is also a
+  // show_frame. This ensures a fwd keyframe does not update all of the buffers
+  if ((cm->frame_type == KEY_FRAME && cm->show_frame) || frame_is_sframe(cm)) {
     for (int ref_frame = 0; ref_frame < REF_FRAMES; ++ref_frame) {
       ref_cnt_fb(pool->frame_bufs,
                  &cm->ref_frame_map[cpi->ref_fb_idx[ref_frame]],
@@ -4756,7 +4759,8 @@
       cpi->oxcf.allow_warped_motion && frame_might_allow_warped_motion(cm);
 
   // Reset the frame packet stamp index.
-  if (cm->frame_type == KEY_FRAME) cm->current_video_frame = 0;
+  if (cm->frame_type == KEY_FRAME && cm->show_frame)
+    cm->current_video_frame = 0;
 
   // NOTE:
   // (1) Move the setup of the ref_frame_flags upfront as it would be
@@ -4770,7 +4774,11 @@
   if (cm->show_existing_frame) {
     // NOTE(zoeliu): In BIDIR_PRED, the existing frame to show is the current
     //               BWDREF_FRAME in the reference frame buffer.
-    cm->frame_type = INTER_FRAME;
+    if (cm->frame_type == KEY_FRAME) {
+      cm->reset_decoder_state = 1;
+    } else {
+      cm->frame_type = INTER_FRAME;
+    }
     cm->show_frame = 1;
     cpi->frame_flags = *frame_flags;
 
@@ -4970,7 +4978,7 @@
   }
 
   // If the encoder forced a KEY_FRAME decision or if frame is an S_FRAME
-  if (cm->frame_type == KEY_FRAME || frame_is_sframe(cm)) {
+  if ((cm->frame_type == KEY_FRAME && cm->show_frame) || frame_is_sframe(cm)) {
     cpi->refresh_last_frame = 1;
   }
 
@@ -5637,8 +5645,8 @@
   cpi->refresh_alt2_ref_frame = 0;
   cpi->refresh_alt_ref_frame = 0;
 
-  // TODO(zoeliu@gmail.com): To support forward-KEY_FRAME and set up the
-  //                         following flag accordingly.
+  // Initialize fields related to forward keyframes
+  cpi->invisible_kf = 0;
   cm->reset_decoder_state = 0;
 
   // Don't allow a show_existing_frame to coincide with an error resilient or
@@ -5728,14 +5736,20 @@
     if ((source = av1_lookahead_peek(cpi->lookahead, arf_src_index)) != NULL) {
       cm->showable_frame = 1;
       cpi->alt_ref_source = source;
-
-      if (oxcf->arnr_max_frames > 0) {
-        // Produce the filtered ARF frame.
-        av1_temporal_filter(cpi, arf_src_index);
-        aom_extend_frame_borders(&cpi->alt_ref_buffer, num_planes);
-        force_src_buffer = &cpi->alt_ref_buffer;
+      if (arf_src_index == rc->frames_to_key) {
+        // Skip temporal filtering and mark as intra_only if we have a fwd_kf
+        const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
+        int which_arf = gf_group->arf_update_idx[gf_group->index];
+        cpi->is_arf_filter_off[which_arf] = 1;
+        cpi->invisible_kf = 1;
+      } else {
+        if (oxcf->arnr_max_frames > 0) {
+          // Produce the filtered ARF frame.
+          av1_temporal_filter(cpi, arf_src_index);
+          aom_extend_frame_borders(&cpi->alt_ref_buffer, num_planes);
+          force_src_buffer = &cpi->alt_ref_buffer;
+        }
       }
-
       cm->show_frame = 0;
       cm->intra_only = 0;
       cpi->refresh_alt_ref_frame = 1;
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 5212db2..2c018fb 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -472,6 +472,7 @@
   AV1EncoderConfig oxcf;
   struct lookahead_ctx *lookahead;
   struct lookahead_entry *alt_ref_source;
+  int invisible_kf;
 
   int optimize_speed_feature;
   int optimize_seg_arr[MAX_SEGMENTS];
diff --git a/av1/encoder/firstpass.c b/av1/encoder/firstpass.c
index 113c068..24cb503 100644
--- a/av1/encoder/firstpass.c
+++ b/av1/encoder/firstpass.c
@@ -2693,7 +2693,18 @@
   }
 
   // Set the interval until the next gf.
-  rc->baseline_gf_interval = i - (is_key_frame || rc->source_alt_ref_pending);
+  if (cpi->oxcf.fwd_kf_enabled) {
+    // Ensure the gf group before the next keyframe will contain an altref
+    if ((rc->frames_to_key - i < rc->min_gf_interval) &&
+        (rc->frames_to_key != i)) {
+      rc->baseline_gf_interval = AOMMIN(rc->frames_to_key - rc->min_gf_interval,
+                                        rc->static_scene_max_gf_interval);
+    } else {
+      rc->baseline_gf_interval = i;
+    }
+  } else {
+    rc->baseline_gf_interval = i - (is_key_frame || rc->source_alt_ref_pending);
+  }
   if (non_zero_stdev_count) avg_raw_err_stdev /= non_zero_stdev_count;
 
   // Disable extra altrefs and backward refs for "still" gf group:
@@ -3444,7 +3455,12 @@
     target_rate = av1_rc_clamp_pframe_target_size(cpi, target_rate);
     rc->base_frame_target = target_rate;
 
-    cm->frame_type = INTER_FRAME;
+    if (cpi->invisible_kf) {
+      assert(gf_group->update_type[gf_group->index] == ARF_UPDATE);
+      cm->frame_type = KEY_FRAME;
+    } else {
+      cm->frame_type = INTER_FRAME;
+    }
 
     // Do the firstpass stats indicate that this frame is skippable for the
     // partition search?