341 lines
10 KiB
C++
341 lines
10 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "Adts.h"
|
|
#include "BitWriter.h"
|
|
#include "MediaData.h"
|
|
#include "PlatformDecoderModule.h"
|
|
#include "mozilla/Array.h"
|
|
#include "mozilla/ArrayUtils.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "ADTSDemuxer.h"
|
|
|
|
extern mozilla::LazyLogModule gMediaDemuxerLog;
|
|
#define LOG(msg, ...) \
|
|
MOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__)
|
|
#define ADTSLOG(msg, ...) \
|
|
DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Debug, msg, ##__VA_ARGS__)
|
|
#define ADTSLOGV(msg, ...) \
|
|
DDMOZ_LOG(gMediaDemuxerLog, LogLevel::Verbose, msg, ##__VA_ARGS__)
|
|
|
|
namespace mozilla {
|
|
namespace ADTS {
|
|
|
|
static const int kADTSHeaderSize = 7;
|
|
|
|
constexpr std::array FREQ_LOOKUP{96000, 88200, 64000, 48000, 44100,
|
|
32000, 24000, 22050, 16000, 12000,
|
|
11025, 8000, 7350, 0};
|
|
|
|
Result<uint8_t, bool> GetFrequencyIndex(uint32_t aSamplesPerSecond) {
|
|
auto found =
|
|
std::find(FREQ_LOOKUP.begin(), FREQ_LOOKUP.end(), aSamplesPerSecond);
|
|
|
|
if (found == FREQ_LOOKUP.end()) {
|
|
return Err(false);
|
|
}
|
|
|
|
return std::distance(FREQ_LOOKUP.begin(), found);
|
|
}
|
|
|
|
bool ConvertSample(uint16_t aChannelCount, uint8_t aFrequencyIndex,
|
|
uint8_t aProfile, MediaRawData* aSample) {
|
|
size_t newSize = aSample->Size() + kADTSHeaderSize;
|
|
|
|
MOZ_LOG(sPDMLog, LogLevel::Debug,
|
|
("Converting sample to ADTS format: newSize: %zu, ch: %u, "
|
|
"profile: %u, freq index: %d",
|
|
newSize, aChannelCount, aProfile, aFrequencyIndex));
|
|
|
|
// ADTS header uses 13 bits for packet size.
|
|
if (newSize >= (1 << 13) || aChannelCount > 15 || aProfile < 1 ||
|
|
aProfile > 4 || aFrequencyIndex >= FREQ_LOOKUP.size()) {
|
|
MOZ_LOG(sPDMLog, LogLevel::Debug,
|
|
("Couldn't convert sample to ADTS format: newSize: %zu, ch: %u, "
|
|
"profile: %u, freq index: %d",
|
|
newSize, aChannelCount, aProfile, aFrequencyIndex));
|
|
return false;
|
|
}
|
|
|
|
Array<uint8_t, kADTSHeaderSize> header;
|
|
header[0] = 0xff;
|
|
header[1] = 0xf1;
|
|
header[2] =
|
|
((aProfile - 1) << 6) + (aFrequencyIndex << 2) + (aChannelCount >> 2);
|
|
header[3] = ((aChannelCount & 0x3) << 6) + (newSize >> 11);
|
|
header[4] = (newSize & 0x7ff) >> 3;
|
|
header[5] = ((newSize & 7) << 5) + 0x1f;
|
|
header[6] = 0xfc;
|
|
|
|
UniquePtr<MediaRawDataWriter> writer(aSample->CreateWriter());
|
|
if (!writer->Prepend(&header[0], std::size(header))) {
|
|
return false;
|
|
}
|
|
|
|
if (aSample->mCrypto.IsEncrypted()) {
|
|
if (aSample->mCrypto.mPlainSizes.Length() == 0) {
|
|
writer->mCrypto.mPlainSizes.AppendElement(kADTSHeaderSize);
|
|
writer->mCrypto.mEncryptedSizes.AppendElement(aSample->Size() -
|
|
kADTSHeaderSize);
|
|
} else {
|
|
writer->mCrypto.mPlainSizes[0] += kADTSHeaderSize;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StripHeader(MediaRawData* aSample) {
|
|
if (aSample->Size() < kADTSHeaderSize) {
|
|
return false;
|
|
}
|
|
|
|
FrameHeader header;
|
|
auto data = Span{aSample->Data(), aSample->Size()};
|
|
MOZ_ASSERT(FrameHeader::MatchesSync(data),
|
|
"Don't attempt to strip the ADTS header of a raw AAC packet.");
|
|
|
|
bool crcPresent = header.mHaveCrc;
|
|
|
|
LOG(("Stripping ADTS, crc %spresent", crcPresent ? "" : "not "));
|
|
|
|
size_t toStrip = crcPresent ? kADTSHeaderSize + 2 : kADTSHeaderSize;
|
|
|
|
UniquePtr<MediaRawDataWriter> writer(aSample->CreateWriter());
|
|
writer->PopFront(toStrip);
|
|
|
|
if (aSample->mCrypto.IsEncrypted()) {
|
|
if (aSample->mCrypto.mPlainSizes.Length() > 0 &&
|
|
writer->mCrypto.mPlainSizes[0] >= kADTSHeaderSize) {
|
|
writer->mCrypto.mPlainSizes[0] -= kADTSHeaderSize;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RevertSample(MediaRawData* aSample) {
|
|
if (aSample->Size() < kADTSHeaderSize) {
|
|
return false;
|
|
}
|
|
|
|
{
|
|
const uint8_t* header = aSample->Data();
|
|
if (header[0] != 0xff || header[1] != 0xf1 || header[6] != 0xfc) {
|
|
// Not ADTS.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
UniquePtr<MediaRawDataWriter> writer(aSample->CreateWriter());
|
|
writer->PopFront(kADTSHeaderSize);
|
|
|
|
if (aSample->mCrypto.IsEncrypted()) {
|
|
if (aSample->mCrypto.mPlainSizes.Length() > 0 &&
|
|
writer->mCrypto.mPlainSizes[0] >= kADTSHeaderSize) {
|
|
writer->mCrypto.mPlainSizes[0] -= kADTSHeaderSize;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FrameHeader::MatchesSync(const Span<const uint8_t>& aData) {
|
|
return aData.Length() >= 2 && aData[0] == 0xFF && (aData[1] & 0xF6) == 0xF0;
|
|
}
|
|
|
|
FrameHeader::FrameHeader() { Reset(); }
|
|
|
|
// Header size
|
|
uint64_t FrameHeader::HeaderSize() const { return (mHaveCrc) ? 9 : 7; }
|
|
|
|
bool FrameHeader::IsValid() const { return mFrameLength > 0; }
|
|
|
|
// Resets the state to allow for a new parsing session.
|
|
void FrameHeader::Reset() { PodZero(this); }
|
|
|
|
// Returns whether the byte creates a valid sequence up to this point.
|
|
bool FrameHeader::Parse(const Span<const uint8_t>& aData) {
|
|
if (!MatchesSync(aData)) {
|
|
return false;
|
|
}
|
|
|
|
// AAC has 1024 samples per frame per channel.
|
|
mSamples = 1024;
|
|
|
|
mHaveCrc = !(aData[1] & 0x01);
|
|
mObjectType = ((aData[2] & 0xC0) >> 6) + 1;
|
|
mSamplingIndex = (aData[2] & 0x3C) >> 2;
|
|
mChannelConfig = (aData[2] & 0x01) << 2 | (aData[3] & 0xC0) >> 6;
|
|
mFrameLength =
|
|
static_cast<uint32_t>((aData[3] & 0x03) << 11 | (aData[4] & 0xFF) << 3 |
|
|
(aData[5] & 0xE0) >> 5);
|
|
mNumAACFrames = (aData[6] & 0x03) + 1;
|
|
|
|
static const uint32_t SAMPLE_RATES[] = {96000, 88200, 64000, 48000, 44100,
|
|
32000, 24000, 22050, 16000, 12000,
|
|
11025, 8000, 7350};
|
|
if (mSamplingIndex >= std::size(SAMPLE_RATES)) {
|
|
LOG(("ADTS: Init() failure: invalid sample-rate index value: %" PRIu32 ".",
|
|
mSamplingIndex));
|
|
// This marks the header as invalid.
|
|
mFrameLength = 0;
|
|
return false;
|
|
}
|
|
mSampleRate = SAMPLE_RATES[mSamplingIndex];
|
|
|
|
MOZ_ASSERT(mChannelConfig < 8);
|
|
mChannels = (mChannelConfig == 7) ? 8 : mChannelConfig;
|
|
|
|
return true;
|
|
}
|
|
|
|
Frame::Frame() : mOffset(0), mHeader() {}
|
|
uint64_t Frame::Offset() const { return mOffset; }
|
|
size_t Frame::Length() const {
|
|
// TODO: If fields are zero'd when invalid, this check wouldn't be
|
|
// necessary.
|
|
if (!mHeader.IsValid()) {
|
|
return 0;
|
|
}
|
|
|
|
return mHeader.mFrameLength;
|
|
}
|
|
|
|
// Returns the offset to the start of frame's raw data.
|
|
uint64_t Frame::PayloadOffset() const { return mOffset + mHeader.HeaderSize(); }
|
|
|
|
// Returns the length of the frame's raw data (excluding the header) in bytes.
|
|
size_t Frame::PayloadLength() const {
|
|
// TODO: If fields are zero'd when invalid, this check wouldn't be
|
|
// necessary.
|
|
if (!mHeader.IsValid()) {
|
|
return 0;
|
|
}
|
|
|
|
return mHeader.mFrameLength - mHeader.HeaderSize();
|
|
}
|
|
|
|
// Returns the parsed frame header.
|
|
const FrameHeader& Frame::Header() const { return mHeader; }
|
|
|
|
bool Frame::IsValid() const { return mHeader.IsValid(); }
|
|
|
|
// Resets the frame header and data.
|
|
void Frame::Reset() {
|
|
mHeader.Reset();
|
|
mOffset = 0;
|
|
}
|
|
|
|
// Returns whether the valid
|
|
bool Frame::Parse(uint64_t aOffset, const uint8_t* aStart,
|
|
const uint8_t* aEnd) {
|
|
MOZ_ASSERT(aStart && aEnd && aStart <= aEnd);
|
|
|
|
bool found = false;
|
|
const uint8_t* ptr = aStart;
|
|
// Require at least 7 bytes of data at the end of the buffer for the minimum
|
|
// ADTS frame header.
|
|
while (ptr < aEnd - 7 && !found) {
|
|
found = mHeader.Parse(Span(ptr, aEnd));
|
|
ptr++;
|
|
}
|
|
|
|
mOffset = aOffset + (static_cast<size_t>(ptr - aStart)) - 1u;
|
|
|
|
return found;
|
|
}
|
|
|
|
const Frame& FrameParser::CurrentFrame() { return mFrame; }
|
|
|
|
const Frame& FrameParser::FirstFrame() const { return mFirstFrame; }
|
|
|
|
void FrameParser::Reset() {
|
|
EndFrameSession();
|
|
mFirstFrame.Reset();
|
|
}
|
|
|
|
void FrameParser::EndFrameSession() { mFrame.Reset(); }
|
|
|
|
bool FrameParser::Parse(uint64_t aOffset, const uint8_t* aStart,
|
|
const uint8_t* aEnd) {
|
|
const bool found = mFrame.Parse(aOffset, aStart, aEnd);
|
|
|
|
if (mFrame.Length() && !mFirstFrame.Length()) {
|
|
mFirstFrame = mFrame;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
// Initialize the AAC AudioSpecificConfig.
|
|
// Only handles two-byte version for AAC-LC.
|
|
void InitAudioSpecificConfig(const ADTS::Frame& frame,
|
|
MediaByteBuffer* aBuffer) {
|
|
const ADTS::FrameHeader& header = frame.Header();
|
|
MOZ_ASSERT(header.IsValid());
|
|
|
|
int audioObjectType = header.mObjectType;
|
|
int samplingFrequencyIndex = header.mSamplingIndex;
|
|
int channelConfig = header.mChannelConfig;
|
|
|
|
uint8_t asc[2];
|
|
asc[0] = (audioObjectType & 0x1F) << 3 | (samplingFrequencyIndex & 0x0E) >> 1;
|
|
asc[1] = (samplingFrequencyIndex & 0x01) << 7 | (channelConfig & 0x0F) << 3;
|
|
|
|
aBuffer->AppendElements(asc, 2);
|
|
}
|
|
|
|
// https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Audio_Specific_Config
|
|
Result<already_AddRefed<MediaByteBuffer>, nsresult> MakeSpecificConfig(
|
|
uint8_t aObjectType, uint32_t aFrequency, uint32_t aChannelCount) {
|
|
if (aObjectType > 45 /* USAC */ || aObjectType == 0x1F /* Escape value */) {
|
|
return Err(NS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (aFrequency > 0x00FFFFFF /* max value of 24 bits */) {
|
|
return Err(NS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
if (aChannelCount > 8 || aChannelCount == 7) {
|
|
return Err(NS_ERROR_INVALID_ARG);
|
|
}
|
|
|
|
uint8_t index = GetFrequencyIndex(aFrequency)
|
|
.unwrapOr(0x0F /* frequency is written explictly */);
|
|
MOZ_ASSERT(index <= 0x0F /* index needs only 4 bits */);
|
|
|
|
uint8_t channelConfig =
|
|
aChannelCount == 8 ? aChannelCount - 1 : aChannelCount;
|
|
|
|
RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer();
|
|
BitWriter bw(buffer);
|
|
|
|
if (aObjectType < 0x1F /* Escape value */) {
|
|
bw.WriteBits(aObjectType, 5);
|
|
} else { // If object type needs more than 5 bits
|
|
MOZ_ASSERT(aObjectType >= 32);
|
|
bw.WriteBits(0x1F, 5);
|
|
// Since aObjectType < 0x3F + 32, it's safe to put it into 6 bits.
|
|
bw.WriteBits(aObjectType - 32, 6);
|
|
}
|
|
|
|
bw.WriteBits(index, 4);
|
|
if (index == 0x0F /* frequency is written explictly */) {
|
|
bw.WriteBits(aFrequency, 24);
|
|
}
|
|
|
|
bw.WriteBits(channelConfig, 4);
|
|
|
|
// Skip extension configuration for now.
|
|
|
|
return buffer.forget();
|
|
}
|
|
|
|
}; // namespace ADTS
|
|
}; // namespace mozilla
|
|
|
|
#undef LOG
|
|
#undef ADTSLOG
|
|
#undef ADTSLOGV
|