summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/gpu/media/h264_parser.cc1187
-rw-r--r--content/common/gpu/media/h264_parser.h406
-rw-r--r--content/common/gpu/media/h264_parser_unittest.cc94
-rw-r--r--content/content_common.gypi6
-rw-r--r--content/content_tests.gypi18
5 files changed, 1709 insertions, 2 deletions
diff --git a/content/common/gpu/media/h264_parser.cc b/content/common/gpu/media/h264_parser.cc
new file mode 100644
index 0000000..fe5e41d
--- /dev/null
+++ b/content/common/gpu/media/h264_parser.cc
@@ -0,0 +1,1187 @@
+// 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 "base/logging.h"
+#include "base/stl_util.h"
+#include "content/common/gpu/media/h264_parser.h"
+
+namespace content {
+
+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));
+}
+
+H264PPS::H264PPS() {
+ memset(this, 0, sizeof(*this));
+}
+
+H264SliceHeader::H264SliceHeader() {
+ memset(this, 0, sizeof(*this));
+}
+
+H264SEIMessage::H264SEIMessage() {
+ memset(this, 0, sizeof(*this));
+}
+
+H264Parser::H264BitReader::H264BitReader()
+ : data_(NULL),
+ bytes_left_(0),
+ curr_byte_(0),
+ num_remaining_bits_in_curr_byte_(0) {
+}
+
+H264Parser::H264BitReader::~H264BitReader() {}
+
+bool H264Parser::H264BitReader::Initialize(const uint8* data, off_t size) {
+ DCHECK(data);
+
+ if (size < 1)
+ return false;
+
+ data_ = data;
+ bytes_left_ = size;
+ num_remaining_bits_in_curr_byte_ = 0;
+ // Initially set to 0xffff to accept all initial two-byte sequences.
+ prev_two_bytes_ = 0xffff;
+
+ return true;
+}
+
+bool H264Parser::H264BitReader::UpdateCurrByte() {
+ if (bytes_left_ < 1)
+ return false;
+
+ // Emulation prevention three-byte detection.
+ // If a sequence of 0x000003 is found, skip (ignore) the last byte (0x03).
+ if (*data_ == 0x03 && (prev_two_bytes_ & 0xffff) == 0) {
+ // Detected 0x000003, skip last byte.
+ ++data_;
+ --bytes_left_;
+ // Need another full three bytes before we can detect the sequence again.
+ prev_two_bytes_ = 0xffff;
+
+ if (bytes_left_ < 1)
+ return false;
+ }
+
+ // Load a new byte and advance pointers.
+ curr_byte_ = *data_++ & 0xff;
+ --bytes_left_;
+ num_remaining_bits_in_curr_byte_ = 8;
+
+ prev_two_bytes_ = (prev_two_bytes_ << 8) | curr_byte_;
+
+ return true;
+}
+
+// Read |num_bits| (1 to 31 inclusive) from the stream and return them
+// in |out|, with first bit in the stream as MSB in |out| at position
+// (|num_bits| - 1).
+bool H264Parser::H264BitReader::ReadBits(int num_bits, int *out) {
+ int bits_left = num_bits;
+ *out = 0;
+ DCHECK(num_bits <= 31);
+
+ while (num_remaining_bits_in_curr_byte_ < bits_left) {
+ // Take all that's left in current byte, shift to make space for the rest.
+ *out = (curr_byte_ << (bits_left - num_remaining_bits_in_curr_byte_));
+ bits_left -= num_remaining_bits_in_curr_byte_;
+
+ if (!UpdateCurrByte())
+ return false;
+ }
+
+ *out |= (curr_byte_ >> (num_remaining_bits_in_curr_byte_ - bits_left));
+ *out &= ((1 << num_bits) - 1);
+ num_remaining_bits_in_curr_byte_ -= bits_left;
+
+ return true;
+}
+
+off_t H264Parser::H264BitReader::NumBitsLeft() {
+ return (num_remaining_bits_in_curr_byte_ + bytes_left_ * 8);
+}
+
+bool H264Parser::H264BitReader::HasMoreRBSPData() {
+ // Make sure we have more bits, if we are at 0 bits in current byte
+ // and updating current byte fails, we don't have more data anyway.
+ if (num_remaining_bits_in_curr_byte_ == 0 && !UpdateCurrByte())
+ return false;
+
+ // On last byte?
+ if (bytes_left_)
+ return true;
+
+ // Last byte, look for stop bit;
+ // We have more RBSP data if the last non-zero bit we find is not the
+ // first available bit.
+ return curr_byte_ & ((1 << (num_remaining_bits_in_curr_byte_ - 1)) - 1);
+}
+
+#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_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)
+
+H264Parser::H264Parser() {
+ Reset();
+}
+
+H264Parser::~H264Parser() {
+ STLDeleteValues(&active_SPSes_);
+ STLDeleteValues(&active_PPSes_);
+}
+
+void H264Parser::Reset() {
+ stream_ = NULL;
+ bytes_left_ = 0;
+}
+
+void H264Parser::SetStream(const uint8* stream, off_t stream_size) {
+ DCHECK(stream);
+ DCHECK(stream_size > 0);
+
+ stream_ = stream;
+ bytes_left_ = stream_size;
+}
+
+const H264PPS* H264Parser::GetPPS(int pps_id) {
+ return active_PPSes_[pps_id];
+}
+
+const H264SPS* H264Parser::GetSPS(int sps_id) {
+ return active_SPSes_[sps_id];
+}
+
+static inline bool IsStartCode(const uint8* data) {
+ return data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x01;
+}
+
+// Find offset from start of data to next NALU start code
+// and size of found start code (3 or 4 bytes).
+static bool FindStartCode(const uint8* data, off_t data_size,
+ off_t* offset,
+ off_t *start_code_size) {
+ 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.
+ return false;
+}
+
+// Find the next NALU in stream, returning its start offset without the start
+// code (i.e. at the beginning of NALU data).
+// Size will include trailing zero bits, and will be from start offset to
+// before the start code of the next NALU (or end of stream).
+static bool LocateNALU(const uint8* stream, off_t stream_size,
+ off_t* nalu_start_off, off_t* nalu_size) {
+ off_t start_code_size;
+
+ // Find start code of the next NALU.
+ if (!FindStartCode(stream, stream_size, nalu_start_off, &start_code_size)) {
+ DVLOG(4) << "Could not find start code, end of stream?";
+ return false;
+ }
+
+ // Discard its start code.
+ *nalu_start_off += start_code_size;
+ // Move the stream to the beginning of it (skip the start code).
+ stream_size -= *nalu_start_off;
+ stream += *nalu_start_off;
+
+ // Find the start code of next NALU; if successful, NALU size 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 if there
+ // are still some bytes left after the first start code.
+ // nalu_size is the offset to the next start code
+ if (!FindStartCode(stream, stream_size, nalu_size, &start_code_size)) {
+ // end of stream (no next NALU), but still valid NALU if any bytes left
+ *nalu_size = stream_size;
+ if (*nalu_size < 1) {
+ DVLOG(3) << "End of stream";
+ return false;
+ }
+ }
+
+ 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.
+ *val = (1 << num_bits) - 1;
+
+ 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) {
+ int data;
+ off_t off_to_nalu_start;
+
+ if (!LocateNALU(stream_, bytes_left_, &off_to_nalu_start, &nalu->size)) {
+ DVLOG(4) << "Could not find next NALU, bytes left in stream: "
+ << bytes_left_;
+ return kEOStream;
+ }
+
+ nalu->data = stream_ + off_to_nalu_start;
+
+ // Initialize bit reader at the start of found NALU.
+ if (!br_.Initialize(nalu->data, nalu->size))
+ return kEOStream;
+
+ DVLOG(4) << "Looking for NALU, Stream bytes left: " << bytes_left_
+ << " off to next nalu: " << off_to_nalu_start;
+
+ // 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_ += off_to_nalu_start + nalu->size;
+ bytes_left_ -= off_to_nalu_start + nalu->size;
+
+ // Read NALU header, skip the forbidden_zero_bit, but check for it.
+ 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: " << (int)nalu->nal_unit_type
+ << " at: " << (void*)nalu->data << " size: " << nalu->size
+ << " ref: " << (int)nalu->nal_ref_idc;
+
+ return kOk;
+}
+
+// Default scaling lists (per spec).
+static const int kDefault4x4Intra[16] = {
+ 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, };
+
+static const int kDefault4x4Inter[16] = {
+ 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34, };
+
+static const int kDefault8x8Intra[64] = {
+ 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[64] = {
+ 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[][16]) {
+ DCHECK(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[][64]) {
+ DCHECK(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[][16]) {
+ switch (i) {
+ case 0:
+ memcpy(scaling_list4x4[i], default_scaling_list_intra,
+ sizeof(default_scaling_list_intra));
+ break;
+
+ case 1:
+ memcpy(scaling_list4x4[i], scaling_list4x4[0],
+ sizeof(scaling_list4x4[0]));
+ break;
+
+ case 2:
+ memcpy(scaling_list4x4[i], scaling_list4x4[1],
+ sizeof(scaling_list4x4[1]));
+ break;
+
+ case 3:
+ memcpy(scaling_list4x4[i], default_scaling_list_inter,
+ sizeof(default_scaling_list_inter));
+ break;
+
+ case 4:
+ memcpy(scaling_list4x4[i], scaling_list4x4[3],
+ sizeof(scaling_list4x4[3]));
+ break;
+
+ case 5:
+ memcpy(scaling_list4x4[i], scaling_list4x4[4],
+ sizeof(scaling_list4x4[4]));
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+static void FallbackScalingList8x8(int i,
+ const int default_scaling_list_intra[],
+ const int default_scaling_list_inter[],
+ int scaling_list8x8[][64]) {
+ switch (i) {
+ case 0:
+ memcpy(scaling_list8x8[i], default_scaling_list_intra,
+ sizeof(default_scaling_list_intra));
+ break;
+
+ case 1:
+ memcpy(scaling_list8x8[i], default_scaling_list_inter,
+ sizeof(default_scaling_list_inter));
+ break;
+
+ case 2:
+ memcpy(scaling_list8x8[i], scaling_list8x8[0],
+ sizeof(scaling_list8x8[0]));
+ break;
+
+ case 3:
+ memcpy(scaling_list8x8[i], scaling_list8x8[1],
+ sizeof(scaling_list8x8[1]));
+ break;
+
+ case 4:
+ memcpy(scaling_list8x8[i], scaling_list8x8[2],
+ sizeof(scaling_list8x8[2]));
+ break;
+
+ case 5:
+ memcpy(scaling_list8x8[i], scaling_list8x8[3],
+ sizeof(scaling_list8x8[3]));
+ 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_BITS_OR_RETURN(1, &seq_scaling_list_present_flag);
+
+ if (seq_scaling_list_present_flag) {
+ res = ParseScalingList(sizeof(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_BITS_OR_RETURN(1, &seq_scaling_list_present_flag);
+
+ if (seq_scaling_list_present_flag) {
+ res = ParseScalingList(sizeof(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_BITS_OR_RETURN(1, &pic_scaling_list_present_flag);
+
+ if (pic_scaling_list_present_flag) {
+ res = ParseScalingList(sizeof(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_BITS_OR_RETURN(1, &pic_scaling_list_present_flag);
+
+ if (pic_scaling_list_present_flag) {
+ res = ParseScalingList(sizeof(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;
+}
+
+static void FillDefaultSeqScalingLists(H264SPS* sps) {
+ // Assumes ints in arrays.
+ memset(sps->scaling_list4x4, 16, sizeof(sps->scaling_list4x4));
+ memset(sps->scaling_list8x8, 16, sizeof(sps->scaling_list8x8));
+}
+
+H264Parser::Result H264Parser::ParseSPS(int* sps_id) {
+ // See 7.4.2.1.
+ int data;
+ Result res;
+
+ *sps_id = -1;
+
+ scoped_ptr<H264SPS> sps(new H264SPS());
+
+ READ_BITS_OR_RETURN(8, &sps->profile_idc);
+ // Skip constraint_setx_flag and reserved flags.
+ READ_BITS_OR_RETURN(8, &data);
+ 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_BITS_OR_RETURN(1, &sps->separate_colour_plane_flag);
+
+ 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->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_BITS_OR_RETURN(1, &sps->qpprime_y_zero_transform_bypass_flag);
+ READ_BITS_OR_RETURN(1, &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());
+ }
+ }
+
+ 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);
+ } else if (sps->pic_order_cnt_type == 1) {
+ READ_BITS_OR_RETURN(1, &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);
+ 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]);
+ }
+
+ READ_UE_OR_RETURN(&sps->max_num_ref_frames);
+ READ_BITS_OR_RETURN(1, &sps->gaps_in_frame_num_value_allowed_flag);
+
+ if (sps->gaps_in_frame_num_value_allowed_flag)
+ return kUnsupportedStream;
+
+ READ_UE_OR_RETURN(&sps->pic_width_in_mbs_minus1);
+ READ_UE_OR_RETURN(&sps->pic_height_in_map_units_minus1);
+
+ READ_BITS_OR_RETURN(1, &sps->frame_mbs_only_flag);
+ if (!sps->frame_mbs_only_flag)
+ READ_BITS_OR_RETURN(1, &sps->mb_adaptive_frame_field_flag);
+
+ READ_BITS_OR_RETURN(1, &sps->direct_8x8_inference_flag);
+
+ READ_BITS_OR_RETURN(1, &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_BITS_OR_RETURN(1, &sps->vui_parameters_present_flag);
+ if (sps->vui_parameters_present_flag) {
+ DVLOG(1) << "VUI parameters present in SPS, ignoring";
+ }
+
+ // If an SPS with the same id already exists, replace it.
+ *sps_id = sps->seq_parameter_set_id;
+ delete active_SPSes_[*sps_id];
+ active_SPSes_[*sps_id] = sps.release();
+
+ return kOk;
+}
+
+H264Parser::Result H264Parser::ParsePPS(int* pps_id) {
+ // See 7.4.2.2.
+ const H264SPS* sps;
+ Result res;
+
+ *pps_id = -1;
+
+ scoped_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);
+
+ sps = GetSPS(pps->seq_parameter_set_id);
+ TRUE_OR_RETURN(sps);
+
+ READ_BITS_OR_RETURN(1, &pps->entropy_coding_mode_flag);
+ READ_BITS_OR_RETURN(1, &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_BITS_OR_RETURN(1, &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);
+
+ READ_BITS_OR_RETURN(1, &pps->deblocking_filter_control_present_flag);
+ READ_BITS_OR_RETURN(1, &pps->constrained_intra_pred_flag);
+ READ_BITS_OR_RETURN(1, &pps->redundant_pic_cnt_present_flag);
+
+ if (br_.HasMoreRBSPData()) {
+ READ_BITS_OR_RETURN(1, &pps->transform_8x8_mode_flag);
+ READ_BITS_OR_RETURN(1, &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;
+ delete active_PPSes_[*pps_id];
+ active_PPSes_[*pps_id] = pps.release();
+
+ 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_BITS_OR_RETURN(1, &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_BITS_OR_RETURN(1, &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_BITS_OR_RETURN(1, &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_BITS_OR_RETURN(1, &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) {
+
+ if (shdr->idr_pic_flag) {
+ READ_BITS_OR_RETURN(1, &shdr->no_output_of_prior_pics_flag);
+ READ_BITS_OR_RETURN(1, &shdr->long_term_reference_flag);
+ } else {
+ READ_BITS_OR_RETURN(1, &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;
+ }
+ }
+ }
+
+ 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) ? true : false);
+ 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_BITS_OR_RETURN(1, &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);
+
+ 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_cnt[0]);
+ if (pps->bottom_field_pic_order_in_frame_present_flag &&
+ !shdr->field_pic_flag)
+ READ_SE_OR_RETURN(&shdr->delta_pic_order_cnt[1]);
+ }
+
+ 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_BITS_OR_RETURN(1, &shdr->direct_spatial_mv_pred_flag);
+
+ if (shdr->IsPSlice() || shdr->IsSPSlice() || shdr->IsBSlice()) {
+ READ_BITS_OR_RETURN(1, &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;
+ 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_BITS_OR_RETURN(1, &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;
+ }
+
+ shdr->header_bit_size = shdr->nalu_size * 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;
+
+ 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_BITS_OR_RETURN(1, &sei_msg->recovery_point.exact_match_flag);
+ READ_BITS_OR_RETURN(1, &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 content
+
diff --git a/content/common/gpu/media/h264_parser.h b/content/common/gpu/media/h264_parser.h
new file mode 100644
index 0000000..9a52dbb
--- /dev/null
+++ b/content/common/gpu/media/h264_parser.h
@@ -0,0 +1,406 @@
+// 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.
+//
+// This file contains an implementation of an H264 Annex-B video stream parser.
+
+#ifndef CONTENT_COMMON_GPU_MEDIA_H264_PARSER_H_
+#define CONTENT_COMMON_GPU_MEDIA_H264_PARSER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace content {
+
+// 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,
+ kIDRSlice = 5,
+ kSEIMessage = 6,
+ kSPS = 7,
+ kPPS = 8,
+ kEOSeq = 9,
+ kEOStream = 11,
+ 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* data;
+ off_t size; // From after start code to start code of next NALU (or EOS).
+
+ int nal_ref_idc;
+ int nal_unit_type;
+};
+
+struct H264SPS {
+ H264SPS();
+
+ int profile_idc;
+ 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;
+ // Changing types below will break assumptions in code that fills
+ // default scaling lists.
+ int scaling_list4x4[6][16];
+ int scaling_list8x8[6][64];
+
+ 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 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 chroma_array_type;
+};
+
+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][16];
+ int scaling_list8x8[6][64];
+
+ 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* 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;
+ 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_cnt[2];
+ 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;
+};
+
+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
+ };
+
+ H264Parser();
+ ~H264Parser();
+
+ void Reset();
+ // Set current stream pointer to |stream| of |stream_size| in bytes,
+ // |stream| owned by caller.
+ void SetStream(const uint8* stream, off_t stream_size);
+
+ // 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 H264PPS* GetPPS(int pps_id);
+
+ // 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:
+ // A class to provide bit-granularity reading of H.264 streams.
+ // This is not a generic bit reader class, as it takes into account
+ // H.264 stream-specific constraints, such as skipping emulation-prevention
+ // bytes and stop bits. See spec for more details.
+ // TODO(posciak): need separate unittests for this class.
+ class H264BitReader {
+ public:
+ H264BitReader();
+ ~H264BitReader();
+
+ // Initialize the reader to start reading at |data|, |size| being size
+ // of |data| in bytes.
+ // Return false on insufficient size of stream..
+ // TODO(posciak,fischman): consider replacing Initialize() with
+ // heap-allocating and creating bit readers on demand instead.
+ bool Initialize(const uint8* data, off_t size);
+
+ // Read |num_bits| next bits from stream and return in |*out|, first bit
+ // from the stream starting at |num_bits| position in |*out|.
+ // |num_bits| may be 1-32, inclusive.
+ // Return false if the given number of bits cannot be read (not enough
+ // bits in the stream), true otherwise.
+ bool ReadBits(int num_bits, int *out);
+
+ // Return the number of bits left in the stream.
+ off_t NumBitsLeft();
+
+ // See the definition of more_rbsp_data() in spec.
+ bool HasMoreRBSPData();
+
+ private:
+ // Advance to the next byte, loading it into curr_byte_.
+ // Return false on end of stream.
+ bool UpdateCurrByte();
+
+ // Pointer to the next unread (not in curr_byte_) byte in the stream.
+ const uint8* data_;
+
+ // Bytes left in the stream (without the curr_byte_).
+ off_t bytes_left_;
+
+ // Contents of the current byte; first unread bit starting at position
+ // 8 - num_remaining_bits_in_curr_byte_ from MSB.
+ int curr_byte_;
+
+ // Number of bits remaining in curr_byte_
+ int num_remaining_bits_in_curr_byte_;
+
+ // Used in emulation prevention three byte detection (see spec).
+ // Initially set to 0xffff to accept all initial two-byte sequences.
+ int prev_two_bytes_;
+
+ DISALLOW_COPY_AND_ASSIGN(H264BitReader);
+ };
+
+ // 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 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* stream_;
+
+ // Bytes left in the stream after the current NALU.
+ off_t bytes_left_;
+
+ H264BitReader br_;
+
+ // PPSes and SPSes stored for future reference.
+ typedef std::map<int, H264SPS*> SPSById;
+ typedef std::map<int, H264PPS*> PPSById;
+ SPSById active_SPSes_;
+ PPSById active_PPSes_;
+
+ DISALLOW_COPY_AND_ASSIGN(H264Parser);
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_GPU_MEDIA_H264_PARSER_H_
+
diff --git a/content/common/gpu/media/h264_parser_unittest.cc b/content/common/gpu/media/h264_parser_unittest.cc
new file mode 100644
index 0000000..53d1b35
--- /dev/null
+++ b/content/common/gpu/media/h264_parser_unittest.cc
@@ -0,0 +1,94 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "content/common/gpu/media/h264_parser.h"
+
+using content::H264Parser;
+using content::H264NALU;
+
+const FilePath::CharType* test_stream_filename =
+ FILE_PATH_LITERAL("content/common/gpu/testdata/test-25fps.h264");
+// Number of NALUs in the stream to be parsed.
+int num_nalus = 759;
+
+TEST(H264ParserTest, StreamFileParsing) {
+ FilePath fp(test_stream_filename);
+ file_util::MemoryMappedFile stream;
+ CHECK(stream.Initialize(fp)) << "Couldn't open stream file: "
+ << test_stream_filename;
+ DVLOG(1) << "Parsing file: " << test_stream_filename;
+
+ H264Parser parser;
+ parser.SetStream(stream.data(), stream.length());
+
+ // Parse until the end of stream/unsupported stream/error in stream is found.
+ int num_parsed_nalus = 0;
+ while (true) {
+ content::H264SliceHeader shdr;
+ content::H264SEIMessage sei_msg;
+ H264NALU nalu;
+ H264Parser::Result res = parser.AdvanceToNextNALU(&nalu);
+ if (res == H264Parser::kEOStream) {
+ DVLOG(1) << "Number of successfully parsed NALUs before EOS: "
+ << num_parsed_nalus;
+ ASSERT_EQ(num_nalus, num_parsed_nalus);
+ return;
+ }
+ ASSERT_EQ(res, H264Parser::kOk);
+
+ ++num_parsed_nalus;
+
+ int id;
+ switch (nalu.nal_unit_type) {
+ case H264NALU::kIDRSlice:
+ case H264NALU::kNonIDRSlice:
+ ASSERT_EQ(parser.ParseSliceHeader(nalu, &shdr), H264Parser::kOk);
+ break;
+
+ case H264NALU::kSPS:
+ ASSERT_EQ(parser.ParseSPS(&id), H264Parser::kOk);
+ break;
+
+ case H264NALU::kPPS:
+ ASSERT_EQ(parser.ParsePPS(&id), H264Parser::kOk);
+ break;
+
+ case H264NALU::kSEIMessage:
+ ASSERT_EQ(parser.ParseSEI(&sei_msg), H264Parser::kOk);
+ break;
+
+ default:
+ // Skip unsupported NALU.
+ DVLOG(4) << "Skipping unsupported NALU";
+ break;
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ CommandLine::Init(argc, argv);
+
+ const CommandLine::SwitchMap& switches =
+ CommandLine::ForCurrentProcess()->GetSwitches();
+ for (CommandLine::SwitchMap::const_iterator it = switches.begin();
+ it != switches.end(); ++it) {
+ if (it->first == "test_stream") {
+ test_stream_filename = it->second.c_str();
+ } else if (it->first == "num_nalus") {
+ CHECK(base::StringToInt(it->second, &num_nalus));
+ } else {
+ LOG(FATAL) << "Unexpected switch: " << it->first << ":" << it->second;
+ }
+ }
+
+ return RUN_ALL_TESTS();
+}
+
diff --git a/content/content_common.gypi b/content/content_common.gypi
index cb3156e..7a04117 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -389,6 +389,12 @@
],
},
}],
+ ['chromeos == 1', {
+ 'sources': [
+ 'common/gpu/media/h264_parser.cc',
+ 'common/gpu/media/h264_parser.h',
+ ],
+ }],
['OS=="win"', {
'dependencies': [
'../media/media.gyp:media',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 3ed669d..35b3ca8 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -514,7 +514,21 @@
],
}
],
- },
- ],
+ }],
+ ['chromeos == 1', {
+ 'targets': [
+ {
+ 'target_name': 'h264_parser_unittest',
+ 'type': 'executable',
+ 'dependencies': [
+ 'content_common',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'common/gpu/media/h264_parser_unittest.cc',
+ ],
+ }
+ ],
+ }],
],
}