ESR: add syntax for 2d superres scaling factor

The new syntax supports 4 possible scaling factors, which will be
applied to both horizontal and vertical directions. The index is
first parsed, and the decoder will retrieve the scaling factor from
a lookup table. Currently the place holder is {8/10, 8/12, 8/14,
8/16}.
Also the data structures are generalized in order to support more
flexible factors instead of sticking with a fixed numerator(8).

Change-Id: I3174795d3be00102411b416cd35252e2f6fcfac0
diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h
index 11006e2..e5689ea 100644
--- a/av1/common/av1_common_int.h
+++ b/av1/common/av1_common_int.h
@@ -786,11 +786,24 @@
   int superres_upscaled_height; /*!< Super-resolved frame height */
   /**@}*/
 
+#if CONFIG_EXT_SUPERRES
+  /**
+   * \name Super-resolution scaling factor information.
+   * The index, numerator, and denominator of the superres scale used by this
+   * frame.
+   */
+  /**@{*/
+  uint8_t superres_scale_index;       /*!< Superres scaling index */
+  uint8_t superres_scale_numerator;   /*!< Superres scaling numerator */
+  uint8_t superres_scale_denominator; /*!< Superres scaling denominator */
+  /**@}*/
+#else   // CONFIG_EXT_SUPERRES
   /*!
    * The denominator of the superres scale used by this frame.
    * Note: The numerator is fixed to be SCALE_NUMERATOR.
    */
   uint8_t superres_scale_denominator;
+#endif  // CONFIG_EXT_SUPERRES
 
   /*!
    * If true, buffer removal times are present.
diff --git a/av1/common/enums.h b/av1/common/enums.h
index dd9a790..25824c5 100644
--- a/av1/common/enums.h
+++ b/av1/common/enums.h
@@ -678,8 +678,13 @@
   SCALABILITY_SS = 14
 } UENUM1BYTE(SCALABILITY_STRUCTURES);
 
+#if CONFIG_EXT_SUPERRES
+#define SUPERRES_SCALE_BITS 2
+#define SUPERRES_SCALES (1 << SUPERRES_SCALE_BITS)
+#else  // CONFIG_EXT_SUPERRES
 #define SUPERRES_SCALE_BITS 3
 #define SUPERRES_SCALE_DENOMINATOR_MIN (SCALE_NUMERATOR + 1)
+#endif  // CONFIG_EXT_SUPERRES
 
 // In large_scale_tile coding, external references are used.
 #define MAX_EXTERNAL_REFERENCES 128
diff --git a/av1/common/resize.c b/av1/common/resize.c
index 95d3368..ffe2346 100644
--- a/av1/common/resize.c
+++ b/av1/common/resize.c
@@ -1348,6 +1348,38 @@
   }
 }
 
+#if CONFIG_EXT_SUPERRES
+// Calculates the scaled dimension given the original dimension and the scale
+// denominator.
+static void calculate_scaled_size_helper(int *dim, int denom, int num) {
+  if (denom != num) {
+    // We need to ensure the constraint in "Appendix A" of the spec:
+    // * FrameWidth is greater than or equal to 16
+    // * FrameHeight is greater than or equal to 16
+    // For this, we clamp the downscaled dimension to at least 16. One
+    // exception: if original dimension itself was < 16, then we keep the
+    // downscaled dimension to be same as the original, to ensure that resizing
+    // is valid.
+    const int min_dim = AOMMIN(16, *dim);
+    // Use this version if we need *dim to be even
+    // *width = (*width * SCALE_NUMERATOR + denom) / (2 * denom);
+    // *width <<= 1;
+    *dim = (*dim * num + denom / 2) / (denom);
+    *dim = AOMMAX(*dim, min_dim);
+  }
+}
+
+void av1_calculate_scaled_size(int *width, int *height, int resize_denom) {
+  calculate_scaled_size_helper(width, resize_denom, SCALE_NUMERATOR);
+  calculate_scaled_size_helper(height, resize_denom, SCALE_NUMERATOR);
+}
+
+void av1_calculate_scaled_superres_size(int *width, int *height,
+                                        int superres_denom, int superres_num) {
+  calculate_scaled_size_helper(width, superres_denom, superres_num);
+  calculate_scaled_size_helper(height, superres_denom, superres_num);
+}
+#else   // CONFIG_EXT_SUPERRES
 // Calculates the scaled dimension given the original dimension and the scale
 // denominator.
 static void calculate_scaled_size_helper(int *dim, int denom) {
@@ -1378,6 +1410,7 @@
   (void)height;
   calculate_scaled_size_helper(width, superres_denom);
 }
+#endif  // CONFIG_EXT_SUPERRES
 
 void av1_calculate_unscaled_superres_size(int *width, int *height, int denom) {
   if (denom != SCALE_NUMERATOR) {
diff --git a/av1/common/resize.h b/av1/common/resize.h
index b08de80..db188a8 100644
--- a/av1/common/resize.h
+++ b/av1/common/resize.h
@@ -20,6 +20,17 @@
 extern "C" {
 #endif
 
+typedef struct {
+  uint8_t scale_num;
+  uint8_t scale_denom;
+} ScaleFactor;
+
+#if CONFIG_EXT_SUPERRES
+static const ScaleFactor superres_scales[SUPERRES_SCALES] = {
+  { 8, 10 }, { 8, 12 }, { 8, 14 }, { 8, 16 }
+};
+#endif  // CONFIG_EXT_SUPERRES
+
 void av1_resize_plane(const uint8_t *const input, int height, int width,
                       int in_stride, uint8_t *output, int height2, int width2,
                       int out_stride);
@@ -83,11 +94,17 @@
 // Calculates the scaled dimensions from the given original dimensions and the
 // resize scale denominator.
 void av1_calculate_scaled_size(int *width, int *height, int resize_denom);
-
+#if CONFIG_EXT_SUPERRES
+// Similar to above, but calculates scaled dimensions after superres from the
+// given original dimensions and superres scale denominator.
+void av1_calculate_scaled_superres_size(int *width, int *height,
+                                        int superres_denom, int superres_num);
+#else   // CONFIG_EXT_SUPERRES
 // Similar to above, but calculates scaled dimensions after superres from the
 // given original dimensions and superres scale denominator.
 void av1_calculate_scaled_superres_size(int *width, int *height,
                                         int superres_denom);
+#endif  // CONFIG_EXT_SUPERRES
 
 // Inverse of av1_calculate_scaled_superres_size() above: calculates the
 // original dimensions from the given scaled dimensions and the scale
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 9cd6888..d21180f 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -59,6 +59,9 @@
 #include "av1/common/tile_common.h"
 #include "av1/common/warped_motion.h"
 #include "av1/common/obmc.h"
+#if CONFIG_EXT_SUPERRES
+#include "av1/common/resize.h"
+#endif  // CONFIG_EXT_SUPERRES
 #include "av1/decoder/decodeframe.h"
 #include "av1/decoder/decodemv.h"
 #include "av1/decoder/decoder.h"
@@ -2013,6 +2016,17 @@
   if (!seq_params->enable_superres) return;
 
   if (aom_rb_read_bit(rb)) {
+#if CONFIG_EXT_SUPERRES
+    cm->superres_scale_index =
+        (uint8_t)aom_rb_read_literal(rb, SUPERRES_SCALE_BITS);
+    cm->superres_scale_denominator =
+        superres_scales[cm->superres_scale_index].scale_denom;
+    cm->superres_scale_numerator =
+        superres_scales[cm->superres_scale_index].scale_num;
+    av1_calculate_scaled_superres_size(width, height,
+                                       cm->superres_scale_denominator,
+                                       cm->superres_scale_numerator);
+#else   // CONFIG_EXT_SUPERRES
     cm->superres_scale_denominator =
         (uint8_t)aom_rb_read_literal(rb, SUPERRES_SCALE_BITS);
     cm->superres_scale_denominator += SUPERRES_SCALE_DENOMINATOR_MIN;
@@ -2020,9 +2034,13 @@
     // resized correctly
     av1_calculate_scaled_superres_size(width, height,
                                        cm->superres_scale_denominator);
+#endif  // CONFIG_EXT_SUPERRES
   } else {
     // 1:1 scaling - ie. no scaling, scale not provided
     cm->superres_scale_denominator = SCALE_NUMERATOR;
+#if CONFIG_EXT_SUPERRES
+    cm->superres_scale_numerator = SCALE_NUMERATOR;
+#endif  // CONFIG_EXT_SUPERRES
   }
 }
 
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index f484e58..88a8dc4 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -2430,21 +2430,42 @@
                                             struct aom_write_bit_buffer *wb) {
   const SequenceHeader *const seq_params = &cm->seq_params;
   if (!seq_params->enable_superres) {
+#if CONFIG_EXT_SUPERRES
+    assert(cm->superres_scale_denominator == cm->superres_scale_numerator);
+#else   // CONFIG_EXT_SUPERRES
     assert(cm->superres_scale_denominator == SCALE_NUMERATOR);
+#endif  // CONFIG_EXT_SUPERRES
     return;
   }
 
   // First bit is whether to to scale or not
+#if CONFIG_EXT_SUPERRES
+  if (cm->superres_scale_denominator == cm->superres_scale_numerator) {
+#else                         // CONFIG_EXT_SUPERRES
   if (cm->superres_scale_denominator == SCALE_NUMERATOR) {
+#endif                        // CONFIG_EXT_SUPERRES
     aom_wb_write_bit(wb, 0);  // no scaling
   } else {
     aom_wb_write_bit(wb, 1);  // scaling, write scale factor
+#if CONFIG_EXT_SUPERRES
+    assert(cm->superres_scale_denominator > cm->superres_scale_numerator);
+    // Current across-scale prediction can handle downsampling factor <= 2
+    assert(cm->superres_scale_denominator <= 2 * cm->superres_scale_numerator);
+    assert(cm->superres_scale_index >= 0);
+    assert(cm->superres_scale_index < SUPERRES_SCALES);
+    assert(cm->superres_scale_denominator ==
+           superres_scale_denominators[cm->superres_scale_index]);
+    assert(cm->superres_scale_numerator ==
+           superres_scale_numerators[cm->superres_scale_index]);
+    aom_wb_write_literal(wb, cm->superres_scale_index, SUPERRES_SCALE_BITS);
+#else   // CONFIG_EXT_SUPERRES
     assert(cm->superres_scale_denominator >= SUPERRES_SCALE_DENOMINATOR_MIN);
     assert(cm->superres_scale_denominator <
            SUPERRES_SCALE_DENOMINATOR_MIN + (1 << SUPERRES_SCALE_BITS));
     aom_wb_write_literal(
         wb, cm->superres_scale_denominator - SUPERRES_SCALE_DENOMINATOR_MIN,
         SUPERRES_SCALE_BITS);
+#endif  // CONFIG_EXT_SUPERRES
   }
 }
 
diff --git a/av1/encoder/ratectrl.h b/av1/encoder/ratectrl.h
index 9ce60bb..5beaa83 100644
--- a/av1/encoder/ratectrl.h
+++ b/av1/encoder/ratectrl.h
@@ -54,6 +54,9 @@
   int resize_width;
   int resize_height;
   uint8_t superres_denom;
+#if CONFIG_EXT_SUPERRES
+  uint8_t superres_num;
+#endif  // CONFIG_EXT_SUPERRES
 } size_params_type;
 
 enum {
diff --git a/av1/encoder/superres_scale.c b/av1/encoder/superres_scale.c
index 1ab260d..6c1eca7 100644
--- a/av1/encoder/superres_scale.c
+++ b/av1/encoder/superres_scale.c
@@ -77,6 +77,7 @@
   // Choose an arbitrary random number
   static unsigned int seed = 56789;
   const ResizeCfg *resize_cfg = &cpi->oxcf.resize_cfg;
+
   if (is_stat_generation_stage(cpi)) return SCALE_NUMERATOR;
   uint8_t new_denom = SCALE_NUMERATOR;
 
@@ -184,15 +185,29 @@
   return denom;
 }
 
+#if CONFIG_EXT_SUPERRES
+// TODO(yuec): redesign the algorithm to return a valid option that is in the
+// new lookup table.
+static ScaleFactor calculate_next_superres_scale(AV1_COMP *cpi) {
+#else   // CONFIG_EXT_SUPERRES
 static uint8_t calculate_next_superres_scale(AV1_COMP *cpi) {
+#endif  // CONFIG_EXT_SUPERRES
   // Choose an arbitrary random number
   static unsigned int seed = 34567;
   const AV1EncoderConfig *oxcf = &cpi->oxcf;
   const SuperResCfg *const superres_cfg = &oxcf->superres_cfg;
   const FrameDimensionCfg *const frm_dim_cfg = &oxcf->frm_dim_cfg;
   const RateControlCfg *const rc_cfg = &oxcf->rc_cfg;
+#if CONFIG_EXT_SUPERRES
+  ScaleFactor factor = { SCALE_NUMERATOR, SCALE_NUMERATOR };
+#endif  // CONFIG_EXT_SUPERRES
 
-  if (is_stat_generation_stage(cpi)) return SCALE_NUMERATOR;
+  if (is_stat_generation_stage(cpi))
+#if CONFIG_EXT_SUPERRES
+    return factor;
+#else   // CONFIG_EXT_SUPERRES
+    return SCALE_NUMERATOR;
+#endif  // CONFIG_EXT_SUPERRES
   uint8_t new_denom = SCALE_NUMERATOR;
 
   // Make sure that superres mode of the frame is consistent with the
@@ -269,9 +284,27 @@
     }
     default: assert(0);
   }
+#if CONFIG_EXT_SUPERRES
+  factor.scale_denom = new_denom;
+  return factor;
+#else   // CONFIG_EXT_SUPERRES
   return new_denom;
+#endif  // CONFIG_EXT_SUPERRES
 }
 
+#if CONFIG_EXT_SUPERRES
+static int dimension_is_ok(int orig_dim, int resized_dim, int denom, int nom) {
+  return (resized_dim * nom >= orig_dim * denom / 2);
+}
+
+static int dimensions_are_ok(int owidth, int oheight, size_params_type *rsz) {
+  const uint8_t denom = rsz->superres_denom;
+  const uint8_t nom = rsz->superres_num;
+
+  return dimension_is_ok(owidth, rsz->resize_width, denom, nom) &&
+         dimension_is_ok(oheight, rsz->resize_height, denom, nom);
+}
+#else   // CONFIG_EXT_SUPERRES
 static int dimension_is_ok(int orig_dim, int resized_dim, int denom) {
   return (resized_dim * SCALE_NUMERATOR >= orig_dim * denom / 2);
 }
@@ -281,6 +314,7 @@
   (void)oheight;
   return dimension_is_ok(owidth, rsz->resize_width, rsz->superres_denom);
 }
+#endif  // CONFIG_EXT_SUPERRES
 
 static int validate_size_scales(RESIZE_MODE resize_mode,
                                 aom_superres_mode superres_mode, int owidth,
@@ -323,7 +357,12 @@
              superres_mode == AOM_SUPERRES_RANDOM) {
     // Alter both resize and superres scales as needed to enforce conformity.
     do {
+#if CONFIG_EXT_SUPERRES
+      if (resize_denom * rsz->superres_num >
+          rsz->superres_denom * SCALE_NUMERATOR)
+#else   // CONFIG_EXT_SUPERRES
       if (resize_denom > rsz->superres_denom)
+#endif  // CONFIG_EXT_SUPERRES
         --resize_denom;
       else
         --rsz->superres_denom;
@@ -333,7 +372,11 @@
                                 resize_denom);
     } while (!dimensions_are_ok(owidth, oheight, rsz) &&
              (resize_denom > SCALE_NUMERATOR ||
+#if CONFIG_EXT_SUPERRES
+              rsz->superres_denom > rsz->superres_num));
+#else       // CONFIG_EXT_SUPERRES
               rsz->superres_denom > SCALE_NUMERATOR));
+#endif      // CONFIG_EXT_SUPERRES
   } else {  // We are allowed to alter neither resize scale nor superres
             // scale.
     return 0;
@@ -346,8 +389,14 @@
   const AV1EncoderConfig *oxcf = &cpi->oxcf;
   ResizePendingParams *resize_pending_params = &cpi->resize_pending_params;
   const FrameDimensionCfg *const frm_dim_cfg = &oxcf->frm_dim_cfg;
+#if CONFIG_EXT_SUPERRES
+  size_params_type rsz = { frm_dim_cfg->width, frm_dim_cfg->height,
+                           SCALE_NUMERATOR, SCALE_NUMERATOR };
+  ScaleFactor factor;
+#else   // CONFIG_EXT_SUPERRES
   size_params_type rsz = { frm_dim_cfg->width, frm_dim_cfg->height,
                            SCALE_NUMERATOR };
+#endif  // CONFIG_EXT_SUPERRES
   int resize_denom = SCALE_NUMERATOR;
   if (has_no_stats_stage(cpi) && cpi->use_svc &&
       cpi->svc.spatial_layer_id < cpi->svc.number_spatial_layers - 1) {
@@ -368,7 +417,13 @@
     av1_calculate_scaled_size(&rsz.resize_width, &rsz.resize_height,
                               resize_denom);
   }
+#if CONFIG_EXT_SUPERRES
+  factor = calculate_next_superres_scale(cpi);
+  rsz.superres_denom = factor.scale_denom;
+  rsz.superres_num = factor.scale_num;
+#else   // CONFIG_EXT_SUPERRES
   rsz.superres_denom = calculate_next_superres_scale(cpi);
+#endif  // CONFIG_EXT_SUPERRES
   if (!validate_size_scales(oxcf->resize_cfg.resize_mode, cpi->superres_mode,
                             frm_dim_cfg->width, frm_dim_cfg->height, &rsz))
     assert(0 && "Invalid scale parameters");
@@ -384,8 +439,14 @@
   cm->superres_upscaled_width = encode_width;
   cm->superres_upscaled_height = encode_height;
   cm->superres_scale_denominator = rsz->superres_denom;
+#if CONFIG_EXT_SUPERRES
+  cm->superres_scale_numerator = rsz->superres_num;
+  av1_calculate_scaled_superres_size(&encode_width, &encode_height,
+                                     rsz->superres_denom, rsz->superres_num);
+#else
   av1_calculate_scaled_superres_size(&encode_width, &encode_height,
                                      rsz->superres_denom);
+#endif
   av1_set_frame_size(cpi, encode_width, encode_height);
 }
 
@@ -393,7 +454,19 @@
   AV1_COMMON *cm = &cpi->common;
   // Reset superres params from previous frame.
   cm->superres_scale_denominator = SCALE_NUMERATOR;
+#if CONFIG_EXT_SUPERRES
+  cm->superres_scale_numerator = SCALE_NUMERATOR;
+#endif  // CONFIG_EXT_SUPERRES
   const size_params_type rsz = calculate_next_size_params(cpi);
+#if CONFIG_EXT_SUPERRES
+  // TODO(yuec): get the index from the decision made above once the new
+  // algorithm for selecting the scaling factor is completed.
+  cm->superres_scale_index = 0;
+  cm->superres_scale_denominator =
+      superres_scales[cm->superres_scale_index].scale_denom;
+  cm->superres_scale_numerator =
+      superres_scales[cm->superres_scale_index].scale_num;
+#endif  // CONFIG_EXT_SUPERRES
   setup_frame_size_from_params(cpi, &rsz);
 
   assert(av1_is_min_tile_width_satisfied(cm));
diff --git a/av1/encoder/superres_scale.h b/av1/encoder/superres_scale.h
index 450a4ed..2a348a2 100644
--- a/av1/encoder/superres_scale.h
+++ b/av1/encoder/superres_scale.h
@@ -13,6 +13,7 @@
 #define AOM_AV1_ENCODER_SUPERRES_SCALE_H_
 
 #include "av1/encoder/encoder.h"
+#include "av1/common/resize.h"
 
 #ifdef __cplusplus
 extern "C" {