Skip to content

Commit

Permalink
Refactor EsParserAdts to handle other audio
Browse files Browse the repository at this point in the history
Rename will happen in next CL

Change-Id: I28df29bb264eea7a6225b0a4a2eb38de7968f9d6
  • Loading branch information
kqyang committed Oct 25, 2017
1 parent 8672446 commit 2a2493e
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 182 deletions.
65 changes: 25 additions & 40 deletions packager/media/formats/mp2t/adts_header.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,27 @@
#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"

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).
Expand All @@ -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;
Expand All @@ -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 {
Expand Down
75 changes: 27 additions & 48 deletions packager/media/formats/mp2t/adts_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}

/// 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;
AdtsHeader() = default;
~AdtsHeader() override = default;

/// @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_;

DISALLOW_COPY_AND_ASSIGN(AdtsHeader);
AdtsHeader(const AdtsHeader&) = delete;
AdtsHeader& operator=(const AdtsHeader&) = delete;

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
Expand Down
36 changes: 17 additions & 19 deletions packager/media/formats/mp2t/adts_header_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -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));
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()));
}
// 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());

TEST_F(AdtsHeaderTest, GetHeaderSizeSuccess) {
const size_t kExpectedHeaderSize(7);
EXPECT_EQ(kExpectedHeaderSize,
AdtsHeader::GetAdtsHeaderSize(adts_frame_.data(),
adts_frame_.size()));
// 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(), header_size - 1));
}

} // Namespace mp2t
Expand Down
79 changes: 79 additions & 0 deletions packager/media/formats/mp2t/audio_header.h
Original file line number Diff line number Diff line change
@@ -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_
Loading

0 comments on commit 2a2493e

Please sign in to comment.