aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPin-chih Lin <johnylin@google.com>2017-05-18 21:05:28 +0800
committerPin-chih Lin <johnylin@google.com>2017-05-19 12:37:18 +0800
commitf2604506384a4402de1fd5b8e32844663585d0bd (patch)
tree030ec5c03f6443761599a3b411b1652f4a268a15
parent46e08ebfc9611aea5f4910e322e98d12f24e0681 (diff)
downloadv4l2_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.mk5
-rw-r--r--vda/h264_parser.cc1382
-rw-r--r--vda/h264_parser.h502
-rw-r--r--vda/ranges.cc15
-rw-r--r--vda/ranges.h162
-rw-r--r--vda/size.h60
-rw-r--r--vda/subsample_entry.h31
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_