Add decoder controls for getting last quantizer.

Also adds --framestats=file.csv to aomdec that prints a CSV file with
frame size and frame QP.

Change-Id: I3b70c4b3df35d0b97bdd83cfc4631f096573b4a2
diff --git a/aom/aomdx.h b/aom/aomdx.h
index 834cdff..2a00ecb 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -115,7 +115,10 @@
    */
   AV1_GET_ACCOUNTING,
 
-  AOM_DECODER_CTRL_ID_MAX,
+  /** control function to get last decoded frame quantizer. Returned value uses
+   * internal quantizer scale defined by the codec.
+   */
+  AOMD_GET_LAST_QUANTIZER,
 
   /** control function to set the range of tile decoding. A value that is
    * greater and equal to zero indicates only the specific row/column is
@@ -124,7 +127,9 @@
    * decoded.
    */
   AV1_SET_DECODE_TILE_ROW,
-  AV1_SET_DECODE_TILE_COL
+  AV1_SET_DECODE_TILE_COL,
+
+  AOM_DECODER_CTRL_ID_MAX,
 };
 
 /** Decrypt n bytes of data from input -> output, using the decrypt_state
@@ -159,6 +164,8 @@
 #define AOM_CTRL_AOMD_GET_FRAME_CORRUPTED
 AOM_CTRL_USE_TYPE(AOMD_GET_LAST_REF_USED, int *)
 #define AOM_CTRL_AOMD_GET_LAST_REF_USED
+AOM_CTRL_USE_TYPE(AOMD_GET_LAST_QUANTIZER, int *)
+#define AOM_CTRL_AOMD_GET_LAST_QUANTIZER
 AOM_CTRL_USE_TYPE(AOMD_SET_DECRYPTOR, aom_decrypt_init *)
 #define AOM_CTRL_AOMD_SET_DECRYPTOR
 // AOM_CTRL_USE_TYPE(AOMD_SET_DECRYPTOR, aom_decrypt_init *)
diff --git a/aomdec.c b/aomdec.c
index 026cb95..acfc7ce 100644
--- a/aomdec.c
+++ b/aomdec.c
@@ -97,6 +97,8 @@
     ARG_DEF(NULL, "frame-buffers", 1, "Number of frame buffers to use");
 static const arg_def_t md5arg =
     ARG_DEF(NULL, "md5", 0, "Compute the MD5 sum of the decoded frame");
+static const arg_def_t framestatsarg =
+    ARG_DEF(NULL, "framestats", 1, "Output per-frame stats (.csv format)");
 #if CONFIG_AOM_HIGHBITDEPTH
 static const arg_def_t outbitdeptharg =
     ARG_DEF(NULL, "output-bit-depth", 1, "Output bit-depth for decoded frames");
@@ -128,6 +130,7 @@
                                        &scalearg,
                                        &fb_arg,
                                        &md5arg,
+                                       &framestatsarg,
                                        &error_concealment,
                                        &continuearg,
 #if CONFIG_AOM_HIGHBITDEPTH
@@ -541,6 +544,8 @@
   char outfile_name[PATH_MAX] = { 0 };
   FILE *outfile = NULL;
 
+  FILE *framestats_file = NULL;
+
   MD5Context md5_ctx;
   unsigned char md5_digest[16];
 
@@ -567,9 +572,9 @@
         die("Error: Unrecognized argument (%s) to --codec\n", arg.val);
     } else if (arg_match(&arg, &looparg, argi)) {
       // no-op
-    } else if (arg_match(&arg, &outputfile, argi))
+    } else if (arg_match(&arg, &outputfile, argi)) {
       outfile_pattern = arg.val;
-    else if (arg_match(&arg, &use_yv12, argi)) {
+    } else if (arg_match(&arg, &use_yv12, argi)) {
       use_y4m = 0;
       flipuv = 1;
       opt_yv12 = 1;
@@ -579,24 +584,31 @@
       opt_i420 = 1;
     } else if (arg_match(&arg, &rawvideo, argi)) {
       use_y4m = 0;
-    } else if (arg_match(&arg, &flipuvarg, argi))
+    } else if (arg_match(&arg, &flipuvarg, argi)) {
       flipuv = 1;
-    else if (arg_match(&arg, &noblitarg, argi))
+    } else if (arg_match(&arg, &noblitarg, argi)) {
       noblit = 1;
-    else if (arg_match(&arg, &progressarg, argi))
+    } else if (arg_match(&arg, &progressarg, argi)) {
       progress = 1;
-    else if (arg_match(&arg, &limitarg, argi))
+    } else if (arg_match(&arg, &limitarg, argi)) {
       stop_after = arg_parse_uint(&arg);
-    else if (arg_match(&arg, &skiparg, argi))
+    } else if (arg_match(&arg, &skiparg, argi)) {
       arg_skip = arg_parse_uint(&arg);
-    else if (arg_match(&arg, &postprocarg, argi))
+    } else if (arg_match(&arg, &postprocarg, argi)) {
       postproc = 1;
-    else if (arg_match(&arg, &md5arg, argi))
+    } else if (arg_match(&arg, &md5arg, argi)) {
       do_md5 = 1;
-    else if (arg_match(&arg, &summaryarg, argi))
+    } else if (arg_match(&arg, &framestatsarg, argi)) {
+      framestats_file = fopen(arg.val, "w");
+      if (!framestats_file) {
+        die("Error: Could not open --framestats file (%s) for writing.\n",
+            arg.val);
+      }
+    } else if (arg_match(&arg, &summaryarg, argi)) {
       summary = 1;
-    else if (arg_match(&arg, &threadsarg, argi))
+    } else if (arg_match(&arg, &threadsarg, argi)) {
       cfg.threads = arg_parse_uint(&arg);
+    }
 #if CONFIG_AV1_DECODER
     else if (arg_match(&arg, &frameparallelarg, argi))
       frame_parallel = 1;
@@ -756,6 +768,8 @@
   frame_avail = 1;
   got_data = 0;
 
+  if (framestats_file) fprintf(framestats_file, "bytes,qp\r\n");
+
   /* Decode file */
   while (frame_avail || got_data) {
     aom_codec_iter_t iter = NULL;
@@ -781,6 +795,16 @@
           if (!keep_going) goto fail;
         }
 
+        if (framestats_file) {
+          int qp;
+          if (aom_codec_control(&decoder, AOMD_GET_LAST_QUANTIZER, &qp)) {
+            warn("Failed AOMD_GET_LAST_QUANTIZER: %s",
+                 aom_codec_error(&decoder));
+            if (!keep_going) goto fail;
+          }
+          fprintf(framestats_file, "%d,%d\r\n", (int)bytes_in_buffer, qp);
+        }
+
         aom_usec_timer_mark(&timer);
         dx_time += aom_usec_timer_elapsed(&timer);
       } else {
@@ -1017,6 +1041,8 @@
   free(ext_fb_list.ext_fb);
 
   fclose(infile);
+  if (framestats_file) fclose(framestats_file);
+
   free(argv);
 
   return ret;
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 644f31b..8b0637a 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -947,6 +947,15 @@
   return AOM_CODEC_INVALID_PARAM;
 }
 
+static aom_codec_err_t ctrl_get_last_quantizer(aom_codec_alg_priv_t *ctx,
+                                               va_list args) {
+  int *const arg = va_arg(args, int *);
+  if (arg == NULL) return AOM_CODEC_INVALID_PARAM;
+  *arg =
+      ((FrameWorkerData *)ctx->frame_workers[0].data1)->pbi->common.base_qindex;
+  return AOM_CODEC_OK;
+}
+
 static aom_codec_err_t ctrl_get_frame_corrupted(aom_codec_alg_priv_t *ctx,
                                                 va_list args) {
   int *corrupted = va_arg(args, int *);
@@ -1142,14 +1151,15 @@
   { AV1_SET_DECODE_TILE_COL, ctrl_set_decode_tile_col },
 
   // Getters
-  { AOMD_GET_LAST_REF_UPDATES, ctrl_get_last_ref_updates },
   { AOMD_GET_FRAME_CORRUPTED, ctrl_get_frame_corrupted },
-  { AV1_GET_REFERENCE, ctrl_get_reference },
-  { AV1D_GET_DISPLAY_SIZE, ctrl_get_render_size },
+  { AOMD_GET_LAST_QUANTIZER, ctrl_get_last_quantizer },
+  { AOMD_GET_LAST_REF_UPDATES, ctrl_get_last_ref_updates },
   { AV1D_GET_BIT_DEPTH, ctrl_get_bit_depth },
+  { AV1D_GET_DISPLAY_SIZE, ctrl_get_render_size },
   { AV1D_GET_FRAME_SIZE, ctrl_get_frame_size },
   { AV1_GET_ACCOUNTING, ctrl_get_accounting },
   { AV1_GET_NEW_FRAME_IMAGE, ctrl_get_new_frame_image },
+  { AV1_GET_REFERENCE, ctrl_get_reference },
 
   { -1, NULL },
 };