[NORMATIVE-DECODING] option of annex-b/non-annex-b bitstreams

libaom modified to encode/decode AV1 as annex-b or non-annex-b

Encoder option --annexb=0 or 1  (default 0)
Decoder option --annexb

BUG=aomedia:1590
BUG=aomedia:1591

Change-Id: I0f092b45fbf30e689c7fe3c6540d22f497946880
diff --git a/aom/aom_decoder.h b/aom/aom_decoder.h
index 3c67fb3..3973c50 100644
--- a/aom/aom_decoder.h
+++ b/aom/aom_decoder.h
@@ -85,6 +85,7 @@
   unsigned int h;                      /**< Height (or 0 for unknown/default) */
   unsigned int is_kf;                  /**< Current frame is a keyframe */
   unsigned int enhancement_layers_cnt; /**< Enhancement layers */
+  unsigned int is_annexb;              /**< Is Bitstream in Annex-B format */
 } aom_codec_stream_info_t;
 
 /* REQUIRED FUNCTIONS
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index e93d081..d54d89f 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -663,6 +663,14 @@
    */
   unsigned int monochrome;
 
+  /*!\brief Bitstream syntax mode
+   *
+   * This value indicates the bitstream syntax mode.
+   * A value of 0 indicates bitstream is saved as Section 5 bitstream. A value
+   * of 1 indicates the bitstream is saved in Annex-B format
+   */
+  unsigned int save_as_annexb;
+
   /*!\brief Number of explicit tile widths specified
    *
    * This value indicates the number of tile widths specified
diff --git a/aom/aomdx.h b/aom/aomdx.h
index 2beba60..b3a5fb1 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -146,6 +146,9 @@
    */
   AV1_SET_TILE_MODE,
 
+  /** control function to indicate whether bitstream is in Annex-B format. */
+  AV1D_SET_IS_ANNEXB,
+
   /** control function to set an aom_inspect_cb callback that is invoked each
    * time a frame is decoded.  When compiled without --enable-inspection, this
    * returns AOM_CODEC_INCAPABLE.
@@ -187,6 +190,8 @@
 #define AOM_CTRL_AV1_SET_DECODE_TILE_COL
 AOM_CTRL_USE_TYPE(AV1_SET_TILE_MODE, unsigned int)
 #define AOM_CTRL_AV1_SET_TILE_MODE
+AOM_CTRL_USE_TYPE(AV1D_SET_IS_ANNEXB, unsigned int)
+#define AOM_CTRL_AV1D_SET_IS_ANNEXB
 AOM_CTRL_USE_TYPE(AV1_SET_INSPECTION_CALLBACK, aom_inspect_init *)
 #define AOM_CTRL_AV1_SET_INSPECTION_CALLBACK
 /*!\endcond */
diff --git a/aomdec.c b/aomdec.c
index b765335..9fd8f0a 100644
--- a/aomdec.c
+++ b/aomdec.c
@@ -111,13 +111,17 @@
 static const arg_def_t tilec = ARG_DEF(NULL, "tile-column", 1,
                                        "Column index of tile to decode "
                                        "(-1 for all columns)");
+static const arg_def_t isannexb =
+    ARG_DEF(NULL, "annexb", 0, "Bitstream is in Annex-B format");
 
 static const arg_def_t *all_args[] = {
-  &help,           &codecarg,   &use_yv12,    &use_i420,      &flipuvarg,
-  &rawvideo,       &noblitarg,  &progressarg, &limitarg,      &skiparg,
-  &postprocarg,    &summaryarg, &outputfile,  &threadsarg,    &verbosearg,
-  &scalearg,       &fb_arg,     &md5arg,      &framestatsarg, &continuearg,
-  &outbitdeptharg, &tilem,      &tiler,       &tilec,         NULL
+  &help,           &codecarg,   &use_yv12,      &use_i420,
+  &flipuvarg,      &rawvideo,   &noblitarg,     &progressarg,
+  &limitarg,       &skiparg,    &postprocarg,   &summaryarg,
+  &outputfile,     &threadsarg, &verbosearg,    &scalearg,
+  &fb_arg,         &md5arg,     &framestatsarg, &continuearg,
+  &outbitdeptharg, &tilem,      &tiler,         &tilec,
+  &isannexb,       NULL
 };
 
 #if CONFIG_LIBYUV
@@ -500,6 +504,7 @@
   aom_codec_dec_cfg_t cfg = { 0, 0, 0, CONFIG_LOWBITDEPTH, { 1 } };
   unsigned int output_bit_depth = 0;
   unsigned int tile_mode = 0;
+  unsigned int is_annexb = 0;
   int tile_row = -1;
   int tile_col = -1;
   int frames_corrupted = 0;
@@ -527,7 +532,7 @@
   memset(&webm_ctx, 0, sizeof(webm_ctx));
   input.webm_ctx = &webm_ctx;
 #endif
-  struct ObuDecInputContext obu_ctx = { NULL, NULL, 0, 0 };
+  struct ObuDecInputContext obu_ctx = { NULL, NULL, 0, 0, 0 };
   obu_ctx.avx_ctx = &aom_input_ctx;
   input.obu_ctx = &obu_ctx;
   input.aom_input_ctx = &aom_input_ctx;
@@ -604,6 +609,9 @@
       output_bit_depth = arg_parse_uint(&arg);
     } else if (arg_match(&arg, &tilem, argi)) {
       tile_mode = arg_parse_int(&arg);
+    } else if (arg_match(&arg, &isannexb, argi)) {
+      is_annexb = 1;
+      input.obu_ctx->is_annexb = 1;
     } else if (arg_match(&arg, &tiler, argi)) {
       tile_row = arg_parse_int(&arg);
     } else if (arg_match(&arg, &tilec, argi)) {
@@ -718,6 +726,11 @@
     goto fail;
   }
 
+  if (aom_codec_control(&decoder, AV1D_SET_IS_ANNEXB, is_annexb)) {
+    fprintf(stderr, "Failed to set is_annexb: %s\n", aom_codec_error(&decoder));
+    goto fail;
+  }
+
   if (aom_codec_control(&decoder, AV1_SET_DECODE_TILE_ROW, tile_row)) {
     fprintf(stderr, "Failed to set decode_tile_row: %s\n",
             aom_codec_error(&decoder));
diff --git a/aomenc.c b/aomenc.c
index b601080..6afa006 100644
--- a/aomenc.c
+++ b/aomenc.c
@@ -380,6 +380,8 @@
     ARG_DEF(NULL, "sframe-dist", 1, "S-Frame interval (frames)");
 static const arg_def_t sframe_mode =
     ARG_DEF(NULL, "sframe-mode", 1, "S-Frame insertion mode (1..2)");
+static const arg_def_t save_as_annexb =
+    ARG_DEF(NULL, "annexb", 1, "Save as Annex-B");
 static const arg_def_t noise_sens =
     ARG_DEF(NULL, "noise-sensitivity", 1, "Noise sensitivity (frames to blur)");
 static const arg_def_t sharpness =
@@ -653,6 +655,7 @@
                                        &inbitdeptharg,
                                        &sframe_dist,
                                        &sframe_mode,
+                                       &save_as_annexb,
                                        NULL };
 static const int av1_arg_ctrl_map[] = { AOME_SET_CPUUSED,
                                         AOME_SET_DEVSF,
@@ -1225,6 +1228,8 @@
       config->cfg.sframe_dist = arg_parse_uint(&arg);
     } else if (arg_match(&arg, &sframe_mode, argi)) {
       config->cfg.sframe_mode = arg_parse_uint(&arg);
+    } else if (arg_match(&arg, &save_as_annexb, argi)) {
+      config->cfg.save_as_annexb = arg_parse_uint(&arg);
     } else if (arg_match(&arg, &tile_width, argi)) {
       config->cfg.tile_width_count =
           arg_parse_list(&arg, config->cfg.tile_widths, MAX_TILE_WIDTHS);
@@ -1538,6 +1543,10 @@
                         stream->config.cfg.large_scale_tile);
       ctx_exit_on_error(&stream->decoder, "Failed to set decode_tile_mode");
 
+      aom_codec_control(&stream->decoder, AV1D_SET_IS_ANNEXB,
+                        stream->config.cfg.save_as_annexb);
+      ctx_exit_on_error(&stream->decoder, "Failed to set is_annexb");
+
       aom_codec_control(&stream->decoder, AV1_SET_DECODE_TILE_ROW, -1);
       ctx_exit_on_error(&stream->decoder, "Failed to set decode_tile_row");
 
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index fd94390..f93e21c 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -655,6 +655,8 @@
   oxcf->aq_mode = extra_cfg->aq_mode;
   oxcf->deltaq_mode = extra_cfg->deltaq_mode;
 
+  oxcf->save_as_annexb = cfg->save_as_annexb;
+
   oxcf->frame_periodic_boost = extra_cfg->frame_periodic_boost;
   oxcf->motion_vector_unit_test = extra_cfg->motion_vector_unit_test;
   return AOM_CODEC_OK;
@@ -1313,6 +1315,15 @@
             obu_header_size + obu_payload_size + length_field_size;
       }
 
+      if (ctx->oxcf.save_as_annexb) {
+        size_t curr_frame_size = pkt.data.frame.sz;
+        if (av1_convert_sect5obus_to_annexb(ctx->pending_cx_data,
+                                            &curr_frame_size) != AOM_CODEC_OK) {
+          return AOM_CODEC_ERROR;
+        }
+        pkt.data.frame.sz = curr_frame_size;
+      }
+
       pkt.data.frame.pts = ticks_to_timebase_units(timebase, dst_time_stamp);
       pkt.data.frame.flags = get_frame_pkt_flags(cpi, lib_flags);
       pkt.data.frame.duration = (uint32_t)ticks_to_timebase_units(
@@ -1703,6 +1714,7 @@
         1,            // sframe_mode
         0,            // large_scale_tile
         0,            // monochrome
+        0,            // save_as_annexb
         0,            // tile_width_count
         0,            // tile_height_count
         { 0 },        // tile_widths
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index bcf7078..f3ae661 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -59,6 +59,7 @@
   int decode_tile_row;
   int decode_tile_col;
   unsigned int tile_mode;
+  unsigned int is_annexb;
 
   AVxWorker *frame_workers;
   int num_frame_workers;
@@ -181,15 +182,25 @@
   intra_only_flag = 1;
   si->is_kf = 1;
 
-  struct aom_read_bit_buffer rb = { data, data + data_sz, 0, NULL, NULL };
+  size_t length_field_size = 0;
+  if (si->is_annexb) {
+    length_field_size = get_obu_length_field_size(data, data_sz - 1);
+  }
+  struct aom_read_bit_buffer rb = { data + length_field_size, data + data_sz, 0,
+                                    NULL, NULL };
+
   const uint8_t obu_header = (uint8_t)aom_rb_read_literal(&rb, 8);
   OBU_TYPE obu_type;
 
   if (get_obu_type(obu_header, &obu_type) != 0)
     return AOM_CODEC_UNSUP_BITSTREAM;
 
-  // One byte has been consumed by the OBU header.
-  rb.bit_buffer += get_obu_length_field_size(data + 1, data_sz - 1) * 8;
+  if (!si->is_annexb) {
+    // One byte has been consumed by the OBU header.
+    // TODO(shan): Assuming 1-byte obu_header (need to account in case extension
+    // exists)
+    rb.bit_buffer += get_obu_length_field_size(data + 1, data_sz - 1) + 2;
+  }
 
   // This check is disabled because existing behavior is depended upon by
   // decoder tests (see decode_test_driver.cc), scalability_decoder (see
@@ -365,6 +376,7 @@
     frame_worker_data->pbi->max_threads = ctx->cfg.threads;
     frame_worker_data->pbi->inv_tile_order = ctx->invert_tile_order;
     frame_worker_data->pbi->common.large_scale_tile = ctx->tile_mode;
+    frame_worker_data->pbi->common.is_annexb = ctx->is_annexb;
     frame_worker_data->pbi->dec_tile_row = ctx->decode_tile_row;
     frame_worker_data->pbi->dec_tile_col = ctx->decode_tile_col;
     worker->hook = (AVxWorkerHook)frame_worker_hook;
@@ -402,6 +414,7 @@
   // of the heap.
   if (!ctx->si.h) {
     int is_intra_only = 0;
+    ctx->si.is_annexb = ctx->is_annexb;
     const aom_codec_err_t res =
         decoder_peek_si_internal(*data, data_sz, &ctx->si, &is_intra_only);
     if (res != AOM_CODEC_OK) return res;
@@ -425,6 +438,8 @@
   frame_worker_data->pbi->dec_tile_row = ctx->decode_tile_row;
   frame_worker_data->pbi->dec_tile_col = ctx->decode_tile_col;
 
+  frame_worker_data->pbi->common.is_annexb = ctx->is_annexb;
+
   worker->had_error = 0;
   winterface->execute(worker);
 
@@ -461,6 +476,7 @@
     res = init_decoder(ctx);
     if (res != AOM_CODEC_OK) return res;
   }
+
   // Decode in serial mode.
   if (frame_count > 0) {
     int i;
@@ -904,6 +920,12 @@
   return AOM_CODEC_OK;
 }
 
+static aom_codec_err_t ctrl_set_is_annexb(aom_codec_alg_priv_t *ctx,
+                                          va_list args) {
+  ctx->is_annexb = va_arg(args, unsigned int);
+  return AOM_CODEC_OK;
+}
+
 static aom_codec_err_t ctrl_set_inspection_callback(aom_codec_alg_priv_t *ctx,
                                                     va_list args) {
 #if !CONFIG_INSPECTION
@@ -934,6 +956,7 @@
   { AV1_SET_DECODE_TILE_ROW, ctrl_set_decode_tile_row },
   { AV1_SET_DECODE_TILE_COL, ctrl_set_decode_tile_col },
   { AV1_SET_TILE_MODE, ctrl_set_tile_mode },
+  { AV1D_SET_IS_ANNEXB, ctrl_set_is_annexb },
   { AV1_SET_INSPECTION_CALLBACK, ctrl_set_inspection_callback },
 
   // Getters
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index 2623445..b491724 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -516,6 +516,9 @@
   int tpl_mvs_mem_size;
   // TODO(jingning): This can be combined with sign_bias later.
   int8_t ref_frame_side[REF_FRAMES];
+
+  int is_annexb;
+
   int frame_refs_short_signaling;
   int temporal_layer_id;
   int enhancement_layer_id;
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index 4a3828b..40434bf 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -76,7 +76,7 @@
 
 // Parses OBU header and stores values in 'header'.
 static aom_codec_err_t read_obu_header(struct aom_read_bit_buffer *rb,
-                                       ObuHeader *header) {
+                                       int is_annexb, ObuHeader *header) {
   if (!rb || !header) return AOM_CODEC_INVALID_PARAM;
 
   header->size = 1;
@@ -90,8 +90,9 @@
 
   header->has_extension = aom_rb_read_bit(rb);
   header->has_length_field = aom_rb_read_bit(rb);
-  if (!header->has_length_field) {
-    // libaom does not support streams with this bit set to 0.
+
+  if (!header->has_length_field && !is_annexb) {
+    // section 5 obu streams must have length field set.
     return AOM_CODEC_UNSUP_BITSTREAM;
   }
 
@@ -109,14 +110,15 @@
 }
 
 aom_codec_err_t aom_read_obu_header(uint8_t *buffer, size_t buffer_length,
-                                    size_t *consumed, ObuHeader *header) {
+                                    size_t *consumed, ObuHeader *header,
+                                    int is_annexb) {
   if (buffer_length < 1 || !consumed || !header) return AOM_CODEC_INVALID_PARAM;
 
   // TODO(tomfinegan): Set the error handler here and throughout this file, and
   // confirm parsing work done via aom_read_bit_buffer is successful.
   struct aom_read_bit_buffer rb = { buffer, buffer + buffer_length, 0, NULL,
                                     NULL };
-  aom_codec_err_t parse_result = read_obu_header(&rb, header);
+  aom_codec_err_t parse_result = read_obu_header(&rb, is_annexb, header);
   if (parse_result == AOM_CODEC_OK) *consumed = header->size;
   return parse_result;
 }
@@ -382,25 +384,39 @@
     }
 
     size_t length_field_size = 0;
+    size_t obu_size = 0;
+    if (cm->is_annexb) {
+      if (read_obu_size(data, bytes_available, &obu_size, &length_field_size) !=
+          AOM_CODEC_OK) {
+        cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
+        return;
+      }
+    }
     if (data_end < data + length_field_size) {
       cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
       return;
     }
     av1_init_read_bit_buffer(pbi, &rb, data + length_field_size, data_end);
 
-    const aom_codec_err_t status = read_obu_header(&rb, &obu_header);
+    const aom_codec_err_t status =
+        read_obu_header(&rb, cm->is_annexb, &obu_header);
     if (status != AOM_CODEC_OK) {
       cm->error.error_code = status;
       return;
     }
 
-    if (read_obu_size(data + obu_header.size, bytes_available - obu_header.size,
-                      &payload_size, &length_field_size) != AOM_CODEC_OK) {
-      cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
-      return;
+    if (!cm->is_annexb) {
+      if (read_obu_size(data + obu_header.size,
+                        bytes_available - obu_header.size, &payload_size,
+                        &length_field_size) != AOM_CODEC_OK) {
+        cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
+        return;
+      }
+      av1_init_read_bit_buffer(
+          pbi, &rb, data + length_field_size + obu_header.size, data_end);
+    } else {
+      payload_size = obu_size - obu_header.size;
     }
-    av1_init_read_bit_buffer(
-        pbi, &rb, data + length_field_size + obu_header.size, data_end);
 
     data += length_field_size + obu_header.size;
     if (data_end < data) {
diff --git a/av1/decoder/obu.h b/av1/decoder/obu.h
index 9fe5795..2295c69 100644
--- a/av1/decoder/obu.h
+++ b/av1/decoder/obu.h
@@ -35,7 +35,8 @@
 int get_obu_type(uint8_t obu_header_byte, OBU_TYPE *obu_type);
 
 aom_codec_err_t aom_read_obu_header(uint8_t *buffer, size_t buffer_length,
-                                    size_t *consumed, ObuHeader *header);
+                                    size_t *consumed, ObuHeader *header,
+                                    int is_annexb);
 
 void av1_decode_frame_from_obus(struct AV1Decoder *pbi, const uint8_t *data,
                                 const uint8_t *data_end,
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index ac1ae9b..7849ae3 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -5935,6 +5935,63 @@
 
 int av1_get_quantizer(AV1_COMP *cpi) { return cpi->common.base_qindex; }
 
+int av1_convert_sect5obus_to_annexb(uint8_t *buffer, size_t *frame_size) {
+  size_t output_size = 0;
+  size_t total_bytes_read = 0;
+  size_t remaining_size = *frame_size;
+  uint8_t *buff_ptr = buffer;
+
+  // go through each OBUs
+  while (total_bytes_read < *frame_size) {
+    uint8_t saved_obu_header[2];
+    uint64_t obu_payload_size;
+    size_t length_of_payload_size;
+    size_t length_of_obu_size;
+    uint32_t obu_header_size = (buff_ptr[0] >> 2) & 0x1 ? 2 : 1;
+    size_t obu_bytes_read = obu_header_size;  // bytes read for current obu
+
+    // save the obu header (1 or 2 bytes)
+    memmove(saved_obu_header, buff_ptr, obu_header_size);
+    // clear the obu_has_size_field
+    saved_obu_header[0] = saved_obu_header[0] & (~0x2);
+
+    // get the payload_size and length of payload_size
+    if (aom_uleb_decode(buff_ptr + obu_header_size, remaining_size,
+                        &obu_payload_size, &length_of_payload_size) != 0) {
+      return AOM_CODEC_ERROR;
+    }
+    obu_bytes_read += length_of_payload_size;
+
+    // calculate the length of size of the obu header plus payload
+    length_of_obu_size =
+        aom_uleb_size_in_bytes((uint64_t)(obu_header_size + obu_payload_size));
+
+    // move the rest of data to new location
+    memmove(buff_ptr + length_of_obu_size + obu_header_size,
+            buff_ptr + obu_bytes_read, remaining_size - obu_bytes_read);
+    obu_bytes_read += obu_payload_size;
+
+    // write the new obu size
+    const uint64_t obu_size = obu_header_size + obu_payload_size;
+    size_t coded_obu_size;
+    if (aom_uleb_encode(obu_size, sizeof(obu_size), buff_ptr,
+                        &coded_obu_size) != 0) {
+      return AOM_CODEC_ERROR;
+    }
+
+    // write the saved (modified) obu_header following obu size
+    memmove(buff_ptr + length_of_obu_size, saved_obu_header, obu_header_size);
+
+    total_bytes_read += obu_bytes_read;
+    remaining_size -= obu_bytes_read;
+    buff_ptr += length_of_obu_size + obu_size;
+    output_size += length_of_obu_size + obu_size;
+  }
+
+  *frame_size = output_size;
+  return AOM_CODEC_OK;
+}
+
 void av1_apply_encoding_flags(AV1_COMP *cpi, aom_enc_frame_flags_t flags) {
   // TODO(yunqingwang): For what references to use, external encoding flags
   // should be consistent with internal reference frame selection. Need to
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index df7c434..fde3298 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -288,6 +288,7 @@
   int enable_warped_motion;
   int allow_warped_motion;
   int enable_superres;
+  unsigned int save_as_annexb;
 } AV1EncoderConfig;
 
 static INLINE int is_lossless_requested(const AV1EncoderConfig *cfg) {
@@ -619,6 +620,8 @@
 
 int av1_get_quantizer(struct AV1_COMP *cpi);
 
+int av1_convert_sect5obus_to_annexb(uint8_t *buffer, size_t *input_size);
+
 static INLINE int frame_is_kf_gf_arf(const AV1_COMP *cpi) {
   return frame_is_intra_only(&cpi->common) || cpi->refresh_alt_ref_frame ||
          (cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref);
diff --git a/examples/scalable_decoder.c b/examples/scalable_decoder.c
index 3586df6..6b41c85 100644
--- a/examples/scalable_decoder.c
+++ b/examples/scalable_decoder.c
@@ -102,7 +102,7 @@
   size_t buffer_size = 0;
   int next_layer_id = 0;
   struct AvxInputContext aom_input_ctx;
-  struct ObuDecInputContext obu_ctx = { &aom_input_ctx, NULL, 0, 0 };
+  struct ObuDecInputContext obu_ctx = { &aom_input_ctx, NULL, 0, 0, 0 };
   aom_codec_stream_info_t si;
   uint8_t tmpbuf[32];
   unsigned int i;
diff --git a/obudec.c b/obudec.c
index 4d035c1..1ce7d24 100644
--- a/obudec.c
+++ b/obudec.c
@@ -35,6 +35,10 @@
   if (!f || !value_buffer || !value_length || !value) return -1;
   for (int len = 0; len < OBU_MAX_LENGTH_FIELD_SIZE; ++len) {
     const size_t num_read = fread(&value_buffer[len], 1, 1, f);
+    if (num_read == 0 && feof(f)) {
+      *value_length = 0;
+      return 0;
+    }
     if (num_read != 1) {
       // Ran out of data before completing read of value.
       return -1;
@@ -82,8 +86,8 @@
 // success, and non-zero on failure. When end of file is reached, the return
 // value is 0 and the 'bytes_read' value is set to 0.
 static int obudec_read_obu_header(FILE *f, size_t buffer_capacity,
-                                  uint8_t *obu_data, ObuHeader *obu_header,
-                                  size_t *bytes_read) {
+                                  int is_annexb, uint8_t *obu_data,
+                                  ObuHeader *obu_header, size_t *bytes_read) {
   if (!f || buffer_capacity < (OBU_HEADER_SIZE + OBU_EXTENSION_SIZE) ||
       !obu_data || !obu_header || !bytes_read) {
     return -1;
@@ -107,8 +111,8 @@
   }
 
   size_t obu_bytes_parsed = 0;
-  const aom_codec_err_t parse_result =
-      aom_read_obu_header(obu_data, *bytes_read, &obu_bytes_parsed, obu_header);
+  const aom_codec_err_t parse_result = aom_read_obu_header(
+      obu_data, *bytes_read, &obu_bytes_parsed, obu_header, is_annexb);
   if (parse_result != AOM_CODEC_OK || *bytes_read != obu_bytes_parsed) {
     fprintf(stderr, "obudec: Error parsing OBU header.\n");
     return -1;
@@ -133,7 +137,7 @@
   return 0;
 }
 
-static int obudec_read_one_obu(FILE *f, size_t buffer_capacity,
+static int obudec_read_one_obu(FILE *f, size_t buffer_capacity, int is_annexb,
                                uint8_t *obu_data, uint64_t *obu_length,
                                ObuHeader *obu_header) {
   const size_t kMinimumBufferSize = OBU_DETECTION_SIZE;
@@ -142,8 +146,22 @@
     return -1;
   }
 
+  uint64_t obu_payload_length = 0;
+  size_t leb128_length = 0;
+  uint64_t obu_size = 0;
+  if (is_annexb) {
+    if (obudec_read_leb128(f, &obu_data[0], &leb128_length, &obu_size) != 0) {
+      fprintf(stderr, "obudec: Failure reading OBU size length.\n");
+      return -1;
+    } else if (leb128_length == 0) {
+      *obu_length = 0;
+      return 0;
+    }
+  }
+
   size_t bytes_read = 0;
-  if (obudec_read_obu_header(f, buffer_capacity, obu_data, obu_header,
+  if (obudec_read_obu_header(f, buffer_capacity, is_annexb,
+                             obu_data + leb128_length, obu_header,
                              &bytes_read) != 0) {
     return -1;
   } else if (bytes_read == 0) {
@@ -151,13 +169,18 @@
     return 0;
   }
 
-  uint64_t obu_payload_length = 0;
-  size_t leb128_length = 0;
-  if (obudec_read_leb128(f, &obu_data[bytes_read], &leb128_length,
-                         &obu_payload_length) != 0) {
-    fprintf(stderr, "obudec: Failure reading OBU payload length.\n");
-    return -1;
+  if (!is_annexb) {
+    if (obudec_read_leb128(f, &obu_data[bytes_read], &leb128_length,
+                           &obu_payload_length) != 0) {
+      fprintf(stderr, "obudec: Failure reading OBU payload length.\n");
+      return -1;
+    }
   }
+
+  if (is_annexb) {
+    obu_payload_length = obu_size - bytes_read;
+  }
+
   bytes_read += leb128_length;
 
   if (UINT64_MAX - bytes_read < obu_payload_length) return -1;
@@ -181,14 +204,14 @@
 
   struct AvxInputContext *avx_ctx = obu_ctx->avx_ctx;
   uint8_t detect_buf[OBU_DETECTION_SIZE] = { 0 };
-
+  const int is_annexb = obu_ctx->is_annexb;
   FILE *f = avx_ctx->file;
   uint64_t obu_length = 0;
   ObuHeader obu_header;
   memset(&obu_header, 0, sizeof(obu_header));
 
-  if (obudec_read_one_obu(f, OBU_DETECTION_SIZE, &detect_buf[0], &obu_length,
-                          &obu_header) != 0) {
+  if (obudec_read_one_obu(f, OBU_DETECTION_SIZE, is_annexb, &detect_buf[0],
+                          &obu_length, &obu_header) != 0) {
     fprintf(stderr, "obudec: Failure reading first OBU.\n");
     rewind(f);
     return 0;
@@ -213,10 +236,10 @@
       rewind(f);
       return 0;
     }
-  } else {
+  } else if (!is_annexb) {
     fprintf(stderr, "obudec: OBU size fields required, cannot decode input.\n");
     rewind(f);
-    return 0;
+    return (0);
   }
 
   // Appears that input is valid Section 5 AV1 stream.
@@ -246,6 +269,7 @@
     return 1;
   }
 
+  const int is_annexb = obu_ctx->is_annexb;
   while (1) {
     ObuHeader obu_header;
     memset(&obu_header, 0, sizeof(obu_header));
@@ -254,7 +278,8 @@
     uint8_t *data = obu_ctx->buffer + obu_ctx->bytes_buffered;
     const size_t capacity = obu_ctx->buffer_capacity - obu_ctx->bytes_buffered;
 
-    if (obudec_read_one_obu(f, capacity, data, &obu_size, &obu_header) != 0) {
+    if (obudec_read_one_obu(f, capacity, is_annexb, data, &obu_size,
+                            &obu_header) != 0) {
       fprintf(stderr, "obudec: read_one_obu failed in TU loop\n");
       return -1;
     }
diff --git a/obudec.h b/obudec.h
index 158202e..c413f20 100644
--- a/obudec.h
+++ b/obudec.h
@@ -22,9 +22,11 @@
   uint8_t *buffer;
   size_t buffer_capacity;
   size_t bytes_buffered;
+  int is_annexb;
 };
 
-// Returns 1 when file data starts with what appears to be a Temporal Delimiter
+// Returns 1 when file data starts (if Annex B stream, after reading the
+// size of the OBU) with what appears to be a Temporal Delimiter
 // OBU as defined by Section 5 of the AV1 bitstream specification.
 int file_is_obu(struct ObuDecInputContext *obu_ctx);