diff options
author | xiaomings@google.com <xiaomings@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-09 22:57:24 +0000 |
---|---|---|
committer | xiaomings@google.com <xiaomings@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-09 22:57:24 +0000 |
commit | ecc703929354d51b5e48854d41e222c51497f5d3 (patch) | |
tree | ab65a0ecf2f03c629b3c87fe2fbd79472e2e5fa2 | |
parent | eb55b4927d8f7593ca386ac670673a4108c9bc7b (diff) | |
download | chromium_src-ecc703929354d51b5e48854d41e222c51497f5d3.zip chromium_src-ecc703929354d51b5e48854d41e222c51497f5d3.tar.gz chromium_src-ecc703929354d51b5e48854d41e222c51497f5d3.tar.bz2 |
Add HE AAC support to ISO BMFF.
Also abstract common code in H264BitReader into BitReader for reusing.
Was: https://chromiumcodereview.appspot.com/10710002/
In the submitted patch of the last issue, the media.gyp included a non-existing file. This error hadn't been caught by try but later got caught by the build process and reverted the commit. So I create this issue to submit the fixed patch.
BUG=134445
TEST=BitReaderTest, AACTest
Review URL: https://chromiumcodereview.appspot.com/10753005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145769 0039d316-1c4b-4281-b951-d872f2087c98
-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 | ||||
-rw-r--r-- | media/base/bit_reader.cc | 74 | ||||
-rw-r--r-- | media/base/bit_reader.h | 110 | ||||
-rw-r--r-- | media/base/bit_reader_unittest.cc | 110 | ||||
-rw-r--r-- | media/media.gyp | 9 | ||||
-rw-r--r-- | media/mp4/aac.cc | 252 | ||||
-rw-r--r-- | media/mp4/aac.h | 67 | ||||
-rw-r--r-- | media/mp4/aac_unittest.cc | 104 | ||||
-rw-r--r-- | media/mp4/avc.cc | 22 | ||||
-rw-r--r-- | media/mp4/avc.h | 4 | ||||
-rw-r--r-- | media/mp4/box_definitions.cc | 28 | ||||
-rw-r--r-- | media/mp4/box_definitions.h | 9 | ||||
-rw-r--r-- | media/mp4/es_descriptor.cc | 112 | ||||
-rw-r--r-- | media/mp4/es_descriptor.h | 57 | ||||
-rw-r--r-- | media/mp4/es_descriptor_unittest.cc | 92 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser.cc | 16 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser.h | 4 |
23 files changed, 1189 insertions, 187 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', ], } diff --git a/media/base/bit_reader.cc b/media/base/bit_reader.cc new file mode 100644 index 0000000..e4106fa --- /dev/null +++ b/media/base/bit_reader.cc @@ -0,0 +1,74 @@ +// 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 "media/base/bit_reader.h" + +namespace media { + +BitReader::BitReader() + : data_(NULL), + bytes_left_(0), + position_(0), + curr_byte_(0), + num_remaining_bits_in_curr_byte_(0) { +} + +BitReader::BitReader(const uint8* data, off_t size) { + Initialize(data, size); +} + +BitReader::~BitReader() {} + +void BitReader::Initialize(const uint8* data, off_t size) { + DCHECK(data != NULL || size == 0); // Data cannot be NULL if size is not 0. + + data_ = data; + bytes_left_ = size; + position_ = 0; + num_remaining_bits_in_curr_byte_ = 0; + + UpdateCurrByte(); +} + +void BitReader::UpdateCurrByte() { + DCHECK_EQ(num_remaining_bits_in_curr_byte_, 0); + + if (bytes_left_ < 1) + return; + + // Load a new byte and advance pointers. + curr_byte_ = *data_; + ++data_; + --bytes_left_; + num_remaining_bits_in_curr_byte_ = 8; +} + +bool BitReader::SkipBits(int num_bits) { + int dummy; + const int kDummySize = static_cast<int>(sizeof(dummy)) * 8; + + while (num_bits > kDummySize) { + if (ReadBits(kDummySize, &dummy)) { + num_bits -= kDummySize; + } else { + return false; + } + } + + return ReadBits(num_bits, &dummy); +} + +off_t BitReader::Tell() const { + return position_; +} + +off_t BitReader::NumBitsLeft() const { + return (num_remaining_bits_in_curr_byte_ + bytes_left_ * 8); +} + +bool BitReader::HasMoreData() const { + return num_remaining_bits_in_curr_byte_ != 0; +} + +} // namespace media diff --git a/media/base/bit_reader.h b/media/base/bit_reader.h new file mode 100644 index 0000000..35c62a9 --- /dev/null +++ b/media/base/bit_reader.h @@ -0,0 +1,110 @@ +// 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 MEDIA_BASE_BIT_READER_H_ +#define MEDIA_BASE_BIT_READER_H_ + +#include <sys/types.h> +#include <algorithm> +#include <climits> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "media/base/media_export.h" + + +namespace media { + +// A class to read bit streams. +// Classes inherited this class can override its UpdateCurrByte function +// to support specific escape mechanism, which is widely used by streaming +// formats like H.264 Annex B. +class MEDIA_EXPORT BitReader { + public: + BitReader(); + BitReader(const uint8* data, off_t size); + virtual ~BitReader(); + + // Initialize the reader to start reading at |data|, |size| being size + // of |data| in bytes. + void 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| cannot be larger than the bits the type can hold. + // Return false if the given number of bits cannot be read (not enough + // bits in the stream), true otherwise. When return false, the stream will + // enter a state where further ReadBits operations will always return false + // unless |num_bits| is 0. The type |T| has to be a primitive integer type. + template<typename T> + bool ReadBits(int num_bits, T *out) { + DCHECK(num_bits <= static_cast<int>(sizeof(T) * CHAR_BIT)); + + *out = 0; + position_ += num_bits; + + while (num_remaining_bits_in_curr_byte_ != 0 && num_bits != 0) { + int bits_to_take = std::min(num_remaining_bits_in_curr_byte_, num_bits); + *out = (*out << bits_to_take) + + (curr_byte_ >> (num_remaining_bits_in_curr_byte_ - bits_to_take)); + num_bits -= bits_to_take; + num_remaining_bits_in_curr_byte_ -= bits_to_take; + curr_byte_ &= (1 << num_remaining_bits_in_curr_byte_) - 1; + + if (num_remaining_bits_in_curr_byte_ == 0) + UpdateCurrByte(); + } + + if (num_bits == 0) + return true; + + *out = 0; + num_remaining_bits_in_curr_byte_ = 0; + bytes_left_ = 0; + + return false; + } + + bool SkipBits(int num_bits); + + // Return the current position in the stream in unit of bit. + // This includes the skipped escape bytes if there are any. + off_t Tell() const; + + // Return the number of bits left in the stream. + // This doesn't take any escape sequence into account. + off_t NumBitsLeft() const; + + bool HasMoreData() const; + + protected: + // Advance to the next byte, loading it into curr_byte_. + // This function can be overridden to support specific escape mechanism. + // If the num_remaining_bits_in_curr_byte_ is 0 after this function returns, + // the stream has reached the end. + virtual void 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_; + + // Current position in bits. + off_t position_; + + // Contents of the current byte; first unread bit starting at position + // 8 - num_remaining_bits_in_curr_byte_ from MSB. + uint8 curr_byte_; + + // Number of bits remaining in curr_byte_ + int num_remaining_bits_in_curr_byte_; + + private: + DISALLOW_COPY_AND_ASSIGN(BitReader); +}; + +} // namespace media + +#endif // MEDIA_BASE_BIT_READER_H_ diff --git a/media/base/bit_reader_unittest.cc b/media/base/bit_reader_unittest.cc new file mode 100644 index 0000000..00d425f --- /dev/null +++ b/media/base/bit_reader_unittest.cc @@ -0,0 +1,110 @@ +// 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 "media/base/bit_reader.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +TEST(BitReaderTest, EmptyStreamTest) { + BitReader reader(NULL, 0); + uint8 value8 = 0xff; + + ASSERT_FALSE(reader.HasMoreData()); + ASSERT_EQ(reader.Tell(), 0); + ASSERT_TRUE(reader.ReadBits(0, &value8)); + ASSERT_TRUE(reader.SkipBits(0)); + ASSERT_EQ(reader.Tell(), 0); + ASSERT_FALSE(reader.HasMoreData()); + ASSERT_FALSE(reader.ReadBits(1, &value8)); + ASSERT_FALSE(reader.SkipBits(1)); + ASSERT_EQ(value8, 0); +} + +TEST(BitReaderTest, NormalOperationTest) { + // 0101 0101 1001 1001 repeats 4 times + uint8 buffer[] = {0x55, 0x99, 0x55, 0x99, 0x55, 0x99, 0x55, 0x99}; + BitReader reader(buffer, 6); // Initialize with 6 bytes only + uint8 value8; + uint64 value64; + + ASSERT_TRUE(reader.HasMoreData()); + ASSERT_EQ(reader.Tell(), 0); + ASSERT_TRUE(reader.ReadBits(1, &value8)); + ASSERT_EQ(value8, 0); + ASSERT_TRUE(reader.ReadBits(8, &value8)); + ASSERT_EQ(value8, 0xab); // 1010 1011 + ASSERT_EQ(reader.Tell(), 9); + ASSERT_TRUE(reader.HasMoreData()); + ASSERT_TRUE(reader.SkipBits(7)); + ASSERT_EQ(reader.Tell(), 16); + ASSERT_TRUE(reader.ReadBits(32, &value64)); + ASSERT_EQ(value64, 0x55995599u); + ASSERT_EQ(reader.Tell(), 48); + ASSERT_FALSE(reader.HasMoreData()); + ASSERT_FALSE(reader.SkipBits(1)); + ASSERT_FALSE(reader.ReadBits(1, &value8)); + ASSERT_TRUE(reader.SkipBits(0)); + value8 = 0xff; + ASSERT_TRUE(reader.ReadBits(0, &value8)); + ASSERT_EQ(value8, 0); + + reader.Initialize(buffer, 8); + ASSERT_TRUE(reader.ReadBits(64, &value64)); + EXPECT_EQ(value64, 0x5599559955995599ull); + EXPECT_FALSE(reader.HasMoreData()); + EXPECT_EQ(reader.Tell(), 64); + EXPECT_FALSE(reader.ReadBits(1, &value8)); + EXPECT_FALSE(reader.SkipBits(1)); + EXPECT_TRUE(reader.ReadBits(0, &value8)); + EXPECT_TRUE(reader.SkipBits(0)); +} + +TEST(BitReaderTest, LongSkipTest) { + uint8 buffer[] = { + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 1 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 2 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 3 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 4 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 5 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 6 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 7 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, // 64 * 8 + 0x87, 0x65 + }; + BitReader reader(buffer, sizeof(buffer)); + uint8 value8; + + EXPECT_TRUE(reader.SkipBits(64 * 8 + 8)); + EXPECT_EQ(reader.Tell(), 64 * 8 + 8); + EXPECT_TRUE(reader.ReadBits(8, &value8)); + EXPECT_EQ(value8, 0x65); + EXPECT_EQ(reader.Tell(), 64 * 8 + 16); + EXPECT_FALSE(reader.HasMoreData()); + EXPECT_EQ(reader.NumBitsLeft(), 0); +} + +TEST(BitReaderTest, ReadBeyondEndTest) { + uint8 buffer[] = {0x12}; + BitReader reader(buffer, sizeof(buffer)); + uint8 value8; + + EXPECT_TRUE(reader.SkipBits(4)); + EXPECT_FALSE(reader.ReadBits(5, &value8)); + EXPECT_FALSE(reader.ReadBits(1, &value8)); + EXPECT_FALSE(reader.SkipBits(1)); + EXPECT_TRUE(reader.ReadBits(0, &value8)); + EXPECT_TRUE(reader.SkipBits(0)); + + reader.Initialize(buffer, sizeof(buffer)); + EXPECT_TRUE(reader.SkipBits(4)); + EXPECT_FALSE(reader.SkipBits(5)); + EXPECT_FALSE(reader.ReadBits(1, &value8)); + EXPECT_FALSE(reader.SkipBits(1)); + EXPECT_TRUE(reader.ReadBits(0, &value8)); + EXPECT_TRUE(reader.SkipBits(0)); +} + +} // namespace media diff --git a/media/media.gyp b/media/media.gyp index 29300fe..c73c314 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -133,6 +133,8 @@ 'base/audio_renderer_mixer.h', 'base/audio_renderer_mixer_input.cc', 'base/audio_renderer_mixer_input.h', + 'base/bit_reader.cc', + 'base/bit_reader.h', 'base/bitstream_buffer.h', 'base/buffers.cc', 'base/buffers.h', @@ -452,6 +454,8 @@ }], ['proprietary_codecs==1 or branding=="Chrome"', { 'sources': [ + 'mp4/aac.cc', + 'mp4/aac.h', 'mp4/avc.cc', 'mp4/avc.h', 'mp4/box_definitions.cc', @@ -460,6 +464,8 @@ 'mp4/box_reader.h', 'mp4/cenc.cc', 'mp4/cenc.h', + 'mp4/es_descriptor.cc', + 'mp4/es_descriptor.h', 'mp4/mp4_stream_parser.cc', 'mp4/mp4_stream_parser.h', 'mp4/offset_byte_queue.cc', @@ -665,6 +671,7 @@ 'audio/win/audio_output_win_unittest.cc', 'base/audio_renderer_mixer_unittest.cc', 'base/audio_renderer_mixer_input_unittest.cc', + 'base/bit_reader_unittest.cc', 'base/buffers_unittest.cc', 'base/clock_unittest.cc', 'base/composite_filter_unittest.cc', @@ -762,8 +769,10 @@ }], ['proprietary_codecs==1 or branding=="Chrome"', { 'sources': [ + 'mp4/aac_unittest.cc', 'mp4/avc_unittest.cc', 'mp4/box_reader_unittest.cc', + 'mp4/es_descriptor_unittest.cc', 'mp4/mp4_stream_parser_unittest.cc', 'mp4/offset_byte_queue_unittest.cc', ], diff --git a/media/mp4/aac.cc b/media/mp4/aac.cc new file mode 100644 index 0000000..e79e927 --- /dev/null +++ b/media/mp4/aac.cc @@ -0,0 +1,252 @@ +// 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 "media/mp4/aac.h" + +#include "base/logging.h" +#include "media/base/bit_reader.h" +#include "media/mp4/rcheck.h" + +// The following conversion table is extracted from ISO 14496 Part 3 - +// Table 1.16 - Sampling Frequency Index. +static const uint32 kFrequencyMap[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, + 22050, 16000, 12000, 11025, 8000, 7350 +}; + +static ChannelLayout GetChannelLayout(uint8 channel_config) { + switch (channel_config) { + case 1: + return CHANNEL_LAYOUT_MONO; + case 2: + return CHANNEL_LAYOUT_STEREO; + case 3: + return CHANNEL_LAYOUT_SURROUND; + case 4: + return CHANNEL_LAYOUT_4_0; + case 5: + return CHANNEL_LAYOUT_5_0; + case 6: + return CHANNEL_LAYOUT_5_1; + case 8: + return CHANNEL_LAYOUT_7_1; + default: + break; + } + + return CHANNEL_LAYOUT_UNSUPPORTED; +} + +namespace media { + +namespace mp4 { + +AAC::AAC() + : profile_(0), frequency_index_(0), channel_config_(0), frequency_(0), + channel_layout_(CHANNEL_LAYOUT_UNSUPPORTED) { +} + +AAC::~AAC() { +} + +bool AAC::Parse(const std::vector<uint8>& data) { + BitReader reader; + uint8 extension_type = 0; + bool ps_present = false; + uint8 extension_frequency_index; + + profile_ = 0; + frequency_index_ = 0; + frequency_ = 0; + channel_config_ = 0; + + reader.Initialize(&data[0], data.size()); + + // The following code is written according to ISO 14496 Part 3 Table 1.13 - + // Syntax of AudioSpecificConfig. + + // Read base configuration + RCHECK(reader.ReadBits(5, &profile_)); + RCHECK(reader.ReadBits(4, &frequency_index_)); + if (frequency_index_ == 0xf) + RCHECK(reader.ReadBits(24, &frequency_)); + RCHECK(reader.ReadBits(4, &channel_config_)); + + extension_frequency_index = frequency_index_; + + // Read extension configuration + if (profile_ == 5 || profile_ == 29) { + ps_present = (profile_ == 29); + extension_type = 5; + RCHECK(reader.ReadBits(4, &extension_frequency_index)); + if (extension_frequency_index == 0xf) + RCHECK(reader.ReadBits(24, &frequency_)); + RCHECK(reader.ReadBits(5, &profile_)); + } + + RCHECK(SkipDecoderGASpecificConfig(&reader)); + RCHECK(SkipErrorSpecificConfig()); + + // Read extension configuration again + if (extension_type != 5 && reader.NumBitsLeft() >= 16) { + uint16 sync_extension_type; + uint8 sbr_present_flag; + uint8 ps_present_flag; + + RCHECK(reader.ReadBits(11, &sync_extension_type)); + + if (sync_extension_type == 0x2b7) { + RCHECK(reader.ReadBits(5, &extension_type)); + + if (extension_type == 5) { + RCHECK(reader.ReadBits(1, &sbr_present_flag)); + + if (sbr_present_flag) { + RCHECK(reader.ReadBits(4, &extension_frequency_index)); + + if (extension_frequency_index == 0xf) + RCHECK(reader.ReadBits(24, &frequency_)); + + RCHECK(reader.ReadBits(11, &sync_extension_type)); + + if (sync_extension_type == 0x548) { + RCHECK(reader.ReadBits(1, &ps_present_flag)); + ps_present = ps_present_flag != 0; + } + } + } + } + } + + if (frequency_ == 0) { + RCHECK(extension_frequency_index < arraysize(kFrequencyMap)); + frequency_ = kFrequencyMap[extension_frequency_index]; + } + + // When Parametric Stereo is on, mono will be played as stereo. + if (ps_present && channel_config_ == 1) + channel_layout_ = GetChannelLayout(channel_config_ + 1); + else + channel_layout_ = GetChannelLayout(channel_config_); + + return frequency_ != 0 && channel_layout_ != CHANNEL_LAYOUT_UNSUPPORTED && + profile_ >= 1 && profile_ <= 4 && frequency_index_ != 0xf && + channel_config_ <= 7; +} + +uint32 AAC::frequency() const { + return frequency_; +} + +ChannelLayout AAC::channel_layout() const { + return channel_layout_; +} + +bool AAC::ConvertEsdsToADTS(std::vector<uint8>* buffer) const { + size_t size = buffer->size() + 7; + + DCHECK(profile_ >= 1 && profile_ <= 4 && frequency_index_ != 0xf && + channel_config_ <= 7); + + // ADTS header uses 13 bits for packet size. + if (size > (1 << 13) - 1) + return false; + + std::vector<uint8>& adts = *buffer; + + adts.insert(buffer->begin(), 7, 0); + adts[0] = 0xff; + adts[1] = 0xf1; + adts[2] = ((profile_ - 1) << 6) + (frequency_index_ << 2) + + (channel_config_ >> 2); + adts[3] = ((channel_config_ & 0x3) << 6) + (size >> 11); + adts[4] = (size & 0x7ff) >> 3; + adts[5] = ((size & 7) << 5) + 0x1f; + adts[6] = 0xfc; + + return true; +} + +// Currently this function only support GASpecificConfig defined in +// ISO 14496 Part 3 Table 4.1 - Syntax of GASpecificConfig() +bool AAC::SkipDecoderGASpecificConfig(BitReader* bit_reader) const { + switch (profile_) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + return SkipGASpecificConfig(bit_reader); + default: + break; + } + + return false; +} + +bool AAC::SkipErrorSpecificConfig() const { + switch (profile_) { + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + return false; + default: + break; + } + + return true; +} + +// The following code is written according to ISO 14496 part 3 Table 4.1 - +// GASpecificConfig. +bool AAC::SkipGASpecificConfig(BitReader* bit_reader) const { + uint8 extension_flag = 0; + uint8 depends_on_core_coder; + + RCHECK(bit_reader->SkipBits(1)); // frameLengthFlag + RCHECK(bit_reader->ReadBits(1, &depends_on_core_coder)); + if (depends_on_core_coder == 1) + RCHECK(bit_reader->SkipBits(14)); // coreCoderDelay + + RCHECK(bit_reader->ReadBits(1, &extension_flag)); + RCHECK(channel_config_ != 0); + + if (profile_ == 6 || profile_ == 20) + RCHECK(bit_reader->SkipBits(3)); + + if (extension_flag) { + if (profile_ == 22) { + RCHECK(bit_reader->SkipBits(5)); + RCHECK(bit_reader->SkipBits(11)); + } + + if (profile_ == 17 || profile_ == 19 || profile_ == 20 || profile_ == 23) { + RCHECK(bit_reader->SkipBits(1)); + RCHECK(bit_reader->SkipBits(1)); + RCHECK(bit_reader->SkipBits(1)); + } + + RCHECK(bit_reader->SkipBits(1)); + } + + return true; +} + +} // namespace mp4 + +} // namespace media diff --git a/media/mp4/aac.h b/media/mp4/aac.h new file mode 100644 index 0000000..7a2a3f8 --- /dev/null +++ b/media/mp4/aac.h @@ -0,0 +1,67 @@ +// 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 MEDIA_MP4_AAC_H_ +#define MEDIA_MP4_AAC_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "media/base/channel_layout.h" +#include "media/base/media_export.h" + +namespace media { + +class BitReader; + +namespace mp4 { + +// This class parses the AAC information from decoder specific information +// embedded in the esds box in an ISO BMFF file. +// Please refer to ISO 14496 Part 3 Table 1.13 - Syntax of AudioSpecificConfig +// for more details. +class MEDIA_EXPORT AAC { + public: + AAC(); + ~AAC(); + + // Parse the AAC config from the raw binary data embedded in esds box. + // The function will parse the data and get the ElementaryStreamDescriptor, + // then it will parse the ElementaryStreamDescriptor to get audio stream + // configurations. + bool Parse(const std::vector<uint8>& data); + + uint32 frequency() const; + ChannelLayout channel_layout() const; + + // This function converts a raw AAC frame into an AAC frame with an ADTS + // header. On success, the function returns true and stores the converted data + // in the buffer. The function returns false on failure and leaves the buffer + // unchanged. + bool ConvertEsdsToADTS(std::vector<uint8>* buffer) const; + + private: + bool SkipDecoderGASpecificConfig(BitReader* bit_reader) const; + bool SkipErrorSpecificConfig() const; + bool SkipGASpecificConfig(BitReader* bit_reader) const; + + // The following variables store the AAC specific configuration information + // that are used to generate the ADTS header. + uint8 profile_; + uint8 frequency_index_; + uint8 channel_config_; + + // The following variables store audio configuration information that + // can be used by Chromium. They are based on the AAC specific + // configuration but can be overridden by extensions in elementary + // stream descriptor. + uint32 frequency_; + ChannelLayout channel_layout_; +}; + +} // namespace mp4 + +} // namespace media + +#endif // MEDIA_MP4_AAC_H_ diff --git a/media/mp4/aac_unittest.cc b/media/mp4/aac_unittest.cc new file mode 100644 index 0000000..2f61de5 --- /dev/null +++ b/media/mp4/aac_unittest.cc @@ -0,0 +1,104 @@ +// 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 "media/mp4/aac.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +namespace mp4 { + +TEST(AACTest, BasicProfileTest) { + AAC aac; + uint8 buffer[] = {0x12, 0x10}; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_TRUE(aac.Parse(data)); + EXPECT_EQ(aac.frequency(), 44100u); + EXPECT_EQ(aac.channel_layout(), CHANNEL_LAYOUT_STEREO); +} + +TEST(AACTest, ExtensionTest) { + AAC aac; + uint8 buffer[] = {0x13, 0x08, 0x56, 0xe5, 0x9d, 0x48, 0x80}; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_TRUE(aac.Parse(data)); + EXPECT_EQ(aac.frequency(), 48000u); + EXPECT_EQ(aac.channel_layout(), CHANNEL_LAYOUT_STEREO); +} + +TEST(AACTest, SixChannelTest) { + AAC aac; + uint8 buffer[] = {0x11, 0xb0}; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_TRUE(aac.Parse(data)); + EXPECT_EQ(aac.frequency(), 48000u); + EXPECT_EQ(aac.channel_layout(), CHANNEL_LAYOUT_5_1); +} + +TEST(AACTest, DataTooShortTest) { + AAC aac; + std::vector<uint8> data; + + EXPECT_FALSE(aac.Parse(data)); + + data.push_back(0x12); + EXPECT_FALSE(aac.Parse(data)); +} + +TEST(AACTest, IncorrectProfileTest) { + AAC aac; + uint8 buffer[] = {0x0, 0x08}; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_FALSE(aac.Parse(data)); + + data[0] = 0x08; + EXPECT_TRUE(aac.Parse(data)); + + data[0] = 0x28; + EXPECT_FALSE(aac.Parse(data)); +} + +TEST(AACTest, IncorrectFrequencyTest) { + AAC aac; + uint8 buffer[] = {0x0f, 0x88}; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_FALSE(aac.Parse(data)); + + data[0] = 0x0e; + data[1] = 0x08; + EXPECT_TRUE(aac.Parse(data)); +} + +TEST(AACTest, IncorrectChannelTest) { + AAC aac; + uint8 buffer[] = {0x0e, 0x00}; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_FALSE(aac.Parse(data)); + + data[1] = 0x08; + EXPECT_TRUE(aac.Parse(data)); +} + +} // namespace mp4 + +} // namespace media diff --git a/media/mp4/avc.cc b/media/mp4/avc.cc index faf9939..2999466 100644 --- a/media/mp4/avc.cc +++ b/media/mp4/avc.cc @@ -88,27 +88,5 @@ bool AVC::InsertParameterSets(const AVCDecoderConfigurationRecord& avc_config, return true; } -// static -ChannelLayout AVC::ConvertAACChannelCountToChannelLayout(int count) { - switch (count) { - case 1: - return CHANNEL_LAYOUT_MONO; - case 2: - return CHANNEL_LAYOUT_STEREO; - case 3: - return CHANNEL_LAYOUT_SURROUND; - case 4: - return CHANNEL_LAYOUT_4_0; - case 5: - return CHANNEL_LAYOUT_5_0; - case 6: - return CHANNEL_LAYOUT_5_1; - case 8: - return CHANNEL_LAYOUT_7_1; - default: - return CHANNEL_LAYOUT_UNSUPPORTED; - } -} - } // namespace mp4 } // namespace media diff --git a/media/mp4/avc.h b/media/mp4/avc.h index 2767c14..8e826b0 100644 --- a/media/mp4/avc.h +++ b/media/mp4/avc.h @@ -8,7 +8,6 @@ #include <vector> #include "base/basictypes.h" -#include "media/base/channel_layout.h" #include "media/base/media_export.h" namespace media { @@ -23,11 +22,8 @@ class MEDIA_EXPORT AVC { static bool InsertParameterSets( const AVCDecoderConfigurationRecord& avc_config, std::vector<uint8>* buffer); - - static ChannelLayout ConvertAACChannelCountToChannelLayout(int count); }; - } // namespace mp4 } // namespace media diff --git a/media/mp4/box_definitions.cc b/media/mp4/box_definitions.cc index ff86372..eb43530 100644 --- a/media/mp4/box_definitions.cc +++ b/media/mp4/box_definitions.cc @@ -5,8 +5,7 @@ #include "media/mp4/box_definitions.h" #include "base/logging.h" -#include "media/mp4/box_reader.h" -#include "media/mp4/fourccs.h" +#include "media/mp4/es_descriptor.h" #include "media/mp4/rcheck.h" namespace media { @@ -389,6 +388,29 @@ bool VideoSampleEntry::Parse(BoxReader* reader) { return true; } +ElementaryStreamDescriptor::ElementaryStreamDescriptor() {} + +ElementaryStreamDescriptor::~ElementaryStreamDescriptor() {} + +FourCC ElementaryStreamDescriptor::BoxType() const { + return FOURCC_ESDS; +} + +bool ElementaryStreamDescriptor::Parse(BoxReader* reader) { + std::vector<uint8> data; + ESDescriptor es_desc; + + RCHECK(reader->ReadFullBoxHeader()); + RCHECK(reader->ReadVec(&data, reader->size() - reader->pos())); + RCHECK(es_desc.Parse(data)); + + object_type = es_desc.object_type(); + + RCHECK(aac.Parse(es_desc.decoder_specific_info())); + + return true; +} + AudioSampleEntry::AudioSampleEntry() : format(FOURCC_NULL), data_reference_index(0), @@ -397,6 +419,7 @@ AudioSampleEntry::AudioSampleEntry() samplerate(0) {} AudioSampleEntry::~AudioSampleEntry() {} + FourCC AudioSampleEntry::BoxType() const { DCHECK(false) << "AudioSampleEntry should be parsed according to the " << "handler type recovered in its Media ancestor."; @@ -419,6 +442,7 @@ bool AudioSampleEntry::Parse(BoxReader* reader) { if (format == FOURCC_ENCA) { RCHECK(reader->ReadChild(&sinf)); } + RCHECK(reader->ReadChild(&esds)); return true; } diff --git a/media/mp4/box_definitions.h b/media/mp4/box_definitions.h index 52a0e86..0b7e771 100644 --- a/media/mp4/box_definitions.h +++ b/media/mp4/box_definitions.h @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "media/base/media_export.h" +#include "media/mp4/aac.h" #include "media/mp4/avc.h" #include "media/mp4/box_reader.h" #include "media/mp4/fourccs.h" @@ -184,6 +185,13 @@ struct VideoSampleEntry : Box { AVCDecoderConfigurationRecord avcc; }; +struct ElementaryStreamDescriptor : Box { + DECLARE_BOX_METHODS(ElementaryStreamDescriptor); + + uint8 object_type; + AAC aac; +}; + struct AudioSampleEntry : Box { DECLARE_BOX_METHODS(AudioSampleEntry); @@ -194,6 +202,7 @@ struct AudioSampleEntry : Box { uint32 samplerate; ProtectionSchemeInfo sinf; + ElementaryStreamDescriptor esds; }; struct SampleDescription : Box { diff --git a/media/mp4/es_descriptor.cc b/media/mp4/es_descriptor.cc new file mode 100644 index 0000000..1cb4c65 --- /dev/null +++ b/media/mp4/es_descriptor.cc @@ -0,0 +1,112 @@ +// 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 "media/mp4/es_descriptor.h" + +#include "media/base/bit_reader.h" +#include "media/mp4/rcheck.h" + +// The elementary stream size is specific by up to 4 bytes. +// The MSB of a byte indicates if there are more bytes for the size. +static bool ReadESSize(media::BitReader* reader, uint32* size) { + uint8 msb; + uint8 byte; + + *size = 0; + + for (size_t i = 0; i < 4; ++i) { + RCHECK(reader->ReadBits(1, &msb)); + RCHECK(reader->ReadBits(7, &byte)); + *size = (*size << 7) + byte; + + if (msb == 0) + break; + } + + return true; +} + +namespace media { + +namespace mp4 { + +ESDescriptor::ESDescriptor() + : object_type_(kForbidden) { +} + +ESDescriptor::~ESDescriptor() {} + +bool ESDescriptor::Parse(const std::vector<uint8>& data) { + BitReader reader(&data[0], data.size()); + uint8 tag; + uint32 size; + uint8 stream_dependency_flag; + uint8 url_flag; + uint8 ocr_stream_flag; + + RCHECK(reader.ReadBits(8, &tag)); + RCHECK(tag == kESDescrTag); + RCHECK(ReadESSize(&reader, &size)); + RCHECK(static_cast<off_t>(size * CHAR_BIT) <= reader.NumBitsLeft()); + + RCHECK(reader.SkipBits(16)); // ES_ID + RCHECK(reader.ReadBits(1, &stream_dependency_flag)); + RCHECK(reader.ReadBits(1, &url_flag)); + RCHECK(reader.ReadBits(1, &ocr_stream_flag)); + RCHECK(reader.SkipBits(5)); // streamPriority + + if (stream_dependency_flag) + reader.SkipBits(16); // dependsOn_ES_ID; + RCHECK(!url_flag); // We don't support url flag + if (ocr_stream_flag) + reader.SkipBits(16); // OCR_ES_Id + + RCHECK(ParseDecoderConfigDescriptor(&reader)); + + return true; +} + +uint8 ESDescriptor::object_type() const { + return object_type_; +} + +const std::vector<uint8>& ESDescriptor::decoder_specific_info() const { + return decoder_specific_info_; +} + +bool ESDescriptor::ParseDecoderConfigDescriptor(BitReader* reader) { + uint8 tag; + uint32 size; + + RCHECK(reader->ReadBits(8, &tag)); + RCHECK(tag == kDecoderConfigDescrTag); + RCHECK(ReadESSize(reader, &size)); + RCHECK(static_cast<off_t>(size * CHAR_BIT) <= reader->NumBitsLeft()); + + RCHECK(reader->ReadBits(8, &object_type_)); + RCHECK(reader->SkipBits(96)); + RCHECK(ParseDecoderSpecificInfo(reader)); + + return true; +} + +bool ESDescriptor::ParseDecoderSpecificInfo(BitReader* reader) { + uint8 tag; + uint32 size; + + RCHECK(reader->ReadBits(8, &tag)); + RCHECK(tag == kDecoderSpecificInfoTag); + RCHECK(ReadESSize(reader, &size)); + RCHECK(static_cast<off_t>(size * CHAR_BIT) <= reader->NumBitsLeft()); + + decoder_specific_info_.resize(size); + for (uint32 i = 0; i < size; ++i) + RCHECK(reader->ReadBits(8, &decoder_specific_info_[i])); + + return true; +} + +} // namespace mp4 + +} // namespace media diff --git a/media/mp4/es_descriptor.h b/media/mp4/es_descriptor.h new file mode 100644 index 0000000..daddbc0 --- /dev/null +++ b/media/mp4/es_descriptor.h @@ -0,0 +1,57 @@ +// 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 MEDIA_MP4_ES_DESCRIPTOR_H_ +#define MEDIA_MP4_ES_DESCRIPTOR_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "media/base/media_export.h" + +namespace media { + +class BitReader; + +namespace mp4 { + +// The following values are extracted from ISO 14496 Part 1 Table 5 - +// objectTypeIndication Values. Only values currently in use are included. +enum ObjectType { + kForbidden = 0, + kISO_14496_3 = 0x40 // MPEG4 AAC +}; + +// This class parse object type and decoder specific information from an +// elementary stream descriptor, which is usually contained in an esds box. +// Please refer to ISO 14496 Part 1 7.2.6.5 for more details. +class MEDIA_EXPORT ESDescriptor { + public: + ESDescriptor(); + ~ESDescriptor(); + + bool Parse(const std::vector<uint8>& data); + + uint8 object_type() const; + const std::vector<uint8>& decoder_specific_info() const; + + private: + enum Tag { + kESDescrTag = 0x03, + kDecoderConfigDescrTag = 0x04, + kDecoderSpecificInfoTag = 0x05 + }; + + bool ParseDecoderConfigDescriptor(BitReader* reader); + bool ParseDecoderSpecificInfo(BitReader* reader); + + uint8 object_type_; + std::vector<uint8> decoder_specific_info_; +}; + +} // namespace mp4 + +} // namespace media + +#endif // MEDIA_MP4_ES_DESCRIPTOR_H_ diff --git a/media/mp4/es_descriptor_unittest.cc b/media/mp4/es_descriptor_unittest.cc new file mode 100644 index 0000000..c3a39fb --- /dev/null +++ b/media/mp4/es_descriptor_unittest.cc @@ -0,0 +1,92 @@ +// 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 "media/mp4/es_descriptor.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +namespace mp4 { + +TEST(ESDescriptorTest, SingleByteLengthTest) { + ESDescriptor es_desc; + uint8 buffer[] = { + 0x03, 0x19, 0x00, 0x01, 0x00, 0x04, 0x11, 0x40, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x12, 0x10, + 0x06, 0x01, 0x02 + }; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_EQ(es_desc.object_type(), kForbidden); + EXPECT_TRUE(es_desc.Parse(data)); + EXPECT_EQ(es_desc.object_type(), kISO_14496_3); + EXPECT_EQ(es_desc.decoder_specific_info().size(), 2u); + EXPECT_EQ(es_desc.decoder_specific_info()[0], 0x12); + EXPECT_EQ(es_desc.decoder_specific_info()[1], 0x10); +} + +TEST(ESDescriptorTest, NonAACTest) { + ESDescriptor es_desc; + uint8 buffer[] = { + 0x03, 0x19, 0x00, 0x01, 0x00, 0x04, 0x11, 0x66, + 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x02, 0x12, 0x10, + 0x06, 0x01, 0x02 + }; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_TRUE(es_desc.Parse(data)); + EXPECT_NE(es_desc.object_type(), kISO_14496_3); + EXPECT_EQ(es_desc.decoder_specific_info().size(), 2u); + EXPECT_EQ(es_desc.decoder_specific_info()[0], 0x12); + EXPECT_EQ(es_desc.decoder_specific_info()[1], 0x10); +} + +TEST(ESDescriptorTest, MultiByteLengthTest) { + ESDescriptor es_desc; + uint8 buffer[] = { + 0x03, 0x80, 0x19, 0x00, 0x01, 0x00, 0x04, 0x80, + 0x80, 0x11, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x80, 0x80, 0x80, 0x02, 0x12, 0x10, 0x06, 0x01, + 0x02 + }; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_TRUE(es_desc.Parse(data)); + EXPECT_EQ(es_desc.object_type(), kISO_14496_3); + EXPECT_EQ(es_desc.decoder_specific_info().size(), 2u); + EXPECT_EQ(es_desc.decoder_specific_info()[0], 0x12); + EXPECT_EQ(es_desc.decoder_specific_info()[1], 0x10); +} + +TEST(ESDescriptorTest, FiveByteLengthTest) { + ESDescriptor es_desc; + uint8 buffer[] = { + 0x03, 0x80, 0x19, 0x00, 0x01, 0x00, 0x04, 0x80, + 0x80, 0x11, 0x40, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x80, 0x80, 0x80, 0x80, 0x02, 0x12, 0x10, 0x06, + 0x01, 0x02 + }; + std::vector<uint8> data; + + data.assign(buffer, buffer + sizeof(buffer)); + + EXPECT_TRUE(es_desc.Parse(data)); + EXPECT_EQ(es_desc.object_type(), kISO_14496_3); + EXPECT_EQ(es_desc.decoder_specific_info().size(), 0u); +} + +} // namespace mp4 + +} // namespace media diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc index bb2a95c..ebaa2a2 100644 --- a/media/mp4/mp4_stream_parser.cc +++ b/media/mp4/mp4_stream_parser.cc @@ -13,6 +13,7 @@ #include "media/base/video_decoder_config.h" #include "media/mp4/box_definitions.h" #include "media/mp4/box_reader.h" +#include "media/mp4/es_descriptor.h" #include "media/mp4/rcheck.h" namespace media { @@ -162,10 +163,13 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { // (entry.format == FOURCC_ENCA && // entry.sinf.format.format == FOURCC_MP4A)); - const ChannelLayout layout = - AVC::ConvertAACChannelCountToChannelLayout(entry.channelcount); - audio_config.Initialize(kCodecAAC, entry.samplesize, layout, - entry.samplerate, NULL, 0, false); + // Check if it is MPEG4 AAC defined in ISO 14496 Part 3. + RCHECK(entry.esds.object_type == kISO_14496_3); + aac_ = entry.esds.aac; + audio_config.Initialize(kCodecAAC, entry.samplesize, + aac_.channel_layout(), aac_.frequency(), + NULL, 0, false); + has_audio_ = true; audio_track_id_ = track->header.track_id; } @@ -291,6 +295,10 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers, } } + if (audio) { + aac_.ConvertEsdsToADTS(&frame_buf); + } + scoped_refptr<StreamParserBuffer> stream_buf = StreamParserBuffer::CopyFrom(&frame_buf[0], frame_buf.size(), runs_.is_keyframe()); diff --git a/media/mp4/mp4_stream_parser.h b/media/mp4/mp4_stream_parser.h index 7d9be27..706aa65 100644 --- a/media/mp4/mp4_stream_parser.h +++ b/media/mp4/mp4_stream_parser.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "media/base/media_export.h" #include "media/base/stream_parser.h" +#include "media/mp4/aac.h" #include "media/mp4/offset_byte_queue.h" #include "media/mp4/track_run_iterator.h" @@ -85,8 +86,9 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser { uint32 audio_track_id_; uint32 video_track_id_; - // We keep this around to avoid having to go digging through the moov with + // We keep them around to avoid having to go digging through the moov with // every frame. + AAC aac_; uint8 size_of_nalu_length_; DISALLOW_COPY_AND_ASSIGN(MP4StreamParser); |