| /* | 
 |  *  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 <cmath> | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <optional> | 
 | #include <vector> | 
 |  | 
 | #include "api/array_view.h" | 
 | #include "common_video/h265/h265_common.h" | 
 | #include "rtc_base/bitstream_reader.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 std::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 std::nullopt;                                               \ | 
 |     }                                                                    \ | 
 |   } while (0) | 
 |  | 
 | namespace { | 
 | using OptionalSps = std::optional<webrtc::H265SpsParser::SpsState>; | 
 | using OptionalShortTermRefPicSet = | 
 |     std::optional<webrtc::H265SpsParser::ShortTermRefPicSet>; | 
 | using OptionalProfileTierLevel = | 
 |     std::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. | 
 | std::optional<H265SpsParser::SpsState> H265SpsParser::ParseSps( | 
 |     ArrayView<const uint8_t> data) { | 
 |   return ParseSpsInternal(H265::ParseRbsp(data)); | 
 | } | 
 |  | 
 | 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(); | 
 | } | 
 |  | 
 | std::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 std::nullopt; | 
 |   } | 
 |  | 
 |   return OptionalShortTermRefPicSet(st_ref_pic_set); | 
 | } | 
 |  | 
 | std::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 std::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 std::nullopt; | 
 |   } | 
 |  | 
 |   return OptionalProfileTierLevel(pf_tier_level); | 
 | } | 
 |  | 
 | std::optional<H265SpsParser::SpsState> H265SpsParser::ParseSpsInternal( | 
 |     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 std::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; | 
 |   const int sub_width_c = | 
 |       ((1 == sps.chroma_format_idc) || (2 == sps.chroma_format_idc)) && | 
 |               (0 == sps.separate_colour_plane_flag) | 
 |           ? 2 | 
 |           : 1; | 
 |   const 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 std::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 std::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) { | 
 |     // 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 std::nullopt; | 
 |   } | 
 |  | 
 |   return OptionalSps(sps); | 
 | } | 
 |  | 
 | }  // namespace webrtc |