summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/common_video/h265/h265_sps_parser.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/common_video/h265/h265_sps_parser.cc')
-rw-r--r--third_party/libwebrtc/common_video/h265/h265_sps_parser.cc686
1 files changed, 686 insertions, 0 deletions
diff --git a/third_party/libwebrtc/common_video/h265/h265_sps_parser.cc b/third_party/libwebrtc/common_video/h265/h265_sps_parser.cc
new file mode 100644
index 0000000000..a2da4b9b7b
--- /dev/null
+++ b/third_party/libwebrtc/common_video/h265/h265_sps_parser.cc
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "common_video/h265/h265_sps_parser.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+#include "common_video/h265/h265_common.h"
+#include "rtc_base/bit_buffer.h"
+#include "rtc_base/logging.h"
+
+#define IN_RANGE_OR_RETURN_NULL(val, min, max) \
+ do { \
+ if (!reader.Ok() || (val) < (min) || (val) > (max)) { \
+ RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " #val \
+ " to be" \
+ << " in range [" << (min) << ":" << (max) << "]" \
+ << " found " << (val) << " instead"; \
+ return absl::nullopt; \
+ } \
+ } while (0)
+
+#define IN_RANGE_OR_RETURN_FALSE(val, min, max) \
+ do { \
+ if (!reader.Ok() || (val) < (min) || (val) > (max)) { \
+ RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " #val \
+ " to be" \
+ << " in range [" << (min) << ":" << (max) << "]" \
+ << " found " << (val) << " instead"; \
+ return false; \
+ } \
+ } while (0)
+
+#define TRUE_OR_RETURN(a) \
+ do { \
+ if (!reader.Ok() || !(a)) { \
+ RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " \
+ << #a; \
+ return absl::nullopt; \
+ } \
+ } while (0)
+
+namespace {
+using OptionalSps = absl::optional<webrtc::H265SpsParser::SpsState>;
+using OptionalShortTermRefPicSet =
+ absl::optional<webrtc::H265SpsParser::ShortTermRefPicSet>;
+using OptionalProfileTierLevel =
+ absl::optional<webrtc::H265SpsParser::ProfileTierLevel>;
+
+constexpr int kMaxNumSizeIds = 4;
+constexpr int kMaxNumMatrixIds = 6;
+constexpr int kMaxNumCoefs = 64;
+} // namespace
+
+namespace webrtc {
+
+H265SpsParser::ShortTermRefPicSet::ShortTermRefPicSet() = default;
+
+H265SpsParser::ProfileTierLevel::ProfileTierLevel() = default;
+
+int H265SpsParser::GetMaxLumaPs(int general_level_idc) {
+ // From Table A.8 - General tier and level limits.
+ // |general_level_idc| is 30x the actual level.
+ if (general_level_idc <= 30) // level 1
+ return 36864;
+ if (general_level_idc <= 60) // level 2
+ return 122880;
+ if (general_level_idc <= 63) // level 2.1
+ return 245760;
+ if (general_level_idc <= 90) // level 3
+ return 552960;
+ if (general_level_idc <= 93) // level 3.1
+ return 983040;
+ if (general_level_idc <= 123) // level 4, 4.1
+ return 2228224;
+ if (general_level_idc <= 156) // level 5, 5.1, 5.2
+ return 8912896;
+ // level 6, 6.1, 6.2 - beyond that there's no actual limit.
+ return 35651584;
+}
+
+size_t H265SpsParser::GetDpbMaxPicBuf(int general_profile_idc) {
+ // From A.4.2 - Profile-specific level limits for the video profiles.
+ // If sps_curr_pic_ref_enabled_flag is required to be zero, than this is 6
+ // otherwise it is 7.
+ return (general_profile_idc >= kProfileIdcMain &&
+ general_profile_idc <= kProfileIdcHighThroughput)
+ ? 6
+ : 7;
+}
+
+// General note: this is based off the 08/2021 version of the H.265 standard.
+// You can find it on this page:
+// http://www.itu.int/rec/T-REC-H.265
+
+// Unpack RBSP and parse SPS state from the supplied buffer.
+absl::optional<H265SpsParser::SpsState> H265SpsParser::ParseSps(
+ const uint8_t* data,
+ size_t length) {
+ RTC_DCHECK(data);
+ return ParseSpsInternal(H265::ParseRbsp(data, length));
+}
+
+bool H265SpsParser::ParseScalingListData(BitstreamReader& reader) {
+ int32_t scaling_list_dc_coef_minus8[kMaxNumSizeIds][kMaxNumMatrixIds] = {};
+ for (int size_id = 0; size_id < kMaxNumSizeIds; size_id++) {
+ for (int matrix_id = 0; matrix_id < kMaxNumMatrixIds;
+ matrix_id += (size_id == 3) ? 3 : 1) {
+ // scaling_list_pred_mode_flag: u(1)
+ bool scaling_list_pred_mode_flag = reader.Read<bool>();
+ if (!scaling_list_pred_mode_flag) {
+ // scaling_list_pred_matrix_id_delta: ue(v)
+ int scaling_list_pred_matrix_id_delta = reader.ReadExponentialGolomb();
+ if (size_id <= 2) {
+ IN_RANGE_OR_RETURN_FALSE(scaling_list_pred_matrix_id_delta, 0,
+ matrix_id);
+ } else { // size_id == 3
+ IN_RANGE_OR_RETURN_FALSE(scaling_list_pred_matrix_id_delta, 0,
+ matrix_id / 3);
+ }
+ } else {
+ uint32_t coef_num = std::min(kMaxNumCoefs, 1 << (4 + (size_id << 1)));
+ if (size_id > 1) {
+ // scaling_list_dc_coef_minus8: se(v)
+ scaling_list_dc_coef_minus8[size_id - 2][matrix_id] =
+ reader.ReadSignedExponentialGolomb();
+ IN_RANGE_OR_RETURN_FALSE(
+ scaling_list_dc_coef_minus8[size_id - 2][matrix_id], -7, 247);
+ }
+ for (uint32_t i = 0; i < coef_num; i++) {
+ // scaling_list_delta_coef: se(v)
+ int32_t scaling_list_delta_coef =
+ reader.ReadSignedExponentialGolomb();
+ IN_RANGE_OR_RETURN_FALSE(scaling_list_delta_coef, -128, 127);
+ }
+ }
+ }
+ }
+ return reader.Ok();
+}
+
+absl::optional<H265SpsParser::ShortTermRefPicSet>
+H265SpsParser::ParseShortTermRefPicSet(
+ uint32_t st_rps_idx,
+ uint32_t num_short_term_ref_pic_sets,
+ const std::vector<H265SpsParser::ShortTermRefPicSet>&
+ short_term_ref_pic_set,
+ uint32_t sps_max_dec_pic_buffering_minus1,
+ BitstreamReader& reader) {
+ H265SpsParser::ShortTermRefPicSet st_ref_pic_set;
+
+ bool inter_ref_pic_set_prediction_flag = false;
+ if (st_rps_idx != 0) {
+ // inter_ref_pic_set_prediction_flag: u(1)
+ inter_ref_pic_set_prediction_flag = reader.Read<bool>();
+ }
+
+ if (inter_ref_pic_set_prediction_flag) {
+ uint32_t delta_idx_minus1 = 0;
+ if (st_rps_idx == num_short_term_ref_pic_sets) {
+ // delta_idx_minus1: ue(v)
+ delta_idx_minus1 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(delta_idx_minus1, 0, st_rps_idx - 1);
+ }
+ // delta_rps_sign: u(1)
+ int delta_rps_sign = reader.ReadBits(1);
+ // abs_delta_rps_minus1: ue(v)
+ int abs_delta_rps_minus1 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(abs_delta_rps_minus1, 0, 0x7FFF);
+ int delta_rps = (1 - 2 * delta_rps_sign) * (abs_delta_rps_minus1 + 1);
+ uint32_t ref_rps_idx = st_rps_idx - (delta_idx_minus1 + 1);
+ uint32_t num_delta_pocs =
+ short_term_ref_pic_set[ref_rps_idx].num_delta_pocs;
+ IN_RANGE_OR_RETURN_NULL(num_delta_pocs, 0, kMaxShortTermRefPicSets);
+ const ShortTermRefPicSet& ref_set = short_term_ref_pic_set[ref_rps_idx];
+ bool used_by_curr_pic_flag[kMaxShortTermRefPicSets] = {};
+ bool use_delta_flag[kMaxShortTermRefPicSets] = {};
+ // 7.4.8 - use_delta_flag defaults to 1 if not present.
+ std::fill_n(use_delta_flag, kMaxShortTermRefPicSets, true);
+
+ for (uint32_t j = 0; j <= num_delta_pocs; j++) {
+ // used_by_curr_pic_flag: u(1)
+ used_by_curr_pic_flag[j] = reader.Read<bool>();
+ if (!used_by_curr_pic_flag[j]) {
+ // use_delta_flag: u(1)
+ use_delta_flag[j] = reader.Read<bool>();
+ }
+ }
+
+ // Calculate delta_poc_s{0,1}, used_by_curr_pic_s{0,1}, num_negative_pics
+ // and num_positive_pics.
+ // Equation 7-61
+ int i = 0;
+ IN_RANGE_OR_RETURN_NULL(
+ ref_set.num_negative_pics + ref_set.num_positive_pics, 0,
+ kMaxShortTermRefPicSets);
+ for (int j = ref_set.num_positive_pics - 1; j >= 0; --j) {
+ int d_poc = ref_set.delta_poc_s1[j] + delta_rps;
+ if (d_poc < 0 && use_delta_flag[ref_set.num_negative_pics + j]) {
+ st_ref_pic_set.delta_poc_s0[i] = d_poc;
+ st_ref_pic_set.used_by_curr_pic_s0[i++] =
+ used_by_curr_pic_flag[ref_set.num_negative_pics + j];
+ }
+ }
+ if (delta_rps < 0 && use_delta_flag[ref_set.num_delta_pocs]) {
+ st_ref_pic_set.delta_poc_s0[i] = delta_rps;
+ st_ref_pic_set.used_by_curr_pic_s0[i++] =
+ used_by_curr_pic_flag[ref_set.num_delta_pocs];
+ }
+ for (uint32_t j = 0; j < ref_set.num_negative_pics; ++j) {
+ int d_poc = ref_set.delta_poc_s0[j] + delta_rps;
+ if (d_poc < 0 && use_delta_flag[j]) {
+ st_ref_pic_set.delta_poc_s0[i] = d_poc;
+ st_ref_pic_set.used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[j];
+ }
+ }
+ st_ref_pic_set.num_negative_pics = i;
+ // Equation 7-62
+ i = 0;
+ for (int j = ref_set.num_negative_pics - 1; j >= 0; --j) {
+ int d_poc = ref_set.delta_poc_s0[j] + delta_rps;
+ if (d_poc > 0 && use_delta_flag[j]) {
+ st_ref_pic_set.delta_poc_s1[i] = d_poc;
+ st_ref_pic_set.used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[j];
+ }
+ }
+ if (delta_rps > 0 && use_delta_flag[ref_set.num_delta_pocs]) {
+ st_ref_pic_set.delta_poc_s1[i] = delta_rps;
+ st_ref_pic_set.used_by_curr_pic_s1[i++] =
+ used_by_curr_pic_flag[ref_set.num_delta_pocs];
+ }
+ for (uint32_t j = 0; j < ref_set.num_positive_pics; ++j) {
+ int d_poc = ref_set.delta_poc_s1[j] + delta_rps;
+ if (d_poc > 0 && use_delta_flag[ref_set.num_negative_pics + j]) {
+ st_ref_pic_set.delta_poc_s1[i] = d_poc;
+ st_ref_pic_set.used_by_curr_pic_s1[i++] =
+ used_by_curr_pic_flag[ref_set.num_negative_pics + j];
+ }
+ }
+ st_ref_pic_set.num_positive_pics = i;
+ IN_RANGE_OR_RETURN_NULL(st_ref_pic_set.num_negative_pics, 0,
+ sps_max_dec_pic_buffering_minus1);
+ IN_RANGE_OR_RETURN_NULL(
+ st_ref_pic_set.num_positive_pics, 0,
+ sps_max_dec_pic_buffering_minus1 - st_ref_pic_set.num_negative_pics);
+
+ } else {
+ // num_negative_pics: ue(v)
+ st_ref_pic_set.num_negative_pics = reader.ReadExponentialGolomb();
+ // num_positive_pics: ue(v)
+ st_ref_pic_set.num_positive_pics = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(st_ref_pic_set.num_negative_pics, 0,
+ sps_max_dec_pic_buffering_minus1);
+ IN_RANGE_OR_RETURN_NULL(
+ st_ref_pic_set.num_positive_pics, 0,
+ sps_max_dec_pic_buffering_minus1 - st_ref_pic_set.num_negative_pics);
+
+ for (uint32_t i = 0; i < st_ref_pic_set.num_negative_pics; i++) {
+ // delta_poc_s0_minus1: ue(v)
+ int delta_poc_s0_minus1 = 0;
+ delta_poc_s0_minus1 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(delta_poc_s0_minus1, 0, 0x7FFF);
+ if (i == 0) {
+ st_ref_pic_set.delta_poc_s0[i] = -(delta_poc_s0_minus1 + 1);
+ } else {
+ st_ref_pic_set.delta_poc_s0[i] =
+ st_ref_pic_set.delta_poc_s0[i - 1] - (delta_poc_s0_minus1 + 1);
+ }
+ // used_by_curr_pic_s0_flag: u(1)
+ st_ref_pic_set.used_by_curr_pic_s0[i] = reader.Read<bool>();
+ }
+
+ for (uint32_t i = 0; i < st_ref_pic_set.num_positive_pics; i++) {
+ // delta_poc_s1_minus1: ue(v)
+ int delta_poc_s1_minus1 = 0;
+ delta_poc_s1_minus1 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(delta_poc_s1_minus1, 0, 0x7FFF);
+ if (i == 0) {
+ st_ref_pic_set.delta_poc_s1[i] = delta_poc_s1_minus1 + 1;
+ } else {
+ st_ref_pic_set.delta_poc_s1[i] =
+ st_ref_pic_set.delta_poc_s1[i - 1] + delta_poc_s1_minus1 + 1;
+ }
+ // used_by_curr_pic_s1_flag: u(1)
+ st_ref_pic_set.used_by_curr_pic_s1[i] = reader.Read<bool>();
+ }
+ }
+
+ st_ref_pic_set.num_delta_pocs =
+ st_ref_pic_set.num_negative_pics + st_ref_pic_set.num_positive_pics;
+
+ if (!reader.Ok()) {
+ return absl::nullopt;
+ }
+
+ return OptionalShortTermRefPicSet(st_ref_pic_set);
+}
+
+absl::optional<H265SpsParser::ProfileTierLevel>
+H265SpsParser::ParseProfileTierLevel(bool profile_present,
+ int max_num_sub_layers_minus1,
+ BitstreamReader& reader) {
+ H265SpsParser::ProfileTierLevel pf_tier_level;
+ // 7.4.4
+ if (profile_present) {
+ int general_profile_space;
+ general_profile_space = reader.ReadBits(2);
+ TRUE_OR_RETURN(general_profile_space == 0);
+ // general_tier_flag or reserved 0: u(1)
+ reader.ConsumeBits(1);
+ pf_tier_level.general_profile_idc = reader.ReadBits(5);
+ IN_RANGE_OR_RETURN_NULL(pf_tier_level.general_profile_idc, 0, 11);
+ uint16_t general_profile_compatibility_flag_high16 = reader.ReadBits(16);
+ uint16_t general_profile_compatibility_flag_low16 = reader.ReadBits(16);
+ pf_tier_level.general_profile_compatibility_flags =
+ (general_profile_compatibility_flag_high16 << 16) +
+ general_profile_compatibility_flag_low16;
+ pf_tier_level.general_progressive_source_flag = reader.ReadBits(1);
+ pf_tier_level.general_interlaced_source_flag = reader.ReadBits(1);
+ if (!reader.Ok() || (!pf_tier_level.general_progressive_source_flag &&
+ pf_tier_level.general_interlaced_source_flag)) {
+ RTC_LOG(LS_WARNING) << "Interlaced streams not supported";
+ return absl::nullopt;
+ }
+ pf_tier_level.general_non_packed_constraint_flag = reader.ReadBits(1);
+ pf_tier_level.general_frame_only_constraint_flag = reader.ReadBits(1);
+ // general_reserved_zero_7bits
+ reader.ConsumeBits(7);
+ pf_tier_level.general_one_picture_only_constraint_flag = reader.ReadBits(1);
+ // general_reserved_zero_35bits
+ reader.ConsumeBits(35);
+ // general_inbld_flag
+ reader.ConsumeBits(1);
+ }
+ pf_tier_level.general_level_idc = reader.ReadBits(8);
+ bool sub_layer_profile_present_flag[8] = {};
+ bool sub_layer_level_present_flag[8] = {};
+ for (int i = 0; i < max_num_sub_layers_minus1; ++i) {
+ sub_layer_profile_present_flag[i] = reader.ReadBits(1);
+ sub_layer_level_present_flag[i] = reader.ReadBits(1);
+ }
+ if (max_num_sub_layers_minus1 > 0) {
+ for (int i = max_num_sub_layers_minus1; i < 8; i++) {
+ reader.ConsumeBits(2);
+ }
+ }
+ for (int i = 0; i < max_num_sub_layers_minus1; i++) {
+ if (sub_layer_profile_present_flag[i]) {
+ // sub_layer_profile_space
+ reader.ConsumeBits(2);
+ // sub_layer_tier_flag
+ reader.ConsumeBits(1);
+ // sub_layer_profile_idc
+ reader.ConsumeBits(5);
+ // sub_layer_profile_compatibility_flag
+ reader.ConsumeBits(32);
+ // sub_layer_{progressive,interlaced}_source_flag
+ reader.ConsumeBits(2);
+ // Ignore sub_layer_non_packed_constraint_flag and
+ // sub_layer_frame_only_constraint_flag.
+ reader.ConsumeBits(2);
+ // Skip the compatibility flags, they are always 43 bits.
+ reader.ConsumeBits(43);
+ // sub_layer_inbld_flag
+ reader.ConsumeBits(1);
+ }
+ if (sub_layer_level_present_flag[i]) {
+ // sub_layer_level_idc
+ reader.ConsumeBits(8);
+ }
+ }
+
+ if (!reader.Ok()) {
+ return absl::nullopt;
+ }
+
+ return OptionalProfileTierLevel(pf_tier_level);
+}
+
+absl::optional<H265SpsParser::SpsState> H265SpsParser::ParseSpsInternal(
+ rtc::ArrayView<const uint8_t> buffer) {
+ BitstreamReader reader(buffer);
+
+ // Now, we need to use a bit buffer to parse through the actual H265 SPS
+ // format. See Section 7.3.2.2.1 ("General sequence parameter set data
+ // syntax") of the H.265 standard for a complete description.
+ // Since we only care about resolution, we ignore the majority of fields, but
+ // we still have to actively parse through a lot of the data, since many of
+ // the fields have variable size.
+ // We're particularly interested in:
+ // chroma_format_idc -> affects crop units
+ // pic_{width,height}_* -> resolution of the frame in macroblocks (16x16).
+ // frame_crop_*_offset -> crop information
+ SpsState sps;
+
+ // sps_video_parameter_set_id: u(4)
+ uint32_t sps_video_parameter_set_id = 0;
+ sps_video_parameter_set_id = reader.ReadBits(4);
+ IN_RANGE_OR_RETURN_NULL(sps_video_parameter_set_id, 0, 15);
+
+ // sps_max_sub_layers_minus1: u(3)
+ uint32_t sps_max_sub_layers_minus1 = 0;
+ sps_max_sub_layers_minus1 = reader.ReadBits(3);
+ IN_RANGE_OR_RETURN_NULL(sps_max_sub_layers_minus1, 0, kMaxSubLayers - 1);
+ sps.sps_max_sub_layers_minus1 = sps_max_sub_layers_minus1;
+ // sps_temporal_id_nesting_flag: u(1)
+ reader.ConsumeBits(1);
+ // profile_tier_level(1, sps_max_sub_layers_minus1).
+ OptionalProfileTierLevel profile_tier_level =
+ ParseProfileTierLevel(true, sps.sps_max_sub_layers_minus1, reader);
+ if (!profile_tier_level) {
+ return absl::nullopt;
+ }
+ // sps_seq_parameter_set_id: ue(v)
+ sps.sps_id = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.sps_id, 0, 15);
+ // chrome_format_idc: ue(v)
+ sps.chroma_format_idc = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.chroma_format_idc, 0, 3);
+ if (sps.chroma_format_idc == 3) {
+ // seperate_colour_plane_flag: u(1)
+ sps.separate_colour_plane_flag = reader.Read<bool>();
+ }
+ uint32_t pic_width_in_luma_samples = 0;
+ uint32_t pic_height_in_luma_samples = 0;
+ // pic_width_in_luma_samples: ue(v)
+ pic_width_in_luma_samples = reader.ReadExponentialGolomb();
+ TRUE_OR_RETURN(pic_width_in_luma_samples != 0);
+ // pic_height_in_luma_samples: ue(v)
+ pic_height_in_luma_samples = reader.ReadExponentialGolomb();
+ TRUE_OR_RETURN(pic_height_in_luma_samples != 0);
+
+ // Equation A-2: Calculate max_dpb_size.
+ uint32_t max_luma_ps = GetMaxLumaPs(profile_tier_level->general_level_idc);
+ uint32_t max_dpb_size = 0;
+ uint32_t pic_size_in_samples_y = pic_height_in_luma_samples;
+ pic_size_in_samples_y *= pic_width_in_luma_samples;
+ size_t max_dpb_pic_buf =
+ GetDpbMaxPicBuf(profile_tier_level->general_profile_idc);
+ if (pic_size_in_samples_y <= (max_luma_ps >> 2))
+ max_dpb_size = std::min(4 * max_dpb_pic_buf, size_t{16});
+ else if (pic_size_in_samples_y <= (max_luma_ps >> 1))
+ max_dpb_size = std::min(2 * max_dpb_pic_buf, size_t{16});
+ else if (pic_size_in_samples_y <= ((3 * max_luma_ps) >> 2))
+ max_dpb_size = std::min((4 * max_dpb_pic_buf) / 3, size_t{16});
+ else
+ max_dpb_size = max_dpb_pic_buf;
+
+ // conformance_window_flag: u(1)
+ bool conformance_window_flag = reader.Read<bool>();
+
+ uint32_t conf_win_left_offset = 0;
+ uint32_t conf_win_right_offset = 0;
+ uint32_t conf_win_top_offset = 0;
+ uint32_t conf_win_bottom_offset = 0;
+ int sub_width_c =
+ ((1 == sps.chroma_format_idc) || (2 == sps.chroma_format_idc)) &&
+ (0 == sps.separate_colour_plane_flag)
+ ? 2
+ : 1;
+ int sub_height_c =
+ (1 == sps.chroma_format_idc) && (0 == sps.separate_colour_plane_flag) ? 2
+ : 1;
+ if (conformance_window_flag) {
+ // conf_win_left_offset: ue(v)
+ conf_win_left_offset = reader.ReadExponentialGolomb();
+ // conf_win_right_offset: ue(v)
+ conf_win_right_offset = reader.ReadExponentialGolomb();
+ // conf_win_top_offset: ue(v)
+ conf_win_top_offset = reader.ReadExponentialGolomb();
+ // conf_win_bottom_offset: ue(v)
+ conf_win_bottom_offset = reader.ReadExponentialGolomb();
+ uint32_t width_crop = conf_win_left_offset;
+ width_crop += conf_win_right_offset;
+ width_crop *= sub_width_c;
+ TRUE_OR_RETURN(width_crop < pic_width_in_luma_samples);
+ uint32_t height_crop = conf_win_top_offset;
+ height_crop += conf_win_bottom_offset;
+ height_crop *= sub_height_c;
+ TRUE_OR_RETURN(height_crop < pic_height_in_luma_samples);
+ }
+
+ // bit_depth_luma_minus8: ue(v)
+ sps.bit_depth_luma_minus8 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.bit_depth_luma_minus8, 0, 8);
+ // bit_depth_chroma_minus8: ue(v)
+ uint32_t bit_depth_chroma_minus8 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(bit_depth_chroma_minus8, 0, 8);
+ // log2_max_pic_order_cnt_lsb_minus4: ue(v)
+ sps.log2_max_pic_order_cnt_lsb_minus4 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.log2_max_pic_order_cnt_lsb_minus4, 0, 12);
+ uint32_t sps_sub_layer_ordering_info_present_flag = 0;
+ // sps_sub_layer_ordering_info_present_flag: u(1)
+ sps_sub_layer_ordering_info_present_flag = reader.Read<bool>();
+ uint32_t sps_max_num_reorder_pics[kMaxSubLayers] = {};
+ for (uint32_t i = (sps_sub_layer_ordering_info_present_flag != 0)
+ ? 0
+ : sps_max_sub_layers_minus1;
+ i <= sps_max_sub_layers_minus1; i++) {
+ // sps_max_dec_pic_buffering_minus1: ue(v)
+ sps.sps_max_dec_pic_buffering_minus1[i] = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.sps_max_dec_pic_buffering_minus1[i], 0,
+ max_dpb_size - 1);
+ // sps_max_num_reorder_pics: ue(v)
+ sps_max_num_reorder_pics[i] = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps_max_num_reorder_pics[i], 0,
+ sps.sps_max_dec_pic_buffering_minus1[i]);
+ if (i > 0) {
+ TRUE_OR_RETURN(sps.sps_max_dec_pic_buffering_minus1[i] >=
+ sps.sps_max_dec_pic_buffering_minus1[i - 1]);
+ TRUE_OR_RETURN(sps_max_num_reorder_pics[i] >=
+ sps_max_num_reorder_pics[i - 1]);
+ }
+ // sps_max_latency_increase_plus1: ue(v)
+ reader.ReadExponentialGolomb();
+ }
+ if (!sps_sub_layer_ordering_info_present_flag) {
+ // Fill in the default values for the other sublayers.
+ for (uint32_t i = 0; i < sps_max_sub_layers_minus1; ++i) {
+ sps.sps_max_dec_pic_buffering_minus1[i] =
+ sps.sps_max_dec_pic_buffering_minus1[sps_max_sub_layers_minus1];
+ }
+ }
+ // log2_min_luma_coding_block_size_minus3: ue(v)
+ sps.log2_min_luma_coding_block_size_minus3 = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.log2_min_luma_coding_block_size_minus3, 0, 27);
+ // log2_diff_max_min_luma_coding_block_size: ue(v)
+ sps.log2_diff_max_min_luma_coding_block_size = reader.ReadExponentialGolomb();
+ int min_cb_log2_size_y = sps.log2_min_luma_coding_block_size_minus3 + 3;
+ int ctb_log2_size_y = min_cb_log2_size_y;
+ ctb_log2_size_y += sps.log2_diff_max_min_luma_coding_block_size;
+ IN_RANGE_OR_RETURN_NULL(ctb_log2_size_y, 0, 30);
+ int min_cb_size_y = 1 << min_cb_log2_size_y;
+ int ctb_size_y = 1 << ctb_log2_size_y;
+ sps.pic_width_in_ctbs_y =
+ std::ceil(static_cast<float>(pic_width_in_luma_samples) / ctb_size_y);
+ sps.pic_height_in_ctbs_y =
+ std::ceil(static_cast<float>(pic_height_in_luma_samples) / ctb_size_y);
+ TRUE_OR_RETURN(pic_width_in_luma_samples % min_cb_size_y == 0);
+ TRUE_OR_RETURN(pic_height_in_luma_samples % min_cb_size_y == 0);
+ // log2_min_luma_transform_block_size_minus2: ue(v)
+ int log2_min_luma_transform_block_size_minus2 =
+ reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(log2_min_luma_transform_block_size_minus2, 0,
+ min_cb_log2_size_y - 3);
+ int min_tb_log2_size_y = log2_min_luma_transform_block_size_minus2 + 2;
+ // log2_diff_max_min_luma_transform_block_size: ue(v)
+ int log2_diff_max_min_luma_transform_block_size =
+ reader.ReadExponentialGolomb();
+ TRUE_OR_RETURN(log2_diff_max_min_luma_transform_block_size <=
+ std::min(ctb_log2_size_y, 5) - min_tb_log2_size_y);
+ // max_transform_hierarchy_depth_inter: ue(v)
+ int max_transform_hierarchy_depth_inter = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(max_transform_hierarchy_depth_inter, 0,
+ ctb_log2_size_y - min_tb_log2_size_y);
+ // max_transform_hierarchy_depth_intra: ue(v)
+ int max_transform_hierarchy_depth_intra = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(max_transform_hierarchy_depth_intra, 0,
+ ctb_log2_size_y - min_tb_log2_size_y);
+ // scaling_list_enabled_flag: u(1)
+ bool scaling_list_enabled_flag = reader.Read<bool>();
+ if (scaling_list_enabled_flag) {
+ // sps_scaling_list_data_present_flag: u(1)
+ bool sps_scaling_list_data_present_flag = reader.Read<bool>();
+ if (sps_scaling_list_data_present_flag) {
+ // scaling_list_data()
+ if (!ParseScalingListData(reader)) {
+ return absl::nullopt;
+ }
+ }
+ }
+
+ // amp_enabled_flag: u(1)
+ reader.ConsumeBits(1);
+ // sample_adaptive_offset_enabled_flag: u(1)
+ sps.sample_adaptive_offset_enabled_flag = reader.Read<bool>();
+ // pcm_enabled_flag: u(1)
+ bool pcm_enabled_flag = reader.Read<bool>();
+ if (pcm_enabled_flag) {
+ // pcm_sample_bit_depth_luma_minus1: u(4)
+ reader.ConsumeBits(4);
+ // pcm_sample_bit_depth_chroma_minus1: u(4)
+ reader.ConsumeBits(4);
+ // log2_min_pcm_luma_coding_block_size_minus3: ue(v)
+ int log2_min_pcm_luma_coding_block_size_minus3 =
+ reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(log2_min_pcm_luma_coding_block_size_minus3, 0, 2);
+ int log2_min_ipcm_cb_size_y =
+ log2_min_pcm_luma_coding_block_size_minus3 + 3;
+ IN_RANGE_OR_RETURN_NULL(log2_min_ipcm_cb_size_y,
+ std::min(min_cb_log2_size_y, 5),
+ std::min(ctb_log2_size_y, 5));
+ // log2_diff_max_min_pcm_luma_coding_block_size: ue(v)
+ int log2_diff_max_min_pcm_luma_coding_block_size =
+ reader.ReadExponentialGolomb();
+ TRUE_OR_RETURN(log2_diff_max_min_pcm_luma_coding_block_size <=
+ std::min(ctb_log2_size_y, 5) - log2_min_ipcm_cb_size_y);
+ // pcm_loop_filter_disabled_flag: u(1)
+ reader.ConsumeBits(1);
+ }
+
+ // num_short_term_ref_pic_sets: ue(v)
+ sps.num_short_term_ref_pic_sets = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.num_short_term_ref_pic_sets, 0,
+ kMaxShortTermRefPicSets);
+ sps.short_term_ref_pic_set.resize(sps.num_short_term_ref_pic_sets);
+ for (uint32_t st_rps_idx = 0; st_rps_idx < sps.num_short_term_ref_pic_sets;
+ st_rps_idx++) {
+ uint32_t sps_max_dec_pic_buffering_minus1 =
+ sps.sps_max_dec_pic_buffering_minus1[sps.sps_max_sub_layers_minus1];
+ // st_ref_pic_set()
+ OptionalShortTermRefPicSet ref_pic_set = ParseShortTermRefPicSet(
+ st_rps_idx, sps.num_short_term_ref_pic_sets, sps.short_term_ref_pic_set,
+ sps_max_dec_pic_buffering_minus1, reader);
+ if (ref_pic_set) {
+ sps.short_term_ref_pic_set[st_rps_idx] = *ref_pic_set;
+ } else {
+ return absl::nullopt;
+ }
+ }
+
+ // long_term_ref_pics_present_flag: u(1)
+ sps.long_term_ref_pics_present_flag = reader.Read<bool>();
+ if (sps.long_term_ref_pics_present_flag) {
+ // num_long_term_ref_pics_sps: ue(v)
+ sps.num_long_term_ref_pics_sps = reader.ReadExponentialGolomb();
+ IN_RANGE_OR_RETURN_NULL(sps.num_long_term_ref_pics_sps, 0,
+ kMaxLongTermRefPicSets);
+ sps.used_by_curr_pic_lt_sps_flag.resize(sps.num_long_term_ref_pics_sps, 0);
+ for (uint32_t i = 0; i < sps.num_long_term_ref_pics_sps; i++) {
+ // lt_ref_pic_poc_lsb_sps: u(v)
+ uint32_t lt_ref_pic_poc_lsb_sps_bits =
+ sps.log2_max_pic_order_cnt_lsb_minus4 + 4;
+ reader.ConsumeBits(lt_ref_pic_poc_lsb_sps_bits);
+ // used_by_curr_pic_lt_sps_flag: u(1)
+ sps.used_by_curr_pic_lt_sps_flag[i] = reader.Read<bool>();
+ }
+ }
+
+ // sps_temporal_mvp_enabled_flag: u(1)
+ sps.sps_temporal_mvp_enabled_flag = reader.Read<bool>();
+
+ // Far enough! We don't use the rest of the SPS.
+
+ sps.vps_id = sps_video_parameter_set_id;
+
+ sps.pic_width_in_luma_samples = pic_width_in_luma_samples;
+ sps.pic_height_in_luma_samples = pic_height_in_luma_samples;
+
+ // Start with the resolution determined by the pic_width/pic_height fields.
+ sps.width = pic_width_in_luma_samples;
+ sps.height = pic_height_in_luma_samples;
+
+ if (conformance_window_flag) {
+ int sub_width_c =
+ ((1 == sps.chroma_format_idc) || (2 == sps.chroma_format_idc)) &&
+ (0 == sps.separate_colour_plane_flag)
+ ? 2
+ : 1;
+ int sub_height_c =
+ (1 == sps.chroma_format_idc) && (0 == sps.separate_colour_plane_flag)
+ ? 2
+ : 1;
+ // the offset includes the pixel within conformance window. so don't need to
+ // +1 as per spec
+ sps.width -= sub_width_c * (conf_win_right_offset + conf_win_left_offset);
+ sps.height -= sub_height_c * (conf_win_top_offset + conf_win_bottom_offset);
+ }
+
+ if (!reader.Ok()) {
+ return absl::nullopt;
+ }
+
+ return OptionalSps(sps);
+}
+
+} // namespace webrtc