ext-tile: support NV12 output format

As approved in WG meeting, this patch added support for NV12 output
format.

BUG=aomedia:2047

Change-Id: Ie38315cdcf6426cb222c53401f7e007bafb4d013
diff --git a/common/tools_common.c b/common/tools_common.c
index ef5e9a7..b74576b 100644
--- a/common/tools_common.c
+++ b/common/tools_common.c
@@ -465,3 +465,37 @@
     *img_ptr = img_shifted;
   }
 }
+
+// Related to I420, NV12 format has one luma "luminance" plane Y and one plane
+// with U and V values interleaved.
+void aom_img_write_nv12(const aom_image_t *img, FILE *file) {
+  // Y plane
+  const unsigned char *buf = img->planes[0];
+  int stride = img->stride[0];
+  int w = aom_img_plane_width(img, 0) *
+          ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+  int h = aom_img_plane_height(img, 0);
+  int x, y;
+
+  for (y = 0; y < h; ++y) {
+    fwrite(buf, 1, w, file);
+    buf += stride;
+  }
+
+  // Interleaved U and V plane
+  const unsigned char *ubuf = img->planes[1];
+  const unsigned char *vbuf = img->planes[2];
+  stride = img->stride[1];
+  w = aom_img_plane_width(img, 1) *
+      ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+  h = aom_img_plane_height(img, 1);
+
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      fputc(ubuf[x], file);
+      fputc(vbuf[x], file);
+    }
+    ubuf += stride;
+    vbuf += stride;
+  }
+}
diff --git a/common/tools_common.h b/common/tools_common.h
index c981361..458ca99 100644
--- a/common/tools_common.h
+++ b/common/tools_common.h
@@ -159,6 +159,9 @@
                    aom_image_t **img_shifted_ptr);
 void aom_img_truncate_16_to_8(aom_image_t *dst, const aom_image_t *src);
 
+// Output in NV12 format.
+void aom_img_write_nv12(const aom_image_t *img, FILE *file);
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
diff --git a/examples/lightfield_decoder.c b/examples/lightfield_decoder.c
index b57accb..2796179 100644
--- a/examples/lightfield_decoder.c
+++ b/examples/lightfield_decoder.c
@@ -15,11 +15,16 @@
 // This is an example of a simple lightfield decoder. It builds upon the
 // simple_decoder.c example.  It takes an input file containing the compressed
 // data (in ivf format), treating it as a lightfield instead of a video; and a
-// text file with a list of tiles to decode.
+// text file with a list of tiles to decode. There is an option allowing to
+// choose the output format, and the supported formats are i420(default) and
+// nv12.
 // After running the lightfield encoder, run lightfield decoder to decode a
 // batch of tiles:
 // examples/lightfield_decoder vase10x10.ivf vase_reference.yuv 4 tile_list.txt
-//
+// or
+// examples/lightfield_decoder vase10x10.ivf vase_reference.yuv 4 tile_list.txt
+// 1
+// if nv12 output format is preferred.
 // The tile_list.txt is expected to be of the form:
 // Frame <frame_index0>
 // <image_index0> <anchor_index0> <tile_col0> <tile_row0>
@@ -46,8 +51,13 @@
 
 static const char *exec_name;
 
+#define I420 0
+#define NV12 1
+
 void usage_exit(void) {
-  fprintf(stderr, "Usage: %s <infile> <outfile> <num_references> <tile_list>\n",
+  fprintf(stderr,
+          "Usage: %s <infile> <outfile> <num_references> <tile_list> <output "
+          "format(optional)>\n",
           exec_name);
   exit(EXIT_FAILURE);
 }
@@ -134,6 +144,14 @@
   (*tile_idx)++;
 }
 
+static void img_write_to_file(const aom_image_t *img, FILE *file,
+                              int output_format) {
+  if (output_format == I420)
+    aom_img_write(img, file);
+  else  // NV12
+    aom_img_write_nv12(img, file);
+}
+
 int main(int argc, char **argv) {
   FILE *outfile = NULL;
   aom_codec_ctx_t codec;
@@ -149,9 +167,10 @@
   const unsigned char *frame = NULL;
   int i, j;
   const char *tile_list_file = NULL;
+  int output_format = I420;
   exec_name = argv[0];
 
-  if (argc != 5) die("Invalid number of arguments.");
+  if (argc < 5) die("Invalid number of arguments.");
 
   reader = aom_video_reader_open(argv[1]);
   if (!reader) die("Failed to open %s for reading.", argv[1]);
@@ -162,6 +181,8 @@
   num_references = (int)strtol(argv[3], NULL, 0);
   tile_list_file = argv[4];
 
+  if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
+
   info = aom_video_reader_get_info(reader);
 
   decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
@@ -271,7 +292,7 @@
         // Shift up or down if necessary
         if (output_bit_depth != 0)
           aom_shift_img(output_bit_depth, &out, &output_shifted);
-        aom_img_write(out, outfile);
+        img_write_to_file(out, outfile, output_format);
         tile_list_writes++;
       }
 
@@ -304,7 +325,7 @@
     // Shift up or down if necessary
     if (output_bit_depth != 0)
       aom_shift_img(output_bit_depth, &out, &output_shifted);
-    aom_img_write(out, outfile);
+    img_write_to_file(out, outfile, output_format);
   }
 
   if (output_shifted) aom_img_free(output_shifted);
@@ -321,3 +342,5 @@
 
   return EXIT_SUCCESS;
 }
+#undef I420
+#undef NV12
diff --git a/examples/lightfield_tile_list_decoder.c b/examples/lightfield_tile_list_decoder.c
index d8f244f..4307efe 100644
--- a/examples/lightfield_tile_list_decoder.c
+++ b/examples/lightfield_tile_list_decoder.c
@@ -18,10 +18,16 @@
 // compressed tile data. This input file is reconstructed from the encoded
 // lightfield ivf file, and is decodable by AV1 decoder. num_references is
 // the number of anchor frames coded at the beginning of the light field file.
-// num_tile_lists is the number of tile lists need to be decoded.
+// num_tile_lists is the number of tile lists need to be decoded. There is an
+// option allowing to choose the output format, and the supported formats are
+// i420(default) and nv12.
 // Run lightfield tile list decoder to decode an AV1 tile list file:
 // examples/lightfield_tile_list_decoder vase_tile_list.ivf vase_tile_list.yuv
 // 4 2
+// or
+// examples/lightfield_tile_list_decoder vase_tile_list.ivf vase_tile_list.yuv
+// 4 2 1
+// if nv12 output format is preferred.
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,13 +43,25 @@
 
 static const char *exec_name;
 
+#define I420 0
+#define NV12 1
+
 void usage_exit(void) {
   fprintf(stderr,
-          "Usage: %s <infile> <outfile> <num_references> <num_tile_lists>\n",
+          "Usage: %s <infile> <outfile> <num_references> <num_tile_lists> "
+          "<output format(optional)>\n",
           exec_name);
   exit(EXIT_FAILURE);
 }
 
+static void img_write_to_file(const aom_image_t *img, FILE *file,
+                              int output_format) {
+  if (output_format == I420)
+    aom_img_write(img, file);
+  else  // NV12
+    aom_img_write_nv12(img, file);
+}
+
 int main(int argc, char **argv) {
   FILE *outfile = NULL;
   aom_codec_ctx_t codec;
@@ -55,11 +73,12 @@
   aom_image_t reference_images[MAX_EXTERNAL_REFERENCES];
   size_t frame_size = 0;
   const unsigned char *frame = NULL;
+  int output_format = I420;
   int i, j, n;
 
   exec_name = argv[0];
 
-  if (argc != 5) die("Invalid number of arguments.");
+  if (argc < 5) die("Invalid number of arguments.");
 
   reader = aom_video_reader_open(argv[1]);
   if (!reader) die("Failed to open %s for reading.", argv[1]);
@@ -70,6 +89,8 @@
   num_references = (int)strtol(argv[3], NULL, 0);
   num_tile_lists = (int)strtol(argv[4], NULL, 0);
 
+  if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
+
   info = aom_video_reader_get_info(reader);
 
   decoder = get_aom_decoder_by_fourcc(info->codec_fourcc);
@@ -148,7 +169,7 @@
       die_codec(&codec, "Failed to decode the tile list.");
     aom_codec_iter_t iter = NULL;
     aom_image_t *img = aom_codec_get_frame(&codec, &iter);
-    aom_img_write(img, outfile);
+    img_write_to_file(img, outfile, output_format);
   }
 
   for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]);
@@ -158,3 +179,5 @@
 
   return EXIT_SUCCESS;
 }
+#undef I420
+#undef NV12