Command-line option for max references per frame.

Currently works for 7 references (default) or 3 references.

Change-Id: Ib4133600352e52975efab080734b8107d06ec82a
diff --git a/aom/aomcx.h b/aom/aomcx.h
index b1536a5..223e7c3 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -979,6 +979,10 @@
   /*!\brief Control to select maximum height for the GF group pyramid structure
    * (valid values: 1 - 4) */
   AV1E_SET_GF_MAX_PYRAMID_HEIGHT,
+
+  /*!\brief Control to select maximum reference frames allowed per frame
+   * (valid values: 3 - 7) */
+  AV1E_SET_MAX_REFERENCE_FRAMES,
 };
 
 /*!\brief aom 1-D scaling mode
@@ -1359,6 +1363,9 @@
 AOM_CTRL_USE_TYPE(AV1E_SET_GF_MAX_PYRAMID_HEIGHT, unsigned int)
 #define AOM_CTRL_AV1E_SET_GF_MAX_PYRAMID_HEIGHT
 
+AOM_CTRL_USE_TYPE(AV1E_SET_MAX_REFERENCE_FRAMES, unsigned int)
+#define AOM_CTRL_AV1E_SET_MAX_REFERENCE_FRAMES
+
 /*!\endcond */
 /*! @} - end defgroup aom_encoder */
 #ifdef __cplusplus
diff --git a/apps/aomenc.c b/apps/aomenc.c
index c4ea248..123ab15 100644
--- a/apps/aomenc.c
+++ b/apps/aomenc.c
@@ -604,6 +604,9 @@
 static const arg_def_t gf_max_pyr_height =
     ARG_DEF(NULL, "gf-max-pyr-height", 1,
             "maximum height for GF group pyramid structure (1 to 4 (default))");
+static const arg_def_t max_reference_frames = ARG_DEF(
+    NULL, "max-reference-frames", 1,
+    "maximum number of reference frames allowed per frame (3 to 7 (default))");
 
 static const struct arg_enum_list color_primaries_enum[] = {
   { "bt709", AOM_CICP_CP_BT_709 },
@@ -782,7 +785,8 @@
 #if CONFIG_DENOISE
                                        &denoise_noise_level,
                                        &denoise_block_size,
-#endif
+#endif  // CONFIG_DENOISE
+                                       &max_reference_frames,
                                        &enable_ref_frame_mvs,
                                        &bitdeptharg,
                                        &inbitdeptharg,
@@ -865,7 +869,8 @@
 #if CONFIG_DENOISE
                                         AV1E_SET_DENOISE_NOISE_LEVEL,
                                         AV1E_SET_DENOISE_BLOCK_SIZE,
-#endif
+#endif  // CONFIG_DENOISE
+                                        AV1E_SET_MAX_REFERENCE_FRAMES,
                                         AV1E_SET_ENABLE_REF_FRAME_MVS,
                                         0 };
 #endif  // CONFIG_AV1_ENCODER
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index d513caf..e15209e 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -25,6 +25,7 @@
 #include "av1/encoder/bitstream.h"
 #include "av1/encoder/encoder.h"
 #include "av1/encoder/firstpass.h"
+#include "common/tools_common.h"
 
 #define MAG_SIZE (4)
 #define MAX_NUM_ENHANCEMENT_LAYERS 3
@@ -96,6 +97,7 @@
   int enable_order_hint;         // enable order hint for sequence
   int enable_tx64;               // enable 64-pt transform usage for sequence
   int enable_dist_wtd_comp;      // enable dist wtd compound for sequence
+  int max_reference_frames;      // maximum number of references per frame
   int enable_ref_frame_mvs;      // sequence level
   int allow_ref_frame_mvs;       // frame level
   int enable_masked_comp;        // enable masked compound for sequence
@@ -191,6 +193,7 @@
   1,                            // frame order hint
   1,                            // enable 64-pt transform usage
   1,                            // dist-wtd compound
+  7,                            // max_reference_frames
   1,                            // enable_ref_frame_mvs sequence level
   1,                            // allow ref_frame_mvs frame level
   1,                            // enable masked compound at sequence level
@@ -421,6 +424,7 @@
 #endif
   }
 
+  RANGE_CHECK(extra_cfg, max_reference_frames, 3, 7);
   RANGE_CHECK_HI(extra_cfg, chroma_subsampling_x, 1);
   RANGE_CHECK_HI(extra_cfg, chroma_subsampling_y, 1);
 
@@ -714,6 +718,14 @@
   oxcf->enable_order_hint = extra_cfg->enable_order_hint;
   oxcf->enable_dist_wtd_comp =
       extra_cfg->enable_dist_wtd_comp & extra_cfg->enable_order_hint;
+  oxcf->max_reference_frames = extra_cfg->max_reference_frames;
+  if (oxcf->max_reference_frames > 3 && oxcf->max_reference_frames < 7) {
+    // TODO(urvang): Enable all possible values, after they work properly.
+    warn(
+        "max_reference_frames values between 3 and 7 are not supported yet. "
+        "Using value 3 instead.");
+    oxcf->max_reference_frames = 3;
+  }
   oxcf->enable_masked_comp = extra_cfg->enable_masked_comp;
   oxcf->enable_diff_wtd_comp =
       extra_cfg->enable_masked_comp & extra_cfg->enable_diff_wtd_comp;
@@ -1112,6 +1124,13 @@
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
+static aom_codec_err_t ctrl_set_max_reference_frames(aom_codec_alg_priv_t *ctx,
+                                                     va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.max_reference_frames = CAST(AV1E_SET_MAX_REFERENCE_FRAMES, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+
 static aom_codec_err_t ctrl_set_enable_ref_frame_mvs(aom_codec_alg_priv_t *ctx,
                                                      va_list args) {
   struct av1_extracfg extra_cfg = ctx->extra_cfg;
@@ -1997,6 +2016,7 @@
   { AV1E_SET_ENABLE_ORDER_HINT, ctrl_set_enable_order_hint },
   { AV1E_SET_ENABLE_TX64, ctrl_set_enable_tx64 },
   { AV1E_SET_ENABLE_DIST_WTD_COMP, ctrl_set_enable_dist_wtd_comp },
+  { AV1E_SET_MAX_REFERENCE_FRAMES, ctrl_set_max_reference_frames },
   { AV1E_SET_ENABLE_REF_FRAME_MVS, ctrl_set_enable_ref_frame_mvs },
   { AV1E_SET_ALLOW_REF_FRAME_MVS, ctrl_set_allow_ref_frame_mvs },
   { AV1E_SET_ENABLE_MASKED_COMP, ctrl_set_enable_masked_comp },
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 9a4e533..f7abda7 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -329,6 +329,7 @@
   int enable_order_hint;
   int enable_dist_wtd_comp;
   int enable_ref_frame_mvs;
+  unsigned int max_reference_frames;
   unsigned int allow_ref_frame_mvs;
   int enable_masked_comp;
   int enable_interintra_comp;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index f87a99b..44a364d 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -10867,6 +10867,28 @@
   }
 
   mask->mode[INTRA_FRAME] |= ~(sf->intra_y_mode_mask[max_txsize_lookup[bsize]]);
+
+  // Disable some frames to enforce max number of reference frames requested.
+  // TODO(urvang): Should this logic be moved to enforce_max_ref_frames()?
+  const MV_REFERENCE_FRAME disable_order[] = {
+    LAST3_FRAME, LAST2_FRAME, ALTREF2_FRAME, BWDREF_FRAME,
+    // GOLDEN_FRAME,
+  };
+  assert(cpi->oxcf.max_reference_frames <= INTER_REFS_PER_FRAME);
+  const int num_frames_to_disable =
+      INTER_REFS_PER_FRAME - cpi->oxcf.max_reference_frames;
+  for (int i = 0; i < num_frames_to_disable; ++i) {
+    const MV_REFERENCE_FRAME ref_frame_to_disable = disable_order[i];
+
+    mask->ref_frame1 |= (1 << ref_frame_to_disable);
+    mask->ref_frame2 |= (1 << ref_frame_to_disable);
+
+    if (ref_frame_to_disable == BWDREF_FRAME &&
+        (cpi->ref_frame_flags & AOM_BWD_FLAG)) {
+      mask->ref_frame1 |= (1 << ALTREF_FRAME);
+      mask->ref_frame2 |= (1 << ALTREF_FRAME);
+    }
+  }
 }
 
 // Please add/modify parameter setting in this function, making it consistent