diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/common/gpu/media/h264_bit_reader.cc | 45 | ||||
-rw-r--r-- | content/common/gpu/media/h264_bit_reader.h | 31 | ||||
-rw-r--r-- | content/common/gpu/media/h264_bit_reader_unittest.cc | 68 | ||||
-rw-r--r-- | content/common/gpu/media/h264_parser.cc | 103 | ||||
-rw-r--r-- | content/common/gpu/media/h264_parser.h | 56 | ||||
-rw-r--r-- | content/content_common.gypi | 2 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 |
7 files changed, 152 insertions, 154 deletions
diff --git a/content/common/gpu/media/h264_bit_reader.cc b/content/common/gpu/media/h264_bit_reader.cc new file mode 100644 index 0000000..b8d00e1 --- /dev/null +++ b/content/common/gpu/media/h264_bit_reader.cc @@ -0,0 +1,45 @@ +// 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 "content/common/gpu/media/h264_bit_reader.h" + +namespace content { + +H264BitReader::H264BitReader() { +} + +H264BitReader::~H264BitReader() {} + +void H264BitReader::UpdateCurrByte() { + DCHECK_EQ(num_remaining_bits_in_curr_byte_, 0); + + if (bytes_left_ >= 1) { + // Emulation prevention three-byte detection. + // If a sequence of 0x000003 is found, skip (ignore) the last byte (0x03). + if (*data_ == 0x03 && Tell() >= 16 && data_[-1] == 0 && data_[-2] == 0) { + // Detected 0x000003, skip last byte. + ++data_; + --bytes_left_; + position_ += 8; + } + } + + if (bytes_left_ >= 1) { + // Load a new byte and advance pointers. + curr_byte_ = *data_; + ++data_; + --bytes_left_; + num_remaining_bits_in_curr_byte_ = 8; + } + + // Check if this is the end of RBSP data. + if (bytes_left_ == 0) { + while (num_remaining_bits_in_curr_byte_ != 0 && !(curr_byte_ & 0x1)) { + --num_remaining_bits_in_curr_byte_; + curr_byte_ >>= 1; + } + } +} + +} // namespace content diff --git a/content/common/gpu/media/h264_bit_reader.h b/content/common/gpu/media/h264_bit_reader.h new file mode 100644 index 0000000..652c869 --- /dev/null +++ b/content/common/gpu/media/h264_bit_reader.h @@ -0,0 +1,31 @@ +// 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 CONTENT_COMMON_GPU_MEDIA_H264_BIT_READER_H_ +#define CONTENT_COMMON_GPU_MEDIA_H264_BIT_READER_H_ + +#include "base/compiler_specific.h" +#include "media/base/bit_reader.h" + +namespace content { + +// A class to provide bit-granularity reading of H.264 streams. +// This class takes into account H.264 stream-specific constraints, such as +// skipping emulation-prevention bytes and stop bits. See spec for more +// details. +class H264BitReader : public media::BitReader { + public: + H264BitReader(); + virtual ~H264BitReader(); + + private: + // This function handles the H.264 escape sequence and stop bit. + virtual void UpdateCurrByte() OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(H264BitReader); +}; + +} // namespace content + +#endif // CONTENT_COMMON_GPU_MEDIA_H264_BIT_READER_H_ diff --git a/content/common/gpu/media/h264_bit_reader_unittest.cc b/content/common/gpu/media/h264_bit_reader_unittest.cc new file mode 100644 index 0000000..cf90e02 --- /dev/null +++ b/content/common/gpu/media/h264_bit_reader_unittest.cc @@ -0,0 +1,68 @@ +// 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 "content/common/gpu/media/h264_bit_reader.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using content::H264BitReader; + +TEST(BitReaderTest, H264StreamTest) { + // This stream contains an escape sequence. Its last byte only has 4 bits. + // 0001 0010 0011 0100 0000 0000 0000 0000 0000 0011 0101 0110 0111 0000 + uint8 buffer[] = {0x12, 0x34, 0x00, 0x00, 0x03, 0x56, 0x70}; + H264BitReader reader; + uint8 value8; + uint32 value32; + + reader.Initialize(buffer, sizeof(buffer)); + EXPECT_EQ(reader.Tell(), 0); + EXPECT_TRUE(reader.ReadBits(4, &value8)); + EXPECT_EQ(value8, 1u); + EXPECT_EQ(reader.Tell(), 4); + EXPECT_TRUE(reader.HasMoreData()); + + EXPECT_TRUE(reader.ReadBits(8, &value8)); + EXPECT_EQ(value8, 0x23u); + EXPECT_EQ(reader.Tell(), 12); + EXPECT_TRUE(reader.HasMoreData()); + + EXPECT_TRUE(reader.ReadBits(24, &value32)); + EXPECT_EQ(value32, 0x400005u); + EXPECT_EQ(reader.Tell(), 44); // Include the skipped escape byte + EXPECT_TRUE(reader.HasMoreData()); + + EXPECT_TRUE(reader.ReadBits(8, &value8)); + EXPECT_EQ(value8, 0x67u); + EXPECT_EQ(reader.Tell(), 52); // Include the skipped escape byte + EXPECT_FALSE(reader.HasMoreData()); + + EXPECT_TRUE(reader.ReadBits(0, &value8)); + EXPECT_EQ(reader.Tell(), 52); // Include the skipped escape byte + EXPECT_FALSE(reader.ReadBits(1, &value8)); + EXPECT_FALSE(reader.HasMoreData()); + + // Do it again using SkipBits + reader.Initialize(buffer, sizeof(buffer)); + EXPECT_EQ(reader.Tell(), 0); + EXPECT_TRUE(reader.SkipBits(4)); + EXPECT_EQ(reader.Tell(), 4); + EXPECT_TRUE(reader.HasMoreData()); + + EXPECT_TRUE(reader.SkipBits(8)); + EXPECT_EQ(reader.Tell(), 12); + EXPECT_TRUE(reader.HasMoreData()); + + EXPECT_TRUE(reader.SkipBits(24)); + EXPECT_EQ(reader.Tell(), 44); // Include the skipped escape byte + EXPECT_TRUE(reader.HasMoreData()); + + EXPECT_TRUE(reader.SkipBits(8)); + EXPECT_EQ(reader.Tell(), 52); // Include the skipped escape byte + EXPECT_FALSE(reader.HasMoreData()); + + EXPECT_TRUE(reader.SkipBits(0)); + EXPECT_FALSE(reader.SkipBits(1)); + EXPECT_FALSE(reader.HasMoreData()); +} diff --git a/content/common/gpu/media/h264_parser.cc b/content/common/gpu/media/h264_parser.cc index ace3dab..1452238 100644 --- a/content/common/gpu/media/h264_parser.cc +++ b/content/common/gpu/media/h264_parser.cc @@ -50,103 +50,6 @@ 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), - prev_two_bytes_(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)) != 0; -} - #define READ_BITS_OR_RETURN(num_bits, out) \ do { \ int _out; \ @@ -358,7 +261,9 @@ H264Parser::Result H264Parser::AdvanceToNextNALU(H264NALU *nalu) { nalu->data = stream_ + off_to_nalu_start; // Initialize bit reader at the start of found NALU. - if (!br_.Initialize(nalu->data, nalu->size)) + br_.Initialize(nalu->data, nalu->size); + + if (!br_.HasMoreData()) return kEOStream; DVLOG(4) << "Looking for NALU, Stream bytes left: " << bytes_left_ @@ -812,7 +717,7 @@ H264Parser::Result H264Parser::ParsePPS(int* pps_id) { READ_BOOL_OR_RETURN(&pps->constrained_intra_pred_flag); READ_BOOL_OR_RETURN(&pps->redundant_pic_cnt_present_flag); - if (br_.HasMoreRBSPData()) { + if (br_.HasMoreData()) { READ_BOOL_OR_RETURN(&pps->transform_8x8_mode_flag); READ_BOOL_OR_RETURN(&pps->pic_scaling_matrix_present_flag); diff --git a/content/common/gpu/media/h264_parser.h b/content/common/gpu/media/h264_parser.h index ed76f16..e9aea18 100644 --- a/content/common/gpu/media/h264_parser.h +++ b/content/common/gpu/media/h264_parser.h @@ -13,6 +13,7 @@ #include "base/basictypes.h" #include "content/common/content_export.h" +#include "content/common/gpu/media/h264_bit_reader.h" namespace content { @@ -301,61 +302,6 @@ class CONTENT_EXPORT H264Parser { 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); diff --git a/content/content_common.gypi b/content/content_common.gypi index cb1860f..ae14818 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -216,6 +216,8 @@ 'common/gpu/image_transport_surface_win.cc', 'common/gpu/media/avc_config_record_builder.cc', 'common/gpu/media/avc_config_record_builder.h', + 'common/gpu/media/h264_bit_reader.cc', + 'common/gpu/media/h264_bit_reader.h', 'common/gpu/media/h264_parser.cc', 'common/gpu/media/h264_parser.h', 'common/gpu/media/mac_video_decode_accelerator.h', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index eeb3805..ec33b24 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -645,6 +645,7 @@ '../testing/gtest.gyp:gtest', ], 'sources': [ + 'common/gpu/media/h264_bit_reader_unittest.cc', 'common/gpu/media/h264_parser_unittest.cc', ], } |