aom_qm_ext: add signaling for separate QM for U/V

Change-Id: I9879264011f6450bd2eb6648e39e9ad47f13a7d8
diff --git a/aom/aomcx.h b/aom/aomcx.h
index 1cedd58..45cd64c 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -565,6 +565,45 @@
    */
   AV1E_SET_QM_MAX,
 
+  /*!\brief Codec control function to set the min quant matrix flatness.
+   *
+   * AOM can operate with different ranges of quantisation matrices.
+   * As quantisation levels increase, the matrices get flatter. This
+   * control sets the flatness for luma (Y).
+   *
+   *  By default, the encoder sets this minimum at half the available
+   *  range.
+   *
+   * Experiment: AOM_QM
+   */
+  AV1E_SET_QM_Y,
+
+  /*!\brief Codec control function to set the min quant matrix flatness.
+   *
+   * AOM can operate with different ranges of quantisation matrices.
+   * As quantisation levels increase, the matrices get flatter. This
+   * control sets the flatness for chroma (U).
+   *
+   *  By default, the encoder sets this minimum at half the available
+   *  range.
+   *
+   * Experiment: AOM_QM
+   */
+  AV1E_SET_QM_U,
+
+  /*!\brief Codec control function to set the min quant matrix flatness.
+   *
+   * AOM can operate with different ranges of quantisation matrices.
+   * As quantisation levels increase, the matrices get flatter. This
+   * control sets the flatness for chrome (V).
+   *
+   *  By default, the encoder sets this minimum at half the available
+   *  range.
+   *
+   * Experiment: AOM_QM
+   */
+  AV1E_SET_QM_V,
+
   /*!\brief Codec control function to encode with dist_8x8.
    *
    *  The dist_8x8 is enabled automatically for model tuning parameters that
@@ -882,6 +921,15 @@
 AOM_CTRL_USE_TYPE(AV1E_SET_QM_MAX, unsigned int)
 #define AOM_CTRL_AV1E_SET_QM_MAX
 
+AOM_CTRL_USE_TYPE(AV1E_SET_QM_Y, unsigned int)
+#define AOM_CTRL_AV1E_SET_QM_Y
+
+AOM_CTRL_USE_TYPE(AV1E_SET_QM_U, unsigned int)
+#define AOM_CTRL_AV1E_SET_QM_U
+
+AOM_CTRL_USE_TYPE(AV1E_SET_QM_V, unsigned int)
+#define AOM_CTRL_AV1E_SET_QM_V
+
 AOM_CTRL_USE_TYPE(AV1E_SET_NUM_TG, unsigned int)
 #define AOM_CTRL_AV1E_SET_NUM_TG
 AOM_CTRL_USE_TYPE(AV1E_SET_MTU, unsigned int)
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 20fef35..9430a47 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -67,9 +67,14 @@
   unsigned int enable_cdef;
 #if CONFIG_AOM_QM
   unsigned int enable_qm;
+#if CONFIG_AOM_QM_EXT
+  unsigned int qm_y;
+  unsigned int qm_u;
+  unsigned int qm_v;
+#endif  // CONFIG_AOM_QM_EXT
   unsigned int qm_min;
   unsigned int qm_max;
-#endif
+#endif  // CONFIG_AOM_QM
 #if CONFIG_DIST_8X8
   unsigned int enable_dist_8x8;
 #endif
@@ -143,7 +148,12 @@
   0,              // lossless
   1,              // enable_cdef
 #if CONFIG_AOM_QM
-  0,                 // enable_qm
+  0,  // enable_qm
+#if CONFIG_AOM_QM_EXT
+  DEFAULT_QM_Y,      // qm_y
+  DEFAULT_QM_U,      // qm_u
+  DEFAULT_QM_V,      // qm_v
+#endif               // CONFIG_AOM_QM_EXT
   DEFAULT_QM_FIRST,  // qm_min
   DEFAULT_QM_LAST,   // qm_max
 #endif
@@ -564,6 +574,11 @@
   oxcf->using_cdef = extra_cfg->enable_cdef;
 #if CONFIG_AOM_QM
   oxcf->using_qm = extra_cfg->enable_qm;
+#if CONFIG_AOM_QM_EXT
+  oxcf->qm_y = extra_cfg->qm_y;
+  oxcf->qm_u = extra_cfg->qm_u;
+  oxcf->qm_v = extra_cfg->qm_v;
+#endif  // CONFIG_AOM_QM_EXT
   oxcf->qm_minlevel = extra_cfg->qm_min;
   oxcf->qm_maxlevel = extra_cfg->qm_max;
 #endif
@@ -986,6 +1001,23 @@
   extra_cfg.enable_qm = CAST(AV1E_SET_ENABLE_QM, args);
   return update_extra_cfg(ctx, &extra_cfg);
 }
+#if CONFIG_AOM_QM_EXT
+static aom_codec_err_t ctrl_set_qm_y(aom_codec_alg_priv_t *ctx, va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.qm_y = CAST(AV1E_SET_QM_Y, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+static aom_codec_err_t ctrl_set_qm_u(aom_codec_alg_priv_t *ctx, va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.qm_u = CAST(AV1E_SET_QM_U, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+static aom_codec_err_t ctrl_set_qm_v(aom_codec_alg_priv_t *ctx, va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.qm_v = CAST(AV1E_SET_QM_V, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+#endif  // CONFIG_AOM_QM_EXT
 
 static aom_codec_err_t ctrl_set_qm_min(aom_codec_alg_priv_t *ctx,
                                        va_list args) {
@@ -1774,8 +1806,14 @@
   { AV1E_SET_ENABLE_CDEF, ctrl_set_enable_cdef },
 #if CONFIG_AOM_QM
   { AV1E_SET_ENABLE_QM, ctrl_set_enable_qm },
+#if CONFIG_AOM_QM_EXT
+  { AV1E_SET_QM_Y, ctrl_set_qm_y },
+  { AV1E_SET_QM_U, ctrl_set_qm_u },
+  { AV1E_SET_QM_V, ctrl_set_qm_v },
+#endif  // CONFIG_AOM_QM_EXT
   { AV1E_SET_QM_MIN, ctrl_set_qm_min },
   { AV1E_SET_QM_MAX, ctrl_set_qm_max },
+
 #endif
 #if CONFIG_DIST_8X8
   { AV1E_SET_ENABLE_DIST_8X8, ctrl_set_enable_dist_8x8 },
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index b7e44a8..e7520ad 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -371,6 +371,11 @@
 
   // Encoder
   int using_qmatrix;
+#if CONFIG_AOM_QM_EXT
+  int qm_y;
+  int qm_u;
+  int qm_v;
+#endif  // CONFIG_AOM_QM_EXT
   int min_qmlevel;
   int max_qmlevel;
 #endif
diff --git a/av1/common/quant_common.h b/av1/common/quant_common.h
index e3b4f84..0f2fc6e 100644
--- a/av1/common/quant_common.h
+++ b/av1/common/quant_common.h
@@ -31,6 +31,11 @@
 #define NUM_QM_LEVELS (1 << QM_LEVEL_BITS)
 /* Range of QMS is between first and last value, with offset applied to inter
  * blocks*/
+#if CONFIG_AOM_QM_EXT
+#define DEFAULT_QM_Y 10
+#define DEFAULT_QM_U 11
+#define DEFAULT_QM_V 12
+#endif  // CONFIG_AOM_QM_EXT
 #define DEFAULT_QM_FIRST 5
 #define DEFAULT_QM_LAST 9
 #endif
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 18fa2f0..c649c8e 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -1062,11 +1062,28 @@
 #if CONFIG_AOM_QM
   cm->using_qmatrix = aom_rb_read_bit(rb);
   if (cm->using_qmatrix) {
+#if CONFIG_AOM_QM_EXT
+    cm->qm_y = aom_rb_read_literal(rb, QM_LEVEL_BITS);
+    cm->qm_u = aom_rb_read_literal(rb, QM_LEVEL_BITS);
+#if CONFIG_EXT_QM
+    if (!cm->separate_uv_delta_q)
+      cm->qm_v = cm->qm_u;
+    else
+#endif
+      cm->qm_v = aom_rb_read_literal(rb, QM_LEVEL_BITS);
+#else
     cm->min_qmlevel = aom_rb_read_literal(rb, QM_LEVEL_BITS);
     cm->max_qmlevel = aom_rb_read_literal(rb, QM_LEVEL_BITS);
+#endif
   } else {
+#if CONFIG_AOM_QM_EXT
+    cm->qm_y = 0;
+    cm->qm_u = 0;
+    cm->qm_v = 0;
+#else
     cm->min_qmlevel = 0;
     cm->max_qmlevel = 0;
+#endif
   }
 #endif
 }
@@ -1075,8 +1092,10 @@
 static void setup_segmentation_dequant(AV1_COMMON *const cm) {
 #if CONFIG_AOM_QM
   const int using_qm = cm->using_qmatrix;
+#if !CONFIG_AOM_QM_EXT
   const int minqm = cm->min_qmlevel;
   const int maxqm = cm->max_qmlevel;
+#endif  // !CONFIG_AOM_QM_EXT
 #endif
   // When segmentation is disabled, only the first value is used.  The
   // remaining are don't cares.
@@ -1098,14 +1117,28 @@
     const int lossless = qindex == 0 && cm->y_dc_delta_q == 0 &&
                          cm->u_dc_delta_q == 0 && cm->u_ac_delta_q == 0 &&
                          cm->v_dc_delta_q == 0 && cm->v_ac_delta_q == 0;
-    // NB: depends on base index so there is only 1 set per frame
-    // No quant weighting when lossless or signalled not using QM
+// NB: depends on base index so there is only 1 set per frame
+// No quant weighting when lossless or signalled not using QM
+#if CONFIG_AOM_QM_EXT
+    int qmlevel = (lossless || using_qm == 0) ? NUM_QM_LEVELS - 1 : cm->qm_y;
+#else
     const int qmlevel = (lossless || using_qm == 0)
                             ? NUM_QM_LEVELS - 1
                             : aom_get_qmlevel(cm->base_qindex, minqm, maxqm);
+#endif
     for (int j = 0; j < TX_SIZES_ALL; ++j) {
       cm->y_iqmatrix[i][j] = aom_iqmatrix(cm, qmlevel, AOM_PLANE_Y, j);
+    }
+#if CONFIG_AOM_QM_EXT
+    qmlevel = (lossless || using_qm == 0) ? NUM_QM_LEVELS - 1 : cm->qm_u;
+#endif
+    for (int j = 0; j < TX_SIZES_ALL; ++j) {
       cm->u_iqmatrix[i][j] = aom_iqmatrix(cm, qmlevel, AOM_PLANE_U, j);
+    }
+#if CONFIG_AOM_QM_EXT
+    qmlevel = (lossless || using_qm == 0) ? NUM_QM_LEVELS - 1 : cm->qm_v;
+#endif
+    for (int j = 0; j < TX_SIZES_ALL; ++j) {
       cm->v_iqmatrix[i][j] = aom_iqmatrix(cm, qmlevel, AOM_PLANE_V, j);
     }
 #endif  // CONFIG_AOM_QM
diff --git a/av1/encoder/av1_quantize.c b/av1/encoder/av1_quantize.c
index 2e62dee..b1df95c 100644
--- a/av1/encoder/av1_quantize.c
+++ b/av1/encoder/av1_quantize.c
@@ -2065,12 +2065,18 @@
   const int qindex = av1_get_qindex(&cm->seg, segment_id, current_q_index);
   const int rdmult = av1_compute_rd_mult(cpi, qindex + cm->y_dc_delta_q);
 #if CONFIG_AOM_QM
+#if CONFIG_AOM_QM_EXT
+  int qmlevel = (xd->lossless[segment_id] || cm->using_qmatrix == 0)
+                    ? NUM_QM_LEVELS - 1
+                    : cm->qm_y;
+#else
   int minqm = cm->min_qmlevel;
   int maxqm = cm->max_qmlevel;
   // Quant matrix only depends on the base QP so there is only one set per frame
   int qmlevel = (xd->lossless[segment_id] || cm->using_qmatrix == 0)
                     ? NUM_QM_LEVELS - 1
                     : aom_get_qmlevel(cm->base_qindex, minqm, maxqm);
+#endif  // CONFIG_AOM_QM_EXT
 #endif
 
   // Y
@@ -2100,7 +2106,14 @@
   }
 #endif  // CONFIG_NEW_QUANT
 
-  // U
+// U
+#if CONFIG_AOM_QM
+#if CONFIG_AOM_QM_EXT
+  qmlevel = (xd->lossless[segment_id] || cm->using_qmatrix == 0)
+                ? NUM_QM_LEVELS - 1
+                : cm->qm_u;
+#endif
+#endif
   {
     x->plane[1].quant_QTX = quants->u_quant[qindex];
     x->plane[1].quant_fp_QTX = quants->u_quant_fp[qindex];
@@ -2129,7 +2142,14 @@
     }
 #endif  // CONFIG_NEW_QUANT
   }
-  // V
+// V
+#if CONFIG_AOM_QM
+#if CONFIG_AOM_QM_EXT
+  qmlevel = (xd->lossless[segment_id] || cm->using_qmatrix == 0)
+                ? NUM_QM_LEVELS - 1
+                : cm->qm_v;
+#endif
+#endif
   {
     x->plane[2].quant_QTX = quants->v_quant[qindex];
     x->plane[2].quant_fp_QTX = quants->v_quant_fp[qindex];
@@ -2181,6 +2201,21 @@
   cm->u_ac_delta_q = 0;
   cm->v_dc_delta_q = 0;
   cm->v_ac_delta_q = 0;
+#if CONFIG_AOM_QM
+#if CONFIG_AOM_QM_EXT
+  cm->qm_y = aom_get_qmlevel(cm->base_qindex, cm->min_qmlevel, cm->max_qmlevel);
+  cm->qm_u = aom_get_qmlevel(cm->base_qindex + cm->u_ac_delta_q,
+                             cm->min_qmlevel, cm->max_qmlevel);
+
+#if CONFIG_EXT_QM
+  if (!cm->separate_uv_delta_q)
+    cm->qm_v = cm->qm_u;
+  else
+#endif
+    cm->qm_v = aom_get_qmlevel(cm->base_qindex + cm->v_ac_delta_q,
+                               cm->min_qmlevel, cm->max_qmlevel);
+#endif
+#endif
 }
 
 // Table that converts 0-63 Q-range values passed in outside to the Qindex
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 6b3dfa4..0429b52 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -2473,8 +2473,19 @@
 #if CONFIG_AOM_QM
   aom_wb_write_bit(wb, cm->using_qmatrix);
   if (cm->using_qmatrix) {
+#if CONFIG_AOM_QM_EXT
+    aom_wb_write_literal(wb, cm->qm_y, QM_LEVEL_BITS);
+    aom_wb_write_literal(wb, cm->qm_u, QM_LEVEL_BITS);
+#if CONFIG_EXT_QM
+    if (!cm->separate_uv_delta_q)
+      assert(cm->qm_u == cm->qm_v);
+    else
+#endif
+      aom_wb_write_literal(wb, cm->qm_v, QM_LEVEL_BITS);
+#else
     aom_wb_write_literal(wb, cm->min_qmlevel, QM_LEVEL_BITS);
     aom_wb_write_literal(wb, cm->max_qmlevel, QM_LEVEL_BITS);
+#endif
   }
 #endif
 }
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index dab0ea4..9c18941 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -193,6 +193,11 @@
   int using_cdef;
 #if CONFIG_AOM_QM
   int using_qm;
+#if CONFIG_AOM_QM_EXT
+  int qm_y;
+  int qm_u;
+  int qm_v;
+#endif
   int qm_minlevel;
   int qm_maxlevel;
 #endif