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;
}