Merge "Added ADTS header parsing, removal, and conversion to AudioSpecificConfig. Hooked up mp2t media parser, audio transmux now working."
This commit is contained in:
commit
faa12bd191
|
@ -15,6 +15,7 @@
|
||||||
#include "media/base/media_stream.h"
|
#include "media/base/media_stream.h"
|
||||||
#include "media/base/stream_info.h"
|
#include "media/base/stream_info.h"
|
||||||
#include "media/file/file.h"
|
#include "media/file/file.h"
|
||||||
|
#include "media/formats/mp2t/mp2t_media_parser.h"
|
||||||
#include "media/formats/mp4/mp4_media_parser.h"
|
#include "media/formats/mp4/mp4_media_parser.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -58,6 +59,9 @@ Status Demuxer::Initialize() {
|
||||||
case CONTAINER_MOV:
|
case CONTAINER_MOV:
|
||||||
parser_.reset(new mp4::MP4MediaParser());
|
parser_.reset(new mp4::MP4MediaParser());
|
||||||
break;
|
break;
|
||||||
|
case CONTAINER_MPEG2TS:
|
||||||
|
parser_.reset(new mp2t::MediaParser());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
NOTIMPLEMENTED();
|
NOTIMPLEMENTED();
|
||||||
return Status(error::UNIMPLEMENTED, "Container not supported.");
|
return Status(error::UNIMPLEMENTED, "Container not supported.");
|
||||||
|
|
|
@ -36,8 +36,8 @@
|
||||||
'h264_parser_unittest.cc',
|
'h264_parser_unittest.cc',
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
|
'../../media/base/media_base.gyp:base',
|
||||||
'../../testing/gtest.gyp:gtest',
|
'../../testing/gtest.gyp:gtest',
|
||||||
'../../testing/gtest.gyp:gtest_main',
|
|
||||||
'../test/media_test.gyp:media_test_support',
|
'../test/media_test.gyp:media_test_support',
|
||||||
'filters',
|
'filters',
|
||||||
],
|
],
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -13,26 +13,12 @@
|
||||||
#include "media/base/bit_reader.h"
|
#include "media/base/bit_reader.h"
|
||||||
#include "media/base/media_sample.h"
|
#include "media/base/media_sample.h"
|
||||||
#include "media/base/timestamp.h"
|
#include "media/base/timestamp.h"
|
||||||
|
#include "media/formats/mp2t/adts_header.h"
|
||||||
#include "media/formats/mp2t/mp2t_common.h"
|
#include "media/formats/mp2t/mp2t_common.h"
|
||||||
#include "media/formats/mpeg/adts_constants.h"
|
#include "media/formats/mpeg/adts_constants.h"
|
||||||
|
|
||||||
namespace media {
|
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.
|
// Return true if buf corresponds to an ADTS syncword.
|
||||||
// |buf| size must be at least 2.
|
// |buf| size must be at least 2.
|
||||||
static bool isAdtsSyncWord(const uint8* buf) {
|
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.
|
// The layer field (2 bits) must be set to 0.
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int frame_size = ExtractAdtsFrameSize(cur_buf);
|
int frame_size =
|
||||||
|
mp2t::AdtsHeader::GetAdtsFrameSize(cur_buf, kAdtsHeaderMinSize);
|
||||||
if (frame_size < kAdtsHeaderMinSize) {
|
if (frame_size < kAdtsHeaderMinSize) {
|
||||||
// Too short to be an ADTS frame.
|
// Too short to be an ADTS frame.
|
||||||
continue;
|
continue;
|
||||||
|
@ -131,21 +118,24 @@ bool EsParserAdts::Parse(const uint8* buf, int size, int64 pts, int64 dts) {
|
||||||
int frame_size;
|
int frame_size;
|
||||||
while (LookForSyncWord(raw_es, raw_es_size, es_position,
|
while (LookForSyncWord(raw_es, raw_es_size, es_position,
|
||||||
&es_position, &frame_size)) {
|
&es_position, &frame_size)) {
|
||||||
|
const uint8* frame_ptr = raw_es + es_position;
|
||||||
DVLOG(LOG_LEVEL_ES)
|
DVLOG(LOG_LEVEL_ES)
|
||||||
<< "ADTS syncword @ pos=" << es_position
|
<< "ADTS syncword @ pos=" << es_position
|
||||||
<< " frame_size=" << frame_size;
|
<< " frame_size=" << frame_size;
|
||||||
DVLOG(LOG_LEVEL_ES)
|
DVLOG(LOG_LEVEL_ES)
|
||||||
<< "ADTS header: "
|
<< "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.
|
// Do not process the frame if this one is a partial frame.
|
||||||
int remaining_size = raw_es_size - es_position;
|
int remaining_size = raw_es_size - es_position;
|
||||||
if (frame_size > remaining_size)
|
if (frame_size > remaining_size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
size_t header_size = AdtsHeader::GetAdtsHeaderSize(frame_ptr, frame_size);
|
||||||
|
|
||||||
// Update the audio configuration if needed.
|
// Update the audio configuration if needed.
|
||||||
DCHECK_GE(frame_size, kAdtsHeaderMinSize);
|
DCHECK_GE(frame_size, kAdtsHeaderMinSize);
|
||||||
if (!UpdateAudioConfiguration(&raw_es[es_position]))
|
if (!UpdateAudioConfiguration(frame_ptr, frame_size))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Get the PTS & the duration of this access unit.
|
// 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 =
|
scoped_refptr<MediaSample> sample =
|
||||||
MediaSample::CopyFrom(
|
MediaSample::CopyFrom(
|
||||||
&raw_es[es_position],
|
frame_ptr + header_size,
|
||||||
frame_size,
|
frame_size - header_size,
|
||||||
is_key_frame);
|
is_key_frame);
|
||||||
sample->set_pts(current_pts);
|
sample->set_pts(current_pts);
|
||||||
sample->set_dts(current_pts);
|
sample->set_dts(current_pts);
|
||||||
|
@ -194,37 +184,34 @@ void EsParserAdts::Reset() {
|
||||||
last_audio_decoder_config_ = scoped_refptr<AudioStreamInfo>();
|
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_) {
|
if (last_audio_decoder_config_) {
|
||||||
// Varying audio configurations currently not supported. Just assume that
|
// Verify that the audio decoder config has not changed.
|
||||||
// the audio configuration has not changed.
|
if (last_audio_decoder_config_->extra_data() == audio_specific_config) {
|
||||||
|
// Audio configuration has not changed.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
NOTIMPLEMENTED() << "Varying audio configurations are not supported.";
|
||||||
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).
|
|
||||||
return false;
|
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
|
// 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
|
// Table 1.22. (Table 1.11 refers to the capping to 48000, Table 1.22 refers
|
||||||
// to SBR doubling the AAC sample rate.)
|
// 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_
|
int extended_samples_per_second = sbr_in_mimetype_
|
||||||
? std::min(2 * samples_per_second, 48000)
|
? std::min(2 * samples_per_second, 48000)
|
||||||
: samples_per_second;
|
: samples_per_second;
|
||||||
|
@ -235,19 +222,20 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
|
||||||
kMpeg2Timescale,
|
kMpeg2Timescale,
|
||||||
kInfiniteDuration,
|
kInfiniteDuration,
|
||||||
kCodecAAC,
|
kCodecAAC,
|
||||||
std::string(), // TODO(tinskip): calculate codec string.
|
AudioStreamInfo::GetCodecString(kCodecAAC,
|
||||||
|
adts_header.GetObjectType()),
|
||||||
std::string(),
|
std::string(),
|
||||||
16,
|
kAacSampleSizeBits,
|
||||||
kAdtsNumChannelsTable[channel_configuration],
|
adts_header.GetNumChannels(),
|
||||||
samples_per_second,
|
extended_samples_per_second,
|
||||||
NULL, // TODO(tinskip): calculate AudioSpecificConfig.
|
audio_specific_config.data(),
|
||||||
0,
|
audio_specific_config.size(),
|
||||||
false));
|
false));
|
||||||
|
|
||||||
DVLOG(1) << "Sampling frequency: " << samples_per_second;
|
DVLOG(1) << "Sampling frequency: " << samples_per_second;
|
||||||
DVLOG(1) << "Extended sampling frequency: " << extended_samples_per_second;
|
DVLOG(1) << "Extended sampling frequency: " << extended_samples_per_second;
|
||||||
DVLOG(1) << "Channel config: " << channel_configuration;
|
DVLOG(1) << "Channel config: " << adts_header.GetNumChannels();
|
||||||
DVLOG(1) << "Adts profile: " << adts_profile;
|
DVLOG(1) << "Object type: " << adts_header.GetObjectType();
|
||||||
// Reset the timestamp helper to use a new sampling frequency.
|
// Reset the timestamp helper to use a new sampling frequency.
|
||||||
if (audio_timestamp_helper_) {
|
if (audio_timestamp_helper_) {
|
||||||
int64 base_timestamp = audio_timestamp_helper_->GetTimestamp();
|
int64 base_timestamp = audio_timestamp_helper_->GetTimestamp();
|
||||||
|
@ -256,7 +244,7 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
|
||||||
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp);
|
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp);
|
||||||
} else {
|
} else {
|
||||||
audio_timestamp_helper_.reset(
|
audio_timestamp_helper_.reset(
|
||||||
new AudioTimestampHelper(kMpeg2Timescale, samples_per_second));
|
new AudioTimestampHelper(kMpeg2Timescale, extended_samples_per_second));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio config notification.
|
// Audio config notification.
|
||||||
|
|
|
@ -46,7 +46,7 @@ class EsParserAdts : public EsParser {
|
||||||
// Signal any audio configuration change (if any).
|
// Signal any audio configuration change (if any).
|
||||||
// Return false if the current audio config is not
|
// Return false if the current audio config is not
|
||||||
// a supported ADTS audio config.
|
// 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.
|
// Discard some bytes from the ES stream.
|
||||||
void DiscardEs(int nbytes);
|
void DiscardEs(int nbytes);
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
'target_name': 'mp2t',
|
'target_name': 'mp2t',
|
||||||
'type': '<(component)',
|
'type': '<(component)',
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'adts_header.cc',
|
||||||
|
'adts_header.h',
|
||||||
'es_parser.h',
|
'es_parser.h',
|
||||||
'es_parser_adts.cc',
|
'es_parser_adts.cc',
|
||||||
'es_parser_adts.h',
|
'es_parser_adts.h',
|
||||||
|
@ -45,6 +47,7 @@
|
||||||
'target_name': 'mp2t_unittest',
|
'target_name': 'mp2t_unittest',
|
||||||
'type': '<(gtest_target_type)',
|
'type': '<(gtest_target_type)',
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'adts_header_unittest.cc',
|
||||||
'es_parser_h264_unittest.cc',
|
'es_parser_h264_unittest.cc',
|
||||||
'mp2t_media_parser_unittest.cc',
|
'mp2t_media_parser_unittest.cc',
|
||||||
],
|
],
|
||||||
|
|
|
@ -27,7 +27,10 @@
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'media/event/media_event.gyp:media_event',
|
'media/event/media_event.gyp:media_event',
|
||||||
'media/file/file.gyp:file',
|
'media/file/file.gyp:file',
|
||||||
|
'media/filters/filters.gyp:filters',
|
||||||
|
'media/formats/mp2t/mp2t.gyp:mp2t',
|
||||||
'media/formats/mp4/mp4.gyp:mp4',
|
'media/formats/mp4/mp4.gyp:mp4',
|
||||||
|
'media/formats/mpeg/mpeg.gyp:mpeg',
|
||||||
'third_party/gflags/gflags.gyp:gflags',
|
'third_party/gflags/gflags.gyp:gflags',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -52,7 +55,10 @@
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'media/file/file.gyp:file',
|
'media/file/file.gyp:file',
|
||||||
|
'media/filters/filters.gyp:filters',
|
||||||
|
'media/formats/mp2t/mp2t.gyp:mp2t',
|
||||||
'media/formats/mp4/mp4.gyp:mp4',
|
'media/formats/mp4/mp4.gyp:mp4',
|
||||||
|
'media/formats/mpeg/mpeg.gyp:mpeg',
|
||||||
'media/test/media_test.gyp:media_test_support',
|
'media/test/media_test.gyp:media_test_support',
|
||||||
'testing/gtest.gyp:gtest',
|
'testing/gtest.gyp:gtest',
|
||||||
],
|
],
|
||||||
|
@ -64,6 +70,7 @@
|
||||||
'media/base/media_base.gyp:*',
|
'media/base/media_base.gyp:*',
|
||||||
'media/event/media_event.gyp:*',
|
'media/event/media_event.gyp:*',
|
||||||
'media/file/file.gyp:*',
|
'media/file/file.gyp:*',
|
||||||
|
'media/formats/mp2t/mp2t.gyp:*',
|
||||||
'media/formats/mp4/mp4.gyp:*',
|
'media/formats/mp4/mp4.gyp:*',
|
||||||
'mpd/mpd.gyp:*',
|
'mpd/mpd.gyp:*',
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue