Add av1C generation.

- Add sequence header parser in av1_config.c to produce av1C.
- Add av1C reader and writer functions.

BUG=aomedia:2027

Change-Id: I42322cd56bf2c2c6a7c18b67c1a7d6296fb9fe61
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ab62a51..c99dbed 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -122,12 +122,17 @@
             "${AOM_ROOT}/aom/src/aom_image.c"
             "${AOM_ROOT}/aom/src/aom_integer.c")
 
-list(APPEND AOM_COMMON_APP_UTIL_SOURCES "${AOM_ROOT}/common/args.c"
-            "${AOM_ROOT}/common/args.h" "${AOM_ROOT}/common/md5_utils.c"
+list(APPEND AOM_COMMON_APP_UTIL_SOURCES
+            "${AOM_ROOT}/common/args.c"
+            "${AOM_ROOT}/common/args.h"
+            "${AOM_ROOT}/common/av1_config.c"
+            "${AOM_ROOT}/common/av1_config.h"
+            "${AOM_ROOT}/common/md5_utils.c"
             "${AOM_ROOT}/common/md5_utils.h"
             "${AOM_ROOT}/common/tools_common.c"
             "${AOM_ROOT}/common/tools_common.h"
-            "${AOM_ROOT}/common/video_common.h" "${AOM_ROOT}/common/y4menc.c"
+            "${AOM_ROOT}/common/video_common.h"
+            "${AOM_ROOT}/common/y4menc.c"
             "${AOM_ROOT}/common/y4menc.h")
 
 list(APPEND AOM_DECODER_APP_UTIL_SOURCES "${AOM_ROOT}/common/ivfdec.c"
diff --git a/aom/exports_com b/aom/exports_com
index 0ee7502..2798bd5 100644
--- a/aom/exports_com
+++ b/aom/exports_com
@@ -18,6 +18,7 @@
 text aom_img_set_rect
 text aom_img_wrap
 text aom_malloc
+text aom_rb_bytes_read
 text aom_rb_read_bit
 text aom_rb_read_literal
 text aom_rb_read_uvlc
@@ -25,6 +26,7 @@
 text aom_uleb_encode
 text aom_uleb_encode_fixed_size
 text aom_uleb_size_in_bytes
+text aom_wb_bytes_written
 text aom_wb_write_bit
 text aom_wb_write_literal
 text aom_wb_write_unsigned_literal
diff --git a/common/av1_config.c b/common/av1_config.c
new file mode 100644
index 0000000..08ddc59
--- /dev/null
+++ b/common/av1_config.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2018, 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 <stdio.h>
+#include <string.h>
+
+#include "aom/aom_image.h"
+#include "aom/aom_integer.h"
+#include "aom_dsp/bitreader_buffer.h"
+#include "aom_dsp/bitwriter_buffer.h"
+#include "av1/common/obu_util.h"
+#include "common/av1_config.h"
+#include "config/aom_config.h"
+
+// Helper macros to reduce verbosity required to check for read errors.
+//
+// Note that when using these macros, even single line if statements should use
+// curly braces to avoid unexpected behavior because all but the
+// AV1C_POP_ERROR_HANDLER_DATA() macro consist of multiple statements.
+#define AV1C_READ_BIT_OR_RETURN_ERROR(field)                                   \
+  int field = 0;                                                               \
+  do {                                                                         \
+    field = aom_rb_read_bit(reader);                                           \
+    if (result == -1) {                                                        \
+      fprintf(stderr,                                                          \
+              "av1c: Error reading bit for " #field ", value=%d result=%d.\n", \
+              field, result);                                                  \
+      return -1;                                                               \
+    }                                                                          \
+  } while (0)
+
+#define AV1C_READ_BITS_OR_RETURN_ERROR(field, length) \
+  int field = 0;                                      \
+  do {                                                \
+    field = aom_rb_read_literal(reader, (length));    \
+    if (result == -1) {                               \
+      fprintf(stderr,                                 \
+              "av1c: Could not read bits for " #field \
+              ", value=%d result=%d.\n",              \
+              field, result);                         \
+      return -1;                                      \
+    }                                                 \
+  } while (0)
+
+// Helper macros for setting/restoring the error handler data in
+// aom_read_bit_buffer.
+#define AV1C_PUSH_ERROR_HANDLER_DATA(new_data)                \
+  void *original_error_handler_data = NULL;                   \
+  do {                                                        \
+    original_error_handler_data = reader->error_handler_data; \
+    reader->error_handler_data = &new_data;                   \
+  } while (0)
+
+#define AV1C_POP_ERROR_HANDLER_DATA()                         \
+  do {                                                        \
+    reader->error_handler_data = original_error_handler_data; \
+  } while (0)
+
+static const size_t kAv1cSize = 4;
+
+static void bitreader_error_handler(void *data) {
+  int *error_val = (int *)data;
+  *error_val = -1;
+}
+
+// Parse the AV1 timing_info() structure:
+// timing_info( ) {
+//   num_units_in_display_tick       f(32)
+//   time_scale                      f(32)
+//   equal_picture_interval          f(1)
+//   if (equal_picture_interval)
+//     num_ticks_per_picture_minus_1 uvlc()
+//   }
+static int parse_timing_info(struct aom_read_bit_buffer *reader) {
+  int result = 0;
+  AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(num_units_in_display_tick, 32);
+  AV1C_READ_BITS_OR_RETURN_ERROR(time_scale, 32);
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(equal_picture_interval);
+  if (equal_picture_interval) {
+    uint32_t num_ticks_per_picture_minus_1 = aom_rb_read_uvlc(reader);
+    if (result == -1) {
+      fprintf(stderr,
+              "av1c: Could not read bits for "
+              "num_ticks_per_picture_minus_1, value=%u.\n",
+              num_ticks_per_picture_minus_1);
+      return result;
+    }
+  }
+
+  AV1C_POP_ERROR_HANDLER_DATA();
+  return result;
+}
+
+// Parse the AV1 decoder_model_info() structure:
+// decoder_model_info( ) {
+//   buffer_delay_length_minus_1            f(5)
+//   num_units_in_decoding_tick             f(32)
+//   buffer_removal_time_length_minus_1     f(5)
+//   frame_presentation_time_length_minus_1 f(5)
+// }
+//
+// Returns -1 upon failure, or the value of buffer_delay_length_minus_1 + 1.
+static int parse_decoder_model_info(struct aom_read_bit_buffer *reader) {
+  int result = 0;
+  AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(buffer_delay_length_minus_1, 5);
+  AV1C_READ_BITS_OR_RETURN_ERROR(num_units_in_decoding_tick, 32);
+  AV1C_READ_BITS_OR_RETURN_ERROR(buffer_removal_time_length_minus_1, 5);
+  AV1C_READ_BITS_OR_RETURN_ERROR(frame_presentation_time_length_minus_1, 5);
+
+  AV1C_POP_ERROR_HANDLER_DATA();
+  return buffer_delay_length_minus_1 + 1;
+}
+
+// Parse the AV1 operating_parameters_info() structure:
+// operating_parameters_info( op ) {
+//   n = buffer_delay_length_minus_1 + 1
+//   decoder_buffer_delay[ op ] f(n)
+//   encoder_buffer_delay[ op ] f(n)
+//   low_delay_mode_flag[ op ] f(1)
+// }
+static int parse_operating_parameters_info(struct aom_read_bit_buffer *reader,
+                                           int buffer_delay_length_minus_1) {
+  int result = 0;
+  AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+  const int buffer_delay_length = buffer_delay_length_minus_1 + 1;
+  AV1C_READ_BITS_OR_RETURN_ERROR(decoder_buffer_delay, buffer_delay_length);
+  AV1C_READ_BITS_OR_RETURN_ERROR(encoder_buffer_delay, buffer_delay_length);
+  AV1C_READ_BIT_OR_RETURN_ERROR(low_delay_mode_flag);
+
+  AV1C_POP_ERROR_HANDLER_DATA();
+  return result;
+}
+
+// Parse the AV1 color_config() structure..See:
+// https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=44
+static int parse_color_config(struct aom_read_bit_buffer *reader,
+                              Av1Config *config) {
+  int result = 0;
+  AV1C_PUSH_ERROR_HANDLER_DATA(result);
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(high_bitdepth);
+  config->high_bitdepth = high_bitdepth;
+
+  int bit_depth = 0;
+  if (config->seq_profile == 2 && config->high_bitdepth) {
+    AV1C_READ_BIT_OR_RETURN_ERROR(twelve_bit);
+    config->twelve_bit = twelve_bit;
+    bit_depth = config->twelve_bit ? 12 : 10;
+  } else {
+    bit_depth = config->high_bitdepth ? 10 : 8;
+  }
+
+  if (config->seq_profile != 1) {
+    AV1C_READ_BIT_OR_RETURN_ERROR(mono_chrome);
+    config->monochrome = mono_chrome;
+  }
+
+  int color_primaries = AOM_CICP_CP_UNSPECIFIED;
+  int transfer_characteristics = AOM_CICP_TC_UNSPECIFIED;
+  int matrix_coefficients = AOM_CICP_MC_UNSPECIFIED;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(color_description_present_flag);
+  if (color_description_present_flag) {
+    AV1C_READ_BITS_OR_RETURN_ERROR(color_primaries_val, 8);
+    color_primaries = color_primaries_val;
+    AV1C_READ_BITS_OR_RETURN_ERROR(transfer_characteristics_val, 8);
+    transfer_characteristics = transfer_characteristics_val;
+    AV1C_READ_BITS_OR_RETURN_ERROR(matrix_coefficients_val, 8);
+    matrix_coefficients = matrix_coefficients_val;
+  }
+
+  if (config->monochrome) {
+    AV1C_READ_BIT_OR_RETURN_ERROR(color_range);
+    config->chroma_subsampling_x = 1;
+    config->chroma_subsampling_y = 1;
+  } else if (color_primaries == AOM_CICP_CP_BT_709 &&
+             transfer_characteristics == AOM_CICP_TC_SRGB &&
+             matrix_coefficients == AOM_CICP_MC_IDENTITY) {
+    config->chroma_subsampling_x = 0;
+    config->chroma_subsampling_y = 0;
+  } else {
+    AV1C_READ_BIT_OR_RETURN_ERROR(color_range);
+    if (config->seq_profile == 0) {
+      config->chroma_subsampling_x = 1;
+      config->chroma_subsampling_y = 1;
+    } else if (config->seq_profile == 1) {
+      config->chroma_subsampling_x = 0;
+      config->chroma_subsampling_y = 0;
+    } else {
+      if (bit_depth == 12) {
+        AV1C_READ_BIT_OR_RETURN_ERROR(subsampling_x);
+        config->chroma_subsampling_x = subsampling_x;
+        if (subsampling_x) {
+          AV1C_READ_BIT_OR_RETURN_ERROR(subsampling_y);
+          config->chroma_subsampling_y = subsampling_y;
+        } else {
+          config->chroma_subsampling_y = 0;
+        }
+      } else {
+        config->chroma_subsampling_x = 1;
+        config->chroma_subsampling_y = 0;
+      }
+    }
+
+    if (config->chroma_subsampling_x && config->chroma_subsampling_y) {
+      AV1C_READ_BITS_OR_RETURN_ERROR(chroma_sample_position, 2);
+      config->chroma_sample_position = chroma_sample_position;
+    }
+  }
+
+  if (!config->monochrome) {
+    AV1C_READ_BIT_OR_RETURN_ERROR(separate_uv_delta_q);
+  }
+
+  AV1C_POP_ERROR_HANDLER_DATA();
+  return result;
+}
+
+// Parse AV1 Sequence Header OBU. See:
+// https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=41
+static int parse_sequence_header(const uint8_t *const buffer, size_t length,
+                                 Av1Config *config) {
+  int result = 0;
+  // The reader instance is local to this function, but a pointer to the
+  // reader instance is used within this function and throughout this file to
+  // allow use of the helper macros that reduce parse error checking verbosity.
+  struct aom_read_bit_buffer reader_instance = {
+    buffer, buffer + length, 0, &result, bitreader_error_handler
+  };
+  struct aom_read_bit_buffer *reader = &reader_instance;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(seq_profile, 3);
+  config->seq_profile = seq_profile;
+  AV1C_READ_BIT_OR_RETURN_ERROR(still_picture);
+  AV1C_READ_BIT_OR_RETURN_ERROR(reduced_still_picture_header);
+  if (reduced_still_picture_header) {
+    config->initial_presentation_delay_present = 0;
+    AV1C_READ_BITS_OR_RETURN_ERROR(seq_level_idx_0, 5);
+    config->seq_level_idx_0 = seq_level_idx_0;
+    config->seq_tier_0 = 0;
+  } else {
+    int has_decoder_model = 0;
+    int buffer_delay_length = 0;
+
+    AV1C_READ_BIT_OR_RETURN_ERROR(timing_info_present_flag);
+    if (timing_info_present_flag) {
+      if (parse_timing_info(reader) != 0) return -1;
+
+      AV1C_READ_BIT_OR_RETURN_ERROR(decoder_model_info_present_flag);
+      if (decoder_model_info_present_flag &&
+          (buffer_delay_length = parse_decoder_model_info(reader)) == -1) {
+        return -1;
+      }
+      has_decoder_model = 1;
+    }
+
+    AV1C_READ_BIT_OR_RETURN_ERROR(initial_presentation_delay_present);
+    config->initial_presentation_delay_present =
+        initial_presentation_delay_present;
+
+    AV1C_READ_BITS_OR_RETURN_ERROR(operating_points_cnt_minus_1, 5);
+    const int num_operating_points = operating_points_cnt_minus_1 + 1;
+
+    for (int op_index = 0; op_index < num_operating_points; ++op_index) {
+      AV1C_READ_BITS_OR_RETURN_ERROR(operating_point_idc, 12);
+      AV1C_READ_BITS_OR_RETURN_ERROR(seq_level_idx, 5);
+
+      int seq_tier = 0;
+      if (seq_level_idx > 7) {
+        AV1C_READ_BIT_OR_RETURN_ERROR(seq_tier_this_op);
+        seq_tier = seq_tier_this_op;
+      }
+
+      if (has_decoder_model) {
+        AV1C_READ_BIT_OR_RETURN_ERROR(decoder_model_present_for_op);
+        if (decoder_model_present_for_op) {
+          if (parse_operating_parameters_info(reader, buffer_delay_length) ==
+              -1) {
+            return -1;
+          }
+        }
+      }
+
+      if (config->initial_presentation_delay_present) {
+        // Skip the initial presentation delay bits if present since this
+        // function has no access to the data required to properly set the
+        // field.
+        AV1C_READ_BIT_OR_RETURN_ERROR(
+            initial_presentation_delay_present_for_this_op);
+        if (initial_presentation_delay_present_for_this_op) {
+          AV1C_READ_BITS_OR_RETURN_ERROR(initial_presentation_delay_minus_1, 4);
+        }
+      }
+
+      if (op_index == 0) {
+        // Av1Config needs only the values from the first operating point.
+        config->seq_level_idx_0 = seq_level_idx;
+        config->seq_tier_0 = seq_tier;
+        config->initial_presentation_delay_present = 0;
+        config->initial_presentation_delay_minus_one = 0;
+      }
+    }
+  }
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(frame_width_bits_minus_1, 4);
+  AV1C_READ_BITS_OR_RETURN_ERROR(frame_height_bits_minus_1, 4);
+  AV1C_READ_BITS_OR_RETURN_ERROR(max_frame_width_minus_1,
+                                 frame_width_bits_minus_1 + 1);
+  AV1C_READ_BITS_OR_RETURN_ERROR(max_frame_height_minus_1,
+                                 frame_height_bits_minus_1 + 1);
+
+  int frame_id_numbers_present = 0;
+  if (!reduced_still_picture_header) {
+    AV1C_READ_BIT_OR_RETURN_ERROR(frame_id_numbers_present_flag);
+    frame_id_numbers_present = frame_id_numbers_present_flag;
+  }
+
+  if (frame_id_numbers_present) {
+    AV1C_READ_BITS_OR_RETURN_ERROR(delta_frame_id_length_minus_2, 4);
+    AV1C_READ_BITS_OR_RETURN_ERROR(additional_frame_id_length_minus_1, 3);
+  }
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(use_128x128_superblock);
+  AV1C_READ_BIT_OR_RETURN_ERROR(enable_filter_intra);
+  AV1C_READ_BIT_OR_RETURN_ERROR(enable_intra_edge_filter);
+
+  if (!reduced_still_picture_header) {
+    AV1C_READ_BIT_OR_RETURN_ERROR(enable_interintra_compound);
+    AV1C_READ_BIT_OR_RETURN_ERROR(enable_masked_compound);
+    AV1C_READ_BIT_OR_RETURN_ERROR(enable_warped_motion);
+    AV1C_READ_BIT_OR_RETURN_ERROR(enable_dual_filter);
+
+    AV1C_READ_BIT_OR_RETURN_ERROR(enable_order_hint);
+    if (enable_order_hint) {
+      AV1C_READ_BIT_OR_RETURN_ERROR(enable_jnt_comp);
+      AV1C_READ_BIT_OR_RETURN_ERROR(enable_ref_frame_mvs);
+    }
+
+    const int SELECT_SCREEN_CONTENT_TOOLS = 2;
+    int seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS;
+    AV1C_READ_BIT_OR_RETURN_ERROR(seq_choose_screen_content_tools);
+    if (!seq_choose_screen_content_tools) {
+      AV1C_READ_BIT_OR_RETURN_ERROR(seq_force_screen_content_tools_val);
+      seq_force_screen_content_tools = seq_force_screen_content_tools_val;
+    }
+
+    if (seq_force_screen_content_tools > 0) {
+      AV1C_READ_BIT_OR_RETURN_ERROR(seq_choose_integer_mv);
+
+      if (!seq_choose_integer_mv) {
+        AV1C_READ_BIT_OR_RETURN_ERROR(seq_force_integer_mv);
+      }
+    }
+
+    if (enable_order_hint) {
+      AV1C_READ_BITS_OR_RETURN_ERROR(order_hint_bits_minus_1, 3);
+    }
+  }
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(enable_superres);
+  AV1C_READ_BIT_OR_RETURN_ERROR(enable_cdef);
+  AV1C_READ_BIT_OR_RETURN_ERROR(enable_restoration);
+
+  if (parse_color_config(reader, config) != 0) {
+    fprintf(stderr, "av1c: color_config() parse failed.\n");
+    return -1;
+  }
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(film_grain_params_present);
+  return 0;
+}
+
+int get_av1config_from_obu(const uint8_t *const buffer, size_t length,
+                           int is_annexb, Av1Config *config) {
+  if (!buffer || length == 0 || !config) {
+    return -1;
+  }
+
+  ObuHeader obu_header;
+  memset(&obu_header, 0, sizeof(obu_header));
+
+  size_t sequence_header_length = 0;
+  size_t obu_header_length = 0;
+  if (aom_read_obu_header_and_size(buffer, length, is_annexb, &obu_header,
+                                   &sequence_header_length,
+                                   &obu_header_length) != AOM_CODEC_OK ||
+      obu_header.type != OBU_SEQUENCE_HEADER ||
+      sequence_header_length + obu_header_length > length) {
+    return -1;
+  }
+
+  memset(config, 0, sizeof(*config));
+  config->marker = 1;
+  config->version = 1;
+  return parse_sequence_header(buffer + obu_header_length,
+                               sequence_header_length, config);
+}
+
+int read_av1config(const uint8_t *const buffer, size_t buffer_length,
+                   size_t *bytes_read, Av1Config *config) {
+  if (!buffer || buffer_length < kAv1cSize || !bytes_read || !config) return -1;
+
+  *bytes_read = 0;
+
+  int result = 0;
+  struct aom_read_bit_buffer reader_instance = {
+    buffer, buffer + buffer_length, 0, &result, bitreader_error_handler
+  };
+  struct aom_read_bit_buffer *reader = &reader_instance;
+
+  memset(config, 0, sizeof(*config));
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(marker);
+  config->marker = marker;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(version, 7);
+  config->version = version;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(seq_profile, 3);
+  config->seq_profile = seq_profile;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(seq_level_idx_0, 5);
+  config->seq_level_idx_0 = seq_level_idx_0;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(seq_tier_0);
+  config->seq_tier_0 = seq_tier_0;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(high_bitdepth);
+  config->high_bitdepth = high_bitdepth;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(twelve_bit);
+  config->twelve_bit = twelve_bit;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(monochrome);
+  config->monochrome = monochrome;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(chroma_subsampling_x);
+  config->chroma_subsampling_x = chroma_subsampling_x;
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(chroma_subsampling_y);
+  config->chroma_subsampling_y = chroma_subsampling_y;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(chroma_sample_position, 2);
+  config->chroma_sample_position = chroma_sample_position;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(reserved, 3);
+
+  AV1C_READ_BIT_OR_RETURN_ERROR(initial_presentation_delay_present);
+  config->initial_presentation_delay_present =
+      initial_presentation_delay_present;
+
+  AV1C_READ_BITS_OR_RETURN_ERROR(initial_presentation_delay_minus_one, 4);
+  config->initial_presentation_delay_minus_one =
+      initial_presentation_delay_minus_one;
+
+  *bytes_read = aom_rb_bytes_read(reader);
+
+  return 0;
+}
+
+int write_av1config(const Av1Config *const config, size_t capacity,
+                    size_t *bytes_written, uint8_t *buffer) {
+  if (!config || !buffer || capacity < kAv1cSize || !bytes_written) return -1;
+
+  *bytes_written = 0;
+  memset(buffer, 0, kAv1cSize);
+
+  struct aom_write_bit_buffer writer = { buffer, 0 };
+
+  aom_wb_write_bit(&writer, config->marker);
+  aom_wb_write_literal(&writer, config->version, 7);
+  aom_wb_write_literal(&writer, config->seq_profile, 3);
+  aom_wb_write_literal(&writer, config->seq_level_idx_0, 5);
+  aom_wb_write_bit(&writer, config->seq_tier_0);
+  aom_wb_write_bit(&writer, config->high_bitdepth);
+  aom_wb_write_bit(&writer, config->twelve_bit);
+  aom_wb_write_bit(&writer, config->monochrome);
+  aom_wb_write_bit(&writer, config->chroma_subsampling_x);
+  aom_wb_write_bit(&writer, config->chroma_subsampling_y);
+  aom_wb_write_literal(&writer, config->chroma_sample_position, 2);
+  aom_wb_write_literal(&writer, 0, 3);  // reserved
+  aom_wb_write_bit(&writer, config->initial_presentation_delay_present);
+
+  if (config->initial_presentation_delay_present) {
+    aom_wb_write_literal(&writer, config->initial_presentation_delay_minus_one,
+                         4);
+  } else {
+    aom_wb_write_literal(&writer, 0, 4);  // reserved
+  }
+
+  *bytes_written = aom_wb_bytes_written(&writer);
+  return 0;
+}
+
+#undef AV1C_READ_BIT_OR_RETURN_ERROR
+#undef AV1C_READ_BITS_OR_RETURN_ERROR
+#undef AV1C_PUSH_ERROR_HANDLER_DATA
+#undef AV1C_POP_ERROR_HANDLER_DATA
diff --git a/common/av1_config.h b/common/av1_config.h
new file mode 100644
index 0000000..f6f6bf9
--- /dev/null
+++ b/common/av1_config.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2018, 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 COMMON_AV1_CONFIG_H_
+#define COMMON_AV1_CONFIG_H_
+
+#include "aom/aom_integer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Struct representing ISOBMFF/Matroska AV1 config. See:
+// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax
+//
+// The AV1 config has the following format:
+//
+// unsigned int (1) marker = 1;
+// unsigned int (7) version = 1;
+// unsigned int (3) seq_profile;
+// unsigned int (5) seq_level_idx_0;
+// unsigned int (1) seq_tier_0;
+// unsigned int (1) high_bitdepth;
+// unsigned int (1) twelve_bit;
+// unsigned int (1) monochrome;
+// unsigned int (1) chroma_subsampling_x;
+// unsigned int (1) chroma_subsampling_y;
+// unsigned int (2) chroma_sample_position;
+// unsigned int (3) reserved = 0;
+//
+// unsigned int (1) initial_presentation_delay_present;
+// if (initial_presentation_delay_present) {
+//   unsigned int (4) initial_presentation_delay_minus_one;
+// } else {
+//   unsigned int (4) reserved = 0;
+// }
+//
+// unsigned int (8)[] configOBUs;
+//
+// Note: get_av1config_from_obu() does not currently store 'configOBUs' data, so
+// the field is omitted.
+typedef struct _Av1Config {
+  uint8_t marker;
+  uint8_t version;
+  uint8_t seq_profile;
+  uint8_t seq_level_idx_0;
+  uint8_t seq_tier_0;
+  uint8_t high_bitdepth;
+  uint8_t twelve_bit;
+  uint8_t monochrome;
+  uint8_t chroma_subsampling_x;
+  uint8_t chroma_subsampling_y;
+  uint8_t chroma_sample_position;
+  uint8_t initial_presentation_delay_present;
+  uint8_t initial_presentation_delay_minus_one;
+} Av1Config;
+
+// Attempts to parse a Sequence Header OBU and set the paramenters of 'config'.
+// Returns 0 upon success, and -1 upon failure. 'buffer' can contain multiple
+// OBUs, but the Sequence Header OBU must be the first OBU within the buffer.
+int get_av1config_from_obu(const uint8_t *buffer, size_t length, int is_annexb,
+                           Av1Config *config);
+
+// Attempts to parse an AV1 config from 'buffer'. Returns 0 upon success.
+// Returns -1 when 'buffer_length' is less than 4, when passed NULL pointers, or
+// when parsing of 'buffer' fails.
+int read_av1config(const uint8_t *buffer, size_t buffer_length,
+                   size_t *bytes_read, Av1Config *config);
+
+// Writes 'config' to 'buffer'. Returns 0 upon successful write to 'buffer'.
+// Returns -1 when passed NULL pointers or when 'capacity' insufficient.
+int write_av1config(const Av1Config *config, size_t capacity,
+                    size_t *bytes_written, uint8_t *buffer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif  // COMMON_AV1_CONFIG_H_
diff --git a/test/av1_config_test.cc b/test/av1_config_test.cc
new file mode 100644
index 0000000..e2f2c53
--- /dev/null
+++ b/test/av1_config_test.cc
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2018, 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 <string.h>
+
+#include "common/av1_config.h"
+#include "test/util.h"
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+namespace {
+
+//
+// Input buffers containing exactly one Sequence Header OBU.
+//
+// Each buffer is named according to the OBU storage format (Annex-B vs Low
+// Overhead Bitstream Format) and the type of Sequence Header OBU ("Full"
+// Sequence Header OBUs vs Sequence Header OBUs with the
+// reduced_still_image_flag set).
+//
+const uint8_t kAnnexBFullSequenceHeaderObu[] = {
+  0x0c, 0x08, 0x00, 0x00, 0x00, 0x04, 0x45, 0x7e, 0x3e, 0xff, 0xfc, 0xc0, 0x20
+};
+const uint8_t kAnnexBReducedStillImageSequenceHeaderObu[] = {
+  0x08, 0x08, 0x18, 0x22, 0x2b, 0xf1, 0xfe, 0xc0, 0x20
+};
+
+const uint8_t kLobfFullSequenceHeaderObu[] = {
+  0x0a, 0x0b, 0x00, 0x00, 0x00, 0x04, 0x45, 0x7e, 0x3e, 0xff, 0xfc, 0xc0, 0x20
+};
+
+const uint8_t kLobfReducedStillImageSequenceHeaderObu[] = {
+  0x0a, 0x07, 0x18, 0x22, 0x2b, 0xf1, 0xfe, 0xc0, 0x20
+};
+
+const uint8_t kAv1cAllZero[] = { 0, 0, 0, 0 };
+
+// The size of AV1 config when no configOBUs are present at the end of the
+// configuration structure.
+const size_t kAv1cNoConfigObusSize = 4;
+
+bool VerifyAv1c(const uint8_t *const obu_buffer, size_t obu_buffer_length,
+                bool is_annexb) {
+  Av1Config av1_config;
+  memset(&av1_config, 0, sizeof(av1_config));
+  bool parse_ok = get_av1config_from_obu(obu_buffer, obu_buffer_length,
+                                         is_annexb, &av1_config) == 0;
+  if (parse_ok) {
+    EXPECT_EQ(1, av1_config.marker);
+    EXPECT_EQ(1, av1_config.version);
+    EXPECT_EQ(0, av1_config.seq_profile);
+    EXPECT_EQ(0, av1_config.seq_level_idx_0);
+    EXPECT_EQ(0, av1_config.seq_tier_0);
+    EXPECT_EQ(0, av1_config.high_bitdepth);
+    EXPECT_EQ(0, av1_config.twelve_bit);
+    EXPECT_EQ(0, av1_config.monochrome);
+    EXPECT_EQ(1, av1_config.chroma_subsampling_x);
+    EXPECT_EQ(1, av1_config.chroma_subsampling_y);
+    EXPECT_EQ(0, av1_config.chroma_sample_position);
+    EXPECT_EQ(0, av1_config.initial_presentation_delay_present);
+    EXPECT_EQ(0, av1_config.initial_presentation_delay_minus_one);
+  }
+  return parse_ok && ::testing::Test::HasFailure() == false;
+}
+
+TEST(Av1Config, ObuInvalidInputs) {
+  Av1Config av1_config;
+  memset(&av1_config, 0, sizeof(av1_config));
+  ASSERT_EQ(-1, get_av1config_from_obu(NULL, 0, 0, NULL));
+  ASSERT_EQ(-1,
+            get_av1config_from_obu(&kLobfFullSequenceHeaderObu[0], 0, 0, NULL));
+  ASSERT_EQ(
+      -1, get_av1config_from_obu(&kLobfFullSequenceHeaderObu[0],
+                                 sizeof(kLobfFullSequenceHeaderObu), 0, NULL));
+  ASSERT_EQ(-1, get_av1config_from_obu(NULL, sizeof(kLobfFullSequenceHeaderObu),
+                                       0, NULL));
+  ASSERT_EQ(-1, get_av1config_from_obu(&kLobfFullSequenceHeaderObu[0], 0, 0,
+                                       &av1_config));
+}
+
+TEST(Av1Config, ReadInvalidInputs) {
+  Av1Config av1_config;
+  memset(&av1_config, 0, sizeof(av1_config));
+  size_t bytes_read = 0;
+  ASSERT_EQ(-1, read_av1config(NULL, 0, NULL, NULL));
+  ASSERT_EQ(-1, read_av1config(NULL, 4, NULL, NULL));
+  ASSERT_EQ(-1, read_av1config(&kAv1cAllZero[0], 0, NULL, NULL));
+  ASSERT_EQ(-1, read_av1config(&kAv1cAllZero[0], 4, &bytes_read, NULL));
+  ASSERT_EQ(-1, read_av1config(NULL, 4, &bytes_read, &av1_config));
+}
+
+TEST(Av1Config, WriteInvalidInputs) {
+  Av1Config av1_config;
+  memset(&av1_config, 0, sizeof(av1_config));
+  size_t bytes_written = 0;
+  uint8_t av1c_buffer[4] = { 0 };
+  ASSERT_EQ(-1, write_av1config(NULL, 0, NULL, NULL));
+  ASSERT_EQ(-1, write_av1config(&av1_config, 0, NULL, NULL));
+  ASSERT_EQ(-1, write_av1config(&av1_config, 0, &bytes_written, NULL));
+
+  ASSERT_EQ(-1,
+            write_av1config(&av1_config, 0, &bytes_written, &av1c_buffer[0]));
+  ASSERT_EQ(-1, write_av1config(&av1_config, 4, &bytes_written, NULL));
+}
+
+TEST(Av1Config, GetAv1ConfigFromLobfObu) {
+  // Test parsing of a Sequence Header OBU with the reduced_still_picture_header
+  // unset-- aka a full Sequence Header OBU.
+  ASSERT_TRUE(VerifyAv1c(kLobfFullSequenceHeaderObu,
+                         sizeof(kLobfFullSequenceHeaderObu), false));
+
+  // Test parsing of a reduced still image Sequence Header OBU.
+  ASSERT_TRUE(VerifyAv1c(kLobfReducedStillImageSequenceHeaderObu,
+                         sizeof(kLobfReducedStillImageSequenceHeaderObu),
+                         false));
+}
+
+TEST(Av1Config, GetAv1ConfigFromAnnexBObu) {
+  // Test parsing of a Sequence Header OBU with the reduced_still_picture_header
+  // unset-- aka a full Sequence Header OBU.
+  ASSERT_TRUE(VerifyAv1c(kAnnexBFullSequenceHeaderObu,
+                         sizeof(kAnnexBFullSequenceHeaderObu), true));
+
+  // Test parsing of a reduced still image Sequence Header OBU.
+  ASSERT_TRUE(VerifyAv1c(kAnnexBReducedStillImageSequenceHeaderObu,
+                         sizeof(kAnnexBReducedStillImageSequenceHeaderObu),
+                         true));
+}
+
+TEST(Av1Config, ReadWriteConfig) {
+  Av1Config av1_config;
+  memset(&av1_config, 0, sizeof(av1_config));
+
+  // Test writing out the AV1 config.
+  size_t bytes_written = 0;
+  uint8_t av1c_buffer[4] = { 0 };
+  ASSERT_EQ(0, write_av1config(&av1_config, sizeof(av1c_buffer), &bytes_written,
+                               &av1c_buffer[0]));
+  ASSERT_EQ(kAv1cNoConfigObusSize, bytes_written);
+  for (size_t i = 0; i < kAv1cNoConfigObusSize; ++i) {
+    ASSERT_EQ(kAv1cAllZero[i], av1c_buffer[i])
+        << "Mismatch in output Av1Config at offset=" << i;
+  }
+
+  // Test reading the AV1 config.
+  size_t bytes_read = 0;
+  ASSERT_EQ(0, read_av1config(&kAv1cAllZero[0], sizeof(kAv1cAllZero),
+                              &bytes_read, &av1_config));
+  ASSERT_EQ(kAv1cNoConfigObusSize, bytes_read);
+  ASSERT_EQ(0, write_av1config(&av1_config, sizeof(av1c_buffer), &bytes_written,
+                               &av1c_buffer[0]));
+  for (size_t i = 0; i < kAv1cNoConfigObusSize; ++i) {
+    ASSERT_EQ(kAv1cAllZero[i], av1c_buffer[i])
+        << "Mismatch in output Av1Config at offset=" << i;
+  }
+}
+
+}  // namespace
diff --git a/test/test.cmake b/test/test.cmake
index 32f8116..b16ae14 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -26,6 +26,7 @@
 list(APPEND AOM_UNIT_TEST_COMMON_SOURCES
             "${AOM_ROOT}/test/acm_random.h"
             "${AOM_ROOT}/test/aom_integer_test.cc"
+            "${AOM_ROOT}/test/av1_config_test.cc"
             "${AOM_ROOT}/test/blockd_test.cc"
             "${AOM_ROOT}/test/clear_system_state.h"
             "${AOM_ROOT}/test/codec_factory.h"