Refactor EsParserAdts to handle other audio
Rename will happen in next CL Change-Id: I28df29bb264eea7a6225b0a4a2eb38de7968f9d6
This commit is contained in:
parent
867244645a
commit
2a2493ec2e
|
@ -7,6 +7,7 @@
|
|||
#include "packager/media/formats/mp2t/adts_header.h"
|
||||
|
||||
#include "packager/media/base/bit_reader.h"
|
||||
#include "packager/media/base/bit_writer.h"
|
||||
#include "packager/media/formats/mp2t/mp2t_common.h"
|
||||
#include "packager/media/formats/mpeg/adts_constants.h"
|
||||
|
||||
|
@ -14,32 +15,19 @@ namespace shaka {
|
|||
namespace media {
|
||||
namespace mp2t {
|
||||
|
||||
AdtsHeader::AdtsHeader()
|
||||
: valid_config_(false),
|
||||
profile_(0),
|
||||
sampling_frequency_index_(0),
|
||||
channel_configuration_(0) {}
|
||||
|
||||
size_t AdtsHeader::GetAdtsFrameSize(const uint8_t* 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));
|
||||
bool AdtsHeader::IsSyncWord(const uint8_t* buf) const {
|
||||
return (buf[0] == 0xff) && ((buf[1] & 0xf6) == 0xf0);
|
||||
}
|
||||
|
||||
size_t AdtsHeader::GetAdtsHeaderSize(const uint8_t* data, size_t num_bytes) {
|
||||
if (num_bytes < 2)
|
||||
return 0;
|
||||
if (data[1] & 0x01)
|
||||
return kAdtsHeaderMinSize;
|
||||
return kAdtsHeaderMinSize + sizeof(uint16_t); // Header + CRC.
|
||||
size_t AdtsHeader::GetMinFrameSize() const {
|
||||
return kAdtsHeaderMinSize + 1;
|
||||
}
|
||||
|
||||
bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
|
||||
CHECK(adts_frame);
|
||||
|
||||
valid_config_ = false;
|
||||
if (adts_frame_size < kAdtsHeaderMinSize)
|
||||
return false;
|
||||
|
||||
BitReader frame(adts_frame, adts_frame_size);
|
||||
// Verify frame starts with sync bits (0xfff).
|
||||
|
@ -48,26 +36,18 @@ bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
|
|||
RCHECK(sync == 0xfff);
|
||||
// Skip MPEG version and layer.
|
||||
RCHECK(frame.SkipBits(3));
|
||||
// Get "protection absent" flag.
|
||||
uint8_t protection_absent;
|
||||
RCHECK(frame.ReadBits(1, &protection_absent));
|
||||
// Get profile.
|
||||
RCHECK(frame.ReadBits(1, &protection_absent_));
|
||||
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_t frame_size;
|
||||
RCHECK(frame.ReadBits(13, &frame_size));
|
||||
RCHECK(frame_size == adts_frame_size);
|
||||
RCHECK(frame.ReadBits(13, &frame_size_));
|
||||
// Skip buffer fullness indicator.
|
||||
RCHECK(frame.SkipBits(11));
|
||||
uint8_t num_blocks_minus_1;
|
||||
|
@ -77,21 +57,26 @@ bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
|
|||
"not supported.";
|
||||
return false;
|
||||
}
|
||||
|
||||
valid_config_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AdtsHeader::GetAudioSpecificConfig(std::vector<uint8_t>* buffer) const {
|
||||
DCHECK(buffer);
|
||||
if (!valid_config_)
|
||||
return false;
|
||||
size_t AdtsHeader::GetHeaderSize() const {
|
||||
const size_t kCrcSize = sizeof(uint16_t);
|
||||
return kAdtsHeaderMinSize + (protection_absent_ ? 0 : kCrcSize);
|
||||
}
|
||||
|
||||
buffer->resize(2);
|
||||
(*buffer)[0] = ((profile_ + 1) << 3) | (sampling_frequency_index_ >> 1);
|
||||
(*buffer)[1] = ((sampling_frequency_index_ & 1) << 7) |
|
||||
(channel_configuration_ << 3);
|
||||
return true;
|
||||
size_t AdtsHeader::GetFrameSize() const {
|
||||
return frame_size_;
|
||||
}
|
||||
|
||||
void AdtsHeader::GetAudioSpecificConfig(std::vector<uint8_t>* buffer) const {
|
||||
DCHECK(buffer);
|
||||
buffer->clear();
|
||||
BitWriter config(buffer);
|
||||
config.WriteBits(GetObjectType(), 5);
|
||||
config.WriteBits(sampling_frequency_index_, 4);
|
||||
config.WriteBits(channel_configuration_, 4);
|
||||
config.Flush();
|
||||
}
|
||||
|
||||
uint8_t AdtsHeader::GetObjectType() const {
|
||||
|
|
|
@ -11,62 +11,41 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "packager/base/macros.h"
|
||||
#include "packager/media/formats/mp2t/audio_header.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
namespace mp2t {
|
||||
|
||||
/// Class which parses ADTS headers and synthesizes AudioSpecificConfig
|
||||
/// and audio mime type from ADTS header contents.
|
||||
class AdtsHeader {
|
||||
/// Class which parses ADTS frame (header / metadata) and synthesizes
|
||||
/// AudioSpecificConfig from audio frame content.
|
||||
class AdtsHeader : public AudioHeader {
|
||||
public:
|
||||
AdtsHeader();
|
||||
~AdtsHeader() {}
|
||||
AdtsHeader() = default;
|
||||
~AdtsHeader() override = default;
|
||||
|
||||
/// 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_t* 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_t* 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_t* 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_t>* buffer) const;
|
||||
|
||||
/// @return The audio profile for this ADTS frame.
|
||||
uint8_t GetObjectType() const;
|
||||
|
||||
/// @return The sampling frequency for this ADTS frame.
|
||||
uint32_t GetSamplingFrequency() const;
|
||||
|
||||
/// @return Number of channels for this AAC config.
|
||||
uint8_t GetNumChannels() const;
|
||||
/// @name AudioHeader implementation overrides.
|
||||
/// @{
|
||||
bool IsSyncWord(const uint8_t* buf) const override;
|
||||
size_t GetMinFrameSize() const override;
|
||||
bool Parse(const uint8_t* adts_frame, size_t adts_frame_size) override;
|
||||
size_t GetHeaderSize() const override;
|
||||
size_t GetFrameSize() const override;
|
||||
void GetAudioSpecificConfig(std::vector<uint8_t>* buffer) const override;
|
||||
uint8_t GetObjectType() const override;
|
||||
uint32_t GetSamplingFrequency() const override;
|
||||
uint8_t GetNumChannels() const override;
|
||||
/// @}
|
||||
|
||||
private:
|
||||
bool valid_config_;
|
||||
uint8_t profile_;
|
||||
uint8_t sampling_frequency_index_;
|
||||
uint8_t channel_configuration_;
|
||||
AdtsHeader(const AdtsHeader&) = delete;
|
||||
AdtsHeader& operator=(const AdtsHeader&) = delete;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(AdtsHeader);
|
||||
uint8_t protection_absent_ = 0;
|
||||
uint16_t frame_size_ = 0;
|
||||
uint8_t profile_ = 0;
|
||||
uint8_t sampling_frequency_index_ = 0;
|
||||
uint8_t channel_configuration_ = 0;
|
||||
};
|
||||
|
||||
} // namespace mp2t
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "packager/base/compiler_specific.h"
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/base/strings/string_number_conversions.h"
|
||||
#include "packager/media/formats/mp2t/adts_header.h"
|
||||
|
@ -50,16 +49,19 @@ class AdtsHeaderTest : public testing::Test {
|
|||
};
|
||||
|
||||
TEST_F(AdtsHeaderTest, ParseSuccess) {
|
||||
const size_t kExpectedHeaderSize(7);
|
||||
const uint8_t kExpectedObjectType(2);
|
||||
const uint32_t kExpectedSamplingFrequency(44100);
|
||||
const uint8_t kExpectedNumChannels(2);
|
||||
AdtsHeader adts_header;
|
||||
EXPECT_TRUE(adts_header.Parse(adts_frame_.data(), adts_frame_.size()));
|
||||
ASSERT_TRUE(adts_header.Parse(adts_frame_.data(), adts_frame_.size()));
|
||||
EXPECT_EQ(adts_frame_.size(), adts_header.GetFrameSize());
|
||||
EXPECT_EQ(kExpectedHeaderSize, adts_header.GetHeaderSize());
|
||||
EXPECT_EQ(kExpectedObjectType, adts_header.GetObjectType());
|
||||
EXPECT_EQ(kExpectedSamplingFrequency, adts_header.GetSamplingFrequency());
|
||||
EXPECT_EQ(kExpectedNumChannels, adts_header.GetNumChannels());
|
||||
std::vector<uint8_t> audio_specific_config;
|
||||
ASSERT_TRUE(adts_header.GetAudioSpecificConfig(&audio_specific_config));
|
||||
adts_header.GetAudioSpecificConfig(&audio_specific_config);
|
||||
EXPECT_EQ(arraysize(kExpectedAudioSpecificConfig),
|
||||
audio_specific_config.size());
|
||||
EXPECT_EQ(std::vector<uint8_t>(kExpectedAudioSpecificConfig,
|
||||
|
@ -68,25 +70,21 @@ TEST_F(AdtsHeaderTest, ParseSuccess) {
|
|||
audio_specific_config);
|
||||
}
|
||||
|
||||
TEST_F(AdtsHeaderTest, ParseFailFrameSize) {
|
||||
|
||||
TEST_F(AdtsHeaderTest, ParseVariousDataSize) {
|
||||
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));
|
||||
|
||||
// Parse succeeds as long as the full header is provided.
|
||||
EXPECT_TRUE(adts_header.Parse(adts_frame_.data(), adts_frame_.size() - 1));
|
||||
const size_t header_size = adts_header.GetHeaderSize();
|
||||
EXPECT_EQ(adts_frame_.size(), adts_header.GetFrameSize());
|
||||
|
||||
EXPECT_TRUE(adts_header.Parse(adts_frame_.data(), header_size));
|
||||
EXPECT_EQ(adts_frame_.size(), adts_header.GetFrameSize());
|
||||
EXPECT_EQ(header_size, adts_header.GetHeaderSize());
|
||||
|
||||
// Parse fails if there is not enough data (no full header).
|
||||
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()));
|
||||
EXPECT_FALSE(adts_header.Parse(adts_frame_.data(), header_size - 1));
|
||||
}
|
||||
|
||||
} // Namespace mp2t
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2017 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 PACKAGER_MEDIA_FORMATS_MP2T_AUDIO_HEADER_H_
|
||||
#define PACKAGER_MEDIA_FORMATS_MP2T_AUDIO_HEADER_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
namespace mp2t {
|
||||
|
||||
class AudioHeader {
|
||||
public:
|
||||
AudioHeader() = default;
|
||||
virtual ~AudioHeader() = default;
|
||||
|
||||
/// Check if the leading word (2 bytes) is sync signal.
|
||||
/// @param buf points to the buffer to be checked. Must be at least 2 bytes.
|
||||
/// @return true if corresponds to a syncword.
|
||||
virtual bool IsSyncWord(const uint8_t* buf) const = 0;
|
||||
|
||||
/// @return The minium frame size.
|
||||
virtual size_t GetMinFrameSize() const = 0;
|
||||
|
||||
/// Parse a partial audio frame, extracting the fields within. Only audio
|
||||
/// frame header / metadata is parsed. The audio_frame_size must contain the
|
||||
/// full header / metadata.
|
||||
/// @param audio_frame is an input parameter pointing to an audio frame.
|
||||
/// @param audio_frame_size is the size, in bytes of the input data. It can be
|
||||
/// smaller than the actual frame size, but it should not be smaller
|
||||
/// than the header size.
|
||||
/// @return true if successful, false otherwise.
|
||||
virtual bool Parse(const uint8_t* audio_frame, size_t audio_frame_size) = 0;
|
||||
|
||||
/// Should only be called after a successful Parse.
|
||||
/// @return The size of audio header.
|
||||
virtual size_t GetHeaderSize() const = 0;
|
||||
|
||||
/// Should only be called after a successful Parse.
|
||||
/// @return the size of frame (header + payload).
|
||||
virtual size_t GetFrameSize() const = 0;
|
||||
|
||||
/// Synthesize an AudioSpecificConfig record from the fields within the audio
|
||||
/// header.
|
||||
/// Should only be called after a successful Parse.
|
||||
/// @param [out] buffer is a pointer to a vector to contain the
|
||||
/// AudioSpecificConfig.
|
||||
/// @return true if successful, false otherwise.
|
||||
virtual void GetAudioSpecificConfig(std::vector<uint8_t>* buffer) const = 0;
|
||||
|
||||
/// Should only be called after a successful Parse.
|
||||
/// @return The audio profile for this frame. Only meaningful for AAC.
|
||||
virtual uint8_t GetObjectType() const = 0;
|
||||
|
||||
/// Should only be called after a successful Parse.
|
||||
/// @return The sampling frequency for this frame.
|
||||
virtual uint32_t GetSamplingFrequency() const = 0;
|
||||
|
||||
/// Should only be called after a successful Parse.
|
||||
/// @return Number of channels for this frame.
|
||||
virtual uint8_t GetNumChannels() const = 0;
|
||||
|
||||
private:
|
||||
AudioHeader(const AudioHeader&) = delete;
|
||||
AudioHeader& operator=(const AudioHeader&) = delete;
|
||||
};
|
||||
|
||||
} // namespace mp2t
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
||||
#endif // PACKAGER_MEDIA_FORMATS_MP2T_AUDIO_HEADER_H_
|
|
@ -21,29 +21,26 @@
|
|||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
namespace mp2t {
|
||||
|
||||
// Return true if buf corresponds to an ADTS syncword.
|
||||
// |buf| size must be at least 2.
|
||||
static bool isAdtsSyncWord(const uint8_t* buf) {
|
||||
return (buf[0] == 0xff) && ((buf[1] & 0xf6) == 0xf0);
|
||||
}
|
||||
|
||||
// Look for an ADTS syncword.
|
||||
// Look for a syncword.
|
||||
// |new_pos| returns
|
||||
// - either the byte position of the ADTS frame (if found)
|
||||
// - either the byte position of the frame (if found)
|
||||
// - or the byte position of 1st byte that was not processed (if not found).
|
||||
// In every case, the returned value in |new_pos| is such that new_pos >= pos
|
||||
// |frame_sz| returns the size of the ADTS frame (if found).
|
||||
// |audio_header| is updated with the new audio frame info if a syncword is
|
||||
// found.
|
||||
// Return whether a syncword was found.
|
||||
static bool LookForSyncWord(const uint8_t* raw_es,
|
||||
int raw_es_size,
|
||||
int pos,
|
||||
int* new_pos,
|
||||
int* frame_sz) {
|
||||
AudioHeader* audio_header) {
|
||||
DCHECK_GE(pos, 0);
|
||||
DCHECK_LE(pos, raw_es_size);
|
||||
|
||||
int max_offset = raw_es_size - kAdtsHeaderMinSize;
|
||||
const int max_offset =
|
||||
raw_es_size - static_cast<int>(audio_header->GetMinFrameSize());
|
||||
if (pos >= max_offset) {
|
||||
// Do not change the position if:
|
||||
// - max_offset < 0: not enough bytes to get a full header
|
||||
|
@ -58,28 +55,21 @@ static bool LookForSyncWord(const uint8_t* raw_es,
|
|||
for (int offset = pos; offset < max_offset; offset++) {
|
||||
const uint8_t* cur_buf = &raw_es[offset];
|
||||
|
||||
if (!isAdtsSyncWord(cur_buf))
|
||||
// The first 12 bits must be 1.
|
||||
// The layer field (2 bits) must be set to 0.
|
||||
if (!audio_header->IsSyncWord(cur_buf))
|
||||
continue;
|
||||
|
||||
int frame_size = static_cast<int>(
|
||||
mp2t::AdtsHeader::GetAdtsFrameSize(cur_buf, kAdtsHeaderMinSize));
|
||||
if (frame_size < kAdtsHeaderMinSize) {
|
||||
// Too short to be an ADTS frame.
|
||||
if (!audio_header->Parse(cur_buf, raw_es_size - offset))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check whether there is another frame
|
||||
// |size| apart from the current one.
|
||||
int remaining_size = raw_es_size - offset;
|
||||
if (remaining_size >= frame_size + 2 &&
|
||||
!isAdtsSyncWord(&cur_buf[frame_size])) {
|
||||
// Check whether there is another frame |size| apart from the current one.
|
||||
const size_t remaining_size = static_cast<size_t>(raw_es_size - offset);
|
||||
const int kSyncWordSize = 2;
|
||||
if (remaining_size >= audio_header->GetFrameSize() + kSyncWordSize &&
|
||||
!audio_header->IsSyncWord(&cur_buf[audio_header->GetFrameSize()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*new_pos = offset;
|
||||
*frame_sz = frame_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -87,17 +77,15 @@ static bool LookForSyncWord(const uint8_t* raw_es,
|
|||
return false;
|
||||
}
|
||||
|
||||
namespace mp2t {
|
||||
|
||||
EsParserAdts::EsParserAdts(uint32_t pid,
|
||||
const NewStreamInfoCB& new_stream_info_cb,
|
||||
const EmitSampleCB& emit_sample_cb,
|
||||
bool sbr_in_mimetype)
|
||||
: EsParser(pid),
|
||||
audio_header_(new AdtsHeader),
|
||||
new_stream_info_cb_(new_stream_info_cb),
|
||||
emit_sample_cb_(emit_sample_cb),
|
||||
sbr_in_mimetype_(sbr_in_mimetype) {
|
||||
}
|
||||
sbr_in_mimetype_(sbr_in_mimetype) {}
|
||||
|
||||
EsParserAdts::~EsParserAdts() {
|
||||
}
|
||||
|
@ -120,29 +108,24 @@ bool EsParserAdts::Parse(const uint8_t* buf,
|
|||
es_byte_queue_.Push(buf, static_cast<int>(size));
|
||||
es_byte_queue_.Peek(&raw_es, &raw_es_size);
|
||||
|
||||
// Look for every ADTS frame in the ES buffer starting at offset = 0
|
||||
// Look for every frame in the ES buffer starting at offset = 0
|
||||
int es_position = 0;
|
||||
int frame_size;
|
||||
while (LookForSyncWord(raw_es, raw_es_size, es_position,
|
||||
&es_position, &frame_size)) {
|
||||
while (LookForSyncWord(raw_es, raw_es_size, es_position, &es_position,
|
||||
audio_header_.get())) {
|
||||
const uint8_t* 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(frame_ptr, kAdtsHeaderMinSize);
|
||||
DVLOG(LOG_LEVEL_ES) << "syncword @ pos=" << es_position
|
||||
<< " frame_size=" << audio_header_->GetFrameSize();
|
||||
DVLOG(LOG_LEVEL_ES) << "header: "
|
||||
<< base::HexEncode(frame_ptr,
|
||||
audio_header_->GetHeaderSize());
|
||||
|
||||
// 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)
|
||||
if (static_cast<int>(audio_header_->GetFrameSize()) > 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(frame_ptr, frame_size))
|
||||
if (!UpdateAudioConfiguration(*audio_header_))
|
||||
return false;
|
||||
|
||||
// Get the PTS & the duration of this access unit.
|
||||
|
@ -160,7 +143,9 @@ bool EsParserAdts::Parse(const uint8_t* buf,
|
|||
bool is_key_frame = true;
|
||||
|
||||
std::shared_ptr<MediaSample> sample = MediaSample::CopyFrom(
|
||||
frame_ptr + header_size, frame_size - header_size, is_key_frame);
|
||||
frame_ptr + audio_header_->GetHeaderSize(),
|
||||
audio_header_->GetFrameSize() - audio_header_->GetHeaderSize(),
|
||||
is_key_frame);
|
||||
sample->set_pts(current_pts);
|
||||
sample->set_dts(current_pts);
|
||||
sample->set_duration(frame_duration);
|
||||
|
@ -170,7 +155,7 @@ bool EsParserAdts::Parse(const uint8_t* buf,
|
|||
audio_timestamp_helper_->AddFrames(kSamplesPerAACFrame);
|
||||
|
||||
// Skip the current frame.
|
||||
es_position += frame_size;
|
||||
es_position += static_cast<int>(audio_header_->GetFrameSize());
|
||||
}
|
||||
|
||||
// Discard all the bytes that have been processed.
|
||||
|
@ -188,18 +173,11 @@ void EsParserAdts::Reset() {
|
|||
last_audio_decoder_config_ = std::shared_ptr<AudioStreamInfo>();
|
||||
}
|
||||
|
||||
bool EsParserAdts::UpdateAudioConfiguration(const uint8_t* adts_frame,
|
||||
size_t adts_frame_size) {
|
||||
bool EsParserAdts::UpdateAudioConfiguration(const AudioHeader& audio_header) {
|
||||
const uint8_t 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_t> audio_specific_config;
|
||||
if (!adts_header.GetAudioSpecificConfig(&audio_specific_config))
|
||||
return false;
|
||||
audio_header.GetAudioSpecificConfig(&audio_specific_config);
|
||||
|
||||
if (last_audio_decoder_config_) {
|
||||
// Verify that the audio decoder config has not changed.
|
||||
|
@ -214,23 +192,27 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8_t* adts_frame,
|
|||
// 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.)
|
||||
int samples_per_second = adts_header.GetSamplingFrequency();
|
||||
int samples_per_second = audio_header.GetSamplingFrequency();
|
||||
// TODO(kqyang): Review if it makes sense to have |sbr_in_mimetype_| in
|
||||
// es_parser.
|
||||
int extended_samples_per_second = sbr_in_mimetype_
|
||||
? std::min(2 * samples_per_second, 48000)
|
||||
: samples_per_second;
|
||||
|
||||
const Codec codec = kCodecAAC;
|
||||
last_audio_decoder_config_ = std::make_shared<AudioStreamInfo>(
|
||||
pid(), kMpeg2Timescale, kInfiniteDuration, kCodecAAC,
|
||||
AudioStreamInfo::GetCodecString(kCodecAAC, adts_header.GetObjectType()),
|
||||
pid(), kMpeg2Timescale, kInfiniteDuration, codec,
|
||||
AudioStreamInfo::GetCodecString(codec, audio_header.GetObjectType()),
|
||||
audio_specific_config.data(), audio_specific_config.size(),
|
||||
kAacSampleSizeBits, adts_header.GetNumChannels(),
|
||||
kAacSampleSizeBits, audio_header.GetNumChannels(),
|
||||
extended_samples_per_second, 0 /* seek preroll */, 0 /* codec delay */,
|
||||
0 /* max bitrate */, 0 /* avg bitrate */, std::string(), false);
|
||||
|
||||
DVLOG(1) << "Sampling frequency: " << samples_per_second;
|
||||
DVLOG(1) << "Extended sampling frequency: " << extended_samples_per_second;
|
||||
DVLOG(1) << "Channel config: " << adts_header.GetNumChannels();
|
||||
DVLOG(1) << "Object type: " << adts_header.GetObjectType();
|
||||
DVLOG(1) << "Channel config: "
|
||||
<< static_cast<int>(audio_header.GetNumChannels());
|
||||
DVLOG(1) << "Object type: " << static_cast<int>(audio_header.GetObjectType());
|
||||
// Reset the timestamp helper to use a new sampling frequency.
|
||||
if (audio_timestamp_helper_) {
|
||||
int64_t base_timestamp = audio_timestamp_helper_->GetTimestamp();
|
||||
|
|
|
@ -22,6 +22,8 @@ class BitReader;
|
|||
|
||||
namespace mp2t {
|
||||
|
||||
class AudioHeader;
|
||||
|
||||
class EsParserAdts : public EsParser {
|
||||
public:
|
||||
EsParserAdts(uint32_t pid,
|
||||
|
@ -41,13 +43,14 @@ class EsParserAdts : public EsParser {
|
|||
typedef std::list<EsPts> EsPtsList;
|
||||
|
||||
// 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_t* adts_frame, size_t frame_size);
|
||||
// Return false if the current audio config is not a supported audio config.
|
||||
bool UpdateAudioConfiguration(const AudioHeader& audio_header);
|
||||
|
||||
// Discard some bytes from the ES stream.
|
||||
void DiscardEs(int nbytes);
|
||||
|
||||
std::unique_ptr<AudioHeader> audio_header_;
|
||||
|
||||
// Callbacks:
|
||||
// - to signal a new audio configuration,
|
||||
// - to send ES buffers.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
'sources': [
|
||||
'adts_header.cc',
|
||||
'adts_header.h',
|
||||
'audio_header.h',
|
||||
'continuity_counter.cc',
|
||||
'continuity_counter.h',
|
||||
'es_parser_adts.cc',
|
||||
|
|
|
@ -48,7 +48,6 @@ const uint32_t kPesStreamIdVideo = 0xE0;
|
|||
const uint32_t kPesStreamIdAudioMask = 0xE0;
|
||||
const uint32_t kPesStreamIdAudio = 0xC0;
|
||||
const uint32_t kVersion4 = 4;
|
||||
const size_t kAdtsHeaderMinSize = 7;
|
||||
const uint8_t kAacSampleSizeBits = 16;
|
||||
// Applies to all video streams.
|
||||
const uint8_t kNaluLengthSize = 4; // unit is bytes.
|
||||
|
@ -904,17 +903,15 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) {
|
|||
} else if ((prev_pes_stream_id_ & kPesStreamIdAudioMask) ==
|
||||
kPesStreamIdAudio) {
|
||||
// Set data on the audio stream.
|
||||
int frame_size = static_cast<int>(mp2t::AdtsHeader::GetAdtsFrameSize(
|
||||
sample_data_.data(), kAdtsHeaderMinSize));
|
||||
mp2t::AdtsHeader adts_header;
|
||||
const uint8_t* frame_ptr = sample_data_.data();
|
||||
if (!adts_header.Parse(frame_ptr, frame_size)) {
|
||||
if (!adts_header.Parse(frame_ptr, sample_data_.size())) {
|
||||
LOG(ERROR) << "Could not parse ADTS header";
|
||||
return false;
|
||||
}
|
||||
size_t header_size = adts_header.GetAdtsHeaderSize(frame_ptr,
|
||||
frame_size);
|
||||
media_sample_->SetData(frame_ptr + header_size, frame_size - header_size);
|
||||
media_sample_->SetData(
|
||||
frame_ptr + adts_header.GetHeaderSize(),
|
||||
adts_header.GetFrameSize() - adts_header.GetHeaderSize());
|
||||
if (!is_initialized_) {
|
||||
for (uint32_t i = 0; i < stream_infos_.size(); i++) {
|
||||
if (stream_infos_[i]->stream_type() == kStreamAudio &&
|
||||
|
@ -927,10 +924,7 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) {
|
|||
audio_stream_info->set_sampling_frequency(
|
||||
adts_header.GetSamplingFrequency());
|
||||
std::vector<uint8_t> audio_specific_config;
|
||||
if (!adts_header.GetAudioSpecificConfig(&audio_specific_config)) {
|
||||
LOG(ERROR) << "Could not compute AACAudiospecificconfig";
|
||||
return false;
|
||||
}
|
||||
adts_header.GetAudioSpecificConfig(&audio_specific_config);
|
||||
audio_stream_info->set_codec_config(audio_specific_config);
|
||||
audio_stream_info->set_codec_string(
|
||||
AudioStreamInfo::GetCodecString(
|
||||
|
|
Loading…
Reference in New Issue