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/formats/mp2t/adts_header.h"
|
||||||
|
|
||||||
#include "packager/media/base/bit_reader.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/mp2t/mp2t_common.h"
|
||||||
#include "packager/media/formats/mpeg/adts_constants.h"
|
#include "packager/media/formats/mpeg/adts_constants.h"
|
||||||
|
|
||||||
|
@ -14,32 +15,19 @@ namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp2t {
|
namespace mp2t {
|
||||||
|
|
||||||
AdtsHeader::AdtsHeader()
|
bool AdtsHeader::IsSyncWord(const uint8_t* buf) const {
|
||||||
: valid_config_(false),
|
return (buf[0] == 0xff) && ((buf[1] & 0xf6) == 0xf0);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AdtsHeader::GetAdtsHeaderSize(const uint8_t* data, size_t num_bytes) {
|
size_t AdtsHeader::GetMinFrameSize() const {
|
||||||
if (num_bytes < 2)
|
return kAdtsHeaderMinSize + 1;
|
||||||
return 0;
|
|
||||||
if (data[1] & 0x01)
|
|
||||||
return kAdtsHeaderMinSize;
|
|
||||||
return kAdtsHeaderMinSize + sizeof(uint16_t); // Header + CRC.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
|
bool AdtsHeader::Parse(const uint8_t* adts_frame, size_t adts_frame_size) {
|
||||||
CHECK(adts_frame);
|
CHECK(adts_frame);
|
||||||
|
|
||||||
valid_config_ = false;
|
if (adts_frame_size < kAdtsHeaderMinSize)
|
||||||
|
return false;
|
||||||
|
|
||||||
BitReader frame(adts_frame, adts_frame_size);
|
BitReader frame(adts_frame, adts_frame_size);
|
||||||
// Verify frame starts with sync bits (0xfff).
|
// 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);
|
RCHECK(sync == 0xfff);
|
||||||
// Skip MPEG version and layer.
|
// Skip MPEG version and layer.
|
||||||
RCHECK(frame.SkipBits(3));
|
RCHECK(frame.SkipBits(3));
|
||||||
// Get "protection absent" flag.
|
RCHECK(frame.ReadBits(1, &protection_absent_));
|
||||||
uint8_t protection_absent;
|
|
||||||
RCHECK(frame.ReadBits(1, &protection_absent));
|
|
||||||
// Get profile.
|
|
||||||
RCHECK(frame.ReadBits(2, &profile_));
|
RCHECK(frame.ReadBits(2, &profile_));
|
||||||
// Get sampling frequency.
|
|
||||||
RCHECK(frame.ReadBits(4, &sampling_frequency_index_));
|
RCHECK(frame.ReadBits(4, &sampling_frequency_index_));
|
||||||
RCHECK(sampling_frequency_index_ < kAdtsFrequencyTableSize);
|
RCHECK(sampling_frequency_index_ < kAdtsFrequencyTableSize);
|
||||||
// Skip private stream bit.
|
// Skip private stream bit.
|
||||||
RCHECK(frame.SkipBits(1));
|
RCHECK(frame.SkipBits(1));
|
||||||
// Get number of audio channels.
|
|
||||||
RCHECK(frame.ReadBits(3, &channel_configuration_));
|
RCHECK(frame.ReadBits(3, &channel_configuration_));
|
||||||
RCHECK((channel_configuration_ > 0) &&
|
RCHECK((channel_configuration_ > 0) &&
|
||||||
(channel_configuration_ < kAdtsNumChannelsTableSize));
|
(channel_configuration_ < kAdtsNumChannelsTableSize));
|
||||||
// Skip originality, home and copyright info.
|
// Skip originality, home and copyright info.
|
||||||
RCHECK(frame.SkipBits(4));
|
RCHECK(frame.SkipBits(4));
|
||||||
// Verify that the frame size matches input parameters.
|
RCHECK(frame.ReadBits(13, &frame_size_));
|
||||||
uint16_t frame_size;
|
|
||||||
RCHECK(frame.ReadBits(13, &frame_size));
|
|
||||||
RCHECK(frame_size == adts_frame_size);
|
|
||||||
// Skip buffer fullness indicator.
|
// Skip buffer fullness indicator.
|
||||||
RCHECK(frame.SkipBits(11));
|
RCHECK(frame.SkipBits(11));
|
||||||
uint8_t num_blocks_minus_1;
|
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.";
|
"not supported.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
valid_config_ = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdtsHeader::GetAudioSpecificConfig(std::vector<uint8_t>* buffer) const {
|
size_t AdtsHeader::GetHeaderSize() const {
|
||||||
DCHECK(buffer);
|
const size_t kCrcSize = sizeof(uint16_t);
|
||||||
if (!valid_config_)
|
return kAdtsHeaderMinSize + (protection_absent_ ? 0 : kCrcSize);
|
||||||
return false;
|
}
|
||||||
|
|
||||||
buffer->resize(2);
|
size_t AdtsHeader::GetFrameSize() const {
|
||||||
(*buffer)[0] = ((profile_ + 1) << 3) | (sampling_frequency_index_ >> 1);
|
return frame_size_;
|
||||||
(*buffer)[1] = ((sampling_frequency_index_ & 1) << 7) |
|
}
|
||||||
(channel_configuration_ << 3);
|
|
||||||
return true;
|
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 {
|
uint8_t AdtsHeader::GetObjectType() const {
|
||||||
|
|
|
@ -11,62 +11,41 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include "packager/media/formats/mp2t/audio_header.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp2t {
|
namespace mp2t {
|
||||||
|
|
||||||
/// Class which parses ADTS headers and synthesizes AudioSpecificConfig
|
/// Class which parses ADTS frame (header / metadata) and synthesizes
|
||||||
/// and audio mime type from ADTS header contents.
|
/// AudioSpecificConfig from audio frame content.
|
||||||
class AdtsHeader {
|
class AdtsHeader : public AudioHeader {
|
||||||
public:
|
public:
|
||||||
AdtsHeader();
|
AdtsHeader() = default;
|
||||||
~AdtsHeader() {}
|
~AdtsHeader() override = default;
|
||||||
|
|
||||||
/// Get the size of the ADTS frame from a partial or complete frame.
|
/// @name AudioHeader implementation overrides.
|
||||||
/// @param data is a pointer to the beginning of the ADTS frame.
|
/// @{
|
||||||
/// @param num_bytes is the number of data bytes at @a data.
|
bool IsSyncWord(const uint8_t* buf) const override;
|
||||||
/// @return Size of the ADTS frame (header + payload) if successful, or
|
size_t GetMinFrameSize() const override;
|
||||||
/// zero otherwise.
|
bool Parse(const uint8_t* adts_frame, size_t adts_frame_size) override;
|
||||||
static size_t GetAdtsFrameSize(const uint8_t* data, size_t num_bytes);
|
size_t GetHeaderSize() const override;
|
||||||
|
size_t GetFrameSize() const override;
|
||||||
/// Get the size of the ADTS header from a partial or complete frame.
|
void GetAudioSpecificConfig(std::vector<uint8_t>* buffer) const override;
|
||||||
/// @param data is a pointer to the beginning of the ADTS frame.
|
uint8_t GetObjectType() const override;
|
||||||
/// @param num_bytes is the number of data bytes at @a data.
|
uint32_t GetSamplingFrequency() const override;
|
||||||
/// @return Size of the ADTS header if successful, or zero otherwise.
|
uint8_t GetNumChannels() const override;
|
||||||
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;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool valid_config_;
|
AdtsHeader(const AdtsHeader&) = delete;
|
||||||
uint8_t profile_;
|
AdtsHeader& operator=(const AdtsHeader&) = delete;
|
||||||
uint8_t sampling_frequency_index_;
|
|
||||||
uint8_t channel_configuration_;
|
|
||||||
|
|
||||||
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
|
} // namespace mp2t
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "packager/base/compiler_specific.h"
|
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/media/formats/mp2t/adts_header.h"
|
#include "packager/media/formats/mp2t/adts_header.h"
|
||||||
|
@ -50,16 +49,19 @@ class AdtsHeaderTest : public testing::Test {
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(AdtsHeaderTest, ParseSuccess) {
|
TEST_F(AdtsHeaderTest, ParseSuccess) {
|
||||||
|
const size_t kExpectedHeaderSize(7);
|
||||||
const uint8_t kExpectedObjectType(2);
|
const uint8_t kExpectedObjectType(2);
|
||||||
const uint32_t kExpectedSamplingFrequency(44100);
|
const uint32_t kExpectedSamplingFrequency(44100);
|
||||||
const uint8_t kExpectedNumChannels(2);
|
const uint8_t kExpectedNumChannels(2);
|
||||||
AdtsHeader adts_header;
|
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(kExpectedObjectType, adts_header.GetObjectType());
|
||||||
EXPECT_EQ(kExpectedSamplingFrequency, adts_header.GetSamplingFrequency());
|
EXPECT_EQ(kExpectedSamplingFrequency, adts_header.GetSamplingFrequency());
|
||||||
EXPECT_EQ(kExpectedNumChannels, adts_header.GetNumChannels());
|
EXPECT_EQ(kExpectedNumChannels, adts_header.GetNumChannels());
|
||||||
std::vector<uint8_t> audio_specific_config;
|
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),
|
EXPECT_EQ(arraysize(kExpectedAudioSpecificConfig),
|
||||||
audio_specific_config.size());
|
audio_specific_config.size());
|
||||||
EXPECT_EQ(std::vector<uint8_t>(kExpectedAudioSpecificConfig,
|
EXPECT_EQ(std::vector<uint8_t>(kExpectedAudioSpecificConfig,
|
||||||
|
@ -68,25 +70,21 @@ TEST_F(AdtsHeaderTest, ParseSuccess) {
|
||||||
audio_specific_config);
|
audio_specific_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AdtsHeaderTest, ParseFailFrameSize) {
|
TEST_F(AdtsHeaderTest, ParseVariousDataSize) {
|
||||||
|
|
||||||
AdtsHeader adts_header;
|
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));
|
EXPECT_FALSE(adts_header.Parse(adts_frame_.data(), 1));
|
||||||
}
|
EXPECT_FALSE(adts_header.Parse(adts_frame_.data(), header_size - 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 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 shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
namespace mp2t {
|
||||||
|
|
||||||
// Return true if buf corresponds to an ADTS syncword.
|
// Look for a 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.
|
|
||||||
// |new_pos| returns
|
// |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).
|
// - 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
|
// 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.
|
// Return whether a syncword was found.
|
||||||
static bool LookForSyncWord(const uint8_t* raw_es,
|
static bool LookForSyncWord(const uint8_t* raw_es,
|
||||||
int raw_es_size,
|
int raw_es_size,
|
||||||
int pos,
|
int pos,
|
||||||
int* new_pos,
|
int* new_pos,
|
||||||
int* frame_sz) {
|
AudioHeader* audio_header) {
|
||||||
DCHECK_GE(pos, 0);
|
DCHECK_GE(pos, 0);
|
||||||
DCHECK_LE(pos, raw_es_size);
|
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) {
|
if (pos >= max_offset) {
|
||||||
// Do not change the position if:
|
// Do not change the position if:
|
||||||
// - max_offset < 0: not enough bytes to get a full header
|
// - 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++) {
|
for (int offset = pos; offset < max_offset; offset++) {
|
||||||
const uint8_t* cur_buf = &raw_es[offset];
|
const uint8_t* cur_buf = &raw_es[offset];
|
||||||
|
|
||||||
if (!isAdtsSyncWord(cur_buf))
|
if (!audio_header->IsSyncWord(cur_buf))
|
||||||
// The first 12 bits must be 1.
|
|
||||||
// The layer field (2 bits) must be set to 0.
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int frame_size = static_cast<int>(
|
if (!audio_header->Parse(cur_buf, raw_es_size - offset))
|
||||||
mp2t::AdtsHeader::GetAdtsFrameSize(cur_buf, kAdtsHeaderMinSize));
|
|
||||||
if (frame_size < kAdtsHeaderMinSize) {
|
|
||||||
// Too short to be an ADTS frame.
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether there is another frame
|
// Check whether there is another frame |size| apart from the current one.
|
||||||
// |size| apart from the current one.
|
const size_t remaining_size = static_cast<size_t>(raw_es_size - offset);
|
||||||
int remaining_size = raw_es_size - offset;
|
const int kSyncWordSize = 2;
|
||||||
if (remaining_size >= frame_size + 2 &&
|
if (remaining_size >= audio_header->GetFrameSize() + kSyncWordSize &&
|
||||||
!isAdtsSyncWord(&cur_buf[frame_size])) {
|
!audio_header->IsSyncWord(&cur_buf[audio_header->GetFrameSize()])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
*new_pos = offset;
|
*new_pos = offset;
|
||||||
*frame_sz = frame_size;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,17 +77,15 @@ static bool LookForSyncWord(const uint8_t* raw_es,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace mp2t {
|
|
||||||
|
|
||||||
EsParserAdts::EsParserAdts(uint32_t pid,
|
EsParserAdts::EsParserAdts(uint32_t pid,
|
||||||
const NewStreamInfoCB& new_stream_info_cb,
|
const NewStreamInfoCB& new_stream_info_cb,
|
||||||
const EmitSampleCB& emit_sample_cb,
|
const EmitSampleCB& emit_sample_cb,
|
||||||
bool sbr_in_mimetype)
|
bool sbr_in_mimetype)
|
||||||
: EsParser(pid),
|
: EsParser(pid),
|
||||||
|
audio_header_(new AdtsHeader),
|
||||||
new_stream_info_cb_(new_stream_info_cb),
|
new_stream_info_cb_(new_stream_info_cb),
|
||||||
emit_sample_cb_(emit_sample_cb),
|
emit_sample_cb_(emit_sample_cb),
|
||||||
sbr_in_mimetype_(sbr_in_mimetype) {
|
sbr_in_mimetype_(sbr_in_mimetype) {}
|
||||||
}
|
|
||||||
|
|
||||||
EsParserAdts::~EsParserAdts() {
|
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_.Push(buf, static_cast<int>(size));
|
||||||
es_byte_queue_.Peek(&raw_es, &raw_es_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 es_position = 0;
|
||||||
int frame_size;
|
while (LookForSyncWord(raw_es, raw_es_size, es_position, &es_position,
|
||||||
while (LookForSyncWord(raw_es, raw_es_size, es_position,
|
audio_header_.get())) {
|
||||||
&es_position, &frame_size)) {
|
|
||||||
const uint8_t* frame_ptr = raw_es + es_position;
|
const uint8_t* frame_ptr = raw_es + es_position;
|
||||||
DVLOG(LOG_LEVEL_ES)
|
DVLOG(LOG_LEVEL_ES) << "syncword @ pos=" << es_position
|
||||||
<< "ADTS syncword @ pos=" << es_position
|
<< " frame_size=" << audio_header_->GetFrameSize();
|
||||||
<< " frame_size=" << frame_size;
|
DVLOG(LOG_LEVEL_ES) << "header: "
|
||||||
DVLOG(LOG_LEVEL_ES)
|
<< base::HexEncode(frame_ptr,
|
||||||
<< "ADTS header: "
|
audio_header_->GetHeaderSize());
|
||||||
<< 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 (static_cast<int>(audio_header_->GetFrameSize()) > 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);
|
if (!UpdateAudioConfiguration(*audio_header_))
|
||||||
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.
|
||||||
|
@ -160,7 +143,9 @@ bool EsParserAdts::Parse(const uint8_t* buf,
|
||||||
bool is_key_frame = true;
|
bool is_key_frame = true;
|
||||||
|
|
||||||
std::shared_ptr<MediaSample> sample = MediaSample::CopyFrom(
|
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_pts(current_pts);
|
||||||
sample->set_dts(current_pts);
|
sample->set_dts(current_pts);
|
||||||
sample->set_duration(frame_duration);
|
sample->set_duration(frame_duration);
|
||||||
|
@ -170,7 +155,7 @@ bool EsParserAdts::Parse(const uint8_t* buf,
|
||||||
audio_timestamp_helper_->AddFrames(kSamplesPerAACFrame);
|
audio_timestamp_helper_->AddFrames(kSamplesPerAACFrame);
|
||||||
|
|
||||||
// Skip the current frame.
|
// Skip the current frame.
|
||||||
es_position += frame_size;
|
es_position += static_cast<int>(audio_header_->GetFrameSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discard all the bytes that have been processed.
|
// Discard all the bytes that have been processed.
|
||||||
|
@ -188,18 +173,11 @@ void EsParserAdts::Reset() {
|
||||||
last_audio_decoder_config_ = std::shared_ptr<AudioStreamInfo>();
|
last_audio_decoder_config_ = std::shared_ptr<AudioStreamInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EsParserAdts::UpdateAudioConfiguration(const uint8_t* adts_frame,
|
bool EsParserAdts::UpdateAudioConfiguration(const AudioHeader& audio_header) {
|
||||||
size_t adts_frame_size) {
|
|
||||||
const uint8_t kAacSampleSizeBits(16);
|
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;
|
std::vector<uint8_t> audio_specific_config;
|
||||||
if (!adts_header.GetAudioSpecificConfig(&audio_specific_config))
|
audio_header.GetAudioSpecificConfig(&audio_specific_config);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (last_audio_decoder_config_) {
|
if (last_audio_decoder_config_) {
|
||||||
// Verify that the audio decoder config has not changed.
|
// 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
|
// 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.)
|
||||||
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_
|
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;
|
||||||
|
|
||||||
|
const Codec codec = kCodecAAC;
|
||||||
last_audio_decoder_config_ = std::make_shared<AudioStreamInfo>(
|
last_audio_decoder_config_ = std::make_shared<AudioStreamInfo>(
|
||||||
pid(), kMpeg2Timescale, kInfiniteDuration, kCodecAAC,
|
pid(), kMpeg2Timescale, kInfiniteDuration, codec,
|
||||||
AudioStreamInfo::GetCodecString(kCodecAAC, adts_header.GetObjectType()),
|
AudioStreamInfo::GetCodecString(codec, audio_header.GetObjectType()),
|
||||||
audio_specific_config.data(), audio_specific_config.size(),
|
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 */,
|
extended_samples_per_second, 0 /* seek preroll */, 0 /* codec delay */,
|
||||||
0 /* max bitrate */, 0 /* avg bitrate */, std::string(), false);
|
0 /* max bitrate */, 0 /* avg bitrate */, std::string(), 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: " << adts_header.GetNumChannels();
|
DVLOG(1) << "Channel config: "
|
||||||
DVLOG(1) << "Object type: " << adts_header.GetObjectType();
|
<< 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.
|
// Reset the timestamp helper to use a new sampling frequency.
|
||||||
if (audio_timestamp_helper_) {
|
if (audio_timestamp_helper_) {
|
||||||
int64_t base_timestamp = audio_timestamp_helper_->GetTimestamp();
|
int64_t base_timestamp = audio_timestamp_helper_->GetTimestamp();
|
||||||
|
|
|
@ -22,6 +22,8 @@ class BitReader;
|
||||||
|
|
||||||
namespace mp2t {
|
namespace mp2t {
|
||||||
|
|
||||||
|
class AudioHeader;
|
||||||
|
|
||||||
class EsParserAdts : public EsParser {
|
class EsParserAdts : public EsParser {
|
||||||
public:
|
public:
|
||||||
EsParserAdts(uint32_t pid,
|
EsParserAdts(uint32_t pid,
|
||||||
|
@ -41,13 +43,14 @@ class EsParserAdts : public EsParser {
|
||||||
typedef std::list<EsPts> EsPtsList;
|
typedef std::list<EsPts> EsPtsList;
|
||||||
|
|
||||||
// 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 audio config.
|
||||||
// a supported ADTS audio config.
|
bool UpdateAudioConfiguration(const AudioHeader& audio_header);
|
||||||
bool UpdateAudioConfiguration(const uint8_t* 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);
|
||||||
|
|
||||||
|
std::unique_ptr<AudioHeader> audio_header_;
|
||||||
|
|
||||||
// Callbacks:
|
// Callbacks:
|
||||||
// - to signal a new audio configuration,
|
// - to signal a new audio configuration,
|
||||||
// - to send ES buffers.
|
// - to send ES buffers.
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
'sources': [
|
'sources': [
|
||||||
'adts_header.cc',
|
'adts_header.cc',
|
||||||
'adts_header.h',
|
'adts_header.h',
|
||||||
|
'audio_header.h',
|
||||||
'continuity_counter.cc',
|
'continuity_counter.cc',
|
||||||
'continuity_counter.h',
|
'continuity_counter.h',
|
||||||
'es_parser_adts.cc',
|
'es_parser_adts.cc',
|
||||||
|
|
|
@ -48,7 +48,6 @@ const uint32_t kPesStreamIdVideo = 0xE0;
|
||||||
const uint32_t kPesStreamIdAudioMask = 0xE0;
|
const uint32_t kPesStreamIdAudioMask = 0xE0;
|
||||||
const uint32_t kPesStreamIdAudio = 0xC0;
|
const uint32_t kPesStreamIdAudio = 0xC0;
|
||||||
const uint32_t kVersion4 = 4;
|
const uint32_t kVersion4 = 4;
|
||||||
const size_t kAdtsHeaderMinSize = 7;
|
|
||||||
const uint8_t kAacSampleSizeBits = 16;
|
const uint8_t kAacSampleSizeBits = 16;
|
||||||
// Applies to all video streams.
|
// Applies to all video streams.
|
||||||
const uint8_t kNaluLengthSize = 4; // unit is bytes.
|
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) ==
|
} else if ((prev_pes_stream_id_ & kPesStreamIdAudioMask) ==
|
||||||
kPesStreamIdAudio) {
|
kPesStreamIdAudio) {
|
||||||
// Set data on the audio stream.
|
// Set data on the audio stream.
|
||||||
int frame_size = static_cast<int>(mp2t::AdtsHeader::GetAdtsFrameSize(
|
|
||||||
sample_data_.data(), kAdtsHeaderMinSize));
|
|
||||||
mp2t::AdtsHeader adts_header;
|
mp2t::AdtsHeader adts_header;
|
||||||
const uint8_t* frame_ptr = sample_data_.data();
|
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";
|
LOG(ERROR) << "Could not parse ADTS header";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
size_t header_size = adts_header.GetAdtsHeaderSize(frame_ptr,
|
media_sample_->SetData(
|
||||||
frame_size);
|
frame_ptr + adts_header.GetHeaderSize(),
|
||||||
media_sample_->SetData(frame_ptr + header_size, frame_size - header_size);
|
adts_header.GetFrameSize() - adts_header.GetHeaderSize());
|
||||||
if (!is_initialized_) {
|
if (!is_initialized_) {
|
||||||
for (uint32_t i = 0; i < stream_infos_.size(); i++) {
|
for (uint32_t i = 0; i < stream_infos_.size(); i++) {
|
||||||
if (stream_infos_[i]->stream_type() == kStreamAudio &&
|
if (stream_infos_[i]->stream_type() == kStreamAudio &&
|
||||||
|
@ -927,10 +924,7 @@ bool WvmMediaParser::Output(bool output_encrypted_sample) {
|
||||||
audio_stream_info->set_sampling_frequency(
|
audio_stream_info->set_sampling_frequency(
|
||||||
adts_header.GetSamplingFrequency());
|
adts_header.GetSamplingFrequency());
|
||||||
std::vector<uint8_t> audio_specific_config;
|
std::vector<uint8_t> audio_specific_config;
|
||||||
if (!adts_header.GetAudioSpecificConfig(&audio_specific_config)) {
|
adts_header.GetAudioSpecificConfig(&audio_specific_config);
|
||||||
LOG(ERROR) << "Could not compute AACAudiospecificconfig";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
audio_stream_info->set_codec_config(audio_specific_config);
|
audio_stream_info->set_codec_config(audio_specific_config);
|
||||||
audio_stream_info->set_codec_string(
|
audio_stream_info->set_codec_string(
|
||||||
AudioStreamInfo::GetCodecString(
|
AudioStreamInfo::GetCodecString(
|
||||||
|
|
Loading…
Reference in New Issue