ext-tile: add back the 1D tile output format

This patch added back the 1D tile output format. Now, the supported tile
output formats are YUV1D(default), YUV, and NV12.

BUG=aomedia:2047

Change-Id: I1b02c34ea3137a1239405bb72aaf4789470702b9
diff --git a/aom/aomdx.h b/aom/aomdx.h
index 54372a7..c71eaf9 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -141,6 +141,9 @@
   /** control function to get the size of the tile. */
   AV1D_GET_TILE_SIZE,
 
+  /** control function to get the tile count in a tile list. */
+  AV1D_GET_TILE_COUNT,
+
   /** control function to set the byte alignment of the planes in the reference
    * buffers. Valid values are power of 2, from 32 to 1024. A value of 0 sets
    * legacy alignment. I.e. Y plane is aligned to 32 bytes, U plane directly
@@ -276,6 +279,8 @@
 #define AOM_CTRL_AV1D_GET_IMG_FORMAT
 AOM_CTRL_USE_TYPE(AV1D_GET_TILE_SIZE, unsigned int *)
 #define AOM_CTRL_AV1D_GET_TILE_SIZE
+AOM_CTRL_USE_TYPE(AV1D_GET_TILE_COUNT, unsigned int *)
+#define AOM_CTRL_AV1D_GET_TILE_COUNT
 AOM_CTRL_USE_TYPE(AV1D_GET_FRAME_SIZE, int *)
 #define AOM_CTRL_AV1D_GET_FRAME_SIZE
 AOM_CTRL_USE_TYPE(AV1_INVERT_TILE_DECODE_ORDER, int)
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 2b8ed7b..ff77289 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -1133,6 +1133,24 @@
   return AOM_CODEC_INVALID_PARAM;
 }
 
+static aom_codec_err_t ctrl_get_tile_count(aom_codec_alg_priv_t *ctx,
+                                           va_list args) {
+  unsigned int *const tile_count = va_arg(args, unsigned int *);
+
+  if (tile_count) {
+    AVxWorker *const worker = &ctx->frame_workers[ctx->next_output_worker_id];
+    if (worker) {
+      FrameWorkerData *const frame_worker_data =
+          (FrameWorkerData *)worker->data1;
+      *tile_count = frame_worker_data->pbi->tile_count_minus_1 + 1;
+      return AOM_CODEC_OK;
+    } else {
+      return AOM_CODEC_ERROR;
+    }
+  }
+  return AOM_CODEC_INVALID_PARAM;
+}
+
 static aom_codec_err_t ctrl_set_invert_tile_order(aom_codec_alg_priv_t *ctx,
                                                   va_list args) {
   ctx->invert_tile_order = va_arg(args, int);
@@ -1299,6 +1317,7 @@
   { AV1D_GET_BIT_DEPTH, ctrl_get_bit_depth },
   { AV1D_GET_IMG_FORMAT, ctrl_get_img_format },
   { AV1D_GET_TILE_SIZE, ctrl_get_tile_size },
+  { AV1D_GET_TILE_COUNT, ctrl_get_tile_count },
   { AV1D_GET_DISPLAY_SIZE, ctrl_get_render_size },
   { AV1D_GET_FRAME_SIZE, ctrl_get_frame_size },
   { AV1_GET_ACCOUNTING, ctrl_get_accounting },
diff --git a/common/tools_common.c b/common/tools_common.c
index b74576b..2e32f61 100644
--- a/common/tools_common.c
+++ b/common/tools_common.c
@@ -485,17 +485,19 @@
   // Interleaved U and V plane
   const unsigned char *ubuf = img->planes[1];
   const unsigned char *vbuf = img->planes[2];
+  const size_t size = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
   stride = img->stride[1];
-  w = aom_img_plane_width(img, 1) *
-      ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
+  w = aom_img_plane_width(img, 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);
+      fwrite(ubuf, size, 1, file);
+      fwrite(vbuf, size, 1, file);
+      ubuf += size;
+      vbuf += size;
     }
-    ubuf += stride;
-    vbuf += stride;
+    ubuf += (stride - w * size);
+    vbuf += (stride - w * size);
   }
 }
diff --git a/common/tools_common.h b/common/tools_common.h
index 458ca99..df3b62b 100644
--- a/common/tools_common.h
+++ b/common/tools_common.h
@@ -77,6 +77,13 @@
   FILE_TYPE_WEBM
 };
 
+// Used in lightfield example.
+typedef enum OUTPUT_FORMAT {
+  YUV1D,  // 1D tile output for conformance test.
+  YUV,    // Tile output in YUV format.
+  NV12,   // Tile output in NV12 format.
+} OUTPUT_FORMAT;
+
 struct FileTypeDetectionBuffer {
   char buf[4];
   size_t buf_read;
diff --git a/examples/lightfield_decoder.c b/examples/lightfield_decoder.c
index 2796179..23dac98 100644
--- a/examples/lightfield_decoder.c
+++ b/examples/lightfield_decoder.c
@@ -15,16 +15,13 @@
 // 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. There is an option allowing to
-// choose the output format, and the supported formats are i420(default) and
-// nv12.
+// text file with a list of tiles to decode. There is an optional parameter
+// allowing to choose the output format, and the supported formats are
+// YUV1D(default), YUV, 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.
+// 0(optional)
 // The tile_list.txt is expected to be of the form:
 // Frame <frame_index0>
 // <image_index0> <anchor_index0> <tile_col0> <tile_row0>
@@ -51,9 +48,6 @@
 
 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> <output "
@@ -99,7 +93,8 @@
 void decode_tile(aom_codec_ctx_t *codec, const unsigned char *frame,
                  size_t frame_size, int tr, int tc, int ref_idx,
                  aom_image_t *reference_images, aom_image_t *output,
-                 int *tile_idx, unsigned int *output_bit_depth) {
+                 int *tile_idx, unsigned int *output_bit_depth,
+                 aom_image_t **img_ptr, int output_format) {
   aom_codec_control_(codec, AV1_SET_TILE_MODE, 1);
   aom_codec_control_(codec, AV1D_EXT_TILE_DEBUG, 1);
   aom_codec_control_(codec, AV1_SET_DECODE_TILE_ROW, tr);
@@ -119,6 +114,7 @@
   aom_codec_iter_t iter = NULL;
   aom_image_t *img = aom_codec_get_frame(codec, &iter);
   if (!img) die_codec(codec, "Failed to get frame.");
+  *img_ptr = img;
 
   // aom_img_alloc() sets bit_depth as follows:
   // output->bit_depth = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
@@ -127,29 +123,34 @@
   output->bit_depth = img->bit_depth;
   *output_bit_depth = img->bit_depth;
 
-  // read out the tile size.
-  unsigned int tile_size = 0;
-  if (aom_codec_control(codec, AV1D_GET_TILE_SIZE, &tile_size))
-    die_codec(codec, "Failed to get the tile size");
-  const unsigned int tile_width = tile_size >> 16;
-  const unsigned int tile_height = tile_size & 65535;
-  const uint8_t output_frame_width_in_tiles = output_frame_width / tile_width;
+  if (output_format != YUV1D) {
+    // read out the tile size.
+    unsigned int tile_size = 0;
+    if (aom_codec_control(codec, AV1D_GET_TILE_SIZE, &tile_size))
+      die_codec(codec, "Failed to get the tile size");
+    const unsigned int tile_width = tile_size >> 16;
+    const unsigned int tile_height = tile_size & 65535;
+    const uint8_t output_frame_width_in_tiles = output_frame_width / tile_width;
 
-  // Copy the tile to the output frame.
-  const int row_offset =
-      (*tile_idx / output_frame_width_in_tiles) * tile_height;
-  const int col_offset = (*tile_idx % output_frame_width_in_tiles) * tile_width;
+    // Copy the tile to the output frame.
+    const int row_offset =
+        (*tile_idx / output_frame_width_in_tiles) * tile_height;
+    const int col_offset =
+        (*tile_idx % output_frame_width_in_tiles) * tile_width;
 
-  aom_img_copy_tile(img, output, row_offset, col_offset);
-  (*tile_idx)++;
+    aom_img_copy_tile(img, output, row_offset, col_offset);
+    (*tile_idx)++;
+  }
 }
 
 static void img_write_to_file(const aom_image_t *img, FILE *file,
                               int output_format) {
-  if (output_format == I420)
+  if (output_format == YUV)
     aom_img_write(img, file);
-  else  // NV12
+  else if (output_format == NV12)
     aom_img_write_nv12(img, file);
+  else
+    die("Invalid output format");
 }
 
 int main(int argc, char **argv) {
@@ -167,7 +168,7 @@
   const unsigned char *frame = NULL;
   int i, j;
   const char *tile_list_file = NULL;
-  int output_format = I420;
+  int output_format = YUV1D;
   exec_name = argv[0];
 
   if (argc < 5) die("Invalid number of arguments.");
@@ -182,6 +183,8 @@
   tile_list_file = argv[4];
 
   if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
+  if (output_format < YUV1D || output_format > NV12)
+    die("Output format out of range [0, 2]");
 
   info = aom_video_reader_get_info(reader);
 
@@ -268,12 +271,14 @@
   }
   printf("Read %d frames.\n", num_frames);
 
-  // Allocate the output frame.
-  aom_img_fmt_t out_fmt = ref_fmt;
-  if (!CONFIG_LOWBITDEPTH) out_fmt |= AOM_IMG_FMT_HIGHBITDEPTH;
-  if (!aom_img_alloc(&output, out_fmt, output_frame_width, output_frame_height,
-                     32))
-    die("Failed to allocate output image.");
+  if (output_format != YUV1D) {
+    // Allocate the output frame.
+    aom_img_fmt_t out_fmt = ref_fmt;
+    if (!CONFIG_LOWBITDEPTH) out_fmt |= AOM_IMG_FMT_HIGHBITDEPTH;
+    if (!aom_img_alloc(&output, out_fmt, output_frame_width,
+                       output_frame_height, 32))
+      die("Failed to allocate output image.");
+  }
 
   printf("Decoding tile list from file.\n");
   char line[1024];
@@ -286,20 +291,21 @@
 
   while ((fgets(line, 1024, tile_list_fptr)) != NULL) {
     if (line[0] == 'F') {
-      // Write out the tile list.
-      if (tile_list_cnt) {
-        out = &output;
-        // Shift up or down if necessary
-        if (output_bit_depth != 0)
-          aom_shift_img(output_bit_depth, &out, &output_shifted);
-        img_write_to_file(out, outfile, output_format);
-        tile_list_writes++;
-      }
+      if (output_format != YUV1D) {
+        // Write out the tile list.
+        if (tile_list_cnt) {
+          out = &output;
+          if (output_bit_depth != 0)
+            aom_shift_img(output_bit_depth, &out, &output_shifted);
+          img_write_to_file(out, outfile, output_format);
+          tile_list_writes++;
+        }
 
-      tile_list_cnt++;
-      tile_idx = 0;
-      // Then memset the frame.
-      memset(output.img_data, 0, output.sz);
+        tile_list_cnt++;
+        tile_idx = 0;
+        // Then memset the frame.
+        memset(output.img_data, 0, output.sz);
+      }
       continue;
     }
 
@@ -315,21 +321,30 @@
     }
     frame = frames[image_idx];
     frame_size = frame_sizes[image_idx];
+
+    aom_image_t *img = NULL;
     decode_tile(&codec, frame, frame_size, tr, tc, ref_idx, reference_images,
-                &output, &tile_idx, &output_bit_depth);
+                &output, &tile_idx, &output_bit_depth, &img, output_format);
+    if (output_format == YUV1D) {
+      out = img;
+      if (output_bit_depth != 0)
+        aom_shift_img(output_bit_depth, &out, &output_shifted);
+      aom_img_write(out, outfile);
+    }
   }
 
-  // Write out the last tile list.
-  if (tile_list_writes < tile_list_cnt) {
-    out = &output;
-    // Shift up or down if necessary
-    if (output_bit_depth != 0)
-      aom_shift_img(output_bit_depth, &out, &output_shifted);
-    img_write_to_file(out, outfile, output_format);
+  if (output_format != YUV1D) {
+    // Write out the last tile list.
+    if (tile_list_writes < tile_list_cnt) {
+      out = &output;
+      if (output_bit_depth != 0)
+        aom_shift_img(output_bit_depth, &out, &output_shifted);
+      img_write_to_file(out, outfile, output_format);
+    }
   }
 
   if (output_shifted) aom_img_free(output_shifted);
-  aom_img_free(&output);
+  if (output_format != YUV1D) aom_img_free(&output);
   for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]);
   for (int f = 0; f < num_frames; ++f) {
     free(frames[f]);
@@ -342,5 +357,3 @@
 
   return EXIT_SUCCESS;
 }
-#undef I420
-#undef NV12
diff --git a/examples/lightfield_tile_list_decoder.c b/examples/lightfield_tile_list_decoder.c
index 4307efe..4aabde1 100644
--- a/examples/lightfield_tile_list_decoder.c
+++ b/examples/lightfield_tile_list_decoder.c
@@ -19,15 +19,11 @@
 // 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. There is an
-// option allowing to choose the output format, and the supported formats are
-// i420(default) and nv12.
+// optional parameter allowing to choose the output format, and the supported
+// formats are YUV1D(default), YUV, 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.
+// 4 2 0(optional)
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -43,9 +39,6 @@
 
 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> "
@@ -54,12 +47,53 @@
   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);
+static void write_tile_yuv1d(aom_codec_ctx_t *codec, const aom_image_t *img,
+                             FILE *file) {
+  // read out the tile size.
+  unsigned int tile_size = 0;
+  if (aom_codec_control(codec, AV1D_GET_TILE_SIZE, &tile_size))
+    die_codec(codec, "Failed to get the tile size");
+  const unsigned int tile_width = tile_size >> 16;
+  const unsigned int tile_height = tile_size & 65535;
+  const uint8_t output_frame_width_in_tiles = img->d_w / tile_width;
+
+  unsigned int tile_count = 0;
+  if (aom_codec_control(codec, AV1D_GET_TILE_COUNT, &tile_count))
+    die_codec(codec, "Failed to get the tile size");
+
+  // Write tile to file.
+  const int shift = (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 1 : 0;
+  unsigned int tile_idx;
+
+  for (tile_idx = 0; tile_idx < tile_count; ++tile_idx) {
+    const int row_offset =
+        (tile_idx / output_frame_width_in_tiles) * tile_height;
+    const int col_offset =
+        (tile_idx % output_frame_width_in_tiles) * tile_width;
+    int plane;
+
+    for (plane = 0; plane < 3; ++plane) {
+      const unsigned char *buf = img->planes[plane];
+      const int stride = img->stride[plane];
+      const int roffset =
+          (plane > 0) ? row_offset >> img->y_chroma_shift : row_offset;
+      const int coffset =
+          (plane > 0) ? col_offset >> img->x_chroma_shift : col_offset;
+      const int w = (plane > 0) ? ((tile_width >> img->x_chroma_shift) << shift)
+                                : (tile_width << shift);
+      const int h =
+          (plane > 0) ? (tile_height >> img->y_chroma_shift) : tile_height;
+      int y;
+
+      // col offset needs to be adjusted for HBD.
+      buf += roffset * stride + (coffset << shift);
+
+      for (y = 0; y < h; ++y) {
+        fwrite(buf, 1, w, file);
+        buf += stride;
+      }
+    }
+  }
 }
 
 int main(int argc, char **argv) {
@@ -73,7 +107,7 @@
   aom_image_t reference_images[MAX_EXTERNAL_REFERENCES];
   size_t frame_size = 0;
   const unsigned char *frame = NULL;
-  int output_format = I420;
+  int output_format = YUV1D;
   int i, j, n;
 
   exec_name = argv[0];
@@ -90,6 +124,8 @@
   num_tile_lists = (int)strtol(argv[4], NULL, 0);
 
   if (argc > 5) output_format = (int)strtol(argv[5], NULL, 0);
+  if (output_format < YUV1D || output_format > NV12)
+    die("Output format out of range [0, 2]");
 
   info = aom_video_reader_get_info(reader);
 
@@ -169,7 +205,16 @@
       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);
-    img_write_to_file(img, outfile, output_format);
+    if (!img) die_codec(&codec, "Failed to get frame.");
+
+    if (output_format == YUV1D)
+      // write the tile to the output file in 1D format.
+      write_tile_yuv1d(&codec, img, outfile);
+    else if (output_format == YUV)
+      aom_img_write(img, outfile);
+    else
+      // NV12 output format
+      aom_img_write_nv12(img, outfile);
   }
 
   for (i = 0; i < num_references; i++) aom_img_free(&reference_images[i]);
@@ -179,5 +224,3 @@
 
   return EXIT_SUCCESS;
 }
-#undef I420
-#undef NV12