Set obu_extension_flag correctly for frame OBUs

Add the has_nonzero_operating_point_idc boolean member to the
SequenceHeader struct. Pass the is_layer_specific_obu and
has_nonzero_operating_point_idc boolean parameters to
av1_write_obu_header().

This CL changes only the encoder side of libaom and is backward
compatible with current AV1 decoders.

Bug: aomedia:3076, aomedia:3582

Change-Id: I47bfe94e9d3f18005f4d574a7a4fe9eb963ae31f
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 6f9125e..6205257 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -9,6 +9,7 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 #include <limits.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -3352,9 +3353,11 @@
 
         const size_t move_offset = obu_header_size + length_field_size;
         memmove(ctx->cx_data + move_offset, ctx->cx_data, cpi_data.frame_size);
-        obu_header_size =
-            av1_write_obu_header(&ppi->level_params, &cpi->frame_header_count,
-                                 OBU_TEMPORAL_DELIMITER, 0, ctx->cx_data);
+        obu_header_size = av1_write_obu_header(
+            &ppi->level_params, &cpi->frame_header_count,
+            OBU_TEMPORAL_DELIMITER,
+            /*is_layer_specific_obu=*/false,
+            ppi->seq_params.has_nonzero_operating_point_idc, 0, ctx->cx_data);
 
         // OBUs are preceded/succeeded by an unsigned leb128 coded integer.
         if (av1_write_uleb_obu_size(obu_header_size, obu_payload_size,
diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h
index 857a2ea..17afbf4 100644
--- a/av1/common/av1_common_int.h
+++ b/av1/common/av1_common_int.h
@@ -12,6 +12,8 @@
 #ifndef AOM_AV1_COMMON_AV1_COMMON_INT_H_
 #define AOM_AV1_COMMON_AV1_COMMON_INT_H_
 
+#include <stdbool.h>
+
 #include "config/aom_config.h"
 #include "config/av1_rtcd.h"
 
@@ -317,6 +319,9 @@
   // Operating point info.
   int operating_points_cnt_minus_1;
   int operating_point_idc[MAX_NUM_OPERATING_POINTS];
+  // True if operating_point_idc[op] is not equal to 0 for any value of op from
+  // 0 to operating_points_cnt_minus_1.
+  bool has_nonzero_operating_point_idc;
   int timing_info_present;
   aom_timing_info_t timing_info;
   uint8_t decoder_model_info_present_flag;
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index fb1b0e8..6d6aa41 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -10,6 +10,7 @@
  */
 
 #include <assert.h>
+#include <stdbool.h>
 
 #include "config/aom_config.h"
 #include "config/aom_scale_rtcd.h"
@@ -134,6 +135,7 @@
     seq_params->display_model_info_present_flag = 0;
     seq_params->operating_points_cnt_minus_1 = 0;
     seq_params->operating_point_idc[0] = 0;
+    seq_params->has_nonzero_operating_point_idc = false;
     if (!read_bitstream_level(&seq_params->seq_level_idx[0], rb)) {
       pbi->error.error_code = AOM_CODEC_UNSUP_BITSTREAM;
       return 0;
@@ -155,9 +157,12 @@
     seq_params->display_model_info_present_flag = aom_rb_read_bit(rb);
     seq_params->operating_points_cnt_minus_1 =
         aom_rb_read_literal(rb, OP_POINTS_CNT_MINUS_1_BITS);
+    seq_params->has_nonzero_operating_point_idc = false;
     for (int i = 0; i < seq_params->operating_points_cnt_minus_1 + 1; i++) {
       seq_params->operating_point_idc[i] =
           aom_rb_read_literal(rb, OP_POINTS_IDC_BITS);
+      if (seq_params->operating_point_idc[i] != 0)
+        seq_params->has_nonzero_operating_point_idc = true;
       if (!read_bitstream_level(&seq_params->seq_level_idx[i], rb)) {
         pbi->error.error_code = AOM_CODEC_UNSUP_BITSTREAM;
         return 0;
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index e485faa..4f5ec12 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -11,6 +11,7 @@
 
 #include <assert.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdio.h>
 
 #include "aom/aom_encoder.h"
@@ -3355,21 +3356,28 @@
 
 uint32_t av1_write_obu_header(AV1LevelParams *const level_params,
                               int *frame_header_count, OBU_TYPE obu_type,
+                              bool is_layer_specific_obu,
+                              bool has_nonzero_operating_point_idc,
                               int obu_extension, uint8_t *const dst) {
+  assert(IMPLIES(!is_layer_specific_obu, obu_extension == 0));
+  assert(IMPLIES(!has_nonzero_operating_point_idc, obu_extension == 0));
+
   if (level_params->keep_level_stats &&
       (obu_type == OBU_FRAME || obu_type == OBU_FRAME_HEADER))
     ++(*frame_header_count);
 
   struct aom_write_bit_buffer wb = { dst, 0 };
   uint32_t size = 0;
+  int obu_extension_flag =
+      has_nonzero_operating_point_idc && is_layer_specific_obu;
 
   aom_wb_write_literal(&wb, 0, 1);  // forbidden bit.
   aom_wb_write_literal(&wb, (int)obu_type, 4);
-  aom_wb_write_literal(&wb, obu_extension ? 1 : 0, 1);
+  aom_wb_write_literal(&wb, obu_extension_flag, 1);
   aom_wb_write_literal(&wb, 1, 1);  // obu_has_size_field
   aom_wb_write_literal(&wb, 0, 1);  // reserved
 
-  if (obu_extension) {
+  if (obu_extension_flag) {
     aom_wb_write_literal(&wb, obu_extension & 0xFF, 8);
   }
 
@@ -3536,8 +3544,14 @@
   // 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;
+  // We pass obu_extension=0 to av1_write_obu_header(), so
+  // has_nonzero_operating_point_idc must be false.
+  assert(!cpi->common.seq_params->has_nonzero_operating_point_idc);
   lst_obu->tg_hdr_size = av1_write_obu_header(
-      level_params, &cpi->frame_header_count, obu_type, 0, *data);
+      level_params, &cpi->frame_header_count, obu_type,
+      /*is_layer_specific_obu=*/true,
+      cpi->common.seq_params->has_nonzero_operating_point_idc,
+      /*obu_extension=*/0, *data);
   *data += lst_obu->tg_hdr_size;
 
   const uint32_t frame_header_size =
@@ -3735,6 +3749,8 @@
   const OBU_TYPE obu_type = (cpi->num_tg == 1) ? OBU_FRAME : OBU_TILE_GROUP;
   *curr_tg_hdr_size = av1_write_obu_header(
       &cpi->ppi->level_params, &cpi->frame_header_count, obu_type,
+      /*is_layer_specific_obu=*/true,
+      cm->seq_params->has_nonzero_operating_point_idc,
       pack_bs_params->obu_extn_header, pack_bs_params->tile_data_curr);
   pack_bs_params->obu_header_size = *curr_tg_hdr_size;
 
@@ -3833,9 +3849,12 @@
 
     // Rewrite the OBU header to change the OBU type to Redundant Frame
     // Header.
-    av1_write_obu_header(&cpi->ppi->level_params, &cpi->frame_header_count,
-                         OBU_REDUNDANT_FRAME_HEADER, obu_extn_header,
-                         &curr_tg_start[fh_info->obu_header_byte_offset]);
+    av1_write_obu_header(
+        &cpi->ppi->level_params, &cpi->frame_header_count,
+        OBU_REDUNDANT_FRAME_HEADER,
+        /*is_layer_specific_obu=*/true,
+        cpi->common.seq_params->has_nonzero_operating_point_idc,
+        obu_extn_header, &curr_tg_start[fh_info->obu_header_byte_offset]);
 
     *curr_tg_data_size += (int)(fh_info->total_length);
     *total_size += (uint32_t)(fh_info->total_length);
@@ -4134,9 +4153,13 @@
           (cm->current_frame.frame_type != KEY_FRAME &&
            current_metadata->insert_flag == AOM_MIF_NON_KEY_FRAME) ||
           current_metadata->insert_flag == AOM_MIF_ANY_FRAME) {
-        obu_header_size = av1_write_obu_header(&cpi->ppi->level_params,
-                                               &cpi->frame_header_count,
-                                               OBU_METADATA, 0, dst);
+        // Whether METADATA_TYPE_ITUT_T35 is layer-specific or not is
+        // payload-specific. Other metadata types are not layer-specific.
+        const bool is_layer_specific_obu = false;
+        obu_header_size = av1_write_obu_header(
+            &cpi->ppi->level_params, &cpi->frame_header_count, OBU_METADATA,
+            is_layer_specific_obu,
+            cm->seq_params->has_nonzero_operating_point_idc, 0, dst);
         obu_payload_size =
             av1_write_metadata_obu(current_metadata, dst + obu_header_size);
         length_field_size = obu_memmove(obu_header_size, obu_payload_size, dst);
@@ -4185,7 +4208,9 @@
   if (cm->current_frame.frame_type == INTRA_ONLY_FRAME ||
       cm->current_frame.frame_type == KEY_FRAME) {
     obu_header_size = av1_write_obu_header(
-        level_params, &cpi->frame_header_count, OBU_SEQUENCE_HEADER, 0, data);
+        level_params, &cpi->frame_header_count, OBU_SEQUENCE_HEADER,
+        /*is_layer_specific_obu=*/false,
+        cm->seq_params->has_nonzero_operating_point_idc, 0, data);
     obu_payload_size =
         av1_write_sequence_header_obu(cm->seq_params, data + obu_header_size);
     const size_t length_field_size =
@@ -4208,9 +4233,11 @@
   if (write_frame_header) {
     // Write Frame Header OBU.
     fh_info.frame_header = data;
-    obu_header_size =
-        av1_write_obu_header(level_params, &cpi->frame_header_count,
-                             OBU_FRAME_HEADER, obu_extension_header, data);
+    obu_header_size = av1_write_obu_header(
+        level_params, &cpi->frame_header_count, OBU_FRAME_HEADER,
+        /*is_layer_specific_obu=*/true,
+        cm->seq_params->has_nonzero_operating_point_idc, obu_extension_header,
+        data);
     obu_payload_size = write_frame_header_obu(cpi, &cpi->td.mb.e_mbd, &saved_wb,
                                               data + obu_header_size, 1);
 
diff --git a/av1/encoder/bitstream.h b/av1/encoder/bitstream.h
index f0b0fd0..232c430 100644
--- a/av1/encoder/bitstream.h
+++ b/av1/encoder/bitstream.h
@@ -16,6 +16,8 @@
 extern "C" {
 #endif
 
+#include <stdbool.h>
+
 #include "av1/common/av1_common_int.h"
 #include "av1/common/blockd.h"
 #include "av1/common/enums.h"
@@ -89,9 +91,12 @@
                                        uint8_t *const dst);
 
 // Writes the OBU header byte, and the OBU header extension byte when
-// 'obu_extension' is non-zero. Returns number of bytes written to 'dst'.
+// has_nonzero_operating_point_idc is true and the OBU is layer-specific.
+// Returns number of bytes written to 'dst'.
 uint32_t av1_write_obu_header(AV1LevelParams *const level_params,
                               int *frame_header_count, OBU_TYPE obu_type,
+                              bool is_layer_specific_obu,
+                              bool has_nonzero_operating_point_idc,
                               int obu_extension, uint8_t *const dst);
 
 int av1_write_uleb_obu_size(size_t obu_header_size, size_t obu_payload_size,
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index c4c333f..1a132fe 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -9,12 +9,14 @@
  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
  */
 
-#include <limits.h>
+#include <assert.h>
 #include <float.h>
+#include <limits.h>
 #include <math.h>
+#include <stdbool.h>
 #include <stdio.h>
-#include <time.h>
 #include <stdlib.h>
+#include <time.h>
 
 #include "av1/common/scale.h"
 #include "config/aom_config.h"
@@ -547,6 +549,7 @@
 
   if (seq->operating_points_cnt_minus_1 == 0) {
     seq->operating_point_idc[0] = 0;
+    seq->has_nonzero_operating_point_idc = false;
   } else {
     // Set operating_point_idc[] such that the i=0 point corresponds to the
     // highest quality operating point (all layers), and subsequent
@@ -555,11 +558,13 @@
     int i = 0;
     assert(seq->operating_points_cnt_minus_1 ==
            (int)(ppi->number_spatial_layers * ppi->number_temporal_layers - 1));
+    seq->has_nonzero_operating_point_idc = true;
     for (unsigned int sl = 0; sl < ppi->number_spatial_layers; sl++) {
       for (unsigned int tl = 0; tl < ppi->number_temporal_layers; tl++) {
         seq->operating_point_idc[i] =
             (~(~0u << (ppi->number_spatial_layers - sl)) << 8) |
             ~(~0u << (ppi->number_temporal_layers - tl));
+        assert(seq->operating_point_idc[i] != 0);
         i++;
       }
     }
@@ -5387,7 +5392,9 @@
   memmove(&header_buf[payload_offset], &header_buf[0], sequence_header_size);
 
   if (av1_write_obu_header(&ppi->level_params, &ppi->cpi->frame_header_count,
-                           OBU_SEQUENCE_HEADER, 0,
+                           OBU_SEQUENCE_HEADER,
+                           /*is_layer_specific_obu=*/false,
+                           ppi->seq_params.has_nonzero_operating_point_idc, 0,
                            &header_buf[0]) != obu_header_size) {
     return NULL;
   }