obu: Merge FH and TG if there is only 1 TG

Guarded by CONFIG_OBU_FRAME flag.

Change-Id: I331cf5ae93beb9aba299159ba2af28c0ffd31df5
diff --git a/aom/aom_codec.h b/aom/aom_codec.h
index ff9647a..7838008 100644
--- a/aom/aom_codec.h
+++ b/aom/aom_codec.h
@@ -488,6 +488,7 @@
   OBU_FRAME_HEADER = 3,
   OBU_TILE_GROUP = 4,
   OBU_METADATA = 5,
+  OBU_FRAME = 6,
   OBU_PADDING = 15,
 } OBU_TYPE;
 
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index afd589d..addcf61 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -72,6 +72,7 @@
     case OBU_TEMPORAL_DELIMITER:
     case OBU_FRAME_HEADER:
     case OBU_TILE_GROUP:
+    case OBU_FRAME:
     case OBU_METADATA:
     case OBU_PADDING: valid_type = 1; break;
     default: break;
@@ -323,6 +324,7 @@
     struct aom_read_bit_buffer rb;
     size_t payload_size = 0;
     size_t decoded_payload_size = 0;
+    size_t obu_payload_offset = 0;
     const size_t bytes_available = data_end - data;
 
     if (bytes_available < 1) {
@@ -401,6 +403,9 @@
           decoded_payload_size = seq_header_size;
         }
         break;
+#if CONFIG_OBU_FRAME
+      case OBU_FRAME:
+#endif  // CONFIG_OBU_FRAME
       case OBU_FRAME_HEADER:
         // Only decode first frame header received
         if (!frame_header_received) {
@@ -409,16 +414,25 @@
           frame_header_received = 1;
         }
         decoded_payload_size = frame_header_size;
-        if (cm->show_existing_frame) frame_decoding_finished = 1;
+        if (cm->show_existing_frame) {
+          frame_decoding_finished = 1;
+          break;
+        }
+#if CONFIG_OBU_FRAME
+        if (obu_header.type == OBU_FRAME_HEADER) break;
+        obu_payload_offset = frame_header_size;
+        AOM_FALLTHROUGH_INTENDED;  // fall through to read tile group.
+#else
         break;
+#endif  // CONFIG_OBU_FRAME
       case OBU_TILE_GROUP:
         if (!frame_header_received) {
           cm->error.error_code = AOM_CODEC_CORRUPT_FRAME;
           return;
         }
-        decoded_payload_size = read_one_tile_group_obu(
-            pbi, &rb, is_first_tg_obu_received, data, data + payload_size,
-            p_data_end, &frame_decoding_finished);
+        decoded_payload_size += read_one_tile_group_obu(
+            pbi, &rb, is_first_tg_obu_received, data + obu_payload_offset,
+            data + payload_size, p_data_end, &frame_decoding_finished);
         is_first_tg_obu_received = 0;
         break;
       case OBU_METADATA:
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index c6dc82e..e8a16e9 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -3680,11 +3680,22 @@
   *max_tile_col_size = 0;
 
   if (cm->large_scale_tile) {
+#if CONFIG_OBU_FRAME
+    // For large_scale_tile case, we always have only one tile group, so it can
+    // be written as an OBU_FRAME.
+    const OBU_TYPE obu_type = OBU_FRAME;
+#else
+    const OBU_TYPE obu_type = OBU_TILE_GROUP;
+#endif  // CONFIG_OBU_FRAME
     uint32_t tg_hdr_size =
-        write_obu_header(OBU_TILE_GROUP, 0, data + PRE_OBU_SIZE_BYTES);
+        write_obu_header(obu_type, 0, data + PRE_OBU_SIZE_BYTES);
     tg_hdr_size += PRE_OBU_SIZE_BYTES;
     data += tg_hdr_size;
 
+#if CONFIG_OBU_FRAME
+    total_size += write_frame_header_obu(cpi, saved_wb, data);
+#endif  // CONFIG_OBU_FRAME
+
     int tile_size_bytes = 0;
     int tile_col_size_bytes = 0;
 
@@ -3829,11 +3840,23 @@
 
         // A new tile group begins at this tile.  Write the obu header and
         // tile group header
-        curr_tg_data_size = write_obu_header(
-            OBU_TILE_GROUP, obu_extension_header, data + PRE_OBU_SIZE_BYTES);
+#if CONFIG_OBU_FRAME
+        const OBU_TYPE obu_type =
+            (num_tg_hdrs == 1) ? OBU_FRAME : OBU_TILE_GROUP;
+#else
+        const OBU_TYPE obu_type = OBU_TILE_GROUP;
+#endif
+        curr_tg_data_size = write_obu_header(obu_type, obu_extension_header,
+                                             data + PRE_OBU_SIZE_BYTES);
 #if CONFIG_OBU_SIZING
         obu_header_size = curr_tg_data_size;
 #endif  // CONFIG_OBU_SIZING
+#if CONFIG_OBU_FRAME
+        if (num_tg_hdrs == 1) {
+          curr_tg_data_size += write_frame_header_obu(
+              cpi, saved_wb, data + curr_tg_data_size + PRE_OBU_SIZE_BYTES);
+        }
+#endif
         curr_tg_data_size += write_tile_group_header(
             data + curr_tg_data_size + PRE_OBU_SIZE_BYTES, tile_idx,
             AOMMIN(tile_idx + tg_size - 1, tile_cols * tile_rows - 1),
@@ -3957,7 +3980,7 @@
   AV1_COMMON *const cm = &cpi->common;
   uint32_t obu_header_size = 0;
   uint32_t obu_payload_size = 0;
-  FrameHeaderInfo fh_info;
+  FrameHeaderInfo fh_info = { NULL, 0 };
 #if CONFIG_SCALABILITY
   const uint8_t enhancement_layers_cnt = cm->enhancement_layers_cnt;
   const uint8_t obu_extension_header =
@@ -4000,33 +4023,41 @@
     data += obu_header_size + obu_payload_size + length_field_size;
   }
 
-  struct aom_write_bit_buffer saved_wb;
+#if CONFIG_OBU_FRAME
+  const int write_frame_header = (cm->num_tg > 1 || cm->show_existing_frame);
+#else
+  const int write_frame_header = 1;
+#endif  // CONFIG_OBU_FRAME
 
-  // Write Frame Header OBU.
-  fh_info.frame_header = data;
-  obu_header_size = write_obu_header(OBU_FRAME_HEADER, obu_extension_header,
-                                     data + PRE_OBU_SIZE_BYTES);
-  obu_payload_size = write_frame_header_obu(
-      cpi, &saved_wb, data + PRE_OBU_SIZE_BYTES + obu_header_size);
+  struct aom_write_bit_buffer saved_wb;
+  if (write_frame_header) {
+    // Write Frame Header OBU.
+    fh_info.frame_header = data;
+    obu_header_size = write_obu_header(OBU_FRAME_HEADER, obu_extension_header,
+                                       data + PRE_OBU_SIZE_BYTES);
+    obu_payload_size = write_frame_header_obu(
+        cpi, &saved_wb, data + PRE_OBU_SIZE_BYTES + obu_header_size);
 
 #if CONFIG_OBU_SIZING
-  const size_t length_field_size =
-      obu_memmove(obu_header_size, obu_payload_size, data);
-  if (write_uleb_obu_size(obu_header_size, obu_payload_size, data) !=
-      AOM_CODEC_OK) {
-    return AOM_CODEC_ERROR;
-  }
+    const size_t length_field_size =
+        obu_memmove(obu_header_size, obu_payload_size, data);
+    if (write_uleb_obu_size(obu_header_size, obu_payload_size, data) !=
+        AOM_CODEC_OK) {
+      return AOM_CODEC_ERROR;
+    }
 #else
-  const size_t length_field_size = PRE_OBU_SIZE_BYTES;
-  mem_put_le32(data, obu_header_size + obu_payload_size);
+    const size_t length_field_size = PRE_OBU_SIZE_BYTES;
+    mem_put_le32(data, obu_header_size + obu_payload_size);
 #endif  // CONFIG_OBU_SIZING
 
-  fh_info.total_length = obu_header_size + obu_payload_size + length_field_size;
-  data += fh_info.total_length;
+    fh_info.total_length =
+        obu_header_size + obu_payload_size + length_field_size;
+    data += fh_info.total_length;
 
-  // Since length_field_size is determined adaptively after frame header
-  // encoding, saved_wb must be adjusted accordingly.
-  saved_wb.bit_buffer += length_field_size;
+    // Since length_field_size is determined adaptively after frame header
+    // encoding, saved_wb must be adjusted accordingly.
+    saved_wb.bit_buffer += length_field_size;
+  }
 
 #define EXT_TILE_DEBUG 0
 #if EXT_TILE_DEBUG
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 5d76ce5..a447ed5 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -113,6 +113,7 @@
 set(CONFIG_MAX_TILE 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_MONO_VIDEO 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_NO_FRAME_CONTEXT_SIGNALING 0 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_OBU_FRAME 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_OBU_NO_IVF 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_OBU_SIZE_AFTER_HEADER 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_OBU_SIZING 1 CACHE NUMBER "AV1 experiment flag.")