Adds metadata container for aom_image

- Adds aom_metadata struct to contain the different types of metadata
payloads an image can carry along with its alloc and free functions.
- Adds aom_metadata_array struct to aom_image to contain the different
metadata payloads an aom_image could contain.
- Adds the code needed to move metadata around libaom in lookahead.c and
to pass it from yuv12_buffer to aom_image and viceversa.
- Adds tests to check alloc/dealloc metadata functions.

The expectation is for user to use aom_add_metadata_to_img() function
to add a metadata payload to an aom_image_t to prevent him from doing
direct metadata pointer assignation. The function makes sure the memory
belongs to libaom by doing a memcpy of the input. User should free his
own memory.

BUG=aomedia:2507

Change-Id: I2e2b4f5dcd3c61d8715b11949802b23191390beb
diff --git a/aom/aom_image.h b/aom/aom_image.h
index 5f615d7..d07d485 100644
--- a/aom/aom_image.h
+++ b/aom/aom_image.h
@@ -30,7 +30,7 @@
  * types, removing or reassigning enums, adding/removing/rearranging
  * fields to structures
  */
-#define AOM_IMAGE_ABI_VERSION (5) /**<\hideinitializer*/
+#define AOM_IMAGE_ABI_VERSION (6) /**<\hideinitializer*/
 
 #define AOM_IMG_FMT_PLANAR 0x100  /**< Image is a planar format. */
 #define AOM_IMG_FMT_UV_FLIP 0x200 /**< V plane precedes U in memory. */
@@ -137,6 +137,16 @@
   AOM_CSP_RESERVED = 3          /**< Reserved value */
 } aom_chroma_sample_position_t; /**< alias for enum aom_transfer_function */
 
+/*!\brief Array of aom_metadata structs for an image. */
+typedef struct aom_metadata_array aom_metadata_array_t;
+
+/*!\brief Metadata payload. */
+typedef struct aom_metadata {
+  uint8_t type;     /**< Metadata type */
+  uint8_t *payload; /**< Metadata payload data */
+  size_t sz;        /**< Metadata payload size */
+} aom_metadata_t;
+
 /**\brief Image Descriptor */
 typedef struct aom_image {
   aom_img_fmt_t fmt;                 /**< Image Format */
@@ -188,6 +198,9 @@
   int img_data_owner;      /**< private */
   int self_allocd;         /**< private */
 
+  aom_metadata_array_t
+      *metadata; /**< Metadata payloads associated with the image. */
+
   void *fb_priv; /**< Frame buffer data associated with the image. */
 } aom_image_t;   /**< alias for struct aom_image */
 
@@ -324,6 +337,52 @@
  */
 int aom_img_plane_height(const aom_image_t *img, int plane);
 
+/*!\brief Add metadata to image.
+ *
+ * Adds metadata to aom_image_t.
+ * Function makes a copy of the provided data parameter.
+ *
+ * \param[in]    img       Image descriptor
+ * \param[in]    type      Metadata type
+ * \param[in]    data      Metadata contents
+ * \param[in]    sz        Metadata contents size
+ */
+int aom_img_add_metadata(aom_image_t *img, uint8_t type, uint8_t *data,
+                         size_t sz);
+
+/*!\brief Remove metadata from image.
+ *
+ * Removes all metadata in image metadata list and sets metadata list pointer
+ * to NULL.
+ * Returns the number of deleted metadata structs.
+ *
+ * \param[in]    img       Image descriptor
+ */
+size_t aom_img_remove_metadata(aom_image_t *img);
+
+/*!\brief Allocate memory for aom_metadata struct.
+ *
+ * Allocates memory for aom_metadata struct and sets its type. Optionally
+ * allocates storage for the metadata payload and copies the payload data
+ * into the aom_metadata struct:
+ *   - When sz is > 0 and data is NULL, allocates metadata payload buffer of sz.
+ *   - When sz is > 0 and data is non-NULL, a metadata payload buffer of sz
+ *     is allocated and sz bytes are copied from data into the payload buffer.
+ *
+ * \param[in]    type      Metadata type
+ * \param[in]    data      Metadata data pointer
+ * \param[in]    sz        Metadata size
+ */
+aom_metadata_t *aom_img_metadata_alloc(uint8_t type, uint8_t *data, size_t sz);
+
+/*!\brief Free metadata struct.
+ *
+ * Free metadata struct and its buffer.
+ *
+ * \param[in]    metadata       Metadata struct pointer
+ */
+int aom_img_metadata_free(aom_metadata_t *metadata);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/aom/exports_com b/aom/exports_com
index cf99bc5..a192cf9 100644
--- a/aom/exports_com
+++ b/aom/exports_com
@@ -10,12 +10,18 @@
 text aom_codec_version_extra_str
 text aom_codec_version_str
 text aom_free
+text aom_img_add_metadata
 text aom_img_alloc
 text aom_img_alloc_with_border
 text aom_img_flip
 text aom_img_free
+text aom_img_metadata_array_free
+text aom_img_metadata_array_alloc
+text aom_img_metadata_free
+text aom_img_metadata_alloc
 text aom_img_plane_height
 text aom_img_plane_width
+text aom_img_remove_metadata
 text aom_img_set_rect
 text aom_img_wrap
 text aom_malloc
diff --git a/aom/exports_test b/aom/exports_test
index 01b864b..452a532 100644
--- a/aom/exports_test
+++ b/aom/exports_test
@@ -1,2 +1,4 @@
+text aom_copy_metadata_to_frame_buffer
 text aom_dsp_rtcd
+text aom_remove_metadata_from_frame_buffer
 text aom_scale_rtcd
diff --git a/aom/internal/aom_image_internal.h b/aom/internal/aom_image_internal.h
index ba9cb64..2629d65 100644
--- a/aom/internal/aom_image_internal.h
+++ b/aom/internal/aom_image_internal.h
@@ -23,6 +23,32 @@
 extern "C" {
 #endif
 
+/*!\brief Array of aom_metadata structs for an image. */
+struct aom_metadata_array {
+  size_t sz;                       /* Number of metadata structs in the list */
+  aom_metadata_t **metadata_array; /* Array of metadata structs */
+};
+
+/*!\brief Alloc memory for aom_metadata_array struct.
+ *
+ * Allocate memory for aom_metadata_array struct.
+ * If sz is 0 the aom_metadata_array structs internal buffer list will be NULL,
+ * but the aom_metadata_array struct itself will still be allocated.
+ * Returns a pointer to the allocated struct or NULL on failure.
+ *
+ * \param[in]    sz       Size of internal metadata list buffer
+ */
+aom_metadata_array_t *aom_img_metadata_array_alloc(size_t sz);
+
+/*!\brief Free metadata array struct.
+ *
+ * Free metadata array struct and all metadata structs inside.
+ * Returns the number of deleted metadata structs.
+ *
+ * \param[in]    arr       Metadata array struct pointer
+ */
+size_t aom_img_metadata_array_free(aom_metadata_array_t *arr);
+
 typedef void *(*aom_alloc_img_data_cb_fn_t)(void *priv, size_t size);
 
 /*!\brief Open a descriptor, allocating storage for the underlying image by
diff --git a/aom/src/aom_image.c b/aom/src/aom_image.c
index f703504..9f7ed99 100644
--- a/aom/src/aom_image.c
+++ b/aom/src/aom_image.c
@@ -267,6 +267,7 @@
 
 void aom_img_free(aom_image_t *img) {
   if (img) {
+    aom_img_remove_metadata(img);
     if (img->img_data && img->img_data_owner) aom_free(img->img_data);
 
     if (img->self_allocd) free(img);
@@ -286,3 +287,99 @@
   else
     return img->d_h;
 }
+
+aom_metadata_t *aom_img_metadata_alloc(uint8_t type, uint8_t *data, size_t sz) {
+  aom_metadata_t *metadata =
+      (aom_metadata_t *)calloc(1, sizeof(aom_metadata_t));
+  if (!metadata) return NULL;
+  metadata->type = type;
+  if (sz > 0) {
+    metadata->payload = (uint8_t *)calloc(sz, sizeof(uint8_t));
+    if (!metadata->payload) {
+      free(metadata);
+      return NULL;
+    }
+    if (data) {
+      memcpy(metadata->payload, data, sz);
+      metadata->sz = sz;
+    }
+  }
+  return metadata;
+}
+
+int aom_img_metadata_free(aom_metadata_t *metadata) {
+  if (!metadata) return -1;
+  if (metadata->payload) free(metadata->payload);
+  free(metadata);
+  return 0;
+}
+
+aom_metadata_array_t *aom_img_metadata_array_alloc(size_t sz) {
+  aom_metadata_array_t *arr =
+      (aom_metadata_array_t *)calloc(1, sizeof(aom_metadata_array_t));
+  if (!arr) return NULL;
+  if (sz > 0) {
+    arr->metadata_array =
+        (aom_metadata_t **)calloc(sz, sizeof(aom_metadata_t *));
+    if (!arr->metadata_array) {
+      aom_img_metadata_array_free(arr);
+      return NULL;
+    }
+    arr->sz = sz;
+  }
+  return arr;
+}
+
+size_t aom_img_metadata_array_free(aom_metadata_array_t *arr) {
+  size_t deleted_metadatas = 0;
+  if (!arr) return deleted_metadatas;
+  if (arr->metadata_array) {
+    for (size_t i = 0; i < arr->sz; i++) {
+      if (aom_img_metadata_free(arr->metadata_array[i]) == 0) {
+        deleted_metadatas++;
+      }
+    }
+    free(arr->metadata_array);
+  }
+  free(arr);
+  return deleted_metadatas;
+}
+
+int aom_img_add_metadata(aom_image_t *img, uint8_t type, uint8_t *data,
+                         size_t sz) {
+  if (!img) return -1;
+  if (!img->metadata) {
+    img->metadata = aom_img_metadata_array_alloc(0);
+    if (!img->metadata) return -1;
+  }
+  aom_metadata_t *metadata = aom_img_metadata_alloc(type, data, sz);
+  if (!metadata) goto fail;
+  if (!img->metadata->metadata_array) {
+    img->metadata->metadata_array =
+        (aom_metadata_t **)calloc(1, sizeof(metadata));
+    if (!img->metadata->metadata_array || img->metadata->sz != 0) {
+      aom_img_metadata_free(metadata);
+      goto fail;
+    }
+  } else {
+    img->metadata->metadata_array =
+        (aom_metadata_t **)realloc(img->metadata->metadata_array,
+                                   (img->metadata->sz + 1) * sizeof(metadata));
+  }
+  img->metadata->metadata_array[img->metadata->sz] = metadata;
+  img->metadata->sz++;
+  return 0;
+fail:
+  aom_img_metadata_array_free(img->metadata);
+  img->metadata = NULL;
+  return -1;
+}
+
+size_t aom_img_remove_metadata(aom_image_t *img) {
+  if (img && img->metadata) {
+    size_t sz = aom_img_metadata_array_free(img->metadata);
+    img->metadata = NULL;
+    return sz;
+  }
+  return 0;
+}
diff --git a/aom_scale/generic/yv12config.c b/aom_scale/generic/yv12config.c
index 6406b18..8de3531 100644
--- a/aom_scale/generic/yv12config.c
+++ b/aom_scale/generic/yv12config.c
@@ -31,7 +31,7 @@
       aom_free(ybf->buffer_alloc);
     }
     if (ybf->y_buffer_8bit) aom_free(ybf->y_buffer_8bit);
-
+    aom_remove_metadata_from_frame_buffer(ybf);
     /* buffer_alloc isn't accessed by most functions.  Rather y_buffer,
       u_buffer and v_buffer point to buffer_alloc and are used.  Clear out
       all of this so that a freed pointer isn't inadvertently used */
@@ -287,3 +287,32 @@
   }
   return AOM_CODEC_MEM_ERROR;
 }
+
+size_t aom_remove_metadata_from_frame_buffer(YV12_BUFFER_CONFIG *ybf) {
+  if (ybf && ybf->metadata) {
+    size_t sz = aom_img_metadata_array_free(ybf->metadata);
+    ybf->metadata = NULL;
+    return sz;
+  }
+  return 0;
+}
+
+int aom_copy_metadata_to_frame_buffer(YV12_BUFFER_CONFIG *ybf,
+                                      aom_metadata_array_t *arr) {
+  if (!ybf || !arr || !arr->metadata_array) return -1;
+  aom_remove_metadata_from_frame_buffer(ybf);
+  ybf->metadata = aom_img_metadata_array_alloc(arr->sz);
+  if (!ybf->metadata) return 0;
+  for (size_t i = 0; i < ybf->metadata->sz; i++) {
+    ybf->metadata->metadata_array[i] = aom_img_metadata_alloc(
+        arr->metadata_array[i]->type, arr->metadata_array[i]->payload,
+        arr->metadata_array[i]->sz);
+    if (ybf->metadata->metadata_array[i] == NULL) {
+      aom_img_metadata_array_free(ybf->metadata);
+      ybf->metadata = NULL;
+      return -1;
+    }
+  }
+  ybf->metadata->sz = arr->sz;
+  return 0;
+}
diff --git a/aom_scale/yv12config.h b/aom_scale/yv12config.h
index 04a1c04..43856f0 100644
--- a/aom_scale/yv12config.h
+++ b/aom_scale/yv12config.h
@@ -21,6 +21,7 @@
 #include "aom/aom_codec.h"
 #include "aom/aom_frame_buffer.h"
 #include "aom/aom_integer.h"
+#include "aom/internal/aom_image_internal.h"
 
 #define AOMINNERBORDERINPIXELS 160
 #define AOM_INTERP_EXTEND 4
@@ -105,6 +106,7 @@
 
   int corrupted;
   int flags;
+  aom_metadata_array_t *metadata;
 } YV12_BUFFER_CONFIG;
 
 #define YV12_FLAG_HIGHBITDEPTH 8
@@ -135,6 +137,30 @@
 
 int aom_free_frame_buffer(YV12_BUFFER_CONFIG *ybf);
 
+/*!\brief Removes metadata from YUV_BUFFER_CONFIG struct.
+ *
+ * Frees metadata in frame buffer.
+ * Frame buffer metadata pointer will be set to NULL.
+ * Returns the number of deleted metadata structs.
+ *
+ * \param[in]    ybf       Frame buffer struct pointer
+ */
+size_t aom_remove_metadata_from_frame_buffer(YV12_BUFFER_CONFIG *ybf);
+
+/*!\brief Copy metadata to YUV_BUFFER_CONFIG struct.
+ *
+ * Copies metadata in frame buffer.
+ * Frame buffer will clear any previous metadata and will reallocate the
+ * metadata array to the new metadata size. Then, it will copy the new metadata
+ * array into it.
+ * Returns 0 on success or -1 on failure.
+ *
+ * \param[in]    ybf       Frame buffer struct pointer
+ * \param[in]    arr       Metadata array struct pointer
+ */
+int aom_copy_metadata_to_frame_buffer(YV12_BUFFER_CONFIG *ybf,
+                                      aom_metadata_array_t *arr);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/av1/av1_iface_common.h b/av1/av1_iface_common.h
index 81ec619..f87fd1b 100644
--- a/av1/av1_iface_common.h
+++ b/av1/av1_iface_common.h
@@ -74,6 +74,7 @@
   img->img_data_owner = 0;
   img->self_allocd = 0;
   img->sz = yv12->frame_size;
+  img->metadata = yv12->metadata;
 }
 
 static aom_codec_err_t image2yuvconfig(const aom_image_t *img,
@@ -133,6 +134,7 @@
   yv12->border = (border < 0) ? 0 : border;
   yv12->subsampling_x = img->x_chroma_shift;
   yv12->subsampling_y = img->y_chroma_shift;
+  yv12->metadata = img->metadata;
   return AOM_CODEC_OK;
 }
 
diff --git a/av1/encoder/lookahead.c b/av1/encoder/lookahead.c
index b6ae9e2..818b3c3 100644
--- a/av1/encoder/lookahead.c
+++ b/av1/encoder/lookahead.c
@@ -180,6 +180,8 @@
   buf->ts_start = ts_start;
   buf->ts_end = ts_end;
   buf->flags = flags;
+  aom_remove_metadata_from_frame_buffer(&buf->img);
+  aom_copy_metadata_to_frame_buffer(&buf->img, src->metadata);
   return 0;
 }
 
diff --git a/test/metadata_memory_handling_test.cc b/test/metadata_memory_handling_test.cc
new file mode 100644
index 0000000..fdd4f92
--- /dev/null
+++ b/test/metadata_memory_handling_test.cc
@@ -0,0 +1,110 @@
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+#include "aom/aom_codec.h"
+#include "aom/internal/aom_image_internal.h"
+#include "aom_scale/yv12config.h"
+
+TEST(MetadataMemoryHandlingTest, MetadataAllocation) {
+  aom_metadata_t *metadata;
+  uint8_t data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  metadata = aom_img_metadata_alloc(OBU_METADATA_TYPE_ITUT_T35, data, 10);
+  ASSERT_TRUE(metadata != NULL);
+
+  int status = aom_img_metadata_free(metadata);
+  EXPECT_EQ(status, 0);
+}
+
+TEST(MetadataMemoryHandlingTest, MetadataArrayAllocation) {
+  aom_metadata_array_t *metadata_array;
+  uint8_t data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  metadata_array = aom_img_metadata_array_alloc(2);
+  ASSERT_TRUE(metadata_array != NULL);
+
+  metadata_array->metadata_array[0] =
+      aom_img_metadata_alloc(OBU_METADATA_TYPE_ITUT_T35, data, 10);
+  metadata_array->metadata_array[1] =
+      aom_img_metadata_alloc(OBU_METADATA_TYPE_ITUT_T35, data, 10);
+
+  int status = aom_img_metadata_array_free(metadata_array);
+  EXPECT_EQ(status, 2);
+}
+
+TEST(MetadataMemoryHandlingTest, AddMetadataToImage) {
+  aom_image_t image;
+  image.metadata = NULL;
+
+  uint8_t data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  int status =
+      aom_img_add_metadata(&image, OBU_METADATA_TYPE_ITUT_T35, data, 10);
+  ASSERT_EQ(status, 0);
+
+  status = aom_img_metadata_array_free(image.metadata);
+  EXPECT_EQ(status, 1);
+
+  status = aom_img_add_metadata(NULL, OBU_METADATA_TYPE_ITUT_T35, data, 10);
+  EXPECT_EQ(status, -1);
+}
+
+TEST(MetadataMemoryHandlingTest, RemoveMetadataFromImage) {
+  aom_image_t image;
+  image.metadata = NULL;
+
+  uint8_t data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  int status =
+      aom_img_add_metadata(&image, OBU_METADATA_TYPE_ITUT_T35, data, 10);
+  ASSERT_EQ(status, 0);
+
+  status = aom_img_remove_metadata(&image);
+  EXPECT_EQ(status, 1);
+
+  status = aom_img_remove_metadata(NULL);
+  EXPECT_EQ(status, 0);
+}
+
+TEST(MetadataMemoryHandlingTest, CopyMetadataToFrameBUffer) {
+  YV12_BUFFER_CONFIG yvBuf;
+  yvBuf.metadata = NULL;
+  aom_metadata_array_t *metadata_array;
+  aom_metadata_array_t *metadata_array_2;
+  uint8_t data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  metadata_array = aom_img_metadata_array_alloc(1);
+  ASSERT_TRUE(metadata_array != NULL);
+
+  metadata_array->metadata_array[0] =
+      aom_img_metadata_alloc(OBU_METADATA_TYPE_ITUT_T35, data, 10);
+
+  // Metadata_array
+  int status = aom_copy_metadata_to_frame_buffer(&yvBuf, metadata_array);
+  EXPECT_EQ(status, 0);
+
+  status = aom_copy_metadata_to_frame_buffer(NULL, metadata_array);
+  EXPECT_EQ(status, -1);
+
+  status = aom_img_metadata_array_free(metadata_array);
+  EXPECT_EQ(status, 1);
+
+  // Metadata_array_2
+  metadata_array_2 = aom_img_metadata_array_alloc(0);
+  ASSERT_TRUE(metadata_array_2 != NULL);
+
+  status = aom_copy_metadata_to_frame_buffer(&yvBuf, metadata_array_2);
+  EXPECT_EQ(status, -1);
+
+  status = aom_img_metadata_array_free(metadata_array_2);
+  EXPECT_EQ(status, 0);
+
+  // YV12_BUFFER_CONFIG
+  status = aom_copy_metadata_to_frame_buffer(&yvBuf, NULL);
+  EXPECT_EQ(status, -1);
+
+  status = aom_remove_metadata_from_frame_buffer(NULL);
+  EXPECT_EQ(status, 0);
+
+  status = aom_remove_metadata_from_frame_buffer(&yvBuf);
+  EXPECT_EQ(status, 1);
+}
diff --git a/test/test.cmake b/test/test.cmake
index 4e3540f..8c721b5 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -34,6 +34,7 @@
             "${AOM_ROOT}/test/decode_test_driver.h"
             "${AOM_ROOT}/test/function_equivalence_test.h"
             "${AOM_ROOT}/test/log2_test.cc"
+            "${AOM_ROOT}/test/metadata_memory_handling_test.cc"
             "${AOM_ROOT}/test/md5_helper.h"
             "${AOM_ROOT}/test/register_state_check.h"
             "${AOM_ROOT}/test/test_vectors.cc"