| /* | |
| * 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_bitstream_parser.h" | |
| #include <cmath> | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <limits> | |
| #include <optional> | |
| #include <vector> | |
| #include "api/array_view.h" | |
| #include "common_video/h265/h265_common.h" | |
| #include "common_video/h265/h265_pps_parser.h" | |
| #include "common_video/h265/h265_sps_parser.h" | |
| #include "common_video/h265/h265_vps_parser.h" | |
| #include "rtc_base/bitstream_reader.h" | |
| #include "rtc_base/logging.h" | |
| #define IN_RANGE_OR_RETURN(val, min, max) \ | |
| do { \ | |
| if (!slice_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 kInvalidStream; \ | |
| } \ | |
| } while (0) | |
| #define IN_RANGE_OR_RETURN_NULL(val, min, max) \ | |
| do { \ | |
| if (!slice_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_VOID(val, min, max) \ | |
| do { \ | |
| if (!slice_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; \ | |
| } \ | |
| } while (0) | |
| #define TRUE_OR_RETURN(a) \ | |
| do { \ | |
| if (!slice_reader.Ok() || !(a)) { \ | |
| RTC_LOG(LS_WARNING) << "Error in stream: invalid value, expected " \ | |
| << #a; \ | |
| return kInvalidStream; \ | |
| } \ | |
| } while (0) | |
| namespace webrtc { | |
| namespace { | |
| constexpr int kMaxAbsQpDeltaValue = 51; | |
| constexpr int kMinQpValue = 0; | |
| constexpr int kMaxQpValue = 51; | |
| constexpr int kMaxRefIdxActive = 15; | |
| } // namespace | |
| H265BitstreamParser::H265BitstreamParser() = default; | |
| H265BitstreamParser::~H265BitstreamParser() = default; | |
| // General note: this is based off the 08/2021 version of the H.265 standard, | |
| // section 7.3.6.1. You can find it on this page: | |
| // http://www.itu.int/rec/T-REC-H.265 | |
| H265BitstreamParser::Result H265BitstreamParser::ParseNonParameterSetNalu( | |
| ArrayView<const uint8_t> source, | |
| uint8_t nalu_type) { | |
| last_slice_qp_delta_ = std::nullopt; | |
| last_slice_pps_id_ = std::nullopt; | |
| const std::vector<uint8_t> slice_rbsp = H265::ParseRbsp(source); | |
| if (slice_rbsp.size() < H265::kNaluHeaderSize) | |
| return kInvalidStream; | |
| BitstreamReader slice_reader(slice_rbsp); | |
| slice_reader.ConsumeBits(H265::kNaluHeaderSize * 8); | |
| // first_slice_segment_in_pic_flag: u(1) | |
| bool first_slice_segment_in_pic_flag = slice_reader.Read<bool>(); | |
| bool irap_pic = (H265::NaluType::kBlaWLp <= nalu_type && | |
| nalu_type <= H265::NaluType::kRsvIrapVcl23); | |
| if (irap_pic) { | |
| // no_output_of_prior_pics_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| // slice_pic_parameter_set_id: ue(v) | |
| uint32_t pps_id = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(pps_id, 0, 63); | |
| const H265PpsParser::PpsState* pps = GetPPS(pps_id); | |
| TRUE_OR_RETURN(pps); | |
| const H265SpsParser::SpsState* sps = GetSPS(pps->sps_id); | |
| TRUE_OR_RETURN(sps); | |
| bool dependent_slice_segment_flag = 0; | |
| if (!first_slice_segment_in_pic_flag) { | |
| if (pps->dependent_slice_segments_enabled_flag) { | |
| // dependent_slice_segment_flag: u(1) | |
| dependent_slice_segment_flag = slice_reader.Read<bool>(); | |
| } | |
| // slice_segment_address: u(v) | |
| int32_t log2_ctb_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3 + | |
| sps->log2_diff_max_min_luma_coding_block_size; | |
| uint32_t ctb_size_y = 1 << log2_ctb_size_y; | |
| uint32_t pic_width_in_ctbs_y = sps->pic_width_in_luma_samples / ctb_size_y; | |
| if (sps->pic_width_in_luma_samples % ctb_size_y) | |
| pic_width_in_ctbs_y++; | |
| uint32_t pic_height_in_ctbs_y = | |
| sps->pic_height_in_luma_samples / ctb_size_y; | |
| if (sps->pic_height_in_luma_samples % ctb_size_y) | |
| pic_height_in_ctbs_y++; | |
| uint32_t slice_segment_address_bits = | |
| H265::Log2Ceiling(pic_height_in_ctbs_y * pic_width_in_ctbs_y); | |
| TRUE_OR_RETURN(slice_segment_address_bits != | |
| std::numeric_limits<uint32_t>::max()); | |
| slice_reader.ConsumeBits(slice_segment_address_bits); | |
| } | |
| if (dependent_slice_segment_flag == 0) { | |
| for (uint32_t i = 0; i < pps->num_extra_slice_header_bits; i++) { | |
| // slice_reserved_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| // slice_type: ue(v) | |
| uint32_t slice_type = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(slice_type, 0, 2); | |
| if (pps->output_flag_present_flag) { | |
| // pic_output_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| if (sps->separate_colour_plane_flag) { | |
| // colour_plane_id: u(2) | |
| slice_reader.ConsumeBits(2); | |
| } | |
| uint32_t num_long_term_sps = 0; | |
| uint32_t num_long_term_pics = 0; | |
| std::vector<bool> used_by_curr_pic_lt_flag; | |
| bool short_term_ref_pic_set_sps_flag = false; | |
| uint32_t short_term_ref_pic_set_idx = 0; | |
| H265SpsParser::ShortTermRefPicSet short_term_ref_pic_set; | |
| bool slice_temporal_mvp_enabled_flag = 0; | |
| if (nalu_type != H265::NaluType::kIdrWRadl && | |
| nalu_type != H265::NaluType::kIdrNLp) { | |
| // slice_pic_order_cnt_lsb: u(v) | |
| uint32_t slice_pic_order_cnt_lsb_bits = | |
| sps->log2_max_pic_order_cnt_lsb_minus4 + 4; | |
| slice_reader.ConsumeBits(slice_pic_order_cnt_lsb_bits); | |
| // short_term_ref_pic_set_sps_flag: u(1) | |
| short_term_ref_pic_set_sps_flag = slice_reader.Read<bool>(); | |
| if (!short_term_ref_pic_set_sps_flag) { | |
| std::optional<H265SpsParser::ShortTermRefPicSet> ref_pic_set = | |
| H265SpsParser::ParseShortTermRefPicSet( | |
| sps->num_short_term_ref_pic_sets, | |
| sps->num_short_term_ref_pic_sets, sps->short_term_ref_pic_set, | |
| sps->sps_max_dec_pic_buffering_minus1 | |
| [sps->sps_max_sub_layers_minus1], | |
| slice_reader); | |
| TRUE_OR_RETURN(ref_pic_set); | |
| short_term_ref_pic_set = *ref_pic_set; | |
| } else if (sps->num_short_term_ref_pic_sets > 1) { | |
| // short_term_ref_pic_set_idx: u(v) | |
| uint32_t short_term_ref_pic_set_idx_bits = | |
| H265::Log2Ceiling(sps->num_short_term_ref_pic_sets); | |
| if ((1 << short_term_ref_pic_set_idx_bits) < | |
| sps->num_short_term_ref_pic_sets) { | |
| short_term_ref_pic_set_idx_bits++; | |
| } | |
| if (short_term_ref_pic_set_idx_bits > 0) { | |
| short_term_ref_pic_set_idx = | |
| slice_reader.ReadBits(short_term_ref_pic_set_idx_bits); | |
| IN_RANGE_OR_RETURN(short_term_ref_pic_set_idx, 0, | |
| sps->num_short_term_ref_pic_sets - 1); | |
| } | |
| } | |
| if (sps->long_term_ref_pics_present_flag) { | |
| if (sps->num_long_term_ref_pics_sps > 0) { | |
| // num_long_term_sps: ue(v) | |
| num_long_term_sps = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(num_long_term_sps, 0, | |
| sps->num_long_term_ref_pics_sps); | |
| } | |
| // num_long_term_pics: ue(v) | |
| num_long_term_pics = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(num_long_term_pics, 0, | |
| kMaxLongTermRefPicSets - num_long_term_sps); | |
| used_by_curr_pic_lt_flag.resize(num_long_term_sps + num_long_term_pics, | |
| 0); | |
| for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) { | |
| if (i < num_long_term_sps) { | |
| uint32_t lt_idx_sps = 0; | |
| if (sps->num_long_term_ref_pics_sps > 1) { | |
| // lt_idx_sps: u(v) | |
| uint32_t lt_idx_sps_bits = | |
| H265::Log2Ceiling(sps->num_long_term_ref_pics_sps); | |
| lt_idx_sps = slice_reader.ReadBits(lt_idx_sps_bits); | |
| IN_RANGE_OR_RETURN(lt_idx_sps, 0, | |
| sps->num_long_term_ref_pics_sps - 1); | |
| } | |
| used_by_curr_pic_lt_flag[i] = | |
| sps->used_by_curr_pic_lt_sps_flag[lt_idx_sps]; | |
| } else { | |
| // poc_lsb_lt: u(v) | |
| uint32_t poc_lsb_lt_bits = | |
| sps->log2_max_pic_order_cnt_lsb_minus4 + 4; | |
| slice_reader.ConsumeBits(poc_lsb_lt_bits); | |
| // used_by_curr_pic_lt_flag: u(1) | |
| used_by_curr_pic_lt_flag[i] = slice_reader.Read<bool>(); | |
| } | |
| // delta_poc_msb_present_flag: u(1) | |
| bool delta_poc_msb_present_flag = slice_reader.Read<bool>(); | |
| if (delta_poc_msb_present_flag) { | |
| // delta_poc_msb_cycle_lt: ue(v) | |
| int delta_poc_msb_cycle_lt = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN( | |
| delta_poc_msb_cycle_lt, 0, | |
| std::pow(2, 32 - sps->log2_max_pic_order_cnt_lsb_minus4 - 4)); | |
| } | |
| } | |
| } | |
| if (sps->sps_temporal_mvp_enabled_flag) { | |
| // slice_temporal_mvp_enabled_flag: u(1) | |
| slice_temporal_mvp_enabled_flag = slice_reader.Read<bool>(); | |
| } | |
| } | |
| if (sps->sample_adaptive_offset_enabled_flag) { | |
| // slice_sao_luma_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| uint32_t chroma_array_type = | |
| sps->separate_colour_plane_flag == 0 ? sps->chroma_format_idc : 0; | |
| if (chroma_array_type != 0) { | |
| // slice_sao_chroma_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| } | |
| if (slice_type == H265::SliceType::kP || | |
| slice_type == H265::SliceType::kB) { | |
| // num_ref_idx_active_override_flag: u(1) | |
| bool num_ref_idx_active_override_flag = slice_reader.Read<bool>(); | |
| uint32_t num_ref_idx_l0_active_minus1 = | |
| pps->num_ref_idx_l0_default_active_minus1; | |
| uint32_t num_ref_idx_l1_active_minus1 = | |
| pps->num_ref_idx_l1_default_active_minus1; | |
| if (num_ref_idx_active_override_flag) { | |
| // num_ref_idx_l0_active_minus1: ue(v) | |
| num_ref_idx_l0_active_minus1 = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(num_ref_idx_l0_active_minus1, 0, | |
| kMaxRefIdxActive - 1); | |
| if (slice_type == H265::SliceType::kB) { | |
| // num_ref_idx_l1_active_minus1: ue(v) | |
| num_ref_idx_l1_active_minus1 = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(num_ref_idx_l1_active_minus1, 0, | |
| kMaxRefIdxActive - 1); | |
| } | |
| } | |
| uint32_t num_pic_total_curr = 0; | |
| uint32_t curr_rps_idx = 0; | |
| if (short_term_ref_pic_set_sps_flag) { | |
| curr_rps_idx = short_term_ref_pic_set_idx; | |
| } else { | |
| curr_rps_idx = sps->num_short_term_ref_pic_sets; | |
| } | |
| const H265SpsParser::ShortTermRefPicSet* ref_pic_set; | |
| if (curr_rps_idx < sps->short_term_ref_pic_set.size()) { | |
| ref_pic_set = &(sps->short_term_ref_pic_set[curr_rps_idx]); | |
| } else { | |
| ref_pic_set = &short_term_ref_pic_set; | |
| } | |
| // Equation 7-57 | |
| IN_RANGE_OR_RETURN(ref_pic_set->num_negative_pics, 0, | |
| kMaxShortTermRefPicSets); | |
| IN_RANGE_OR_RETURN(ref_pic_set->num_positive_pics, 0, | |
| kMaxShortTermRefPicSets); | |
| for (uint32_t i = 0; i < ref_pic_set->num_negative_pics; i++) { | |
| if (ref_pic_set->used_by_curr_pic_s0[i]) { | |
| num_pic_total_curr++; | |
| } | |
| } | |
| for (uint32_t i = 0; i < ref_pic_set->num_positive_pics; i++) { | |
| if (ref_pic_set->used_by_curr_pic_s1[i]) { | |
| num_pic_total_curr++; | |
| } | |
| } | |
| for (uint32_t i = 0; i < num_long_term_sps + num_long_term_pics; i++) { | |
| if (used_by_curr_pic_lt_flag[i]) { | |
| num_pic_total_curr++; | |
| } | |
| } | |
| if (pps->lists_modification_present_flag && num_pic_total_curr > 1) { | |
| // ref_pic_lists_modification() | |
| uint32_t list_entry_bits = H265::Log2Ceiling(num_pic_total_curr); | |
| if ((1 << list_entry_bits) < num_pic_total_curr) { | |
| list_entry_bits++; | |
| } | |
| // ref_pic_list_modification_flag_l0: u(1) | |
| bool ref_pic_list_modification_flag_l0 = slice_reader.Read<bool>(); | |
| if (ref_pic_list_modification_flag_l0) { | |
| for (uint32_t i = 0; i < num_ref_idx_l0_active_minus1; i++) { | |
| // list_entry_l0: u(v) | |
| slice_reader.ConsumeBits(list_entry_bits); | |
| } | |
| } | |
| if (slice_type == H265::SliceType::kB) { | |
| // ref_pic_list_modification_flag_l1: u(1) | |
| bool ref_pic_list_modification_flag_l1 = slice_reader.Read<bool>(); | |
| if (ref_pic_list_modification_flag_l1) { | |
| for (uint32_t i = 0; i < num_ref_idx_l1_active_minus1; i++) { | |
| // list_entry_l1: u(v) | |
| slice_reader.ConsumeBits(list_entry_bits); | |
| } | |
| } | |
| } | |
| } | |
| if (slice_type == H265::SliceType::kB) { | |
| // mvd_l1_zero_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| if (pps->cabac_init_present_flag) { | |
| // cabac_init_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| if (slice_temporal_mvp_enabled_flag) { | |
| bool collocated_from_l0_flag = false; | |
| if (slice_type == H265::SliceType::kB) { | |
| // collocated_from_l0_flag: u(1) | |
| collocated_from_l0_flag = slice_reader.Read<bool>(); | |
| } | |
| if ((collocated_from_l0_flag && num_ref_idx_l0_active_minus1 > 0) || | |
| (!collocated_from_l0_flag && num_ref_idx_l1_active_minus1 > 0)) { | |
| // collocated_ref_idx: ue(v) | |
| uint32_t collocated_ref_idx = slice_reader.ReadExponentialGolomb(); | |
| if ((slice_type == H265::SliceType::kP || | |
| slice_type == H265::SliceType::kB) && | |
| collocated_from_l0_flag) { | |
| IN_RANGE_OR_RETURN(collocated_ref_idx, 0, | |
| num_ref_idx_l0_active_minus1); | |
| } | |
| if (slice_type == H265::SliceType::kB && !collocated_from_l0_flag) { | |
| IN_RANGE_OR_RETURN(collocated_ref_idx, 0, | |
| num_ref_idx_l1_active_minus1); | |
| } | |
| } | |
| } | |
| // pred_weight_table() | |
| if ((pps->weighted_pred_flag && slice_type == H265::SliceType::kP) || | |
| (pps->weighted_bipred_flag && slice_type == H265::SliceType::kB)) { | |
| uint32_t luma_log2_weight_denom = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(luma_log2_weight_denom, 0, 7); | |
| uint32_t chroma_array_type = | |
| sps->separate_colour_plane_flag == 0 ? sps->chroma_format_idc : 0; | |
| int32_t chroma_log2_weight_denom = luma_log2_weight_denom; | |
| // wp_offset_half_range_c and wp_offset_half_range_y depends on | |
| // sps.high_precision_offsets_enable_flag. Since range extension is not | |
| // supported, so for now below two are fixed to 128 instead of 1 << | |
| // (sps.bit_depth_luma|chroma_minus8 + 7). | |
| int32_t wp_offset_half_range_c = (1 << 7); | |
| int32_t wp_offset_half_range_y = (1 << 7); | |
| if (chroma_array_type != 0) { | |
| // delta_chroma_log2_weight_denom: se(v) | |
| int32_t delta_chroma_log2_weight_denom = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_chroma_log2_weight_denom, -7, 7); | |
| chroma_log2_weight_denom += delta_chroma_log2_weight_denom; | |
| } | |
| IN_RANGE_OR_RETURN(chroma_log2_weight_denom, 0, 7); | |
| bool luma_weight_flag_l0[kMaxRefIdxActive] = {}; | |
| bool chroma_weight_flag_l0[kMaxRefIdxActive] = {}; | |
| int32_t delta_chroma_weight_l0[kMaxRefIdxActive][2] = {}; | |
| int32_t luma_offset_l0[kMaxRefIdxActive] = {}; | |
| int32_t delta_chroma_offset_l0[kMaxRefIdxActive][2] = {}; | |
| for (uint32_t i = 0; i <= num_ref_idx_l0_active_minus1; i++) { | |
| // luma_weight_l0_flag: u(1). By syntax this should conditionally | |
| // check if the POC or layer ID of the reference picture is different, | |
| // but we don't support encoding referencing different layers in the | |
| // same AU. Skip the check for now. | |
| luma_weight_flag_l0[i] = slice_reader.Read<bool>(); | |
| } | |
| if (chroma_array_type != 0) { | |
| for (uint32_t i = 0; i <= num_ref_idx_l0_active_minus1; i++) { | |
| // chroma_weight_l0_flag: u(1) | |
| chroma_weight_flag_l0[i] = slice_reader.Read<bool>(); | |
| } | |
| } | |
| for (uint32_t i = 0; i <= num_ref_idx_l0_active_minus1; i++) { | |
| if (luma_weight_flag_l0[i]) { | |
| int32_t delta_luma_weight_l0[kMaxRefIdxActive] = {}; | |
| delta_luma_weight_l0[i] = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_luma_weight_l0[i], -128, 127); | |
| luma_offset_l0[i] = slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(luma_offset_l0[i], -wp_offset_half_range_y, | |
| wp_offset_half_range_y - 1); | |
| } | |
| if (chroma_weight_flag_l0[i]) { | |
| for (uint32_t j = 0; j < 2; j++) { | |
| delta_chroma_weight_l0[i][j] = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_chroma_weight_l0[i][j], -128, 127); | |
| delta_chroma_offset_l0[i][j] = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_chroma_offset_l0[i][j], | |
| -4 * wp_offset_half_range_c, | |
| 4 * wp_offset_half_range_c - 1); | |
| } | |
| } | |
| } | |
| if (slice_type == H265::SliceType::kB) { | |
| bool luma_weight_flag_l1[kMaxRefIdxActive] = {}; | |
| bool chroma_weight_flag_l1[kMaxRefIdxActive] = {}; | |
| int32_t delta_chroma_weight_l1[kMaxRefIdxActive][2] = {}; | |
| int32_t luma_offset_l1[kMaxRefIdxActive] = {}; | |
| int32_t delta_chroma_offset_l1[kMaxRefIdxActive][2] = {}; | |
| for (uint32_t i = 0; i < num_ref_idx_l1_active_minus1; i++) { | |
| luma_weight_flag_l1[i] = slice_reader.Read<bool>(); | |
| } | |
| if (chroma_array_type != 0) { | |
| for (uint32_t i = 0; i <= num_ref_idx_l1_active_minus1; i++) { | |
| chroma_weight_flag_l1[i] = slice_reader.Read<bool>(); | |
| } | |
| } | |
| for (uint32_t i = 0; i <= num_ref_idx_l1_active_minus1; i++) { | |
| if (luma_weight_flag_l1[i]) { | |
| int32_t delta_luma_weight_l1[kMaxRefIdxActive] = {}; | |
| delta_luma_weight_l1[i] = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_luma_weight_l1[i], -128, 127); | |
| luma_offset_l1[i] = slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(luma_offset_l1[i], -wp_offset_half_range_y, | |
| wp_offset_half_range_y - 1); | |
| } | |
| if (chroma_weight_flag_l1[i]) { | |
| for (uint32_t j = 0; j < 2; j++) { | |
| delta_chroma_weight_l1[i][j] = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_chroma_weight_l1[i][j], -128, 127); | |
| delta_chroma_offset_l1[i][j] = | |
| slice_reader.ReadSignedExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(delta_chroma_offset_l1[i][j], | |
| -4 * wp_offset_half_range_c, | |
| 4 * wp_offset_half_range_c - 1); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // five_minus_max_num_merge_cand: ue(v) | |
| uint32_t five_minus_max_num_merge_cand = | |
| slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN(5 - five_minus_max_num_merge_cand, 1, 5); | |
| } | |
| } | |
| // slice_qp_delta: se(v) | |
| int32_t last_slice_qp_delta = slice_reader.ReadSignedExponentialGolomb(); | |
| if (!slice_reader.Ok() || (abs(last_slice_qp_delta) > kMaxAbsQpDeltaValue)) { | |
| // Something has gone wrong, and the parsed value is invalid. | |
| RTC_LOG(LS_ERROR) << "Parsed QP value out of range."; | |
| return kInvalidStream; | |
| } | |
| // 7-54 in H265 spec. | |
| IN_RANGE_OR_RETURN(26 + pps->init_qp_minus26 + last_slice_qp_delta, | |
| -pps->qp_bd_offset_y, 51); | |
| last_slice_qp_delta_ = last_slice_qp_delta; | |
| last_slice_pps_id_ = pps_id; | |
| if (!slice_reader.Ok()) { | |
| return kInvalidStream; | |
| } | |
| return kOk; | |
| } | |
| const H265PpsParser::PpsState* H265BitstreamParser::GetPPS(uint32_t id) const { | |
| auto it = pps_.find(id); | |
| if (it == pps_.end()) { | |
| RTC_LOG(LS_WARNING) << "Requested a nonexistent PPS id " << id; | |
| return nullptr; | |
| } | |
| return &it->second; | |
| } | |
| const H265SpsParser::SpsState* H265BitstreamParser::GetSPS(uint32_t id) const { | |
| auto it = sps_.find(id); | |
| if (it == sps_.end()) { | |
| RTC_LOG(LS_WARNING) << "Requested a nonexistent SPS id " << id; | |
| return nullptr; | |
| } | |
| return &it->second; | |
| } | |
| void H265BitstreamParser::ParseSlice(ArrayView<const uint8_t> slice) { | |
| if (slice.empty()) { | |
| RTC_LOG(LS_WARNING) << "Empty slice in H265 bitstream."; | |
| return; | |
| } | |
| H265::NaluType nalu_type = H265::ParseNaluType(slice[0]); | |
| switch (nalu_type) { | |
| case H265::NaluType::kVps: { | |
| std::optional<H265VpsParser::VpsState> vps_state; | |
| if (slice.size() >= H265::kNaluHeaderSize) { | |
| vps_state = | |
| H265VpsParser::ParseVps(slice.subspan(H265::kNaluHeaderSize)); | |
| } | |
| if (!vps_state) { | |
| RTC_LOG(LS_WARNING) << "Unable to parse VPS from H265 bitstream."; | |
| } else { | |
| vps_[vps_state->id] = *vps_state; | |
| } | |
| break; | |
| } | |
| case H265::NaluType::kSps: { | |
| std::optional<H265SpsParser::SpsState> sps_state; | |
| if (slice.size() >= H265::kNaluHeaderSize) { | |
| sps_state = | |
| H265SpsParser::ParseSps(slice.subspan(H265::kNaluHeaderSize)); | |
| } | |
| if (!sps_state) { | |
| RTC_LOG(LS_WARNING) << "Unable to parse SPS from H265 bitstream."; | |
| } else { | |
| sps_[sps_state->sps_id] = *sps_state; | |
| } | |
| break; | |
| } | |
| case H265::NaluType::kPps: { | |
| std::optional<H265PpsParser::PpsState> pps_state; | |
| if (slice.size() >= H265::kNaluHeaderSize) { | |
| std::vector<uint8_t> unpacked_buffer = | |
| H265::ParseRbsp(slice.subspan(H265::kNaluHeaderSize)); | |
| BitstreamReader slice_reader(unpacked_buffer); | |
| // pic_parameter_set_id: ue(v) | |
| uint32_t pps_id = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN_VOID(pps_id, 0, 63); | |
| // seq_parameter_set_id: ue(v) | |
| uint32_t sps_id = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN_VOID(sps_id, 0, 15); | |
| const H265SpsParser::SpsState* sps = GetSPS(sps_id); | |
| pps_state = | |
| H265PpsParser::ParsePps(slice.subspan(H265::kNaluHeaderSize), sps); | |
| } | |
| if (!pps_state) { | |
| RTC_LOG(LS_WARNING) << "Unable to parse PPS from H265 bitstream."; | |
| } else { | |
| pps_[pps_state->pps_id] = *pps_state; | |
| } | |
| break; | |
| } | |
| case H265::NaluType::kAud: | |
| case H265::NaluType::kPrefixSei: | |
| case H265::NaluType::kSuffixSei: | |
| case H265::NaluType::kAp: | |
| case H265::NaluType::kFu: | |
| break; | |
| default: | |
| Result res = ParseNonParameterSetNalu(slice, nalu_type); | |
| if (res != kOk) { | |
| RTC_LOG(LS_INFO) << "Failed to parse bitstream. Error: " << res; | |
| } | |
| break; | |
| } | |
| } | |
| std::optional<uint32_t> | |
| H265BitstreamParser::ParsePpsIdFromSliceSegmentLayerRbsp( | |
| ArrayView<const uint8_t> data, | |
| uint8_t nalu_type) { | |
| std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data); | |
| BitstreamReader slice_reader(unpacked_buffer); | |
| // first_slice_segment_in_pic_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| if (!slice_reader.Ok()) { | |
| return std::nullopt; | |
| } | |
| if (nalu_type >= H265::NaluType::kBlaWLp && | |
| nalu_type <= H265::NaluType::kRsvIrapVcl23) { | |
| // no_output_of_prior_pics_flag: u(1) | |
| slice_reader.ConsumeBits(1); | |
| } | |
| // slice_pic_parameter_set_id: ue(v) | |
| uint32_t slice_pic_parameter_set_id = slice_reader.ReadExponentialGolomb(); | |
| IN_RANGE_OR_RETURN_NULL(slice_pic_parameter_set_id, 0, 63); | |
| if (!slice_reader.Ok()) { | |
| return std::nullopt; | |
| } | |
| return slice_pic_parameter_set_id; | |
| } | |
| std::optional<bool> H265BitstreamParser::IsFirstSliceSegmentInPic( | |
| ArrayView<const uint8_t> data) { | |
| std::vector<uint8_t> unpacked_buffer = H265::ParseRbsp(data); | |
| BitstreamReader slice_reader(unpacked_buffer); | |
| // first_slice_segment_in_pic_flag: u(1) | |
| bool first_slice_segment_in_pic_flag = slice_reader.Read<bool>(); | |
| if (!slice_reader.Ok()) { | |
| return std::nullopt; | |
| } | |
| return first_slice_segment_in_pic_flag; | |
| } | |
| void H265BitstreamParser::ParseBitstream(ArrayView<const uint8_t> bitstream) { | |
| std::vector<H265::NaluIndex> nalu_indices = H265::FindNaluIndices(bitstream); | |
| for (const H265::NaluIndex& index : nalu_indices) | |
| ParseSlice( | |
| bitstream.subspan(index.payload_start_offset, index.payload_size)); | |
| } | |
| std::optional<int> H265BitstreamParser::GetLastSliceQp() const { | |
| if (!last_slice_qp_delta_ || !last_slice_pps_id_) { | |
| return std::nullopt; | |
| } | |
| const H265PpsParser::PpsState* pps = GetPPS(last_slice_pps_id_.value()); | |
| if (!pps) | |
| return std::nullopt; | |
| const int parsed_qp = 26 + pps->init_qp_minus26 + *last_slice_qp_delta_; | |
| if (parsed_qp < kMinQpValue || parsed_qp > kMaxQpValue) { | |
| RTC_LOG(LS_ERROR) << "Parsed invalid QP from bitstream."; | |
| return std::nullopt; | |
| } | |
| return parsed_qp; | |
| } | |
| std::optional<uint32_t> H265BitstreamParser::GetLastSlicePpsId() const { | |
| if (!last_slice_pps_id_) { | |
| RTC_LOG(LS_ERROR) << "Failed to parse PPS id from bitstream."; | |
| return std::nullopt; | |
| } | |
| return last_slice_pps_id_; | |
| } | |
| } // namespace webrtc |