Fix y4m monochrome output; split raw/y4m logic

BUG=aomedia:2091

Change-Id: I2eed6a0b70c57415b323b7e8207592a7fdebba2b
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c99dbed..8f209ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -132,6 +132,8 @@
             "${AOM_ROOT}/common/tools_common.c"
             "${AOM_ROOT}/common/tools_common.h"
             "${AOM_ROOT}/common/video_common.h"
+            "${AOM_ROOT}/common/rawenc.c"
+            "${AOM_ROOT}/common/rawenc.h"
             "${AOM_ROOT}/common/y4menc.c"
             "${AOM_ROOT}/common/y4menc.h")
 
diff --git a/apps/aomdec.c b/apps/aomdec.c
index 1e34eaa..1bc5029 100644
--- a/apps/aomdec.c
+++ b/apps/aomdec.c
@@ -40,6 +40,7 @@
 #include "common/webmdec.h"
 #endif
 
+#include "common/rawenc.h"
 #include "common/y4menc.h"
 
 #if CONFIG_LIBYUV
@@ -255,44 +256,6 @@
   }
 }
 
-static void update_image_md5(const aom_image_t *img, const int planes[3],
-                             MD5Context *md5) {
-  int i, y;
-
-  for (i = 0; i < 3; ++i) {
-    const int plane = planes[i];
-    const unsigned char *buf = img->planes[plane];
-    const int stride = img->stride[plane];
-    const int w = aom_img_plane_width(img, plane) *
-                  ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
-    const int h = aom_img_plane_height(img, plane);
-
-    for (y = 0; y < h; ++y) {
-      MD5Update(md5, buf, w);
-      buf += stride;
-    }
-  }
-}
-
-static void write_image_file(const aom_image_t *img, const int *planes,
-                             const int num_planes, FILE *file) {
-  int i, y;
-  const int bytes_per_sample = ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
-
-  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];
-    const int w = aom_img_plane_width(img, plane);
-    const int h = aom_img_plane_height(img, plane);
-
-    for (y = 0; y < h; ++y) {
-      fwrite(buf, bytes_per_sample, w, file);
-      buf += stride;
-    }
-  }
-}
-
 static int file_is_raw(struct AvxInputContext *input) {
   uint8_t buf[32];
   int is_raw = 0;
@@ -904,6 +867,7 @@
             scaled_img =
                 aom_img_alloc(NULL, img->fmt, render_width, render_height, 16);
             scaled_img->bit_depth = img->bit_depth;
+            scaled_img->monochrome = img->monochrome;
           }
 
           if (img->d_w != scaled_img->d_w || img->d_h != scaled_img->d_h) {
@@ -960,8 +924,7 @@
         aom_input_ctx.width = img->d_w;
         aom_input_ctx.height = img->d_h;
 
-        int num_planes = (!use_y4m && opt_raw && img->monochrome) ? 1 : 3;
-
+        int num_planes = (opt_raw && img->monochrome) ? 1 : 3;
         if (single_file) {
           if (use_y4m) {
             char y4m_buf[Y4M_BUFFER_SIZE] = { 0 };
@@ -970,8 +933,8 @@
               // Y4M file header
               len = y4m_write_file_header(
                   y4m_buf, sizeof(y4m_buf), aom_input_ctx.width,
-                  aom_input_ctx.height, &aom_input_ctx.framerate, img->fmt,
-                  img->bit_depth);
+                  aom_input_ctx.height, &aom_input_ctx.framerate,
+                  img->monochrome, img->fmt, img->bit_depth);
               if (do_md5) {
                 MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len);
               } else {
@@ -983,8 +946,10 @@
             len = y4m_write_frame_header(y4m_buf, sizeof(y4m_buf));
             if (do_md5) {
               MD5Update(&md5_ctx, (md5byte *)y4m_buf, (unsigned int)len);
+              y4m_update_image_md5(img, planes, &md5_ctx);
             } else {
               fputs(y4m_buf, outfile);
+              y4m_write_image_file(img, planes, outfile);
             }
           } else {
             if (frame_out == 1) {
@@ -1008,24 +973,31 @@
                 }
               }
             }
-          }
-
-          if (do_md5) {
-            update_image_md5(img, planes, &md5_ctx);
-          } else {
-            write_image_file(img, planes, num_planes, outfile);
+            if (do_md5) {
+              raw_update_image_md5(img, planes, num_planes, &md5_ctx);
+            } else {
+              raw_write_image_file(img, planes, num_planes, outfile);
+            }
           }
         } else {
           generate_filename(outfile_pattern, outfile_name, PATH_MAX, img->d_w,
                             img->d_h, frame_in);
           if (do_md5) {
             MD5Init(&md5_ctx);
-            update_image_md5(img, planes, &md5_ctx);
+            if (use_y4m) {
+              y4m_update_image_md5(img, planes, &md5_ctx);
+            } else {
+              raw_update_image_md5(img, planes, num_planes, &md5_ctx);
+            }
             MD5Final(md5_digest, &md5_ctx);
             print_md5(md5_digest, outfile_name);
           } else {
             outfile = open_outfile(outfile_name);
-            write_image_file(img, planes, num_planes, outfile);
+            if (use_y4m) {
+              y4m_write_image_file(img, planes, outfile);
+            } else {
+              raw_write_image_file(img, planes, num_planes, outfile);
+            }
             fclose(outfile);
           }
         }
diff --git a/common/rawenc.c b/common/rawenc.c
new file mode 100644
index 0000000..5a2731d
--- /dev/null
+++ b/common/rawenc.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "common/rawenc.h"
+
+void raw_write_image_file(const aom_image_t *img, const int *planes,
+                          const int num_planes, FILE *file) {
+  const int bytes_per_sample = ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+  for (int i = 0; i < num_planes; ++i) {
+    const int plane = planes[i];
+    const unsigned char *buf = img->planes[plane];
+    const int stride = img->stride[plane];
+    const int w = aom_img_plane_width(img, plane);
+    const int h = aom_img_plane_height(img, plane);
+    for (int y = 0; y < h; ++y) {
+      fwrite(buf, bytes_per_sample, w, file);
+      buf += stride;
+    }
+  }
+}
+
+void raw_update_image_md5(const aom_image_t *img, const int *planes,
+                          const int num_planes, MD5Context *md5) {
+  for (int i = 0; i < num_planes; ++i) {
+    const int plane = planes[i];
+    const unsigned char *buf = img->planes[plane];
+    const int stride = img->stride[plane];
+    const int w = aom_img_plane_width(img, plane) *
+                  ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+    const int h = aom_img_plane_height(img, plane);
+    for (int y = 0; y < h; ++y) {
+      MD5Update(md5, buf, w);
+      buf += stride;
+    }
+  }
+}
diff --git a/common/rawenc.h b/common/rawenc.h
new file mode 100644
index 0000000..5c4a148
--- /dev/null
+++ b/common/rawenc.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef RAWENC_H_
+#define RAWENC_H_
+
+#include "aom/aom_decoder.h"
+#include "common/md5_utils.h"
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void raw_write_image_file(const aom_image_t *img, const int *planes,
+                          const int num_planes, FILE *file);
+void raw_update_image_md5(const aom_image_t *img, const int *planes,
+                          const int num_planes, MD5Context *md5);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // RAWENC_H_
diff --git a/common/y4menc.c b/common/y4menc.c
index d33d031..2daee87 100644
--- a/common/y4menc.c
+++ b/common/y4menc.c
@@ -11,56 +11,80 @@
 
 #include <assert.h>
 
+#include "common/rawenc.h"
 #include "common/y4menc.h"
 
-int y4m_write_file_header(char *buf, size_t len, int width, int height,
-                          const struct AvxRational *framerate,
-                          aom_img_fmt_t fmt, unsigned int bit_depth) {
-  const char *color;
+// Returns the Y4M name associated with the monochrome colorspace.
+const char *monochrome_colorspace(unsigned int bit_depth) {
+  switch (bit_depth) {
+    case 8: return "Cmono";
+    case 9: return "Cmono9";
+    case 10: return "Cmono10";
+    case 12: return "Cmono12";
+    case 16: return "Cmono16";
+    default: assert(0); return NULL;
+  }
+}
+
+// Return the Y4M name of the colorspace, given the bit depth and image format.
+const char *colorspace(unsigned int bit_depth, aom_img_fmt_t fmt) {
   switch (bit_depth) {
     case 8:
-      color = fmt == AOM_IMG_FMT_444A
-                  ? "C444alpha\n"
-                  : fmt == AOM_IMG_FMT_I444
-                        ? "C444\n"
-                        : fmt == AOM_IMG_FMT_I422 ? "C422\n" : "C420jpeg\n";
-      break;
+      return fmt == AOM_IMG_FMT_444A
+                 ? "C444alpha"
+                 : fmt == AOM_IMG_FMT_I444
+                       ? "C444"
+                       : fmt == AOM_IMG_FMT_I422 ? "C422" : "C420jpeg";
     case 9:
-      color = fmt == AOM_IMG_FMT_I44416
-                  ? "C444p9 XYSCSS=444P9\n"
-                  : fmt == AOM_IMG_FMT_I42216 ? "C422p9 XYSCSS=422P9\n"
-                                              : "C420p9 XYSCSS=420P9\n";
-      break;
+      return fmt == AOM_IMG_FMT_I44416
+                 ? "C444p9 XYSCSS=444P9"
+                 : fmt == AOM_IMG_FMT_I42216 ? "C422p9 XYSCSS=422P9"
+                                             : "C420p9 XYSCSS=420P9";
     case 10:
-      color = fmt == AOM_IMG_FMT_I44416
-                  ? "C444p10 XYSCSS=444P10\n"
-                  : fmt == AOM_IMG_FMT_I42216 ? "C422p10 XYSCSS=422P10\n"
-                                              : "C420p10 XYSCSS=420P10\n";
-      break;
+      return fmt == AOM_IMG_FMT_I44416
+                 ? "C444p10 XYSCSS=444P10"
+                 : fmt == AOM_IMG_FMT_I42216 ? "C422p10 XYSCSS=422P10"
+                                             : "C420p10 XYSCSS=420P10";
     case 12:
-      color = fmt == AOM_IMG_FMT_I44416
-                  ? "C444p12 XYSCSS=444P12\n"
-                  : fmt == AOM_IMG_FMT_I42216 ? "C422p12 XYSCSS=422P12\n"
-                                              : "C420p12 XYSCSS=420P12\n";
-      break;
+      return fmt == AOM_IMG_FMT_I44416
+                 ? "C444p12 XYSCSS=444P12"
+                 : fmt == AOM_IMG_FMT_I42216 ? "C422p12 XYSCSS=422P12"
+                                             : "C420p12 XYSCSS=420P12";
     case 14:
-      color = fmt == AOM_IMG_FMT_I44416
-                  ? "C444p14 XYSCSS=444P14\n"
-                  : fmt == AOM_IMG_FMT_I42216 ? "C422p14 XYSCSS=422P14\n"
-                                              : "C420p14 XYSCSS=420P14\n";
-      break;
+      return fmt == AOM_IMG_FMT_I44416
+                 ? "C444p14 XYSCSS=444P14"
+                 : fmt == AOM_IMG_FMT_I42216 ? "C422p14 XYSCSS=422P14"
+                                             : "C420p14 XYSCSS=420P14";
     case 16:
-      color = fmt == AOM_IMG_FMT_I44416
-                  ? "C444p16 XYSCSS=444P16\n"
-                  : fmt == AOM_IMG_FMT_I42216 ? "C422p16 XYSCSS=422P16\n"
-                                              : "C420p16 XYSCSS=420P16\n";
-      break;
-    default: color = NULL; assert(0);
+      return fmt == AOM_IMG_FMT_I44416
+                 ? "C444p16 XYSCSS=444P16"
+                 : fmt == AOM_IMG_FMT_I42216 ? "C422p16 XYSCSS=422P16"
+                                             : "C420p16 XYSCSS=420P16";
+    default: assert(0); return NULL;
   }
-  return snprintf(buf, len, "YUV4MPEG2 W%u H%u F%u:%u I%c %s", width, height,
+}
+
+int y4m_write_file_header(char *buf, size_t len, int width, int height,
+                          const struct AvxRational *framerate, int monochrome,
+                          aom_img_fmt_t fmt, unsigned int bit_depth) {
+  const char *color = monochrome ? monochrome_colorspace(bit_depth)
+                                 : colorspace(bit_depth, fmt);
+  return snprintf(buf, len, "YUV4MPEG2 W%u H%u F%u:%u I%c %s\n", width, height,
                   framerate->numerator, framerate->denominator, 'p', color);
 }
 
 int y4m_write_frame_header(char *buf, size_t len) {
   return snprintf(buf, len, "FRAME\n");
 }
+
+void y4m_write_image_file(const aom_image_t *img, const int *planes,
+                          FILE *file) {
+  int num_planes = img->monochrome ? 1 : 3;
+  raw_write_image_file(img, planes, num_planes, file);
+}
+
+void y4m_update_image_md5(const aom_image_t *img, const int *planes,
+                          MD5Context *md5) {
+  int num_planes = img->monochrome ? 1 : 3;
+  raw_update_image_md5(img, planes, num_planes, md5);
+}
diff --git a/common/y4menc.h b/common/y4menc.h
index 6344176..a133a3d 100644
--- a/common/y4menc.h
+++ b/common/y4menc.h
@@ -13,6 +13,7 @@
 #define Y4MENC_H_
 
 #include "aom/aom_decoder.h"
+#include "common/md5_utils.h"
 #include "common/tools_common.h"
 
 #ifdef __cplusplus
@@ -22,9 +23,13 @@
 #define Y4M_BUFFER_SIZE 128
 
 int y4m_write_file_header(char *buf, size_t len, int width, int height,
-                          const struct AvxRational *framerate,
+                          const struct AvxRational *framerate, int monochrome,
                           aom_img_fmt_t fmt, unsigned int bit_depth);
 int y4m_write_frame_header(char *buf, size_t len);
+void y4m_write_image_file(const aom_image_t *img, const int *planes,
+                          FILE *file);
+void y4m_update_image_md5(const aom_image_t *img, const int *planes,
+                          MD5Context *md5);
 
 #ifdef __cplusplus
 }  // extern "C"
diff --git a/test/y4m_test.cc b/test/y4m_test.cc
index b801193..de241283 100644
--- a/test/y4m_test.cc
+++ b/test/y4m_test.cc
@@ -37,6 +37,8 @@
 const Y4mTestParam kY4mTestVectors[] = {
   { "park_joy_90p_8_420.y4m", 8, AOM_IMG_FMT_I420,
     "e5406275b9fc6bb3436c31d4a05c1cab" },
+  { "park_joy_90p_8_420_monochrome.y4m", 8, AOM_IMG_FMT_I420,
+    "95ef5bf6218580588be24a5271bb6a7f" },
   { "park_joy_90p_8_422.y4m", 8, AOM_IMG_FMT_I422,
     "284a47a47133b12884ec3a14e959a0b6" },
   { "park_joy_90p_8_444.y4m", 8, AOM_IMG_FMT_I444,
@@ -55,24 +57,7 @@
     "5a6481a550821dab6d0192f5c63845e9" },
 };
 
-static void write_image_file(const aom_image_t *img, FILE *file) {
-  int plane, y;
-  for (plane = 0; plane < 3; ++plane) {
-    const unsigned char *buf = img->planes[plane];
-    const int stride = img->stride[plane];
-    const int bytes_per_sample = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
-    const int h =
-        (plane ? (img->d_h + img->y_chroma_shift) >> img->y_chroma_shift
-               : img->d_h);
-    const int w =
-        (plane ? (img->d_w + img->x_chroma_shift) >> img->x_chroma_shift
-               : img->d_w);
-    for (y = 0; y < h; ++y) {
-      fwrite(buf, bytes_per_sample, w, file);
-      buf += stride;
-    }
-  }
-}
+static const int PLANES_YUV[] = { AOM_PLANE_Y, AOM_PLANE_U, AOM_PLANE_V };
 
 class Y4mVideoSourceTest : public ::testing::TestWithParam<Y4mTestParam>,
                            public ::libaom_test::Y4mVideoSource {
@@ -162,12 +147,12 @@
     tmpfile_ = new libaom_test::TempOutFile;
     ASSERT_TRUE(tmpfile_->file() != NULL);
     y4m_write_file_header(buf, sizeof(buf), kWidth, kHeight, &framerate,
-                          y4m_.aom_fmt, y4m_.bit_depth);
+                          img()->monochrome, y4m_.aom_fmt, y4m_.bit_depth);
     fputs(buf, tmpfile_->file());
     for (unsigned int i = start_; i < limit_; i++) {
       y4m_write_frame_header(buf, sizeof(buf));
       fputs(buf, tmpfile_->file());
-      write_image_file(img(), tmpfile_->file());
+      y4m_write_image_file(img(), PLANES_YUV, tmpfile_->file());
       Next();
     }
     ReplaceInputFile(tmpfile_->file());