summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/common/gpu/media/h264_bit_reader.cc45
-rw-r--r--content/common/gpu/media/h264_bit_reader.h31
-rw-r--r--content/common/gpu/media/h264_bit_reader_unittest.cc68
-rw-r--r--content/common/gpu/media/h264_parser.cc103
-rw-r--r--content/common/gpu/media/h264_parser.h56
-rw-r--r--content/content_common.gypi2
-rw-r--r--content/content_tests.gypi1
-rw-r--r--media/base/bit_reader.cc74
-rw-r--r--media/base/bit_reader.h110
-rw-r--r--media/base/bit_reader_unittest.cc110
-rw-r--r--media/media.gyp9
-rw-r--r--media/mp4/aac.cc252
-rw-r--r--media/mp4/aac.h67
-rw-r--r--media/mp4/aac_unittest.cc104
-rw-r--r--media/mp4/avc.cc22
-rw-r--r--media/mp4/avc.h4
-rw-r--r--media/mp4/box_definitions.cc28
-rw-r--r--media/mp4/box_definitions.h9
-rw-r--r--media/mp4/es_descriptor.cc112
-rw-r--r--media/mp4/es_descriptor.h57
-rw-r--r--media/mp4/es_descriptor_unittest.cc92
-rw-r--r--media/mp4/mp4_stream_parser.cc16
-rw-r--r--media/mp4/mp4_stream_parser.h4
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);