Set unused reference frames to first ref

If a reference frame is not referenced, then set the index for that
reference to the first one used/referenced instead of unused slot.
Unused slot means key frame, as key frame resets all slots with itself.

This CL extracts `get_first_ref_frame()` from `reset_fb_idx_unused()`
with a typo fixing, and sets all unused reference frames to first ref in
vp9 uncompressed header.

Bug: webrtc:13442
Change-Id: I99523bc2ceedf27efe376d1113851ff342982181
diff --git a/vp9/encoder/vp9_bitstream.c b/vp9/encoder/vp9_bitstream.c
index c23e150..3c4bdc9 100644
--- a/vp9/encoder/vp9_bitstream.c
+++ b/vp9/encoder/vp9_bitstream.c
@@ -1241,12 +1241,21 @@
       vpx_wb_write_literal(wb, vp9_get_refresh_mask(cpi), REF_FRAMES);
       write_frame_size(cm, wb);
     } else {
+      static const int flag_list[4] = { 0, VP9_LAST_FLAG, VP9_GOLD_FLAG,
+                                        VP9_ALT_FLAG };
+      const MV_REFERENCE_FRAME first_ref = get_first_ref_frame(cpi);
+      const int first_ref_map_idx = get_ref_frame_map_idx(cpi, first_ref);
       MV_REFERENCE_FRAME ref_frame;
       vpx_wb_write_literal(wb, vp9_get_refresh_mask(cpi), REF_FRAMES);
-      for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
-        assert(get_ref_frame_map_idx(cpi, ref_frame) != INVALID_IDX);
-        vpx_wb_write_literal(wb, get_ref_frame_map_idx(cpi, ref_frame),
-                             REF_FRAMES_LOG2);
+
+      // If a reference frame is not referenced, then set the index for that
+      // reference to the first one used/referenced.
+      for (ref_frame = LAST_FRAME; ref_frame < MAX_REF_FRAMES; ++ref_frame) {
+        const int referenced = cpi->ref_frame_flags & flag_list[ref_frame];
+        const int map_idx = referenced ? get_ref_frame_map_idx(cpi, ref_frame)
+                                       : first_ref_map_idx;
+        assert(map_idx != INVALID_IDX);
+        vpx_wb_write_literal(wb, map_idx, REF_FRAMES_LOG2);
         vpx_wb_write_bit(wb, cm->ref_frame_sign_bias[ref_frame]);
       }
 
diff --git a/vp9/encoder/vp9_encoder.h b/vp9/encoder/vp9_encoder.h
index 9774a64..1bca7de 100644
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -1196,14 +1196,24 @@
          (cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref);
 }
 
+static INLINE MV_REFERENCE_FRAME get_first_ref_frame(VP9_COMP *const cpi) {
+  static const int flag_list[4] = { 0, VP9_LAST_FLAG, VP9_GOLD_FLAG,
+                                    VP9_ALT_FLAG };
+  MV_REFERENCE_FRAME ref_frame = LAST_FRAME;
+  while (ref_frame < MAX_REF_FRAMES) {
+    if (cpi->ref_frame_flags & flag_list[ref_frame]) break;
+    ref_frame++;
+  }
+  return ref_frame;
+}
+
 static INLINE int get_ref_frame_map_idx(const VP9_COMP *cpi,
                                         MV_REFERENCE_FRAME ref_frame) {
-  if (ref_frame == LAST_FRAME) {
-    return cpi->lst_fb_idx;
-  } else if (ref_frame == GOLDEN_FRAME) {
-    return cpi->gld_fb_idx;
-  } else {
-    return cpi->alt_fb_idx;
+  switch (ref_frame) {
+    case LAST_FRAME: return cpi->lst_fb_idx;
+    case GOLDEN_FRAME: return cpi->gld_fb_idx;
+    case ALTREF_FRAME: return cpi->alt_fb_idx;
+    default: return INVALID_IDX;
   }
 }
 
diff --git a/vp9/encoder/vp9_svc_layercontext.c b/vp9/encoder/vp9_svc_layercontext.c
index ad3a8f7..f01cb17 100644
--- a/vp9/encoder/vp9_svc_layercontext.c
+++ b/vp9/encoder/vp9_svc_layercontext.c
@@ -73,7 +73,7 @@
     svc->downsample_filter_type[sl] = BILINEAR;
     svc->downsample_filter_phase[sl] = 8;  // Set to 8 for averaging filter.
     svc->framedrop_thresh[sl] = oxcf->drop_frames_water_mark;
-    svc->fb_idx_upd_tl0[sl] = -1;
+    svc->fb_idx_upd_tl0[sl] = INVALID_IDX;
     svc->drop_count[sl] = 0;
     svc->spatial_layer_sync[sl] = 0;
     svc->force_drop_constrained_from_above[sl] = 0;
@@ -462,32 +462,21 @@
   // fb_idx for that reference to the first one used/referenced.
   // This is to avoid setting fb_idx for a reference to a slot that is not
   // used/needed (i.e., since that reference is not referenced or refreshed).
-  static const int flag_list[4] = { 0, VP9_LAST_FLAG, VP9_GOLD_FLAG,
-                                    VP9_ALT_FLAG };
-  MV_REFERENCE_FRAME ref_frame;
-  MV_REFERENCE_FRAME first_ref = 0;
-  int first_fb_idx = 0;
-  int fb_idx[3] = { cpi->lst_fb_idx, cpi->gld_fb_idx, cpi->alt_fb_idx };
-  for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ref_frame++) {
-    if (cpi->ref_frame_flags & flag_list[ref_frame]) {
-      first_ref = ref_frame;
-      first_fb_idx = fb_idx[ref_frame - 1];
-      break;
+  const MV_REFERENCE_FRAME first_ref = get_first_ref_frame(cpi);
+  const int map_idx = get_ref_frame_map_idx(cpi, first_ref);
+  if (map_idx != INVALID_IDX) {
+    if (!(cpi->ref_frame_flags & VP9_LAST_FLAG ||
+          cpi->ext_refresh_last_frame)) {
+      cpi->lst_fb_idx = map_idx;
     }
-  }
-  if (first_ref > 0) {
-    if (first_ref != LAST_FRAME &&
-        !(cpi->ref_frame_flags & flag_list[LAST_FRAME]) &&
-        !cpi->ext_refresh_last_frame)
-      cpi->lst_fb_idx = first_fb_idx;
-    else if (first_ref != GOLDEN_FRAME &&
-             !(cpi->ref_frame_flags & flag_list[GOLDEN_FRAME]) &&
-             !cpi->ext_refresh_golden_frame)
-      cpi->gld_fb_idx = first_fb_idx;
-    else if (first_ref != ALTREF_FRAME &&
-             !(cpi->ref_frame_flags & flag_list[ALTREF_FRAME]) &&
-             !cpi->ext_refresh_alt_ref_frame)
-      cpi->alt_fb_idx = first_fb_idx;
+    if (!(cpi->ref_frame_flags & VP9_GOLD_FLAG ||
+          cpi->ext_refresh_golden_frame)) {
+      cpi->gld_fb_idx = map_idx;
+    }
+    if (!(cpi->ref_frame_flags & VP9_ALT_FLAG ||
+          cpi->ext_refresh_alt_ref_frame)) {
+      cpi->alt_fb_idx = map_idx;
+    }
   }
 }
 
@@ -716,9 +705,9 @@
   int sl = svc->spatial_layer_id = svc->spatial_layer_to_encode;
   cpi->svc.temporal_layer_id = cpi->svc.temporal_layer_id_per_spatial[sl];
   cpi->ext_refresh_frame_flags_pending = 1;
-  cpi->lst_fb_idx = svc->lst_fb_idx[sl];
-  cpi->gld_fb_idx = svc->gld_fb_idx[sl];
-  cpi->alt_fb_idx = svc->alt_fb_idx[sl];
+  if (svc->reference_last[sl]) cpi->lst_fb_idx = svc->lst_fb_idx[sl];
+  if (svc->reference_golden[sl]) cpi->gld_fb_idx = svc->gld_fb_idx[sl];
+  if (svc->reference_altref[sl]) cpi->alt_fb_idx = svc->alt_fb_idx[sl];
   cpi->ext_refresh_last_frame = 0;
   cpi->ext_refresh_golden_frame = 0;
   cpi->ext_refresh_alt_ref_frame = 0;
@@ -875,9 +864,9 @@
     // flags are passed via the encode call (bypass mode). Issue is that we're
     // resetting ext_refresh_frame_flags_pending to 0 on frame drops.
     if (svc->temporal_layering_mode != VP9E_TEMPORAL_LAYERING_MODE_BYPASS) {
-      memset(&svc->lst_fb_idx, -1, sizeof(svc->lst_fb_idx));
-      memset(&svc->gld_fb_idx, -1, sizeof(svc->lst_fb_idx));
-      memset(&svc->alt_fb_idx, -1, sizeof(svc->lst_fb_idx));
+      memset(&svc->lst_fb_idx, INVALID_IDX, sizeof(svc->lst_fb_idx));
+      memset(&svc->gld_fb_idx, INVALID_IDX, sizeof(svc->lst_fb_idx));
+      memset(&svc->alt_fb_idx, INVALID_IDX, sizeof(svc->lst_fb_idx));
       // These are set by API before the superframe is encoded and they are
       // passed to encoder layer by layer. Don't reset them on layer 0 in bypass
       // mode.
@@ -970,7 +959,7 @@
 
   if (svc->temporal_layering_mode != VP9E_TEMPORAL_LAYERING_MODE_BYPASS &&
       svc->last_layer_dropped[svc->spatial_layer_id] &&
-      svc->fb_idx_upd_tl0[svc->spatial_layer_id] != -1 &&
+      svc->fb_idx_upd_tl0[svc->spatial_layer_id] != INVALID_IDX &&
       !svc->layer_context[svc->temporal_layer_id].is_key_frame) {
     // For fixed/non-flexible mode, if the previous frame (same spatial layer
     // from previous superframe) was dropped, make sure the lst_fb_idx
diff --git a/vpx/vp8cx.h b/vpx/vp8cx.h
index 47c38d3..b3c50a9 100644
--- a/vpx/vp8cx.h
+++ b/vpx/vp8cx.h
@@ -897,13 +897,16 @@
   int alt_fb_idx[VPX_SS_MAX_LAYERS];         /**< Altref buffer index. */
   int update_buffer_slot[VPX_SS_MAX_LAYERS]; /**< Update reference frames. */
   // TODO(jianj): Remove update_last/golden/alt_ref, these are deprecated.
-  int update_last[VPX_SS_MAX_LAYERS];       /**< Update last. */
-  int update_golden[VPX_SS_MAX_LAYERS];     /**< Update golden. */
-  int update_alt_ref[VPX_SS_MAX_LAYERS];    /**< Update altref. */
-  int reference_last[VPX_SS_MAX_LAYERS];    /**< Last as reference. */
-  int reference_golden[VPX_SS_MAX_LAYERS];  /**< Golden as reference. */
-  int reference_alt_ref[VPX_SS_MAX_LAYERS]; /**< Altref as reference. */
-  int64_t duration[VPX_SS_MAX_LAYERS];      /**< Duration per spatial layer. */
+  int update_last[VPX_SS_MAX_LAYERS];    /**< Update last. */
+  int update_golden[VPX_SS_MAX_LAYERS];  /**< Update golden. */
+  int update_alt_ref[VPX_SS_MAX_LAYERS]; /**< Update altref. */
+  int reference_last[VPX_SS_MAX_LAYERS];
+  /**< Last as reference. Use first referenced index if FALSE. */
+  int reference_golden[VPX_SS_MAX_LAYERS];
+  /**< Golden as reference. Use first referenced index if FALSE. */
+  int reference_alt_ref[VPX_SS_MAX_LAYERS];
+  /**< Altref as reference. Use first referenced index if FALSE. */
+  int64_t duration[VPX_SS_MAX_LAYERS]; /**< Duration per spatial layer. */
 } vpx_svc_ref_frame_config_t;
 
 /*!\brief VP9 svc frame dropping mode.