summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxiaomings@google.com <xiaomings@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-06 21:59:25 +0000
committerxiaomings@google.com <xiaomings@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-06 21:59:25 +0000
commit0b27daac396b790fcc5f7366465e27dd2b9f91f3 (patch)
treeca510596e4c40c316bcb63e9396990eee7d28f12 /media
parent2c1e6e5a9eeb002ec26b23e7d9d86ca795f240b1 (diff)
downloadchromium_src-0b27daac396b790fcc5f7366465e27dd2b9f91f3.zip
chromium_src-0b27daac396b790fcc5f7366465e27dd2b9f91f3.tar.gz
chromium_src-0b27daac396b790fcc5f7366465e27dd2b9f91f3.tar.bz2
Add HE AAC support to ISO BMFF.
1. Parse esds box to get HE AAC config. 2. Send audio config from AAC header to decoder instead of the one embedded in SampleDescription. 3. Convert raw audio data into ADTS before sending to decoder. 4. Abstract general bit stream code from H264BitReader into media::BitReader. BUG=134445 TEST=BitReaderTest, AACTest Review URL: https://chromiumcodereview.appspot.com/10710002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145617 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-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.gyp10
-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
16 files changed, 1038 insertions, 33 deletions
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..f2d4b5b 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,8 +464,11 @@
'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/object_type.h',
'mp4/offset_byte_queue.cc',
'mp4/offset_byte_queue.h',
'mp4/track_run_iterator.cc',
@@ -665,6 +672,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 +770,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);