Added monochrome option to the decoder.

When this is set (use --monochrome), all decoded frames
will be given constant chroma planes.

If the rawvideo option is used in conjunction with the
monochrome option (i.e. --monochrome --rawvideo), the
written output will only consist of the Y (luma) plane.

Change-Id: I967817f1c3ebb1162fa9771b51cf6431120b835c
diff --git a/aom/aom_decoder.h b/aom/aom_decoder.h
index ceab934..c5c54c8 100644
--- a/aom/aom_decoder.h
+++ b/aom/aom_decoder.h
@@ -104,7 +104,8 @@
   unsigned int w;       /**< Width */
   unsigned int h;       /**< Height */
   unsigned int allow_lowbitdepth; /**< Allow use of low-bitdepth coding path */
-} aom_codec_dec_cfg_t;            /**< alias for struct aom_codec_dec_cfg */
+  unsigned int monochrome; /**< Whether or not the stream is monochrome */
+} aom_codec_dec_cfg_t;     /**< alias for struct aom_codec_dec_cfg */
 
 /*!\brief Initialize a decoder instance
  *
diff --git a/aomdec.c b/aomdec.c
index 89d9403..263cbf9 100644
--- a/aomdec.c
+++ b/aomdec.c
@@ -69,6 +69,10 @@
     ARG_DEF(NULL, "flipuv", 0, "Flip the chroma planes in the output");
 static const arg_def_t rawvideo =
     ARG_DEF(NULL, "rawvideo", 0, "Output raw YUV frames");
+#if CONFIG_MONO_VIDEO
+static const arg_def_t monochrome = ARG_DEF(
+    NULL, "monochrome", 0, "Force monochrome output (constant chroma planes)");
+#endif
 static const arg_def_t noblitarg =
     ARG_DEF(NULL, "noblit", 0, "Don't process the decoded frames");
 static const arg_def_t progressarg =
@@ -118,6 +122,9 @@
                                        &use_i420,
                                        &flipuvarg,
                                        &rawvideo,
+#if CONFIG_MONO_VIDEO
+                                       &monochrome,
+#endif
                                        &noblitarg,
                                        &progressarg,
                                        &limitarg,
@@ -289,8 +296,8 @@
   }
 }
 
-static void write_image_file(const aom_image_t *img, const int planes[3],
-                             FILE *file) {
+static void write_image_file(const aom_image_t *img, const int *planes,
+                             const int num_planes, FILE *file) {
   int i, y;
 #if CONFIG_HIGHBITDEPTH
   const int bytes_per_sample = ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
@@ -298,7 +305,7 @@
   const int bytes_per_sample = 1;
 #endif
 
-  for (i = 0; i < 3; ++i) {
+  for (i = 0; i < num_planes; ++i) {
     const int plane = planes[i];
     const unsigned char *buf = img->planes[plane];
     const int stride = img->stride[plane];
@@ -523,7 +530,10 @@
   int use_y4m = 1;
   int opt_yv12 = 0;
   int opt_i420 = 0;
-  aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH };
+#if CONFIG_MONO_VIDEO
+  int opt_rawvideo = 0;
+#endif
+  aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH, 0 };
 #if CONFIG_HIGHBITDEPTH
   unsigned int output_bit_depth = 0;
 #endif
@@ -589,6 +599,11 @@
       opt_i420 = 1;
     } else if (arg_match(&arg, &rawvideo, argi)) {
       use_y4m = 0;
+#if CONFIG_MONO_VIDEO
+      opt_rawvideo = 1;
+    } else if (arg_match(&arg, &monochrome, argi)) {
+      cfg.monochrome = 1;
+#endif
     } else if (arg_match(&arg, &flipuvarg, argi)) {
       flipuv = 1;
     } else if (arg_match(&arg, &noblitarg, argi)) {
@@ -931,6 +946,12 @@
       aom_input_ctx.height = img->d_h;
 #endif  // CONFIG_EXT_TILE
 
+#if CONFIG_MONO_VIDEO
+      int num_planes = (opt_rawvideo && cfg.monochrome) ? 1 : 3;
+#else
+      int num_planes = 3;
+#endif
+
       if (single_file) {
         if (use_y4m) {
           char y4m_buf[Y4M_BUFFER_SIZE] = { 0 };
@@ -984,7 +1005,7 @@
         if (do_md5) {
           update_image_md5(img, planes, &md5_ctx);
         } else {
-          write_image_file(img, planes, outfile);
+          write_image_file(img, planes, num_planes, outfile);
         }
       } else {
         generate_filename(outfile_pattern, outfile_name, PATH_MAX, img->d_w,
@@ -996,7 +1017,7 @@
           print_md5(md5_digest, outfile_name);
         } else {
           outfile = open_outfile(outfile_name);
-          write_image_file(img, planes, outfile);
+          write_image_file(img, planes, num_planes, outfile);
           fclose(outfile);
         }
       }
diff --git a/aomenc.c b/aomenc.c
index 936a335..6071e38 100644
--- a/aomenc.c
+++ b/aomenc.c
@@ -1461,7 +1461,7 @@
 #if CONFIG_AV1_DECODER
   if (global->test_decode != TEST_DECODE_OFF) {
     const AvxInterface *decoder = get_aom_decoder_by_name(global->codec->name);
-    aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH };
+    aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH, 0 };
     aom_codec_dec_init(&stream->decoder, decoder->codec_interface(), &cfg, 0);
 
 #if CONFIG_EXT_TILE
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 3a02a8b..0d3914d 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -514,6 +514,9 @@
     }
 #endif
     frame_worker_data->pbi->allow_lowbitdepth = ctx->cfg.allow_lowbitdepth;
+#if CONFIG_MONO_VIDEO
+    frame_worker_data->pbi->monochrome = ctx->cfg.monochrome;
+#endif
 
     // If decoding in serial mode, FrameWorker thread could create tile worker
     // thread or loopfilter thread.
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index f079170..0f17c53 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -3607,8 +3607,9 @@
 #endif
 
 #if CONFIG_MONO_VIDEO
-  // If the bit stream is monochrome, set the U and V buffers to a constant.
-  if (cm->seq_params.monochrome) {
+  // If the bit stream is monochrome, or the decoder is set to force a
+  // monochrome output, set the U and V buffers to a constant.
+  if (pbi->monochrome || cm->seq_params.monochrome) {
 #if CONFIG_HIGHBITDEPTH
     const int bytes_per_sample = cm->use_highbitdepth ? 2 : 1;
 #else
diff --git a/av1/decoder/decoder.h b/av1/decoder/decoder.h
index 28227a5..b5d97b7 100644
--- a/av1/decoder/decoder.h
+++ b/av1/decoder/decoder.h
@@ -81,6 +81,9 @@
   void *decrypt_state;
 
   int allow_lowbitdepth;
+#if CONFIG_MONO_VIDEO
+  int monochrome;
+#endif
   int max_threads;
   int inv_tile_order;
   int need_resync;   // wait for key/intra-only frame.