Index: content/common/gpu/media/h264_parser.cc |
diff --git a/content/common/gpu/media/h264_parser.cc b/content/common/gpu/media/h264_parser.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..817edb71b501dc8adab8c58aebe977a3cb4d690a |
--- /dev/null |
+++ b/content/common/gpu/media/h264_parser.cc |
@@ -0,0 +1,1198 @@ |
+// 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. |
+ |
Ami GONE FROM CHROMIUM
2012/03/21 13:16:24
Not part of this CL?
|
+#include "h264_parser.h" |
+ |
+#include "base/logging.h" |
+#include "base/stl_util.h" |
+ |
+#include <fstream> |
+H264BitReader::H264BitReader() |
+ : data_(NULL), |
+ bytes_left_(0), |
+ curr_byte_(0), |
+ bits_in_curr_byte_(0) |
+{} |
+ |
+H264BitReader::~H264BitReader() {} |
+ |
+bool H264BitReader::Initialize(uint8* data, size_t size) { |
+ DCHECK(data); |
+ |
+ if (size < 1) |
+ return false; |
+ |
+ data_ = data; |
+ bytes_left_ = size; |
+ bits_in_curr_byte_ = 0; |
+ // for detecting emulation-prevention three-byte sequences |
+ prev_two_bytes_ = 0xffff; |
+ |
+ return true; |
+} |
+ |
+bool 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) { |
+ // skip |
+ ++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_++; |
+ --bytes_left_; |
+ bits_in_curr_byte_ = 8; |
+ |
+ prev_two_bytes_ = (prev_two_bytes_ << 8) | curr_byte_; |
+ |
+ return true; |
+} |
+ |
+bool H264BitReader::ReadBits(int num_bits, uint32 *out) { |
+ int bits_left = num_bits; |
+ *out = 0; |
+ DCHECK(num_bits <= 32); |
+ |
+ while (bits_in_curr_byte_ < bits_left) { |
+ // Take all that's left in current byte |
+ // shift to make space for the rest |
+ *out = (curr_byte_ << (num_bits - bits_in_curr_byte_)); |
+ bits_left -= bits_in_curr_byte_; |
+ |
+ if (!UpdateCurrByte()) |
+ return false; |
+ } |
+ |
+ *out |= (curr_byte_ >> (bits_in_curr_byte_ - bits_left)); |
+ *out &= ((1 << num_bits) - 1); |
+ bits_in_curr_byte_ -= bits_left; |
+ |
+ return true; |
+} |
+ |
+bool H264BitReader::MoreRBSPData() { |
+ // make sure we have more bits if we are at 0 bits in current byte |
+ // if updating current byte fails, we don't have more data anyway |
+ if (bits_in_curr_byte_ == 0) |
+ if (!UpdateCurrByte()) |
+ return false; |
+ |
+ // not on last byte? |
+ if (bytes_left_ > 0) |
+ return true; |
+ |
+ // last byte, look for stop bit; if there is one, and there are still |
+ // preceding bits in front of it the byte before it, we have more data |
+ // if the last bit == 1 we find is our current bit, no more data |
+ for (int i = 0; i < bits_in_curr_byte_ - 1; ++i) { |
+ if ((curr_byte_ >> i) & 0x01) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+#define read_bits_or_return(num_bits, out) \ |
+do { \ |
+ uint32 out32; \ |
+ if (!br_.ReadBits(num_bits, &out32)) { \ |
+ LOG(INFO) << "Error in stream: unexpected end of data"; \ |
+ DLOG(INFO) << "While trying to read " #out; \ |
+ return kInvalidStream; \ |
+ } \ |
+ *out = out32; \ |
+} while(0) |
+ |
+#define read_ue_or_return(out) \ |
+do { \ |
+ uint32 out32; \ |
+ if (ReadUE(&out32) != kOk) { \ |
+ LOG(INFO) << "Error in stream: invalid stream"; \ |
+ DLOG(INFO) << "While trying to read " #out; \ |
+ return kInvalidStream; \ |
+ } \ |
+ *out = out32; \ |
+} while(0) |
+ |
+#define read_se_or_return(out) \ |
+do { \ |
+ int32 out32; \ |
+ if (ReadSE(&out32) != kOk) { \ |
+ LOG(INFO) << "Error in stream: invalid stream"; \ |
+ DLOG(INFO) << "While trying to read " #out; \ |
+ return kInvalidStream; \ |
+ } \ |
+ *out = out32; \ |
+} while(0) |
+ |
+#define range_in_or_return(val, low, high) \ |
+do { \ |
+ if ((val) < (low) || (val) > (high)) { \ |
+ LOG(INFO) << "Error in stream: invalid value"; \ |
+ DLOG(INFO) << "Expected " #val " to be in range " \ |
+ << "[" << (low) << ":" << (high) << "]" \ |
+ << "found " << (val) << " instead"; \ |
+ return kInvalidStream; \ |
+ } \ |
+} while(0) |
+ |
+#define true_or_return(a) \ |
+do { \ |
+ if (!(a)) { \ |
+ LOG(INFO) << "Error in stream: invalid value"; \ |
+ DLOG(INFO) << "Expected " << #a; \ |
+ return kInvalidStream; \ |
+ } \ |
+} while(0) |
+ |
+ |
+H264Parser::H264Parser() { |
+ Reset(); |
+} |
+ |
+H264Parser::~H264Parser() { |
+ STLDeleteContainerPairSecondPointers(active_SPSes_.begin(), |
+ active_SPSes_.end()); |
+ STLDeleteContainerPairSecondPointers(active_PPSes_.begin(), |
+ active_PPSes_.end()); |
+} |
+ |
+void H264Parser::Reset() { |
+ /*STLDeleteContainerPairSecondPointers(active_SPSes_.begin(), |
+ active_SPSes_.end()); |
+ active_SPSes_.clear(); |
+ |
+ STLDeleteContainerPairSecondPointers(active_PPSes_.begin(), |
+ active_PPSes_.end()); |
+ active_PPSes_.clear();*/ |
+ bytes_left_ = 0; |
+ stream_ = NULL; |
+} |
+ |
+void H264Parser::SetStream(uint8* stream, size_t stream_size) { |
+ DCHECK(stream); |
+ DCHECK(stream_size > 0); |
+ |
+ stream_ = stream; |
+ bytes_left_ = stream_size; |
+} |
+ |
+H264PPS* H264Parser::GetPPS(uint8 pps_id) { |
+ PPSById::iterator it = active_PPSes_.find(pps_id); |
+ if (it == active_PPSes_.end()) |
+ return NULL; |
+ |
+ return it->second; |
+} |
+ |
+H264SPS* H264Parser::GetSPS(uint8 sps_id) { |
+ SPSById::iterator it = active_SPSes_.find(sps_id); |
+ if (it == active_SPSes_.end()) |
+ return NULL; |
+ |
+ return it->second; |
+} |
+ |
+static inline bool IsStartCode(uint8* data) { |
+ DCHECK(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(uint8* data, size_t data_size, |
+ size_t* offset, |
+ size_t *start_code_size) { |
+ int bytes_left = data_size; |
+ |
+ while (bytes_left > 3) { |
+ |
+ if (IsStartCode(data)) { |
+ // found three-byte start code, set pointers 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(uint8* stream, size_t stream_size, |
+ size_t* nalu_start_off, size_t* nalu_size) { |
+ size_t start_code_size; |
+ *nalu_start_off = 0; |
+ |
+ // 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(uint32* val) { |
+ int num_bits = -1; |
+ uint8 bit; |
+ uint32 rest; |
+ |
+ // Count the number of contiguous zero bits |
+ do { |
+ read_bits_or_return(1, &bit); |
+ num_bits++; |
+ } while (bit == 0); |
+ |
+ if (num_bits > 32) |
+ return kInvalidStream; |
+ |
+ // calculate exp-Golomb code value |
+ *val = (1 << num_bits) - 1; |
+ |
+ if (num_bits > 0) { |
+ read_bits_or_return(num_bits, &rest); |
+ *val += rest; |
+ } |
+ |
+ return kOk; |
+} |
+ |
+H264Parser::Result H264Parser::ReadSE(int32* val) { |
+ uint32 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::ParseNextNalu(H264NALU *nalu) { |
+ uint8 data; |
+ size_t off_to_nalu_start; |
+ |
+ DCHECK(stream_); |
+ |
+ 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 |
+ br_.Initialize(nalu->data, nalu->size); |
+ 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 ParseNextNalu |
+ // is called we will effectively be skipping it |
+ // other parsing functions will use the bit reader for parsing |
+ 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); |
+ true_or_return(nalu->nal_unit_type < 31); |
+ |
+ 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 (as per spec) |
+static const uint8 default_4x4_intra[16] = { |
+ 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42, }; |
+ |
+static const uint8 default_4x4_inter[16] = { |
+ 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34, }; |
+ |
+static const uint8 default_8x8_intra[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 uint8 default_8x8_inter[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, uint8 scaling_list4x4[][16]) { |
+ DCHECK(i < 6); |
+ |
+ if (i < 3) |
+ memcpy(scaling_list4x4[i], default_4x4_intra, sizeof(default_4x4_intra)); |
+ else if (i < 6) |
+ memcpy(scaling_list4x4[i], default_4x4_inter, sizeof(default_4x4_inter)); |
+} |
+ |
+static inline void DefaultScalingList8x8(int i, uint8 scaling_list8x8[][64]) { |
+ DCHECK(i < 6); |
+ |
+ if (i % 2 == 0) |
+ memcpy(scaling_list8x8[i], default_8x8_intra, sizeof(default_8x8_intra)); |
+ else |
+ memcpy(scaling_list8x8[i], default_8x8_inter, sizeof(default_8x8_inter)); |
+} |
+ |
+static void FallbackScalingList4x4(int i, |
+ const uint8 default_scaling_list_intra[], |
+ const uint8 default_scaling_list_inter[], |
+ uint8 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 uint8 default_scaling_list_intra[], |
+ const uint8 default_scaling_list_inter[], |
+ uint8 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::ScalingList(uint8* scaling_list, int size, |
+ bool* use_default) { |
+ // see chapter 7.3.2.1.1.1 |
+ uint8 last_scale = 8; |
+ uint8 next_scale = 8; |
+ int32 delta_scale; |
+ |
+ *use_default = false; |
+ |
+ // TODO check if we need to scan in zigzag pattern for the driver |
+ for (int j = 0; j < size; ++j) { |
+ if (next_scale != 0) { |
+ read_se_or_return(&delta_scale); |
+ next_scale = (last_scale + delta_scale) & 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 = ScalingList(sps->scaling_list4x4[i], |
+ sizeof(sps->scaling_list4x4[i]), |
+ &use_default); |
+ if (res != kOk) |
+ return res; |
+ |
+ if (use_default) |
+ DefaultScalingList4x4(i, sps->scaling_list4x4); |
+ |
+ } else { |
+ FallbackScalingList4x4(i, default_4x4_intra, default_4x4_inter, |
+ 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 = ScalingList(sps->scaling_list8x8[i], |
+ sizeof(sps->scaling_list8x8[i]), |
+ &use_default); |
+ if (res != kOk) |
+ return res; |
+ |
+ if (use_default) |
+ DefaultScalingList8x8(i, sps->scaling_list8x8); |
+ |
+ } else { |
+ FallbackScalingList8x8(i, default_8x8_intra, default_8x8_inter, |
+ sps->scaling_list8x8); |
+ } |
+ } |
+ |
+ return kOk; |
+} |
+ |
+H264Parser::Result H264Parser::ParsePPSScalingLists(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 = ScalingList(pps->scaling_list4x4[i], |
+ sizeof(pps->scaling_list4x4[i]), |
+ &use_default); |
+ if (res != kOk) |
+ return res; |
+ |
+ if (use_default) // check SPS scaling flag? |
+ DefaultScalingList4x4(i, pps->scaling_list4x4); |
+ |
+ } else { |
+ if (sps->seq_scaling_matrix_present_flag) { |
+ // table 7-2 fallback rule A |
+ FallbackScalingList4x4(i, default_4x4_intra, default_4x4_inter, |
+ pps->scaling_list4x4); |
+ } else { |
+ // table 7-2 fallback rule B |
+ 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 = ScalingList(pps->scaling_list8x8[i], |
+ sizeof(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 |
+ FallbackScalingList8x8(i, default_8x8_intra, |
+ default_8x8_inter, pps->scaling_list8x8); |
+ } else { |
+ // table 7-2 fallback rule B |
+ FallbackScalingList8x8(i, sps->scaling_list8x8[0], |
+ sps->scaling_list8x8[1], pps->scaling_list8x8); |
+ } |
+ } |
+ } |
+ } |
+ return kOk; |
+} |
+ |
+static void FillDefaultSeqScalingLists(H264SPS* sps) { |
+ // assumes uint8s 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 |
+ uint8 data; |
+ Result res; |
+ |
+ *sps_id = -1; |
+ |
+ scoped_ptr<H264SPS> sps(new H264SPS); |
+ CHECK(sps != NULL); |
+ memset(sps.get(), 0, sizeof(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) { |
+ DLOG(INFO) << "VUI parameters present in SPS, ignoring"; |
+ } |
+ |
+ // If an SPS with the same id already exists, replace it |
+ SPSById::iterator it = active_SPSes_.find(sps->seq_parameter_set_id); |
+ if (it != active_SPSes_.end()) { |
+ delete it->second; |
+ active_SPSes_.erase(it); |
+ } |
+ |
+ *sps_id = sps->seq_parameter_set_id; |
+ active_SPSes_[sps->seq_parameter_set_id] = sps.release(); |
+ |
+ return kOk; |
+} |
+ |
+H264Parser::Result H264Parser::ParsePPS(int* pps_id) { |
+ // see 7.4.2.2 |
+ H264SPS* sps; |
+ Result res; |
+ |
+ *pps_id = -1; |
+ |
+ scoped_ptr<H264PPS> pps(new H264PPS); |
+ CHECK(pps != NULL); |
+ memset(pps.get(), 0, sizeof(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 != NULL); |
+ |
+ 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) { |
+ DLOG(INFO) << "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); |
+ range_in_or_return(pps->pic_init_qp_minus26, -26, 25); |
+ |
+ read_se_or_return(&pps->pic_init_qs_minus26); |
+ range_in_or_return(pps->pic_init_qs_minus26, -26, 25); |
+ |
+ read_se_or_return(&pps->chroma_qp_index_offset); |
+ range_in_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_.MoreRBSPData()) { |
+ 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); |
+ } |
+ |
+ PPSById::iterator it = active_PPSes_.find(pps->pic_parameter_set_id); |
+ if (it != active_PPSes_.end()) { |
+ delete it->second; |
+ active_PPSes_.erase(it); |
+ } |
+ |
+ *pps_id = pps->pic_parameter_set_id; |
+ // If a PPS with the same id already exists, replace it |
+ active_PPSes_[pps->pic_parameter_set_id] = pps.release(); |
+ |
+ return kOk; |
+} |
+ |
+H264Parser::Result H264Parser::ParseRefPicListModification( |
+ uint8 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 < num_ref_idx_active_minus1 + 1; ++i) { |
+ 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 |
+ uint8 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::RefPicListModification(H264SliceHeader* shdr) { |
+ Result res; |
+ |
+ if (shdr->slice_type % 5 != 2 && shdr->slice_type % 5 != 4) { |
+ 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->slice_type % 5 == 1) { |
+ 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( |
+ H264WeightingFactors* w_facts, |
+ int num_ref_idx_active_minus1, |
+ uint8 chroma_array_type, |
+ uint8 luma_log2_weight_denom, |
+ uint8 chroma_log2_weight_denom) { |
+ |
+ int16 def_luma_weight = 1 << luma_log2_weight_denom; |
+ int16 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]); |
+ range_in_or_return(w_facts->luma_weight[i], -128, 127); |
+ |
+ read_se_or_return(&w_facts->luma_offset[i]); |
+ range_in_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]); |
+ range_in_or_return(w_facts->chroma_weight[i][j], -128, 127); |
+ |
+ read_se_or_return(&w_facts->chroma_offset[i][j]); |
+ range_in_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(H264SliceHeader *shdr, |
+ H264SPS* sps) { |
+ Result res; |
+ |
+ 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); |
+ |
+ res = ParseWeightingFactors(&shdr->pred_weight_table_l0, |
+ shdr->num_ref_idx_l0_active_minus1, |
+ sps->chroma_array_type, |
+ shdr->luma_log2_weight_denom, |
+ shdr->chroma_log2_weight_denom); |
+ if (res != kOk) |
+ return res; |
+ |
+ if (shdr->slice_type % 5 == 1) { |
+ res = ParseWeightingFactors(&shdr->pred_weight_table_l1, |
+ shdr->num_ref_idx_l1_active_minus1, |
+ sps->chroma_array_type, |
+ shdr->luma_log2_weight_denom, |
+ shdr->chroma_log2_weight_denom); |
+ 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)) { |
+ LOG(INFO) << "Ran out of dec ref pic marking fields"; |
+ return kUnsupportedStream; |
+ } |
+ } |
+ } |
+ |
+ return kOk; |
+} |
+ |
+H264Parser::Result H264Parser::ParseSliceHeader(H264SliceHeader* shdr, |
+ H264NALU* nalu) { |
+ // see 7.4.3 |
+ H264SPS* sps; |
+ 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 != NULL); |
+ |
+ sps = GetSPS(pps->seq_parameter_set_id); |
+ true_or_return(sps != NULL); |
+ |
+ if (sps->separate_colour_plane_flag) { |
+ DLOG(INFO) << "Interlaced streams not supported"; |
+ return kUnsupportedStream; |
+ //read_bits_or_return(2, &shdr->colour_plane_id); |
+ //true_or_return(shdr->colour_plane_id < 3); |
+ } |
+ |
+ 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) { |
+ DLOG(INFO) << "Interlaced streams not supported"; |
+ return kUnsupportedStream; |
+ //read_bits_or_return(1, &shdr->bottom_field_flag); |
+ } |
+ } |
+ |
+ 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 (IsH264BSlice(shdr)) |
+ read_bits_or_return(1, &shdr->direct_spatial_mv_pred_flag); |
+ |
+ if (IsH264PSlice(shdr) || IsH264SPSlice(shdr) || IsH264BSlice(shdr)) { |
+ 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 (IsH264BSlice(shdr)) |
+ 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 == 20) { |
+ return kUnsupportedStream; |
+ } else { |
+ res = RefPicListModification(shdr); |
+ if (res != kOk) |
+ return res; |
+ } |
+ |
+ if ((pps->weighted_pred_flag && (IsH264PSlice(shdr) || IsH264SPSlice(shdr))) |
+ || (pps->weighted_bipred_idc == 1 && IsH264BSlice(shdr))) { |
+ res = ParsePredWeightTable(shdr, sps); |
+ 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 && |
+ !IsH264ISlice(shdr) && !IsH264SISlice(shdr)) { |
+ 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 (IsH264SPSlice(shdr) || IsH264SISlice(shdr)) { |
+ if (IsH264SPSlice(shdr)) |
+ 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); |
+ range_in_or_return(shdr->slice_alpha_c0_offset_div2, -6, 6); |
+ |
+ read_se_or_return(&shdr->slice_beta_offset_div2); |
+ range_in_or_return(shdr->slice_beta_offset_div2, -6, 6); |
+ } |
+ } |
+ |
+ if (pps->num_slice_groups_minus1 > 0) { |
+ // TODO slice groups |
+ DLOG(INFO) << "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) { |
+ int8 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 kH264SEIRecoveryPoint: |
+ 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; |
+} |
+ |