// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "media/video/h264_parser.h" #include #include #include #include "base/logging.h" #include "base/notreached.h" #include "base/numerics/safe_math.h" #include "media/base/subsample_entry.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" namespace media { namespace { // Converts [|start|, |end|) range with |encrypted_ranges| into a vector of // SubsampleEntry. |encrypted_ranges| must be with in the range defined by // |start| and |end|. // It is OK to pass in empty |encrypted_ranges|; this will return a vector // with single SubsampleEntry with clear_bytes set to the size of the buffer. std::vector EncryptedRangesToSubsampleEntry( const uint8_t* start, const uint8_t* end, const Ranges& encrypted_ranges) { std::vector subsamples; const uint8_t* cur = start; for (size_t i = 0; i < encrypted_ranges.size(); ++i) { SubsampleEntry subsample = {}; const uint8_t* encrypted_start = encrypted_ranges.start(i); DCHECK_GE(encrypted_start, cur) << "Encrypted range started before the current buffer pointer."; subsample.clear_bytes = encrypted_start - cur; const uint8_t* encrypted_end = encrypted_ranges.end(i); subsample.cypher_bytes = encrypted_end - encrypted_start; subsamples.push_back(subsample); cur = encrypted_end; DCHECK_LE(cur, end) << "Encrypted range is outside the buffer range."; } // If there is more data in the buffer but not covered by encrypted_ranges, // then it must be in the clear. if (cur < end) { SubsampleEntry subsample = {}; subsample.clear_bytes = end - cur; subsamples.push_back(subsample); } return subsamples; } } // namespace bool H264SliceHeader::IsPSlice() const { return (slice_type % 5 == kPSlice); } bool H264SliceHeader::IsBSlice() const { return (slice_type % 5 == kBSlice); } bool H264SliceHeader::IsISlice() const { return (slice_type % 5 == kISlice); } bool H264SliceHeader::IsSPSlice() const { return (slice_type % 5 == kSPSlice); } bool H264SliceHeader::IsSISlice() const { return (slice_type % 5 == kSISlice); } H264NALU::H264NALU() { memset(this, 0, sizeof(*this)); } // static void H264SPS::GetLevelConfigFromProfileLevel(VideoCodecProfile profile, uint8_t level, int* level_idc, bool* constraint_set3_flag) { // Spec A.3.1. // Note: we always use h264_output_level = 9 to indicate Level 1b in // VideoEncodeAccelerator::Config, in order to tell apart from Level 1.1 // which level IDC is also 11. // For Baseline and Main profile, if requested level is Level 1b, set // level_idc to 11 and constraint_set3_flag to true. Otherwise, set level_idc // to 9 for Level 1b, and ten times level number for others. if ((profile == H264PROFILE_BASELINE || profile == H264PROFILE_MAIN) && level == kLevelIDC1B) { *level_idc = 11; *constraint_set3_flag = true; } else { *level_idc = level; } } H264SPS::H264SPS() { memset(this, 0, sizeof(*this)); } // Based on T-REC-H.264 7.4.2.1.1, "Sequence parameter set data semantics", // available from http://www.itu.int/rec/T-REC-H.264. absl::optional H264SPS::GetCodedSize() const { // Interlaced frames are twice the height of each field. const int mb_unit = 16; int map_unit = frame_mbs_only_flag ? 16 : 32; // Verify that the values are not too large before multiplying them. // TODO(sandersd): These limits could be much smaller. The currently-largest // specified limit (excluding SVC, multiview, etc., which I didn't bother to // read) is 543 macroblocks (section A.3.1). int max_mb_minus1 = std::numeric_limits::max() / mb_unit - 1; int max_map_units_minus1 = std::numeric_limits::max() / map_unit - 1; if (pic_width_in_mbs_minus1 > max_mb_minus1 || pic_height_in_map_units_minus1 > max_map_units_minus1) { DVLOG(1) << "Coded size is too large."; return absl::nullopt; } return gfx::Size(mb_unit * (pic_width_in_mbs_minus1 + 1), map_unit * (pic_height_in_map_units_minus1 + 1)); } // Also based on section 7.4.2.1.1. absl::optional H264SPS::GetVisibleRect() const { absl::optional coded_size = GetCodedSize(); if (!coded_size) return absl::nullopt; if (!frame_cropping_flag) return gfx::Rect(coded_size.value()); int crop_unit_x; int crop_unit_y; if (chroma_array_type == 0) { crop_unit_x = 1; crop_unit_y = frame_mbs_only_flag ? 1 : 2; } else { // Section 6.2. // |chroma_format_idc| may be: // 1 => 4:2:0 // 2 => 4:2:2 // 3 => 4:4:4 // Everything else has |chroma_array_type| == 0. int sub_width_c = chroma_format_idc > 2 ? 1 : 2; int sub_height_c = chroma_format_idc > 1 ? 1 : 2; crop_unit_x = sub_width_c; crop_unit_y = sub_height_c * (frame_mbs_only_flag ? 1 : 2); } // Verify that the values are not too large before multiplying. if (coded_size->width() / crop_unit_x < frame_crop_left_offset || coded_size->width() / crop_unit_x < frame_crop_right_offset || coded_size->height() / crop_unit_y < frame_crop_top_offset || coded_size->height() / crop_unit_y < frame_crop_bottom_offset) { DVLOG(1) << "Frame cropping exceeds coded size."; return absl::nullopt; } int crop_left = crop_unit_x * frame_crop_left_offset; int crop_right = crop_unit_x * frame_crop_right_offset; int crop_top = crop_unit_y * frame_crop_top_offset; int crop_bottom = crop_unit_y * frame_crop_bottom_offset; // Verify that the values are sane. Note that some decoders also require that // crops are smaller than a macroblock and/or that crops must be adjacent to // at least one corner of the coded frame. if (coded_size->width() - crop_left <= crop_right || coded_size->height() - crop_top <= crop_bottom) { DVLOG(1) << "Frame cropping excludes entire frame."; return absl::nullopt; } return gfx::Rect(crop_left, crop_top, coded_size->width() - crop_left - crop_right, coded_size->height() - crop_top - crop_bottom); } // Based on T-REC-H.264 E.2.1, "VUI parameters semantics", // available from http://www.itu.int/rec/T-REC-H.264. VideoColorSpace H264SPS::GetColorSpace() const { if (colour_description_present_flag) { return VideoColorSpace( colour_primaries, transfer_characteristics, matrix_coefficients, video_full_range_flag ? gfx::ColorSpace::RangeID::FULL : gfx::ColorSpace::RangeID::LIMITED); } else { return VideoColorSpace(); } } VideoChromaSampling H264SPS::GetChromaSampling() const { // Spec section 6.2 switch (chroma_format_idc) { case 0: return VideoChromaSampling::k400; case 1: return VideoChromaSampling::k420; case 2: return VideoChromaSampling::k422; case 3: return VideoChromaSampling::k444; default: DVLOG(1) << "Unknown chroma subsampling format."; return VideoChromaSampling::kUnknown; } } uint8_t H264SPS::GetIndicatedLevel() const { // Spec A.3.1 and A.3.2 // For Baseline, Constrained Baseline and Main profile, the indicated level is // Level 1b if level_idc is equal to 11 and constraint_set3_flag is true. if ((profile_idc == H264SPS::kProfileIDCBaseline || profile_idc == H264SPS::kProfileIDCConstrainedBaseline || profile_idc == H264SPS::kProfileIDCMain) && level_idc == 11 && constraint_set3_flag) { return kLevelIDC1B; // Level 1b } // Otherwise, the level_idc is equal to 9 for Level 1b, and others are equal // to values of ten times the level numbers. return base::checked_cast(level_idc); } bool H264SPS::CheckIndicatedLevelWithinTarget(uint8_t target_level) const { // See table A-1 in spec. // Level 1.0 < 1b < 1.1 < 1.2 .... (in numeric order). uint8_t level = GetIndicatedLevel(); if (target_level == kLevelIDC1p0) return level == kLevelIDC1p0; if (target_level == kLevelIDC1B) return level == kLevelIDC1p0 || level == kLevelIDC1B; return level <= target_level; } H264PPS::H264PPS() { memset(this, 0, sizeof(*this)); } H264SliceHeader::H264SliceHeader() { memset(this, 0, sizeof(*this)); } H264SEIMessage::H264SEIMessage() { memset(this, 0, sizeof(*this)); } #define READ_BITS_OR_RETURN(num_bits, out) \ do { \ int _out; \ if (!br_.ReadBits(num_bits, &_out)) { \ DVLOG(1) \ << "Error in stream: unexpected EOS while trying to read " #out; \ return kInvalidStream; \ } \ *out = _out; \ } while (0) #define READ_BOOL_OR_RETURN(out) \ do { \ int _out; \ if (!br_.ReadBits(1, &_out)) { \ DVLOG(1) \ << "Error in stream: unexpected EOS while trying to read " #out; \ return kInvalidStream; \ } \ *out = _out != 0; \ } while (0) #define READ_UE_OR_RETURN(out) \ do { \ if (ReadUE(out) != kOk) { \ DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \ return kInvalidStream; \ } \ } while (0) #define READ_SE_OR_RETURN(out) \ do { \ if (ReadSE(out) != kOk) { \ DVLOG(1) << "Error in stream: invalid value while trying to read " #out; \ return kInvalidStream; \ } \ } while (0) #define IN_RANGE_OR_RETURN(val, min, max) \ do { \ if ((val) < (min) || (val) > (max)) { \ DVLOG(1) << "Error in stream: invalid value, expected " #val " to be" \ << " in range [" << (min) << ":" << (max) << "]" \ << " found " << (val) << " instead"; \ return kInvalidStream; \ } \ } while (0) #define TRUE_OR_RETURN(a) \ do { \ if (!(a)) { \ DVLOG(1) << "Error in stream: invalid value, expected " << #a; \ return kInvalidStream; \ } \ } while (0) // ISO 14496 part 10 // VUI parameters: Table E-1 "Meaning of sample aspect ratio indicator" static const int kTableSarWidth[] = {0, 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2}; static const int kTableSarHeight[] = {0, 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1}; static_assert(std::size(kTableSarWidth) == std::size(kTableSarHeight), "sar tables must have the same size"); H264Parser::H264Parser() { Reset(); } H264Parser::~H264Parser() = default; void H264Parser::Reset() { stream_ = nullptr; bytes_left_ = 0; encrypted_ranges_.clear(); previous_nalu_range_.clear(); } void H264Parser::SetStream(const uint8_t* stream, off_t stream_size) { std::vector subsamples; SetEncryptedStream(stream, stream_size, subsamples); } void H264Parser::SetEncryptedStream( const uint8_t* stream, off_t stream_size, const std::vector& subsamples) { DCHECK(stream); DCHECK_GT(stream_size, 0); stream_ = stream; bytes_left_ = stream_size; previous_nalu_range_.clear(); encrypted_ranges_.clear(); const uint8_t* start = stream; const uint8_t* stream_end = stream_ + bytes_left_; for (size_t i = 0; i < subsamples.size() && start < stream_end; ++i) { start += subsamples[i].clear_bytes; const uint8_t* end = std::min(start + subsamples[i].cypher_bytes, stream_end); encrypted_ranges_.Add(start, end); start = end; } } const H264PPS* H264Parser::GetPPS(int pps_id) const { auto it = active_PPSes_.find(pps_id); if (it == active_PPSes_.end()) { DVLOG(1) << "Requested a nonexistent PPS id " << pps_id; return nullptr; } return it->second.get(); } const H264SPS* H264Parser::GetSPS(int sps_id) const { auto it = active_SPSes_.find(sps_id); if (it == active_SPSes_.end()) { DVLOG(1) << "Requested a nonexistent SPS id " << sps_id; return nullptr; } return it->second.get(); } static inline bool IsStartCode(const uint8_t* data) { return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01; } // static bool H264Parser::FindStartCode(const uint8_t* data, off_t data_size, off_t* offset, off_t* start_code_size) { DCHECK_GE(data_size, 0); off_t bytes_left = data_size; while (bytes_left >= 3) { // The start code is "\0\0\1", ones are more unusual than zeroes, so let's // search for it first. const uint8_t* tmp = reinterpret_cast(memchr(data + 2, 1, bytes_left - 2)); if (!tmp) { data += bytes_left - 2; bytes_left = 2; break; } tmp -= 2; bytes_left -= tmp - data; data = tmp; if (IsStartCode(data)) { // Found three-byte start code, set pointer at its beginning. *offset = data_size - bytes_left; *start_code_size = 3; // If there is a zero byte before this start code, // then it's actually a four-byte start code, so backtrack one byte. if (*offset > 0 && *(data - 1) == 0x00) { --(*offset); ++(*start_code_size); } return true; } ++data; --bytes_left; } // End of data: offset is pointing to the first byte that was not considered // as a possible start of a start code. // Note: there is no security issue when receiving a negative |data_size| // since in this case, |bytes_left| is equal to |data_size| and thus // |*offset| is equal to 0 (valid offset). *offset = data_size - bytes_left; *start_code_size = 0; return false; } bool H264Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) { // Find the start code of next NALU. off_t nalu_start_off = 0; off_t annexb_start_code_size = 0; if (!FindStartCodeInClearRanges(stream_, bytes_left_, encrypted_ranges_, &nalu_start_off, &annexb_start_code_size)) { DVLOG(4) << "Could not find start code, end of stream?"; return false; } // Move the stream to the beginning of the NALU (pointing at the start code). stream_ += nalu_start_off; bytes_left_ -= nalu_start_off; const uint8_t* nalu_data = stream_ + annexb_start_code_size; off_t max_nalu_data_size = bytes_left_ - annexb_start_code_size; if (max_nalu_data_size <= 0) { DVLOG(3) << "End of stream"; return false; } // Find the start code of next NALU; // if successful, |nalu_size_without_start_code| is the number of bytes from // after previous start code to before this one; // if next start code is not found, it is still a valid NALU since there // are some bytes left after the first start code: all the remaining bytes // belong to the current NALU. off_t next_start_code_size = 0; off_t nalu_size_without_start_code = 0; if (!FindStartCodeInClearRanges( nalu_data, max_nalu_data_size, encrypted_ranges_, &nalu_size_without_start_code, &next_start_code_size)) { nalu_size_without_start_code = max_nalu_data_size; } *nalu_size = nalu_size_without_start_code + annexb_start_code_size; *start_code_size = annexb_start_code_size; return true; } // static bool H264Parser::FindStartCodeInClearRanges( const uint8_t* data, off_t data_size, const Ranges& encrypted_ranges, off_t* offset, off_t* start_code_size) { if (encrypted_ranges.size() == 0) return FindStartCode(data, data_size, offset, start_code_size); DCHECK_GE(data_size, 0); const uint8_t* start = data; do { off_t bytes_left = data_size - (start - data); if (!FindStartCode(start, bytes_left, offset, start_code_size)) return false; // Construct a Ranges object that represents the region occupied // by the start code and the 1 byte needed to read the NAL unit type. const uint8_t* start_code = start + *offset; const uint8_t* start_code_end = start_code + *start_code_size; Ranges start_code_range; start_code_range.Add(start_code, start_code_end + 1); if (encrypted_ranges.IntersectionWith(start_code_range).size() > 0) { // The start code is inside an encrypted section so we need to scan // for another start code. *start_code_size = 0; start += std::min(*offset + 1, bytes_left); } } while (*start_code_size == 0); // Update |*offset| to include the data we skipped over. *offset += start - data; return true; } // static VideoCodecProfile H264Parser::ProfileIDCToVideoCodecProfile(int profile_idc) { switch (profile_idc) { case H264SPS::kProfileIDCBaseline: return H264PROFILE_BASELINE; case H264SPS::kProfileIDCMain: return H264PROFILE_MAIN; case H264SPS::kProfileIDCHigh: return H264PROFILE_HIGH; case H264SPS::kProfileIDHigh10: return H264PROFILE_HIGH10PROFILE; case H264SPS::kProfileIDHigh422: return H264PROFILE_HIGH422PROFILE; case H264SPS::kProfileIDHigh444Predictive: return H264PROFILE_HIGH444PREDICTIVEPROFILE; case H264SPS::kProfileIDScalableBaseline: return H264PROFILE_SCALABLEBASELINE; case H264SPS::kProfileIDScalableHigh: return H264PROFILE_SCALABLEHIGH; case H264SPS::kProfileIDStereoHigh: return H264PROFILE_STEREOHIGH; case H264SPS::kProfileIDSMultiviewHigh: return H264PROFILE_MULTIVIEWHIGH; } DVLOG(1) << "unknown video profile: " << profile_idc; return VIDEO_CODEC_PROFILE_UNKNOWN; } // static bool H264Parser::ParseNALUs(const uint8_t* stream, size_t stream_size, std::vector* nalus) { DCHECK(nalus); H264Parser parser; parser.SetStream(stream, stream_size); while (true) { H264NALU nalu; const H264Parser::Result result = parser.AdvanceToNextNALU(&nalu); if (result == H264Parser::kOk) { nalus->push_back(nalu); } else if (result == media::H264Parser::kEOStream) { return true; } else { DLOG(ERROR) << "Unexpected H264 parser result"; return false; } } NOTREACHED(); return false; } H264Parser::Result H264Parser::ReadUE(int* val) { int num_bits = -1; int bit; int rest; // Count the number of contiguous zero bits. do { READ_BITS_OR_RETURN(1, &bit); num_bits++; } while (bit == 0); if (num_bits > 31) return kInvalidStream; // Calculate exp-Golomb code value of size num_bits. // Special case for |num_bits| == 31 to avoid integer overflow. The only // valid representation as an int is 2^31 - 1, so the remaining bits must // be 0 or else the number is too large. *val = (1u << num_bits) - 1u; if (num_bits == 31) { READ_BITS_OR_RETURN(num_bits, &rest); return (rest == 0) ? kOk : kInvalidStream; } if (num_bits > 0) { READ_BITS_OR_RETURN(num_bits, &rest); *val += rest; } return kOk; } H264Parser::Result H264Parser::ReadSE(int* val) { int ue; Result res; // See Chapter 9 in the spec. res = ReadUE(&ue); if (res != kOk) return res; if (ue % 2 == 0) *val = -(ue / 2); else *val = ue / 2 + 1; return kOk; } H264Parser::Result H264Parser::AdvanceToNextNALU(H264NALU* nalu) { off_t start_code_size; off_t nalu_size_with_start_code; if (!LocateNALU(&nalu_size_with_start_code, &start_code_size)) { DVLOG(4) << "Could not find next NALU, bytes left in stream: " << bytes_left_; stream_ = nullptr; bytes_left_ = 0; return kEOStream; } nalu->data = stream_ + start_code_size; nalu->size = nalu_size_with_start_code - start_code_size; DVLOG(4) << "NALU found: size=" << nalu_size_with_start_code; // Initialize bit reader at the start of found NALU. if (!br_.Initialize(nalu->data, nalu->size)) { stream_ = nullptr; bytes_left_ = 0; return kEOStream; } // Move parser state to after this NALU, so next time AdvanceToNextNALU // is called, we will effectively be skipping it; // other parsing functions will use the position saved // in bit reader for parsing, so we don't have to remember it here. stream_ += nalu_size_with_start_code; bytes_left_ -= nalu_size_with_start_code; // Read NALU header, skip the forbidden_zero_bit, but check for it. int data; READ_BITS_OR_RETURN(1, &data); TRUE_OR_RETURN(data == 0); READ_BITS_OR_RETURN(2, &nalu->nal_ref_idc); READ_BITS_OR_RETURN(5, &nalu->nal_unit_type); DVLOG(4) << "NALU type: " << static_cast(nalu->nal_unit_type) << " at: " << reinterpret_cast(nalu->data) << " size: " << nalu->size << " ref: " << static_cast(nalu->nal_ref_idc); previous_nalu_range_.clear(); previous_nalu_range_.Add(nalu->data, nalu->data + nalu->size); return kOk; } // Default scaling lists (per spec). static const int kDefault4x4Intra[kH264ScalingList4x4Length] = { 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, }; static const int kDefault4x4Inter[kH264ScalingList4x4Length] = { 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34, }; static const int kDefault8x8Intra[kH264ScalingList8x8Length] = { 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23, 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, 31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42, }; static const int kDefault8x8Inter[kH264ScalingList8x8Length] = { 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35, }; static inline void DefaultScalingList4x4( int i, int scaling_list4x4[][kH264ScalingList4x4Length]) { DCHECK_LT(i, 6); if (i < 3) memcpy(scaling_list4x4[i], kDefault4x4Intra, sizeof(kDefault4x4Intra)); else if (i < 6) memcpy(scaling_list4x4[i], kDefault4x4Inter, sizeof(kDefault4x4Inter)); } static inline void DefaultScalingList8x8( int i, int scaling_list8x8[][kH264ScalingList8x8Length]) { DCHECK_LT(i, 6); if (i % 2 == 0) memcpy(scaling_list8x8[i], kDefault8x8Intra, sizeof(kDefault8x8Intra)); else memcpy(scaling_list8x8[i], kDefault8x8Inter, sizeof(kDefault8x8Inter)); } static void FallbackScalingList4x4( int i, const int default_scaling_list_intra[], const int default_scaling_list_inter[], int scaling_list4x4[][kH264ScalingList4x4Length]) { static const int kScalingList4x4ByteSize = sizeof(scaling_list4x4[0][0]) * kH264ScalingList4x4Length; switch (i) { case 0: memcpy(scaling_list4x4[i], default_scaling_list_intra, kScalingList4x4ByteSize); break; case 1: memcpy(scaling_list4x4[i], scaling_list4x4[0], kScalingList4x4ByteSize); break; case 2: memcpy(scaling_list4x4[i], scaling_list4x4[1], kScalingList4x4ByteSize); break; case 3: memcpy(scaling_list4x4[i], default_scaling_list_inter, kScalingList4x4ByteSize); break; case 4: memcpy(scaling_list4x4[i], scaling_list4x4[3], kScalingList4x4ByteSize); break; case 5: memcpy(scaling_list4x4[i], scaling_list4x4[4], kScalingList4x4ByteSize); break; default: NOTREACHED(); break; } } static void FallbackScalingList8x8( int i, const int default_scaling_list_intra[], const int default_scaling_list_inter[], int scaling_list8x8[][kH264ScalingList8x8Length]) { static const int kScalingList8x8ByteSize = sizeof(scaling_list8x8[0][0]) * kH264ScalingList8x8Length; switch (i) { case 0: memcpy(scaling_list8x8[i], default_scaling_list_intra, kScalingList8x8ByteSize); break; case 1: memcpy(scaling_list8x8[i], default_scaling_list_inter, kScalingList8x8ByteSize); break; case 2: memcpy(scaling_list8x8[i], scaling_list8x8[0], kScalingList8x8ByteSize); break; case 3: memcpy(scaling_list8x8[i], scaling_list8x8[1], kScalingList8x8ByteSize); break; case 4: memcpy(scaling_list8x8[i], scaling_list8x8[2], kScalingList8x8ByteSize); break; case 5: memcpy(scaling_list8x8[i], scaling_list8x8[3], kScalingList8x8ByteSize); break; default: NOTREACHED(); break; } } H264Parser::Result H264Parser::ParseScalingList(int size, int* scaling_list, bool* use_default) { // See chapter 7.3.2.1.1.1. int last_scale = 8; int next_scale = 8; int delta_scale; *use_default = false; for (int j = 0; j < size; ++j) { if (next_scale != 0) { READ_SE_OR_RETURN(&delta_scale); IN_RANGE_OR_RETURN(delta_scale, -128, 127); next_scale = (last_scale + delta_scale + 256) & 0xff; if (j == 0 && next_scale == 0) { *use_default = true; return kOk; } } scaling_list[j] = (next_scale == 0) ? last_scale : next_scale; last_scale = scaling_list[j]; } return kOk; } H264Parser::Result H264Parser::ParseSPSScalingLists(H264SPS* sps) { // See 7.4.2.1.1. bool seq_scaling_list_present_flag; bool use_default; Result res; // Parse scaling_list4x4. for (int i = 0; i < 6; ++i) { READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag); if (seq_scaling_list_present_flag) { res = ParseScalingList(std::size(sps->scaling_list4x4[i]), sps->scaling_list4x4[i], &use_default); if (res != kOk) return res; if (use_default) DefaultScalingList4x4(i, sps->scaling_list4x4); } else { FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter, sps->scaling_list4x4); } } // Parse scaling_list8x8. for (int i = 0; i < ((sps->chroma_format_idc != 3) ? 2 : 6); ++i) { READ_BOOL_OR_RETURN(&seq_scaling_list_present_flag); if (seq_scaling_list_present_flag) { res = ParseScalingList(std::size(sps->scaling_list8x8[i]), sps->scaling_list8x8[i], &use_default); if (res != kOk) return res; if (use_default) DefaultScalingList8x8(i, sps->scaling_list8x8); } else { FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter, sps->scaling_list8x8); } } return kOk; } H264Parser::Result H264Parser::ParsePPSScalingLists(const H264SPS& sps, H264PPS* pps) { // See 7.4.2.2. bool pic_scaling_list_present_flag; bool use_default; Result res; for (int i = 0; i < 6; ++i) { READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag); if (pic_scaling_list_present_flag) { res = ParseScalingList(std::size(pps->scaling_list4x4[i]), pps->scaling_list4x4[i], &use_default); if (res != kOk) return res; if (use_default) DefaultScalingList4x4(i, pps->scaling_list4x4); } else { if (!sps.seq_scaling_matrix_present_flag) { // Table 7-2 fallback rule A in spec. FallbackScalingList4x4(i, kDefault4x4Intra, kDefault4x4Inter, pps->scaling_list4x4); } else { // Table 7-2 fallback rule B in spec. FallbackScalingList4x4(i, sps.scaling_list4x4[0], sps.scaling_list4x4[3], pps->scaling_list4x4); } } } if (pps->transform_8x8_mode_flag) { for (int i = 0; i < ((sps.chroma_format_idc != 3) ? 2 : 6); ++i) { READ_BOOL_OR_RETURN(&pic_scaling_list_present_flag); if (pic_scaling_list_present_flag) { res = ParseScalingList(std::size(pps->scaling_list8x8[i]), pps->scaling_list8x8[i], &use_default); if (res != kOk) return res; if (use_default) DefaultScalingList8x8(i, pps->scaling_list8x8); } else { if (!sps.seq_scaling_matrix_present_flag) { // Table 7-2 fallback rule A in spec. FallbackScalingList8x8(i, kDefault8x8Intra, kDefault8x8Inter, pps->scaling_list8x8); } else { // Table 7-2 fallback rule B in spec. FallbackScalingList8x8(i, sps.scaling_list8x8[0], sps.scaling_list8x8[1], pps->scaling_list8x8); } } } } return kOk; } H264Parser::Result H264Parser::ParseAndIgnoreHRDParameters( bool* hrd_parameters_present) { int data; READ_BOOL_OR_RETURN(&data); // {nal,vcl}_hrd_parameters_present_flag if (!data) return kOk; *hrd_parameters_present = true; int cpb_cnt_minus1; READ_UE_OR_RETURN(&cpb_cnt_minus1); IN_RANGE_OR_RETURN(cpb_cnt_minus1, 0, 31); READ_BITS_OR_RETURN(8, &data); // bit_rate_scale, cpb_size_scale for (int i = 0; i <= cpb_cnt_minus1; ++i) { READ_UE_OR_RETURN(&data); // bit_rate_value_minus1[i] READ_UE_OR_RETURN(&data); // cpb_size_value_minus1[i] READ_BOOL_OR_RETURN(&data); // cbr_flag } READ_BITS_OR_RETURN(20, &data); // cpb/dpb delays, etc. return kOk; } H264Parser::Result H264Parser::ParseVUIParameters(H264SPS* sps) { bool aspect_ratio_info_present_flag; READ_BOOL_OR_RETURN(&aspect_ratio_info_present_flag); if (aspect_ratio_info_present_flag) { int aspect_ratio_idc; READ_BITS_OR_RETURN(8, &aspect_ratio_idc); if (aspect_ratio_idc == H264SPS::kExtendedSar) { READ_BITS_OR_RETURN(16, &sps->sar_width); READ_BITS_OR_RETURN(16, &sps->sar_height); } else { const int max_aspect_ratio_idc = std::size(kTableSarWidth) - 1; IN_RANGE_OR_RETURN(aspect_ratio_idc, 0, max_aspect_ratio_idc); sps->sar_width = kTableSarWidth[aspect_ratio_idc]; sps->sar_height = kTableSarHeight[aspect_ratio_idc]; } } int data; // Read and ignore overscan and video signal type info. READ_BOOL_OR_RETURN(&data); // overscan_info_present_flag if (data) READ_BOOL_OR_RETURN(&data); // overscan_appropriate_flag READ_BOOL_OR_RETURN(&sps->video_signal_type_present_flag); if (sps->video_signal_type_present_flag) { READ_BITS_OR_RETURN(3, &sps->video_format); READ_BOOL_OR_RETURN(&sps->video_full_range_flag); READ_BOOL_OR_RETURN(&sps->colour_description_present_flag); if (sps->colour_description_present_flag) { // color description syntax elements READ_BITS_OR_RETURN(8, &sps->colour_primaries); READ_BITS_OR_RETURN(8, &sps->transfer_characteristics); READ_BITS_OR_RETURN(8, &sps->matrix_coefficients); } } READ_BOOL_OR_RETURN(&data); // chroma_loc_info_present_flag if (data) { READ_UE_OR_RETURN(&data); // chroma_sample_loc_type_top_field READ_UE_OR_RETURN(&data); // chroma_sample_loc_type_bottom_field } // Read and ignore timing info. READ_BOOL_OR_RETURN(&data); // timing_info_present_flag if (data) { READ_BITS_OR_RETURN(16, &data); // num_units_in_tick READ_BITS_OR_RETURN(16, &data); // num_units_in_tick READ_BITS_OR_RETURN(16, &data); // time_scale READ_BITS_OR_RETURN(16, &data); // time_scale READ_BOOL_OR_RETURN(&data); // fixed_frame_rate_flag } // Read and ignore NAL HRD parameters, if present. bool hrd_parameters_present = false; Result res = ParseAndIgnoreHRDParameters(&hrd_parameters_present); if (res != kOk) return res; // Read and ignore VCL HRD parameters, if present. res = ParseAndIgnoreHRDParameters(&hrd_parameters_present); if (res != kOk) return res; if (hrd_parameters_present) // One of NAL or VCL params present is enough. READ_BOOL_OR_RETURN(&data); // low_delay_hrd_flag READ_BOOL_OR_RETURN(&data); // pic_struct_present_flag READ_BOOL_OR_RETURN(&sps->bitstream_restriction_flag); if (sps->bitstream_restriction_flag) { READ_BOOL_OR_RETURN(&data); // motion_vectors_over_pic_boundaries_flag READ_UE_OR_RETURN(&data); // max_bytes_per_pic_denom READ_UE_OR_RETURN(&data); // max_bits_per_mb_denom READ_UE_OR_RETURN(&data); // log2_max_mv_length_horizontal READ_UE_OR_RETURN(&data); // log2_max_mv_length_vertical READ_UE_OR_RETURN(&sps->max_num_reorder_frames); READ_UE_OR_RETURN(&sps->max_dec_frame_buffering); TRUE_OR_RETURN(sps->max_dec_frame_buffering >= sps->max_num_ref_frames); IN_RANGE_OR_RETURN(sps->max_num_reorder_frames, 0, sps->max_dec_frame_buffering); } return kOk; } static void FillDefaultSeqScalingLists(H264SPS* sps) { for (int i = 0; i < 6; ++i) for (int j = 0; j < kH264ScalingList4x4Length; ++j) sps->scaling_list4x4[i][j] = 16; for (int i = 0; i < 6; ++i) for (int j = 0; j < kH264ScalingList8x8Length; ++j) sps->scaling_list8x8[i][j] = 16; } H264Parser::Result H264Parser::ParseSPS(int* sps_id) { // See 7.4.2.1. int data; Result res; *sps_id = -1; std::unique_ptr sps(new H264SPS()); READ_BITS_OR_RETURN(8, &sps->profile_idc); READ_BOOL_OR_RETURN(&sps->constraint_set0_flag); READ_BOOL_OR_RETURN(&sps->constraint_set1_flag); READ_BOOL_OR_RETURN(&sps->constraint_set2_flag); READ_BOOL_OR_RETURN(&sps->constraint_set3_flag); READ_BOOL_OR_RETURN(&sps->constraint_set4_flag); READ_BOOL_OR_RETURN(&sps->constraint_set5_flag); READ_BITS_OR_RETURN(2, &data); // reserved_zero_2bits READ_BITS_OR_RETURN(8, &sps->level_idc); READ_UE_OR_RETURN(&sps->seq_parameter_set_id); TRUE_OR_RETURN(sps->seq_parameter_set_id < 32); if (sps->profile_idc == 100 || sps->profile_idc == 110 || sps->profile_idc == 122 || sps->profile_idc == 244 || sps->profile_idc == 44 || sps->profile_idc == 83 || sps->profile_idc == 86 || sps->profile_idc == 118 || sps->profile_idc == 128) { READ_UE_OR_RETURN(&sps->chroma_format_idc); TRUE_OR_RETURN(sps->chroma_format_idc < 4); if (sps->chroma_format_idc == 3) READ_BOOL_OR_RETURN(&sps->separate_colour_plane_flag); READ_UE_OR_RETURN(&sps->bit_depth_luma_minus8); TRUE_OR_RETURN(sps->bit_depth_luma_minus8 < 7); READ_UE_OR_RETURN(&sps->bit_depth_chroma_minus8); TRUE_OR_RETURN(sps->bit_depth_chroma_minus8 < 7); READ_BOOL_OR_RETURN(&sps->qpprime_y_zero_transform_bypass_flag); READ_BOOL_OR_RETURN(&sps->seq_scaling_matrix_present_flag); if (sps->seq_scaling_matrix_present_flag) { DVLOG(4) << "Scaling matrix present"; res = ParseSPSScalingLists(sps.get()); if (res != kOk) return res; } else { FillDefaultSeqScalingLists(sps.get()); } } else { sps->chroma_format_idc = 1; FillDefaultSeqScalingLists(sps.get()); } if (sps->separate_colour_plane_flag) sps->chroma_array_type = 0; else sps->chroma_array_type = sps->chroma_format_idc; READ_UE_OR_RETURN(&sps->log2_max_frame_num_minus4); TRUE_OR_RETURN(sps->log2_max_frame_num_minus4 < 13); READ_UE_OR_RETURN(&sps->pic_order_cnt_type); TRUE_OR_RETURN(sps->pic_order_cnt_type < 3); if (sps->pic_order_cnt_type == 0) { READ_UE_OR_RETURN(&sps->log2_max_pic_order_cnt_lsb_minus4); TRUE_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 < 13); sps->expected_delta_per_pic_order_cnt_cycle = 0; } else if (sps->pic_order_cnt_type == 1) { READ_BOOL_OR_RETURN(&sps->delta_pic_order_always_zero_flag); READ_SE_OR_RETURN(&sps->offset_for_non_ref_pic); READ_SE_OR_RETURN(&sps->offset_for_top_to_bottom_field); READ_UE_OR_RETURN(&sps->num_ref_frames_in_pic_order_cnt_cycle); TRUE_OR_RETURN(sps->num_ref_frames_in_pic_order_cnt_cycle < 255); base::CheckedNumeric offset_acc = 0; for (int i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; ++i) { READ_SE_OR_RETURN(&sps->offset_for_ref_frame[i]); offset_acc += sps->offset_for_ref_frame[i]; } if (!offset_acc.IsValid()) return kInvalidStream; sps->expected_delta_per_pic_order_cnt_cycle = offset_acc.ValueOrDefault(0); } READ_UE_OR_RETURN(&sps->max_num_ref_frames); READ_BOOL_OR_RETURN(&sps->gaps_in_frame_num_value_allowed_flag); READ_UE_OR_RETURN(&sps->pic_width_in_mbs_minus1); READ_UE_OR_RETURN(&sps->pic_height_in_map_units_minus1); READ_BOOL_OR_RETURN(&sps->frame_mbs_only_flag); if (!sps->frame_mbs_only_flag) READ_BOOL_OR_RETURN(&sps->mb_adaptive_frame_field_flag); READ_BOOL_OR_RETURN(&sps->direct_8x8_inference_flag); READ_BOOL_OR_RETURN(&sps->frame_cropping_flag); if (sps->frame_cropping_flag) { READ_UE_OR_RETURN(&sps->frame_crop_left_offset); READ_UE_OR_RETURN(&sps->frame_crop_right_offset); READ_UE_OR_RETURN(&sps->frame_crop_top_offset); READ_UE_OR_RETURN(&sps->frame_crop_bottom_offset); } READ_BOOL_OR_RETURN(&sps->vui_parameters_present_flag); if (sps->vui_parameters_present_flag) { DVLOG(4) << "VUI parameters present"; res = ParseVUIParameters(sps.get()); if (res != kOk) return res; } // If an SPS with the same id already exists, replace it. *sps_id = sps->seq_parameter_set_id; active_SPSes_[*sps_id] = std::move(sps); return kOk; } H264Parser::Result H264Parser::ParsePPS(int* pps_id) { // See 7.4.2.2. const H264SPS* sps; Result res; *pps_id = -1; std::unique_ptr pps(new H264PPS()); READ_UE_OR_RETURN(&pps->pic_parameter_set_id); READ_UE_OR_RETURN(&pps->seq_parameter_set_id); TRUE_OR_RETURN(pps->seq_parameter_set_id < 32); if (active_SPSes_.find(pps->seq_parameter_set_id) == active_SPSes_.end()) { DVLOG(1) << "Invalid stream, no SPS id: " << pps->seq_parameter_set_id; return kInvalidStream; } sps = GetSPS(pps->seq_parameter_set_id); TRUE_OR_RETURN(sps); READ_BOOL_OR_RETURN(&pps->entropy_coding_mode_flag); READ_BOOL_OR_RETURN(&pps->bottom_field_pic_order_in_frame_present_flag); READ_UE_OR_RETURN(&pps->num_slice_groups_minus1); if (pps->num_slice_groups_minus1 > 1) { DVLOG(1) << "Slice groups not supported"; return kUnsupportedStream; } READ_UE_OR_RETURN(&pps->num_ref_idx_l0_default_active_minus1); TRUE_OR_RETURN(pps->num_ref_idx_l0_default_active_minus1 < 32); READ_UE_OR_RETURN(&pps->num_ref_idx_l1_default_active_minus1); TRUE_OR_RETURN(pps->num_ref_idx_l1_default_active_minus1 < 32); READ_BOOL_OR_RETURN(&pps->weighted_pred_flag); READ_BITS_OR_RETURN(2, &pps->weighted_bipred_idc); TRUE_OR_RETURN(pps->weighted_bipred_idc < 3); READ_SE_OR_RETURN(&pps->pic_init_qp_minus26); IN_RANGE_OR_RETURN(pps->pic_init_qp_minus26, -26, 25); READ_SE_OR_RETURN(&pps->pic_init_qs_minus26); IN_RANGE_OR_RETURN(pps->pic_init_qs_minus26, -26, 25); READ_SE_OR_RETURN(&pps->chroma_qp_index_offset); IN_RANGE_OR_RETURN(pps->chroma_qp_index_offset, -12, 12); pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset; READ_BOOL_OR_RETURN(&pps->deblocking_filter_control_present_flag); READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag); READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag); bool pps_remainder_unencrypted = true; if (encrypted_ranges_.size()) { Ranges pps_range; // Only check that the next byte is unencrypted, not the rest of the NALU. const uint8_t* next_byte = previous_nalu_range_.end(0) - br_.NumBitsLeft() / 8; pps_range.Add(next_byte, next_byte + 1); pps_remainder_unencrypted = (encrypted_ranges_.IntersectionWith(pps_range).size() == 0); } if (pps_remainder_unencrypted && br_.HasMoreRBSPData()) { READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag); READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag); if (pps->pic_scaling_matrix_present_flag) { DVLOG(4) << "Picture scaling matrix present"; res = ParsePPSScalingLists(*sps, pps.get()); if (res != kOk) return res; } READ_SE_OR_RETURN(&pps->second_chroma_qp_index_offset); } // If a PPS with the same id already exists, replace it. *pps_id = pps->pic_parameter_set_id; active_PPSes_[*pps_id] = std::move(pps); return kOk; } H264Parser::Result H264Parser::ParseSPSExt(int* sps_id) { // See 7.4.2.1. int local_sps_id = -1; *sps_id = -1; READ_UE_OR_RETURN(&local_sps_id); TRUE_OR_RETURN(local_sps_id < 32); *sps_id = local_sps_id; return kOk; } H264Parser::Result H264Parser::ParseRefPicListModification( int num_ref_idx_active_minus1, H264ModificationOfPicNum* ref_list_mods) { H264ModificationOfPicNum* pic_num_mod; if (num_ref_idx_active_minus1 >= 32) return kInvalidStream; for (int i = 0; i < 32; ++i) { pic_num_mod = &ref_list_mods[i]; READ_UE_OR_RETURN(&pic_num_mod->modification_of_pic_nums_idc); TRUE_OR_RETURN(pic_num_mod->modification_of_pic_nums_idc < 4); switch (pic_num_mod->modification_of_pic_nums_idc) { case 0: case 1: READ_UE_OR_RETURN(&pic_num_mod->abs_diff_pic_num_minus1); break; case 2: READ_UE_OR_RETURN(&pic_num_mod->long_term_pic_num); break; case 3: // Per spec, list cannot be empty. if (i == 0) return kInvalidStream; return kOk; default: return kInvalidStream; } } // If we got here, we didn't get loop end marker prematurely, // so make sure it is there for our client. int modification_of_pic_nums_idc; READ_UE_OR_RETURN(&modification_of_pic_nums_idc); TRUE_OR_RETURN(modification_of_pic_nums_idc == 3); return kOk; } H264Parser::Result H264Parser::ParseRefPicListModifications( H264SliceHeader* shdr) { Result res; if (!shdr->IsISlice() && !shdr->IsSISlice()) { READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l0); if (shdr->ref_pic_list_modification_flag_l0) { res = ParseRefPicListModification(shdr->num_ref_idx_l0_active_minus1, shdr->ref_list_l0_modifications); if (res != kOk) return res; } } if (shdr->IsBSlice()) { READ_BOOL_OR_RETURN(&shdr->ref_pic_list_modification_flag_l1); if (shdr->ref_pic_list_modification_flag_l1) { res = ParseRefPicListModification(shdr->num_ref_idx_l1_active_minus1, shdr->ref_list_l1_modifications); if (res != kOk) return res; } } return kOk; } H264Parser::Result H264Parser::ParseWeightingFactors( int num_ref_idx_active_minus1, int chroma_array_type, int luma_log2_weight_denom, int chroma_log2_weight_denom, H264WeightingFactors* w_facts) { int def_luma_weight = 1 << luma_log2_weight_denom; int def_chroma_weight = 1 << chroma_log2_weight_denom; for (int i = 0; i < num_ref_idx_active_minus1 + 1; ++i) { READ_BOOL_OR_RETURN(&w_facts->luma_weight_flag); if (w_facts->luma_weight_flag) { READ_SE_OR_RETURN(&w_facts->luma_weight[i]); IN_RANGE_OR_RETURN(w_facts->luma_weight[i], -128, 127); READ_SE_OR_RETURN(&w_facts->luma_offset[i]); IN_RANGE_OR_RETURN(w_facts->luma_offset[i], -128, 127); } else { w_facts->luma_weight[i] = def_luma_weight; w_facts->luma_offset[i] = 0; } if (chroma_array_type != 0) { READ_BOOL_OR_RETURN(&w_facts->chroma_weight_flag); if (w_facts->chroma_weight_flag) { for (int j = 0; j < 2; ++j) { READ_SE_OR_RETURN(&w_facts->chroma_weight[i][j]); IN_RANGE_OR_RETURN(w_facts->chroma_weight[i][j], -128, 127); READ_SE_OR_RETURN(&w_facts->chroma_offset[i][j]); IN_RANGE_OR_RETURN(w_facts->chroma_offset[i][j], -128, 127); } } else { for (int j = 0; j < 2; ++j) { w_facts->chroma_weight[i][j] = def_chroma_weight; w_facts->chroma_offset[i][j] = 0; } } } } return kOk; } H264Parser::Result H264Parser::ParsePredWeightTable(const H264SPS& sps, H264SliceHeader* shdr) { READ_UE_OR_RETURN(&shdr->luma_log2_weight_denom); TRUE_OR_RETURN(shdr->luma_log2_weight_denom < 8); if (sps.chroma_array_type != 0) READ_UE_OR_RETURN(&shdr->chroma_log2_weight_denom); TRUE_OR_RETURN(shdr->chroma_log2_weight_denom < 8); Result res = ParseWeightingFactors( shdr->num_ref_idx_l0_active_minus1, sps.chroma_array_type, shdr->luma_log2_weight_denom, shdr->chroma_log2_weight_denom, &shdr->pred_weight_table_l0); if (res != kOk) return res; if (shdr->IsBSlice()) { res = ParseWeightingFactors( shdr->num_ref_idx_l1_active_minus1, sps.chroma_array_type, shdr->luma_log2_weight_denom, shdr->chroma_log2_weight_denom, &shdr->pred_weight_table_l1); if (res != kOk) return res; } return kOk; } H264Parser::Result H264Parser::ParseDecRefPicMarking(H264SliceHeader* shdr) { size_t bits_left_at_start = br_.NumBitsLeft(); if (shdr->idr_pic_flag) { READ_BOOL_OR_RETURN(&shdr->no_output_of_prior_pics_flag); READ_BOOL_OR_RETURN(&shdr->long_term_reference_flag); } else { READ_BOOL_OR_RETURN(&shdr->adaptive_ref_pic_marking_mode_flag); H264DecRefPicMarking* marking; if (shdr->adaptive_ref_pic_marking_mode_flag) { size_t i; for (i = 0; i < std::size(shdr->ref_pic_marking); ++i) { marking = &shdr->ref_pic_marking[i]; READ_UE_OR_RETURN(&marking->memory_mgmnt_control_operation); if (marking->memory_mgmnt_control_operation == 0) break; if (marking->memory_mgmnt_control_operation == 1 || marking->memory_mgmnt_control_operation == 3) READ_UE_OR_RETURN(&marking->difference_of_pic_nums_minus1); if (marking->memory_mgmnt_control_operation == 2) READ_UE_OR_RETURN(&marking->long_term_pic_num); if (marking->memory_mgmnt_control_operation == 3 || marking->memory_mgmnt_control_operation == 6) READ_UE_OR_RETURN(&marking->long_term_frame_idx); if (marking->memory_mgmnt_control_operation == 4) READ_UE_OR_RETURN(&marking->max_long_term_frame_idx_plus1); if (marking->memory_mgmnt_control_operation > 6) return kInvalidStream; } if (i == std::size(shdr->ref_pic_marking)) { DVLOG(1) << "Ran out of dec ref pic marking fields"; return kUnsupportedStream; } } } shdr->dec_ref_pic_marking_bit_size = bits_left_at_start - br_.NumBitsLeft(); return kOk; } H264Parser::Result H264Parser::ParseSliceHeader(const H264NALU& nalu, H264SliceHeader* shdr) { // See 7.4.3. const H264SPS* sps; const H264PPS* pps; Result res; memset(shdr, 0, sizeof(*shdr)); shdr->idr_pic_flag = (nalu.nal_unit_type == 5); shdr->nal_ref_idc = nalu.nal_ref_idc; shdr->nalu_data = nalu.data; shdr->nalu_size = nalu.size; READ_UE_OR_RETURN(&shdr->first_mb_in_slice); READ_UE_OR_RETURN(&shdr->slice_type); TRUE_OR_RETURN(shdr->slice_type < 10); READ_UE_OR_RETURN(&shdr->pic_parameter_set_id); pps = GetPPS(shdr->pic_parameter_set_id); TRUE_OR_RETURN(pps); sps = GetSPS(pps->seq_parameter_set_id); TRUE_OR_RETURN(sps); if (sps->separate_colour_plane_flag) { DVLOG(1) << "Interlaced streams not supported"; return kUnsupportedStream; } READ_BITS_OR_RETURN(sps->log2_max_frame_num_minus4 + 4, &shdr->frame_num); if (!sps->frame_mbs_only_flag) { READ_BOOL_OR_RETURN(&shdr->field_pic_flag); if (shdr->field_pic_flag) { DVLOG(1) << "Interlaced streams not supported"; return kUnsupportedStream; } } if (shdr->idr_pic_flag) READ_UE_OR_RETURN(&shdr->idr_pic_id); size_t bits_left_at_pic_order_cnt_start = br_.NumBitsLeft(); if (sps->pic_order_cnt_type == 0) { READ_BITS_OR_RETURN(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, &shdr->pic_order_cnt_lsb); if (pps->bottom_field_pic_order_in_frame_present_flag && !shdr->field_pic_flag) READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt_bottom); } if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) { READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt0); if (pps->bottom_field_pic_order_in_frame_present_flag && !shdr->field_pic_flag) READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt1); } shdr->pic_order_cnt_bit_size = bits_left_at_pic_order_cnt_start - br_.NumBitsLeft(); if (pps->redundant_pic_cnt_present_flag) { READ_UE_OR_RETURN(&shdr->redundant_pic_cnt); TRUE_OR_RETURN(shdr->redundant_pic_cnt < 128); } if (shdr->IsBSlice()) READ_BOOL_OR_RETURN(&shdr->direct_spatial_mv_pred_flag); if (shdr->IsPSlice() || shdr->IsSPSlice() || shdr->IsBSlice()) { READ_BOOL_OR_RETURN(&shdr->num_ref_idx_active_override_flag); if (shdr->num_ref_idx_active_override_flag) { READ_UE_OR_RETURN(&shdr->num_ref_idx_l0_active_minus1); if (shdr->IsBSlice()) READ_UE_OR_RETURN(&shdr->num_ref_idx_l1_active_minus1); } else { shdr->num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_default_active_minus1; if (shdr->IsBSlice()) { shdr->num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_default_active_minus1; } } } if (shdr->field_pic_flag) { TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 32); TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 32); } else { TRUE_OR_RETURN(shdr->num_ref_idx_l0_active_minus1 < 16); TRUE_OR_RETURN(shdr->num_ref_idx_l1_active_minus1 < 16); } if (nalu.nal_unit_type == H264NALU::kCodedSliceExtension) { return kUnsupportedStream; } else { res = ParseRefPicListModifications(shdr); if (res != kOk) return res; } if ((pps->weighted_pred_flag && (shdr->IsPSlice() || shdr->IsSPSlice())) || (pps->weighted_bipred_idc == 1 && shdr->IsBSlice())) { res = ParsePredWeightTable(*sps, shdr); if (res != kOk) return res; } if (nalu.nal_ref_idc != 0) { res = ParseDecRefPicMarking(shdr); if (res != kOk) return res; } if (pps->entropy_coding_mode_flag && !shdr->IsISlice() && !shdr->IsSISlice()) { READ_UE_OR_RETURN(&shdr->cabac_init_idc); TRUE_OR_RETURN(shdr->cabac_init_idc < 3); } READ_SE_OR_RETURN(&shdr->slice_qp_delta); if (shdr->IsSPSlice() || shdr->IsSISlice()) { if (shdr->IsSPSlice()) READ_BOOL_OR_RETURN(&shdr->sp_for_switch_flag); READ_SE_OR_RETURN(&shdr->slice_qs_delta); } if (pps->deblocking_filter_control_present_flag) { READ_UE_OR_RETURN(&shdr->disable_deblocking_filter_idc); TRUE_OR_RETURN(shdr->disable_deblocking_filter_idc < 3); if (shdr->disable_deblocking_filter_idc != 1) { READ_SE_OR_RETURN(&shdr->slice_alpha_c0_offset_div2); IN_RANGE_OR_RETURN(shdr->slice_alpha_c0_offset_div2, -6, 6); READ_SE_OR_RETURN(&shdr->slice_beta_offset_div2); IN_RANGE_OR_RETURN(shdr->slice_beta_offset_div2, -6, 6); } } if (pps->num_slice_groups_minus1 > 0) { DVLOG(1) << "Slice groups not supported"; return kUnsupportedStream; } size_t epb = br_.NumEmulationPreventionBytesRead(); shdr->header_bit_size = (shdr->nalu_size - epb) * 8 - br_.NumBitsLeft(); return kOk; } H264Parser::Result H264Parser::ParseSEI(H264SEIMessage* sei_msg) { int byte; memset(sei_msg, 0, sizeof(*sei_msg)); READ_BITS_OR_RETURN(8, &byte); while (byte == 0xff) { sei_msg->type += 255; READ_BITS_OR_RETURN(8, &byte); } sei_msg->type += byte; READ_BITS_OR_RETURN(8, &byte); while (byte == 0xff) { sei_msg->payload_size += 255; READ_BITS_OR_RETURN(8, &byte); } sei_msg->payload_size += byte; DVLOG(4) << "Found SEI message type: " << sei_msg->type << " payload size: " << sei_msg->payload_size; switch (sei_msg->type) { case H264SEIMessage::kSEIRecoveryPoint: READ_UE_OR_RETURN(&sei_msg->recovery_point.recovery_frame_cnt); READ_BOOL_OR_RETURN(&sei_msg->recovery_point.exact_match_flag); READ_BOOL_OR_RETURN(&sei_msg->recovery_point.broken_link_flag); READ_BITS_OR_RETURN(2, &sei_msg->recovery_point.changing_slice_group_idc); break; default: DVLOG(4) << "Unsupported SEI message"; break; } return kOk; } std::vector H264Parser::GetCurrentSubsamples() { DCHECK_EQ(previous_nalu_range_.size(), 1u) << "This should only be called after a " "successful call to AdvanceToNextNalu()"; auto intersection = encrypted_ranges_.IntersectionWith(previous_nalu_range_); return EncryptedRangesToSubsampleEntry( previous_nalu_range_.start(0), previous_nalu_range_.end(0), intersection); } } // namespace media