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