Merge "Added ADTS header parsing, removal, and conversion to AudioSpecificConfig. Hooked up mp2t media parser, audio transmux now working."

This commit is contained in:
Thomas Inskip 2014-04-23 20:39:05 +00:00 committed by Gerrit Code Review
commit faa12bd191
9 changed files with 334 additions and 55 deletions

View File

@ -15,6 +15,7 @@
#include "media/base/media_stream.h"
#include "media/base/stream_info.h"
#include "media/file/file.h"
#include "media/formats/mp2t/mp2t_media_parser.h"
#include "media/formats/mp4/mp4_media_parser.h"
namespace {
@ -58,6 +59,9 @@ Status Demuxer::Initialize() {
case CONTAINER_MOV:
parser_.reset(new mp4::MP4MediaParser());
break;
case CONTAINER_MPEG2TS:
parser_.reset(new mp2t::MediaParser());
break;
default:
NOTIMPLEMENTED();
return Status(error::UNIMPLEMENTED, "Container not supported.");

View File

@ -36,8 +36,8 @@
'h264_parser_unittest.cc',
],
'dependencies': [
'../../media/base/media_base.gyp:base',
'../../testing/gtest.gyp:gtest',
'../../testing/gtest.gyp:gtest_main',
'../test/media_test.gyp:media_test_support',
'filters',
],

View File

@ -0,0 +1,114 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "adts_header.h"
#include "media/base/bit_reader.h"
#include "media/formats/mp2t/mp2t_common.h"
#include "media/formats/mpeg/adts_constants.h"
namespace media {
namespace mp2t {
AdtsHeader::AdtsHeader()
: valid_config_(false),
profile_(0),
sampling_frequency_index_(0),
channel_configuration_(0) {}
size_t AdtsHeader::GetAdtsFrameSize(const uint8* data, size_t num_bytes) {
if (num_bytes < 6)
return 0;
return ((static_cast<int>(data[5]) >> 5) |
(static_cast<int>(data[4]) << 3) |
((static_cast<int>(data[3]) & 0x3) << 11));
}
size_t AdtsHeader::GetAdtsHeaderSize(const uint8* data, size_t num_bytes) {
if (num_bytes < 2)
return 0;
if (data[1] & 0x01)
return kAdtsHeaderMinSize;
return kAdtsHeaderMinSize + sizeof(uint16); // Header + CRC.
}
bool AdtsHeader::Parse(
const uint8* adts_frame, size_t adts_frame_size) {
CHECK(adts_frame);
valid_config_ = false;
BitReader frame(adts_frame, adts_frame_size);
// Verify frame starts with sync bits (0xfff).
uint32 sync;
RCHECK(frame.ReadBits(12, &sync));
RCHECK(sync == 0xfff);
// Skip MPEG version and layer.
RCHECK(frame.SkipBits(3));
// Get "protection absent" flag.
bool protection_absent;
RCHECK(frame.ReadBits(1, &protection_absent));
// Get profile.
RCHECK(frame.ReadBits(2, &profile_));
// Get sampling frequency.
RCHECK(frame.ReadBits(4, &sampling_frequency_index_));
RCHECK(sampling_frequency_index_ < kAdtsFrequencyTableSize);
// Skip private stream bit.
RCHECK(frame.SkipBits(1));
// Get number of audio channels.
RCHECK(frame.ReadBits(3, &channel_configuration_));
RCHECK((channel_configuration_ > 0) &&
(channel_configuration_ < kAdtsNumChannelsTableSize));
// Skip originality, home and copyright info.
RCHECK(frame.SkipBits(4));
// Verify that the frame size matches input parameters.
uint16 frame_size;
RCHECK(frame.ReadBits(13, &frame_size));
RCHECK(frame_size == adts_frame_size);
// Skip buffer fullness indicator.
RCHECK(frame.SkipBits(11));
uint8 num_blocks_minus_1;
RCHECK(frame.ReadBits(2, &num_blocks_minus_1));
if (num_blocks_minus_1) {
NOTIMPLEMENTED() << "ADTS frames with more than one data block "
"not supported.";
return false;
}
valid_config_ = true;
return true;
}
bool AdtsHeader::GetAudioSpecificConfig(
std::vector<uint8>* buffer) const {
DCHECK(buffer);
if (!valid_config_)
return false;
buffer->resize(2);
(*buffer)[0] = ((profile_ + 1) << 3) | (sampling_frequency_index_ >> 1);
(*buffer)[1] = ((sampling_frequency_index_ & 1) << 7) |
(channel_configuration_ << 3);
return true;
}
uint8 AdtsHeader::GetObjectType() const {
return profile_ + 1;
}
uint32 AdtsHeader::GetSamplingFrequency() const {
DCHECK_LT(sampling_frequency_index_, kAdtsFrequencyTableSize);
return kAdtsFrequencyTable[sampling_frequency_index_];
}
uint8 AdtsHeader::GetNumChannels() const {
DCHECK_GT(channel_configuration_, 0);
DCHECK_LT(channel_configuration_, kAdtsNumChannelsTableSize);
return kAdtsNumChannelsTable[channel_configuration_];
}
} // namespace mp2t
} // namespace media

View File

@ -0,0 +1,72 @@
// Copyright 2014 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef MEDIA_FORMATS_MP2T_ADTS_HEADER_H_
#define MEDIA_FORMATS_MP2T_ADTS_HEADER_H_
#include "base/basictypes.h"
#include <vector>
namespace media {
namespace mp2t {
/// Class which parses ADTS headers and synthesizes AudioSpecificConfig
/// and audio mime type from ADTS header contents.
class AdtsHeader {
public:
AdtsHeader();
~AdtsHeader() {}
/// Get the size of the ADTS frame from a partial or complete frame.
/// @param data is a pointer to the beginning of the ADTS frame.
/// @param num_bytes is the number of data bytes at @a data.
/// @return Size of the ADTS frame (header + payload) if successful, or
/// zero otherwise.
static size_t GetAdtsFrameSize(const uint8* data, size_t num_bytes);
/// Get the size of the ADTS header from a partial or complete frame.
/// @param data is a pointer to the beginning of the ADTS frame.
/// @param num_bytes is the number of data bytes at @a data.
/// @return Size of the ADTS header if successful, or zero otherwise.
static size_t GetAdtsHeaderSize(const uint8* data, size_t num_bytes);
/// Parse an ADTS header, extracting the fields within.
/// @param adts_frame is an input parameter pointing to the ADTS header
/// of an ADTS-framed audio sample.
/// @param adts_frame_size is the size, in bytes of the input ADTS frame.
/// @return true if successful, false otherwise.
bool Parse(const uint8* adts_frame, size_t adts_frame_size);
/// Synthesize an AudioSpecificConfig record from the fields within the ADTS
/// header.
/// @param [out] buffer is a pointer to a vector to contain the
/// AudioSpecificConfig.
/// @return true if successful, false otherwise.
bool GetAudioSpecificConfig(std::vector<uint8>* buffer) const;
/// @return The audio profile for this ADTS frame.
uint8 GetObjectType() const;
/// @return The sampling frequency for this ADTS frame.
uint32 GetSamplingFrequency() const;
/// @return Number of channels for this AAC config.
uint8 GetNumChannels() const;
private:
bool valid_config_;
uint8 profile_;
uint8 sampling_frequency_index_;
uint8 channel_configuration_;
DISALLOW_COPY_AND_ASSIGN(AdtsHeader);
};
} // namespace mp2t
} // namespace media
#endif // MEDIA_FORMATS_MP2T_ADTS_HEADER_H_

View File

@ -0,0 +1,91 @@
// Copyright 2014 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 "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "media/formats/mp2t/adts_header.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
const char kValidAdtsFrame[] =
"fff15080429ffcda004c61766335332e33352e30004258892a5361062403"
"d040000000001ff9055e9fe77ac56eb1677484e0ef0c3102a39daa8355a5"
"37ecab2b156e4ba73ceedb24ea51194e57c9385fa67eca8edc914902e852"
"3185b52299516e679fb3768aa9f13ccac5b257410080282c9a50318ec94e"
"ba24ea305bafab2b2beab16557ef9ecaa8f17bedea84788c8d42e4b3c65b"
"1e7ecae7528b909bc46c76cca73b906ec980ed9f32b25ecd28f43f9516de"
"3ff249f23bb9c93e64c4808195f284653c40592c1a8dc847f5f11791fd80"
"b18e02c1e1ed9f82c62a1f8ea0f5b6dbf2112c2202973b00de71bb49f906"
"ed1bc63768dda378c8f9c6ed1bb48f68dda378c9f68dda3768dda3768de3"
"23da3768de31bb492a5361062403d040000000001ff9055e9fe77ac56eb1"
"677484e0ef0c3102a39daa8355a537ecab2b156e4ba73ceedb24ea51194e"
"57c9385fa67eca8edc914902e8523185b52299516e679fb3768aa9f13cca"
"c5b257410080282c9a50318ec94eba24ea305bafab2b2beab16557ef9eca"
"a8f17bedea84788c8d42e4b3c65b1e7ecae7528b909bc46c76cca73b906e"
"c980ed9f32b25ecd28f43f9516de3ff249f23bb9c93e64c4808195f28465"
"3c40592c1a8dc847f5f11791fd80b18e02c1e1ed9f82c62a1f8ea0f5b6db"
"f2112c2202973b00de71bb49f906ed1bc63768dda378c8f9c6ed1bb48f68"
"dda378c9f68dda3768dda3768de323da3768de31bb4e";
const uint8 kExpectedAudioSpecificConfig[] = { 0x12, 0x10 };
} // anonymous namespace
namespace media {
namespace mp2t {
class AdtsHeaderTest : public testing::Test {
public:
virtual void SetUp() OVERRIDE {
ASSERT_TRUE(base::HexStringToBytes(kValidAdtsFrame, &adts_frame_));
}
protected:
std::vector<uint8> adts_frame_;
};
TEST_F(AdtsHeaderTest, ParseSuccess) {
const uint8 kExpectedObjectType(2);
const uint32 kExpectedSamplingFrequency(44100);
const uint8 kExpectedNumChannels(2);
AdtsHeader adts_header;
EXPECT_TRUE(adts_header.Parse(adts_frame_.data(), adts_frame_.size()));
EXPECT_EQ(kExpectedObjectType, adts_header.GetObjectType());
EXPECT_EQ(kExpectedSamplingFrequency, adts_header.GetSamplingFrequency());
EXPECT_EQ(kExpectedNumChannels, adts_header.GetNumChannels());
std::vector<uint8> audio_specific_config;
ASSERT_TRUE(adts_header.GetAudioSpecificConfig(&audio_specific_config));
EXPECT_EQ(arraysize(kExpectedAudioSpecificConfig),
audio_specific_config.size());
EXPECT_EQ(
std::vector<uint8>(
kExpectedAudioSpecificConfig,
kExpectedAudioSpecificConfig + arraysize(kExpectedAudioSpecificConfig)),
audio_specific_config);
}
TEST_F(AdtsHeaderTest, ParseFailFrameSize) {
AdtsHeader adts_header;
EXPECT_FALSE(adts_header.Parse(adts_frame_.data(), adts_frame_.size() - 1));
EXPECT_FALSE(adts_header.Parse(adts_frame_.data(), adts_frame_.size() + 1));
EXPECT_FALSE(adts_header.Parse(adts_frame_.data(), 1));
}
TEST_F(AdtsHeaderTest, GetFrameSizeSuccess) {
EXPECT_EQ(adts_frame_.size(),
AdtsHeader::GetAdtsFrameSize(adts_frame_.data(),
adts_frame_.size()));
}
TEST_F(AdtsHeaderTest, GetHeaderSizeSuccess) {
const size_t kExpectedHeaderSize(7);
EXPECT_EQ(kExpectedHeaderSize,
AdtsHeader::GetAdtsHeaderSize(adts_frame_.data(),
adts_frame_.size()));
}
} // Namespace mp2t
} // namespace media

View File

@ -13,26 +13,12 @@
#include "media/base/bit_reader.h"
#include "media/base/media_sample.h"
#include "media/base/timestamp.h"
#include "media/formats/mp2t/adts_header.h"
#include "media/formats/mp2t/mp2t_common.h"
#include "media/formats/mpeg/adts_constants.h"
namespace media {
static int ExtractAdtsFrameSize(const uint8* adts_header) {
return ((static_cast<int>(adts_header[5]) >> 5) |
(static_cast<int>(adts_header[4]) << 3) |
((static_cast<int>(adts_header[3]) & 0x3) << 11));
}
static size_t ExtractAdtsFrequencyIndex(const uint8* adts_header) {
return ((adts_header[2] >> 2) & 0xf);
}
static size_t ExtractAdtsChannelConfig(const uint8* adts_header) {
return (((adts_header[3] >> 6) & 0x3) |
((adts_header[2] & 0x1) << 2));
}
// Return true if buf corresponds to an ADTS syncword.
// |buf| size must be at least 2.
static bool isAdtsSyncWord(const uint8* buf) {
@ -72,7 +58,8 @@ static bool LookForSyncWord(const uint8* raw_es, int raw_es_size,
// The layer field (2 bits) must be set to 0.
continue;
int frame_size = ExtractAdtsFrameSize(cur_buf);
int frame_size =
mp2t::AdtsHeader::GetAdtsFrameSize(cur_buf, kAdtsHeaderMinSize);
if (frame_size < kAdtsHeaderMinSize) {
// Too short to be an ADTS frame.
continue;
@ -131,21 +118,24 @@ bool EsParserAdts::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
int frame_size;
while (LookForSyncWord(raw_es, raw_es_size, es_position,
&es_position, &frame_size)) {
const uint8* frame_ptr = raw_es + es_position;
DVLOG(LOG_LEVEL_ES)
<< "ADTS syncword @ pos=" << es_position
<< " frame_size=" << frame_size;
DVLOG(LOG_LEVEL_ES)
<< "ADTS header: "
<< base::HexEncode(&raw_es[es_position], kAdtsHeaderMinSize);
<< base::HexEncode(frame_ptr, kAdtsHeaderMinSize);
// Do not process the frame if this one is a partial frame.
int remaining_size = raw_es_size - es_position;
if (frame_size > remaining_size)
break;
size_t header_size = AdtsHeader::GetAdtsHeaderSize(frame_ptr, frame_size);
// Update the audio configuration if needed.
DCHECK_GE(frame_size, kAdtsHeaderMinSize);
if (!UpdateAudioConfiguration(&raw_es[es_position]))
if (!UpdateAudioConfiguration(frame_ptr, frame_size))
return false;
// Get the PTS & the duration of this access unit.
@ -164,8 +154,8 @@ bool EsParserAdts::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
scoped_refptr<MediaSample> sample =
MediaSample::CopyFrom(
&raw_es[es_position],
frame_size,
frame_ptr + header_size,
frame_size - header_size,
is_key_frame);
sample->set_pts(current_pts);
sample->set_dts(current_pts);
@ -194,37 +184,34 @@ void EsParserAdts::Reset() {
last_audio_decoder_config_ = scoped_refptr<AudioStreamInfo>();
}
bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_frame,
size_t adts_frame_size) {
const uint8 kAacSampleSizeBits(16);
AdtsHeader adts_header;
if (!adts_header.Parse(adts_frame, adts_frame_size)) {
LOG(ERROR) << "Error parsing ADTS frame header.";
return false;
}
std::vector<uint8> audio_specific_config;
if (!adts_header.GetAudioSpecificConfig(&audio_specific_config))
return false;
if (last_audio_decoder_config_) {
// Varying audio configurations currently not supported. Just assume that
// the audio configuration has not changed.
return true;
}
size_t frequency_index = ExtractAdtsFrequencyIndex(adts_header);
if (frequency_index >= kAdtsFrequencyTableSize) {
// Frequency index 13 & 14 are reserved
// while 15 means that the frequency is explicitly written
// (not supported).
// Verify that the audio decoder config has not changed.
if (last_audio_decoder_config_->extra_data() == audio_specific_config) {
// Audio configuration has not changed.
return true;
}
NOTIMPLEMENTED() << "Varying audio configurations are not supported.";
return false;
}
size_t channel_configuration = ExtractAdtsChannelConfig(adts_header);
if (channel_configuration == 0 ||
channel_configuration >= kAdtsNumChannelsTableSize) {
// TODO(damienv): Add support for inband channel configuration.
return false;
}
// TODO(damienv): support HE-AAC frequency doubling (SBR)
// based on the incoming ADTS profile.
int samples_per_second = kAdtsFrequencyTable[frequency_index];
int adts_profile = (adts_header[2] >> 6) & 0x3;
// The following code is written according to ISO 14496 Part 3 Table 1.11 and
// Table 1.22. (Table 1.11 refers to the capping to 48000, Table 1.22 refers
// to SBR doubling the AAC sample rate.)
// TODO(damienv) : Extend sample rate cap to 96kHz for Level 5 content.
int samples_per_second = adts_header.GetSamplingFrequency();
int extended_samples_per_second = sbr_in_mimetype_
? std::min(2 * samples_per_second, 48000)
: samples_per_second;
@ -235,19 +222,20 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
kMpeg2Timescale,
kInfiniteDuration,
kCodecAAC,
std::string(), // TODO(tinskip): calculate codec string.
AudioStreamInfo::GetCodecString(kCodecAAC,
adts_header.GetObjectType()),
std::string(),
16,
kAdtsNumChannelsTable[channel_configuration],
samples_per_second,
NULL, // TODO(tinskip): calculate AudioSpecificConfig.
0,
kAacSampleSizeBits,
adts_header.GetNumChannels(),
extended_samples_per_second,
audio_specific_config.data(),
audio_specific_config.size(),
false));
DVLOG(1) << "Sampling frequency: " << samples_per_second;
DVLOG(1) << "Extended sampling frequency: " << extended_samples_per_second;
DVLOG(1) << "Channel config: " << channel_configuration;
DVLOG(1) << "Adts profile: " << adts_profile;
DVLOG(1) << "Channel config: " << adts_header.GetNumChannels();
DVLOG(1) << "Object type: " << adts_header.GetObjectType();
// Reset the timestamp helper to use a new sampling frequency.
if (audio_timestamp_helper_) {
int64 base_timestamp = audio_timestamp_helper_->GetTimestamp();
@ -256,7 +244,7 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp);
} else {
audio_timestamp_helper_.reset(
new AudioTimestampHelper(kMpeg2Timescale, samples_per_second));
new AudioTimestampHelper(kMpeg2Timescale, extended_samples_per_second));
}
// Audio config notification.

View File

@ -46,7 +46,7 @@ class EsParserAdts : public EsParser {
// Signal any audio configuration change (if any).
// Return false if the current audio config is not
// a supported ADTS audio config.
bool UpdateAudioConfiguration(const uint8* adts_header);
bool UpdateAudioConfiguration(const uint8* adts_frame, size_t frame_size);
// Discard some bytes from the ES stream.
void DiscardEs(int nbytes);

View File

@ -19,6 +19,8 @@
'target_name': 'mp2t',
'type': '<(component)',
'sources': [
'adts_header.cc',
'adts_header.h',
'es_parser.h',
'es_parser_adts.cc',
'es_parser_adts.h',
@ -45,6 +47,7 @@
'target_name': 'mp2t_unittest',
'type': '<(gtest_target_type)',
'sources': [
'adts_header_unittest.cc',
'es_parser_h264_unittest.cc',
'mp2t_media_parser_unittest.cc',
],

View File

@ -27,7 +27,10 @@
'dependencies': [
'media/event/media_event.gyp:media_event',
'media/file/file.gyp:file',
'media/filters/filters.gyp:filters',
'media/formats/mp2t/mp2t.gyp:mp2t',
'media/formats/mp4/mp4.gyp:mp4',
'media/formats/mpeg/mpeg.gyp:mpeg',
'third_party/gflags/gflags.gyp:gflags',
],
},
@ -52,7 +55,10 @@
],
'dependencies': [
'media/file/file.gyp:file',
'media/filters/filters.gyp:filters',
'media/formats/mp2t/mp2t.gyp:mp2t',
'media/formats/mp4/mp4.gyp:mp4',
'media/formats/mpeg/mpeg.gyp:mpeg',
'media/test/media_test.gyp:media_test_support',
'testing/gtest.gyp:gtest',
],
@ -64,6 +70,7 @@
'media/base/media_base.gyp:*',
'media/event/media_event.gyp:*',
'media/file/file.gyp:*',
'media/formats/mp2t/mp2t.gyp:*',
'media/formats/mp4/mp4.gyp:*',
'mpd/mpd.gyp:*',
],