diff options
author | Pin-chih Lin <johnylin@google.com> | 2017-05-18 21:05:28 +0800 |
---|---|---|
committer | Pin-chih Lin <johnylin@google.com> | 2017-05-19 12:37:18 +0800 |
commit | f2604506384a4402de1fd5b8e32844663585d0bd (patch) | |
tree | 030ec5c03f6443761599a3b411b1652f4a268a15 | |
parent | 46e08ebfc9611aea5f4910e322e98d12f24e0681 (diff) | |
download | v4l2_codec2-f2604506384a4402de1fd5b8e32844663585d0bd.tar.gz |
Port h264_parser from Chromium
Slight modifications
file: src/media/filters/h264_parser.*
src/media/base/ranges.*
src/media/base/subsample_entry.h
commit head: 8522682ae8653566035cd5dd41ebd2679e5aaa2b
size.h is implemented to replace gfx::size usage of original code.
Bug: 32691050
Test: mmm external/v4l2_codec2
Change-Id: I02590798c81d73743fec66e7ca67180fc80a6acf
-rw-r--r-- | vda/Android.mk | 5 | ||||
-rw-r--r-- | vda/h264_parser.cc | 1382 | ||||
-rw-r--r-- | vda/h264_parser.h | 502 | ||||
-rw-r--r-- | vda/ranges.cc | 15 | ||||
-rw-r--r-- | vda/ranges.h | 162 | ||||
-rw-r--r-- | vda/size.h | 60 | ||||
-rw-r--r-- | vda/subsample_entry.h | 31 |
7 files changed, 2156 insertions, 1 deletions
diff --git a/vda/Android.mk b/vda/Android.mk index 35ff999..48e7f3d 100644 --- a/vda/Android.mk +++ b/vda/Android.mk @@ -4,6 +4,8 @@ include $(CLEAR_VARS) LOCAL_CPP_EXTENSION:= .cc LOCAL_SRC_FILES:= \ h264_bit_reader.cc \ + h264_parser.cc \ + ranges.cc \ LOCAL_C_INCLUDES += \ $(TOP)/external/libchrome \ @@ -12,7 +14,8 @@ LOCAL_MODULE:= libv4l2_codec2_vda LOCAL_SHARED_LIBRARIES := libchrome \ -LOCAL_CFLAGS += -Werror -Wall +# -Wno-unused-parameter is needed for libchrome/base codes +LOCAL_CFLAGS += -Werror -Wall -Wno-unused-parameter LOCAL_CLANG := true LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow diff --git a/vda/h264_parser.cc b/vda/h264_parser.cc new file mode 100644 index 0000000..0f37924 --- /dev/null +++ b/vda/h264_parser.cc @@ -0,0 +1,1382 @@ +// 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 "h264_parser.h" + +#include <limits> +#include <memory> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/numerics/safe_math.h" + +namespace media { + +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)); +} + +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. +base::Optional<Size> 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<int>::max() / mb_unit - 1; + int max_map_units_minus1 = std::numeric_limits<int>::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 base::nullopt; + } + + return Size(mb_unit * (pic_width_in_mbs_minus1 + 1), + map_unit * (pic_height_in_map_units_minus1 + 1)); +} + +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(arraysize(kTableSarWidth) == arraysize(kTableSarHeight), + "sar tables must have the same size"); + +H264Parser::H264Parser() { + Reset(); +} + +H264Parser::~H264Parser() { +} + +void H264Parser::Reset() { + stream_ = NULL; + bytes_left_ = 0; + encrypted_ranges_.clear(); +} + +void H264Parser::SetStream(const uint8_t* stream, off_t stream_size) { + std::vector<SubsampleEntry> subsamples; + SetEncryptedStream(stream, stream_size, subsamples); +} + +void H264Parser::SetEncryptedStream( + const uint8_t* stream, + off_t stream_size, + const std::vector<SubsampleEntry>& subsamples) { + DCHECK(stream); + DCHECK_GT(stream_size, 0); + + stream_ = stream; + bytes_left_ = stream_size; + + 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) { + 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; +} + +bool H264Parser::FindStartCodeInClearRanges( + const uint8_t* data, + off_t data_size, + const Ranges<const uint8_t*>& 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<const uint8_t*> 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; +} + +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_; + 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)) + 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<int>(nalu->nal_unit_type) + << " at: " << reinterpret_cast<const void*>(nalu->data) + << " size: " << nalu->size + << " ref: " << static_cast<int>(nalu->nal_ref_idc); + + 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(arraysize(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(arraysize(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(arraysize(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(arraysize(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 = arraysize(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<H264SPS> 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<int> 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<H264PPS> 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); + + if (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::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 < arraysize(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 == arraysize(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; +} + +} // namespace media diff --git a/vda/h264_parser.h b/vda/h264_parser.h new file mode 100644 index 0000000..fdd3f77 --- /dev/null +++ b/vda/h264_parser.h @@ -0,0 +1,502 @@ +// 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. +// +// This file contains an implementation of an H264 Annex-B video stream parser. + +#ifndef H264_PARSER_H_ +#define H264_PARSER_H_ + +#include <stddef.h> +#include <stdint.h> +#include <sys/types.h> + +#include <map> +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/optional.h" +#include "h264_bit_reader.h" +#include "ranges.h" +#include "size.h" +#include "subsample_entry.h" + +namespace media { + +struct SubsampleEntry; + +// For explanations of each struct and its members, see H.264 specification +// at http://www.itu.int/rec/T-REC-H.264. +struct H264NALU { + H264NALU(); + + enum Type { + kUnspecified = 0, + kNonIDRSlice = 1, + kSliceDataA = 2, + kSliceDataB = 3, + kSliceDataC = 4, + kIDRSlice = 5, + kSEIMessage = 6, + kSPS = 7, + kPPS = 8, + kAUD = 9, + kEOSeq = 10, + kEOStream = 11, + kFiller = 12, + kSPSExt = 13, + kReserved14 = 14, + kReserved15 = 15, + kReserved16 = 16, + kReserved17 = 17, + kReserved18 = 18, + kCodedSliceAux = 19, + kCodedSliceExtension = 20, + }; + + // After (without) start code; we don't own the underlying memory + // and a shallow copy should be made when copying this struct. + const uint8_t* data; + off_t size; // From after start code to start code of next NALU (or EOS). + + int nal_ref_idc; + int nal_unit_type; +}; + +enum { + kH264ScalingList4x4Length = 16, + kH264ScalingList8x8Length = 64, +}; + +struct H264SPS { + H264SPS(); + + enum H264ProfileIDC { + kProfileIDCBaseline = 66, + kProfileIDCConstrainedBaseline = kProfileIDCBaseline, + kProfileIDCMain = 77, + kProfileIDScalableBaseline = 83, + kProfileIDScalableHigh = 86, + kProfileIDCHigh = 100, + kProfileIDHigh10 = 110, + kProfileIDSMultiviewHigh = 118, + kProfileIDHigh422 = 122, + kProfileIDStereoHigh = 128, + kProfileIDHigh444Predictive = 244, + }; + + enum AspectRatioIdc { + kExtendedSar = 255, + }; + + enum { + // Constants for HRD parameters (spec ch. E.2.2). + kBitRateScaleConstantTerm = 6, // Equation E-37. + kCPBSizeScaleConstantTerm = 4, // Equation E-38. + kDefaultInitialCPBRemovalDelayLength = 24, + kDefaultDPBOutputDelayLength = 24, + kDefaultTimeOffsetLength = 24, + }; + + int profile_idc; + bool constraint_set0_flag; + bool constraint_set1_flag; + bool constraint_set2_flag; + bool constraint_set3_flag; + bool constraint_set4_flag; + bool constraint_set5_flag; + int level_idc; + int seq_parameter_set_id; + + int chroma_format_idc; + bool separate_colour_plane_flag; + int bit_depth_luma_minus8; + int bit_depth_chroma_minus8; + bool qpprime_y_zero_transform_bypass_flag; + + bool seq_scaling_matrix_present_flag; + int scaling_list4x4[6][kH264ScalingList4x4Length]; + int scaling_list8x8[6][kH264ScalingList8x8Length]; + + int log2_max_frame_num_minus4; + int pic_order_cnt_type; + int log2_max_pic_order_cnt_lsb_minus4; + bool delta_pic_order_always_zero_flag; + int offset_for_non_ref_pic; + int offset_for_top_to_bottom_field; + int num_ref_frames_in_pic_order_cnt_cycle; + int expected_delta_per_pic_order_cnt_cycle; // calculated + int offset_for_ref_frame[255]; + int max_num_ref_frames; + bool gaps_in_frame_num_value_allowed_flag; + int pic_width_in_mbs_minus1; + int pic_height_in_map_units_minus1; + bool frame_mbs_only_flag; + bool mb_adaptive_frame_field_flag; + bool direct_8x8_inference_flag; + bool frame_cropping_flag; + int frame_crop_left_offset; + int frame_crop_right_offset; + int frame_crop_top_offset; + int frame_crop_bottom_offset; + + bool vui_parameters_present_flag; + int sar_width; // Set to 0 when not specified. + int sar_height; // Set to 0 when not specified. + bool bitstream_restriction_flag; + int max_num_reorder_frames; + int max_dec_frame_buffering; + bool timing_info_present_flag; + int num_units_in_tick; + int time_scale; + bool fixed_frame_rate_flag; + + bool video_signal_type_present_flag; + int video_format; + bool video_full_range_flag; + bool colour_description_present_flag; + int colour_primaries; + int transfer_characteristics; + int matrix_coefficients; + + // TODO(posciak): actually parse these instead of ParseAndIgnoreHRDParameters. + bool nal_hrd_parameters_present_flag; + int cpb_cnt_minus1; + int bit_rate_scale; + int cpb_size_scale; + int bit_rate_value_minus1[32]; + int cpb_size_value_minus1[32]; + bool cbr_flag[32]; + int initial_cpb_removal_delay_length_minus_1; + int cpb_removal_delay_length_minus1; + int dpb_output_delay_length_minus1; + int time_offset_length; + + bool low_delay_hrd_flag; + + int chroma_array_type; + + // Helpers to compute frequently-used values. These methods return + // base::nullopt if they encounter integer overflow. They do not verify that + // the results are in-spec for the given profile or level. + base::Optional<Size> GetCodedSize() const; +}; + +struct H264PPS { + H264PPS(); + + int pic_parameter_set_id; + int seq_parameter_set_id; + bool entropy_coding_mode_flag; + bool bottom_field_pic_order_in_frame_present_flag; + int num_slice_groups_minus1; + // TODO(posciak): Slice groups not implemented, could be added at some point. + int num_ref_idx_l0_default_active_minus1; + int num_ref_idx_l1_default_active_minus1; + bool weighted_pred_flag; + int weighted_bipred_idc; + int pic_init_qp_minus26; + int pic_init_qs_minus26; + int chroma_qp_index_offset; + bool deblocking_filter_control_present_flag; + bool constrained_intra_pred_flag; + bool redundant_pic_cnt_present_flag; + bool transform_8x8_mode_flag; + + bool pic_scaling_matrix_present_flag; + int scaling_list4x4[6][kH264ScalingList4x4Length]; + int scaling_list8x8[6][kH264ScalingList8x8Length]; + + int second_chroma_qp_index_offset; +}; + +struct H264ModificationOfPicNum { + int modification_of_pic_nums_idc; + union { + int abs_diff_pic_num_minus1; + int long_term_pic_num; + }; +}; + +struct H264WeightingFactors { + bool luma_weight_flag; + bool chroma_weight_flag; + int luma_weight[32]; + int luma_offset[32]; + int chroma_weight[32][2]; + int chroma_offset[32][2]; +}; + +struct H264DecRefPicMarking { + int memory_mgmnt_control_operation; + int difference_of_pic_nums_minus1; + int long_term_pic_num; + int long_term_frame_idx; + int max_long_term_frame_idx_plus1; +}; + +struct H264SliceHeader { + H264SliceHeader(); + + enum { + kRefListSize = 32, + kRefListModSize = kRefListSize + }; + + enum Type { + kPSlice = 0, + kBSlice = 1, + kISlice = 2, + kSPSlice = 3, + kSISlice = 4, + }; + + bool IsPSlice() const; + bool IsBSlice() const; + bool IsISlice() const; + bool IsSPSlice() const; + bool IsSISlice() const; + + bool idr_pic_flag; // from NAL header + int nal_ref_idc; // from NAL header + const uint8_t* nalu_data; // from NAL header + off_t nalu_size; // from NAL header + off_t header_bit_size; // calculated + + int first_mb_in_slice; + int slice_type; + int pic_parameter_set_id; + int colour_plane_id; // TODO(posciak): use this! http://crbug.com/139878 + int frame_num; + bool field_pic_flag; + bool bottom_field_flag; + int idr_pic_id; + int pic_order_cnt_lsb; + int delta_pic_order_cnt_bottom; + int delta_pic_order_cnt0; + int delta_pic_order_cnt1; + int redundant_pic_cnt; + bool direct_spatial_mv_pred_flag; + + bool num_ref_idx_active_override_flag; + int num_ref_idx_l0_active_minus1; + int num_ref_idx_l1_active_minus1; + bool ref_pic_list_modification_flag_l0; + bool ref_pic_list_modification_flag_l1; + H264ModificationOfPicNum ref_list_l0_modifications[kRefListModSize]; + H264ModificationOfPicNum ref_list_l1_modifications[kRefListModSize]; + + int luma_log2_weight_denom; + int chroma_log2_weight_denom; + + bool luma_weight_l0_flag; + bool chroma_weight_l0_flag; + H264WeightingFactors pred_weight_table_l0; + + bool luma_weight_l1_flag; + bool chroma_weight_l1_flag; + H264WeightingFactors pred_weight_table_l1; + + bool no_output_of_prior_pics_flag; + bool long_term_reference_flag; + + bool adaptive_ref_pic_marking_mode_flag; + H264DecRefPicMarking ref_pic_marking[kRefListSize]; + + int cabac_init_idc; + int slice_qp_delta; + bool sp_for_switch_flag; + int slice_qs_delta; + int disable_deblocking_filter_idc; + int slice_alpha_c0_offset_div2; + int slice_beta_offset_div2; + + // Calculated. + // Size in bits of dec_ref_pic_marking() syntax element. + size_t dec_ref_pic_marking_bit_size; + size_t pic_order_cnt_bit_size; +}; + +struct H264SEIRecoveryPoint { + int recovery_frame_cnt; + bool exact_match_flag; + bool broken_link_flag; + int changing_slice_group_idc; +}; + +struct H264SEIMessage { + H264SEIMessage(); + + enum Type { + kSEIRecoveryPoint = 6, + }; + + int type; + int payload_size; + union { + // Placeholder; in future more supported types will contribute to more + // union members here. + H264SEIRecoveryPoint recovery_point; + }; +}; + +// Class to parse an Annex-B H.264 stream, +// as specified in chapters 7 and Annex B of the H.264 spec. +class H264Parser { + public: + enum Result { + kOk, + kInvalidStream, // error in stream + kUnsupportedStream, // stream not supported by the parser + kEOStream, // end of stream + }; + + // Find offset from start of data to next NALU start code + // and size of found start code (3 or 4 bytes). + // If no start code is found, offset is pointing to the first unprocessed byte + // (i.e. the first byte that was not considered as a possible start of a start + // code) and |*start_code_size| is set to 0. + // Preconditions: + // - |data_size| >= 0 + // Postconditions: + // - |*offset| is between 0 and |data_size| included. + // It is strictly less than |data_size| if |data_size| > 0. + // - |*start_code_size| is either 0, 3 or 4. + static bool FindStartCode(const uint8_t* data, + off_t data_size, + off_t* offset, + off_t* start_code_size); + + // Wrapper for FindStartCode() that skips over start codes that + // may appear inside of |encrypted_ranges_|. + // Returns true if a start code was found. Otherwise returns false. + static bool FindStartCodeInClearRanges(const uint8_t* data, + off_t data_size, + const Ranges<const uint8_t*>& ranges, + off_t* offset, + off_t* start_code_size); + + H264Parser(); + ~H264Parser(); + + void Reset(); + // Set current stream pointer to |stream| of |stream_size| in bytes, + // |stream| owned by caller. + // |subsamples| contains information about what parts of |stream| are + // encrypted. + void SetStream(const uint8_t* stream, off_t stream_size); + void SetEncryptedStream(const uint8_t* stream, + off_t stream_size, + const std::vector<SubsampleEntry>& subsamples); + + // Read the stream to find the next NALU, identify it and return + // that information in |*nalu|. This advances the stream to the beginning + // of this NALU, but not past it, so subsequent calls to NALU-specific + // parsing functions (ParseSPS, etc.) will parse this NALU. + // If the caller wishes to skip the current NALU, it can call this function + // again, instead of any NALU-type specific parse functions below. + Result AdvanceToNextNALU(H264NALU* nalu); + + // NALU-specific parsing functions. + // These should be called after AdvanceToNextNALU(). + + // SPSes and PPSes are owned by the parser class and the memory for their + // structures is managed here, not by the caller, as they are reused + // across NALUs. + // + // Parse an SPS/PPS NALU and save their data in the parser, returning id + // of the parsed structure in |*pps_id|/|*sps_id|. + // To get a pointer to a given SPS/PPS structure, use GetSPS()/GetPPS(), + // passing the returned |*sps_id|/|*pps_id| as parameter. + // TODO(posciak,fischman): consider replacing returning Result from Parse*() + // methods with a scoped_ptr and adding an AtEOS() function to check for EOS + // if Parse*() return NULL. + Result ParseSPS(int* sps_id); + Result ParsePPS(int* pps_id); + + // Return a pointer to SPS/PPS with given |sps_id|/|pps_id| or NULL if not + // present. + const H264SPS* GetSPS(int sps_id) const; + const H264PPS* GetPPS(int pps_id) const; + + // Slice headers and SEI messages are not used across NALUs by the parser + // and can be discarded after current NALU, so the parser does not store + // them, nor does it manage their memory. + // The caller has to provide and manage it instead. + + // Parse a slice header, returning it in |*shdr|. |*nalu| must be set to + // the NALU returned from AdvanceToNextNALU() and corresponding to |*shdr|. + Result ParseSliceHeader(const H264NALU& nalu, H264SliceHeader* shdr); + + // Parse a SEI message, returning it in |*sei_msg|, provided and managed + // by the caller. + Result ParseSEI(H264SEIMessage* sei_msg); + + private: + // Move the stream pointer to the beginning of the next NALU, + // i.e. pointing at the next start code. + // Return true if a NALU has been found. + // If a NALU is found: + // - its size in bytes is returned in |*nalu_size| and includes + // the start code as well as the trailing zero bits. + // - the size in bytes of the start code is returned in |*start_code_size|. + bool LocateNALU(off_t* nalu_size, off_t* start_code_size); + + // Exp-Golomb code parsing as specified in chapter 9.1 of the spec. + // Read one unsigned exp-Golomb code from the stream and return in |*val|. + Result ReadUE(int* val); + + // Read one signed exp-Golomb code from the stream and return in |*val|. + Result ReadSE(int* val); + + // Parse scaling lists (see spec). + Result ParseScalingList(int size, int* scaling_list, bool* use_default); + Result ParseSPSScalingLists(H264SPS* sps); + Result ParsePPSScalingLists(const H264SPS& sps, H264PPS* pps); + + // Parse optional VUI parameters in SPS (see spec). + Result ParseVUIParameters(H264SPS* sps); + // Set |hrd_parameters_present| to true only if they are present. + Result ParseAndIgnoreHRDParameters(bool* hrd_parameters_present); + + // Parse reference picture lists' modifications (see spec). + Result ParseRefPicListModifications(H264SliceHeader* shdr); + Result ParseRefPicListModification(int num_ref_idx_active_minus1, + H264ModificationOfPicNum* ref_list_mods); + + // Parse prediction weight table (see spec). + Result ParsePredWeightTable(const H264SPS& sps, H264SliceHeader* shdr); + + // Parse weighting factors (see spec). + Result ParseWeightingFactors(int num_ref_idx_active_minus1, + int chroma_array_type, + int luma_log2_weight_denom, + int chroma_log2_weight_denom, + H264WeightingFactors* w_facts); + + // Parse decoded reference picture marking information (see spec). + Result ParseDecRefPicMarking(H264SliceHeader* shdr); + + // Pointer to the current NALU in the stream. + const uint8_t* stream_; + + // Bytes left in the stream after the current NALU. + off_t bytes_left_; + + H264BitReader br_; + + // PPSes and SPSes stored for future reference. + std::map<int, std::unique_ptr<H264SPS>> active_SPSes_; + std::map<int, std::unique_ptr<H264PPS>> active_PPSes_; + + // Ranges of encrypted bytes in the buffer passed to + // SetEncryptedStream(). + Ranges<const uint8_t*> encrypted_ranges_; + + DISALLOW_COPY_AND_ASSIGN(H264Parser); +}; + +} // namespace media + +#endif // H264_PARSER_H_ diff --git a/vda/ranges.cc b/vda/ranges.cc new file mode 100644 index 0000000..00400b5 --- /dev/null +++ b/vda/ranges.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2012 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 "ranges.h" + +namespace media { + +template<> +void Ranges<base::TimeDelta>::DCheckLT(const base::TimeDelta& lhs, + const base::TimeDelta& rhs) const { + DCHECK(lhs < rhs) << lhs.ToInternalValue() << " < " << rhs.ToInternalValue(); +} + +} // namespace media diff --git a/vda/ranges.h b/vda/ranges.h new file mode 100644 index 0000000..98b32ce --- /dev/null +++ b/vda/ranges.h @@ -0,0 +1,162 @@ +// Copyright (c) 2012 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. + +#ifndef RANGES_H_ +#define RANGES_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <algorithm> +#include <ostream> +#include <vector> + +#include "base/logging.h" +#include "base/time/time.h" + +namespace media { + +// Ranges allows holding an ordered list of ranges of [start,end) intervals. +// The canonical example use-case is holding the list of ranges of buffered +// bytes or times in a <video> tag. +template <class T> // Endpoint type; typically a base::TimeDelta or an int64_t. +class Ranges { + public: + // Allow copy & assign. + + // Add (start,end) to this object, coallescing overlaps as appropriate. + // Returns the number of stored ranges, post coallescing. + size_t Add(T start, T end); + + // Return the number of disjoint ranges. + size_t size() const; + + // Return the "i"'th range's start & end (0-based). + T start(size_t i) const; + T end(size_t i) const; + + // Clear all ranges. + void clear(); + + // Computes the intersection between this range and |other|. + Ranges<T> IntersectionWith(const Ranges<T>& other) const; + + private: + // Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's. + void DCheckLT(const T& lhs, const T& rhs) const; + + // Disjoint, in increasing order of start. + std::vector<std::pair<T, T> > ranges_; +}; + +////////////////////////////////////////////////////////////////////// +// EVERYTHING BELOW HERE IS IMPLEMENTATION DETAIL!! +////////////////////////////////////////////////////////////////////// + +template<class T> +size_t Ranges<T>::Add(T start, T end) { + if (start == end) // Nothing to be done with empty ranges. + return ranges_.size(); + + DCheckLT(start, end); + size_t i; + // Walk along the array of ranges until |start| is no longer larger than the + // current interval's end. + for (i = 0; i < ranges_.size() && ranges_[i].second < start; ++i) { + // Empty body + } + + // Now we know |start| belongs in the i'th slot. + // If i is the end of the range, append new range and done. + if (i == ranges_.size()) { + ranges_.push_back(std::make_pair(start, end)); + return ranges_.size(); + } + + // If |end| is less than i->first, then [start,end) is a new (non-overlapping) + // i'th entry pushing everyone else back, and done. + if (end < ranges_[i].first) { + ranges_.insert(ranges_.begin() + i, std::make_pair(start, end)); + return ranges_.size(); + } + + // Easy cases done. Getting here means there is overlap between [start,end) + // and the existing ranges. + + // Now: start <= i->second && i->first <= end + if (start < ranges_[i].first) + ranges_[i].first = start; + if (ranges_[i].second < end) + ranges_[i].second = end; + + // Now: [start,end) is contained in the i'th range, and we'd be done, except + // for the fact that the newly-extended i'th range might now overlap + // subsequent ranges. Merge until discontinuities appear. Note that there's + // no need to test/merge previous ranges, since needing that would mean the + // original loop went too far. + while ((i + 1) < ranges_.size() && + ranges_[i + 1].first <= ranges_[i].second) { + ranges_[i].second = std::max(ranges_[i].second, ranges_[i + 1].second); + ranges_.erase(ranges_.begin() + i + 1); + } + + return ranges_.size(); +} + +template<> +void Ranges<base::TimeDelta>::DCheckLT(const base::TimeDelta& lhs, + const base::TimeDelta& rhs) const; + +template<class T> +void Ranges<T>::DCheckLT(const T& lhs, const T& rhs) const { + DCHECK_LT(lhs, rhs); +} + +template<class T> +size_t Ranges<T>::size() const { + return ranges_.size(); +} + +template<class T> +T Ranges<T>::start(size_t i) const { + return ranges_[i].first; +} + +template<class T> +T Ranges<T>::end(size_t i) const { + return ranges_[i].second; +} + +template<class T> +void Ranges<T>::clear() { + ranges_.clear(); +} + +template<class T> +Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) const { + Ranges<T> result; + + size_t i = 0; + size_t j = 0; + + while (i < size() && j < other.size()) { + T max_start = std::max(start(i), other.start(j)); + T min_end = std::min(end(i), other.end(j)); + + // Add an intersection range to the result if the ranges overlap. + if (max_start < min_end) + result.Add(max_start, min_end); + + if (end(i) < other.end(j)) + ++i; + else + ++j; + } + + return result; +} + +} // namespace media + +#endif // RANGES_H_ diff --git a/vda/size.h b/vda/size.h new file mode 100644 index 0000000..4806ddc --- /dev/null +++ b/vda/size.h @@ -0,0 +1,60 @@ +// Copyright 2017 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. + +#ifndef SIZE_H_ +#define SIZE_H_ + +#include <string> + +#include "base/strings/stringprintf.h" + +namespace media { + +// Helper struct for size to replace gfx::size usage from original code. +// Only partial functions of gfx::size is implemented here. +struct Size { + public: + Size() : width_(0), height_(0) {} + Size(int width, int height) + : width_(width < 0 ? 0 : width), height_(height < 0 ? 0 : height) {} + + constexpr int width() const { return width_; } + constexpr int height() const { return height_; } + + void set_width(int width) { width_ = width < 0 ? 0 : width; } + void set_height(int height) { height_ = height < 0 ? 0 : height; } + + void SetSize(int width, int height) { + set_width(width); + set_height(height); + } + + bool IsEmpty() const { return !width() || !height(); } + + std::string ToString() const { + return base::StringPrintf("%dx%d", width(), height()); + } + + Size& operator=(const Size& ps) { + set_width(ps.width()); + set_height(ps.height()); + return *this; + } + + private: + int width_; + int height_; +}; + +inline bool operator==(const Size& lhs, const Size& rhs) { + return lhs.width() == rhs.width() && lhs.height() == rhs.height(); +} + +inline bool operator!=(const Size& lhs, const Size& rhs) { + return !(lhs == rhs); +} + +} // namespace media + +#endif // SIZE_H_ diff --git a/vda/subsample_entry.h b/vda/subsample_entry.h new file mode 100644 index 0000000..e7529fb --- /dev/null +++ b/vda/subsample_entry.h @@ -0,0 +1,31 @@ +// Copyright 2016 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. + +#ifndef SUBSAMPLE_ENTRY_H_ +#define SUBSAMPLE_ENTRY_H_ + +#include <stdint.h> + +namespace media { + +// The Common Encryption spec provides for subsample encryption, where portions +// of a sample are set in cleartext. A SubsampleEntry specifies the number of +// clear and encrypted bytes in each subsample. For decryption, all of the +// encrypted bytes in a sample should be considered a single logical stream, +// regardless of how they are divided into subsamples, and the clear bytes +// should not be considered as part of decryption. This is logically equivalent +// to concatenating all 'cypher_bytes' portions of subsamples, decrypting that +// result, and then copying each byte from the decrypted block over the +// position of the corresponding encrypted byte. +struct SubsampleEntry { + SubsampleEntry() : clear_bytes(0), cypher_bytes(0) {} + SubsampleEntry(uint32_t clear_bytes, uint32_t cypher_bytes) + : clear_bytes(clear_bytes), cypher_bytes(cypher_bytes) {} + uint32_t clear_bytes; + uint32_t cypher_bytes; +}; + +} // namespace media + +#endif // SUBSAMPLE_ENTRY_H_ |