Implement write function for cenc and es_descriptor.
And MP4 code clean ups due to mp4 box changes. Change-Id: I712b2dceb5038bfefa14097fe2fb2ab1fb913cb5
This commit is contained in:
parent
dccb069ffc
commit
01646837e1
|
@ -17,8 +17,6 @@ DecryptConfig::DecryptConfig(const std::string& key_id,
|
||||||
data_offset_(data_offset),
|
data_offset_(data_offset),
|
||||||
subsamples_(subsamples) {
|
subsamples_(subsamples) {
|
||||||
CHECK_GT(key_id.size(), 0u);
|
CHECK_GT(key_id.size(), 0u);
|
||||||
CHECK(iv.size() == static_cast<size_t>(DecryptConfig::kDecryptionKeySize) ||
|
|
||||||
iv.empty());
|
|
||||||
CHECK_GE(data_offset, 0);
|
CHECK_GE(data_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace media {
|
||||||
// result, and then copying each byte from the decrypted block over the
|
// result, and then copying each byte from the decrypted block over the
|
||||||
// position of the corresponding encrypted byte.
|
// position of the corresponding encrypted byte.
|
||||||
struct SubsampleEntry {
|
struct SubsampleEntry {
|
||||||
uint32 clear_bytes;
|
uint16 clear_bytes;
|
||||||
uint32 cypher_bytes;
|
uint32 cypher_bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,12 +31,10 @@ struct SubsampleEntry {
|
||||||
class DecryptConfig {
|
class DecryptConfig {
|
||||||
public:
|
public:
|
||||||
// Keys are always 128 bits.
|
// Keys are always 128 bits.
|
||||||
static const int kDecryptionKeySize = 16;
|
static const size_t kDecryptionKeySize = 16;
|
||||||
|
|
||||||
// |key_id| is the ID that references the decryption key for this sample.
|
// |key_id| is the ID that references the decryption key for this sample.
|
||||||
// |iv| is the initialization vector defined by the encrypted format.
|
// |iv| is the initialization vector defined by the encrypted format.
|
||||||
// Currently |iv| must be 16 bytes as defined by WebM and ISO. Or must be
|
|
||||||
// empty which signals an unencrypted frame.
|
|
||||||
// |data_offset| is the amount of data that should be discarded from the
|
// |data_offset| is the amount of data that should be discarded from the
|
||||||
// head of the sample buffer before applying subsample information. A
|
// head of the sample buffer before applying subsample information. A
|
||||||
// decrypted buffer will be shorter than an encrypted buffer by this amount.
|
// decrypted buffer will be shorter than an encrypted buffer by this amount.
|
||||||
|
|
|
@ -37,8 +37,6 @@ AAC::~AAC() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AAC::Parse(const std::vector<uint8>& data) {
|
bool AAC::Parse(const std::vector<uint8>& data) {
|
||||||
codec_specific_data_ = data;
|
|
||||||
|
|
||||||
if (data.empty())
|
if (data.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ class AAC {
|
||||||
// The function will parse the data and get the ElementaryStreamDescriptor,
|
// The function will parse the data and get the ElementaryStreamDescriptor,
|
||||||
// then it will parse the ElementaryStreamDescriptor to get audio stream
|
// then it will parse the ElementaryStreamDescriptor to get audio stream
|
||||||
// configurations.
|
// configurations.
|
||||||
// |data| is always copied to |codec_specific_data_|.
|
|
||||||
bool Parse(const std::vector<uint8>& data);
|
bool Parse(const std::vector<uint8>& data);
|
||||||
|
|
||||||
// Gets the output sample rate for the AAC stream.
|
// Gets the output sample rate for the AAC stream.
|
||||||
|
@ -61,11 +60,6 @@ class AAC {
|
||||||
return num_channels_;
|
return num_channels_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the codec specific data.
|
|
||||||
std::vector<uint8> codec_specific_data() const {
|
|
||||||
return codec_specific_data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size in bytes of the ADTS header added by ConvertEsdsToADTS().
|
// Size in bytes of the ADTS header added by ConvertEsdsToADTS().
|
||||||
static const size_t kADTSHeaderSize = 7;
|
static const size_t kADTSHeaderSize = 7;
|
||||||
|
|
||||||
|
@ -82,8 +76,6 @@ class AAC {
|
||||||
// Is Parametric Stereo on?
|
// Is Parametric Stereo on?
|
||||||
bool ps_present_;
|
bool ps_present_;
|
||||||
|
|
||||||
std::vector<uint8> codec_specific_data_;
|
|
||||||
|
|
||||||
// The following variables store audio configuration information.
|
// The following variables store audio configuration information.
|
||||||
// They are based on the AAC specific configuration but can be overridden
|
// They are based on the AAC specific configuration but can be overridden
|
||||||
// by extensions in elementary stream descriptor.
|
// by extensions in elementary stream descriptor.
|
||||||
|
|
|
@ -6,46 +6,82 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "media/mp4/box_reader.h"
|
#include "media/base/buffer_reader.h"
|
||||||
|
#include "media/base/buffer_writer.h"
|
||||||
#include "media/mp4/rcheck.h"
|
#include "media/mp4/rcheck.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
|
||||||
|
// 64-bit (8-byte) or 128-bit (16-byte).
|
||||||
|
bool IsIvSizeValid(size_t iv_size) { return iv_size == 8 || iv_size == 16; }
|
||||||
|
|
||||||
|
// 16-bit |clear_bytes| and 32-bit |cypher_bytes|.
|
||||||
|
const size_t kSubsampleEntrySize = sizeof(uint16) + sizeof(uint32);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
FrameCENCInfo::FrameCENCInfo() {}
|
FrameCENCInfo::FrameCENCInfo() {}
|
||||||
|
FrameCENCInfo::FrameCENCInfo(const std::vector<uint8>& iv) : iv_(iv) {}
|
||||||
FrameCENCInfo::~FrameCENCInfo() {}
|
FrameCENCInfo::~FrameCENCInfo() {}
|
||||||
|
|
||||||
bool FrameCENCInfo::Parse(int iv_size, BufferReader* reader) {
|
bool FrameCENCInfo::Parse(uint8 iv_size, BufferReader* reader) {
|
||||||
const int kEntrySize = 6;
|
DCHECK(reader);
|
||||||
// Mandated by CENC spec
|
// Mandated by CENC spec.
|
||||||
RCHECK(iv_size == 8 || iv_size == 16);
|
RCHECK(IsIvSizeValid(iv_size));
|
||||||
|
|
||||||
memset(iv, 0, sizeof(iv));
|
iv_.resize(iv_size);
|
||||||
for (int i = 0; i < iv_size; i++)
|
RCHECK(reader->ReadToVector(&iv_, iv_size));
|
||||||
RCHECK(reader->Read1(&iv[i]));
|
|
||||||
|
|
||||||
if (!reader->HasBytes(1)) return true;
|
if (!reader->HasBytes(1))
|
||||||
|
return true;
|
||||||
|
|
||||||
uint16 subsample_count;
|
uint16 subsample_count;
|
||||||
RCHECK(reader->Read2(&subsample_count) &&
|
RCHECK(reader->Read2(&subsample_count) &&
|
||||||
reader->HasBytes(subsample_count * kEntrySize));
|
reader->HasBytes(subsample_count * kSubsampleEntrySize));
|
||||||
|
|
||||||
subsamples.resize(subsample_count);
|
subsamples_.resize(subsample_count);
|
||||||
for (int i = 0; i < subsample_count; i++) {
|
for (uint16 i = 0; i < subsample_count; ++i) {
|
||||||
uint16 clear_bytes;
|
uint16 clear_bytes;
|
||||||
uint32 cypher_bytes;
|
uint32 cypher_bytes;
|
||||||
RCHECK(reader->Read2(&clear_bytes) &&
|
RCHECK(reader->Read2(&clear_bytes) &&
|
||||||
reader->Read4(&cypher_bytes));
|
reader->Read4(&cypher_bytes));
|
||||||
subsamples[i].clear_bytes = clear_bytes;
|
subsamples_[i].clear_bytes = clear_bytes;
|
||||||
subsamples[i].cypher_bytes = cypher_bytes;
|
subsamples_[i].cypher_bytes = cypher_bytes;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameCENCInfo::Write(BufferWriter* writer) const {
|
||||||
|
DCHECK(writer);
|
||||||
|
DCHECK(IsIvSizeValid(iv_.size()));
|
||||||
|
writer->AppendVector(iv_);
|
||||||
|
|
||||||
|
uint16 subsample_count = subsamples_.size();
|
||||||
|
if (subsample_count == 0)
|
||||||
|
return;
|
||||||
|
writer->AppendInt(subsample_count);
|
||||||
|
|
||||||
|
for (uint16 i = 0; i < subsample_count; ++i) {
|
||||||
|
writer->AppendInt(subsamples_[i].clear_bytes);
|
||||||
|
writer->AppendInt(subsamples_[i].cypher_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FrameCENCInfo::ComputeSize() const {
|
||||||
|
uint16 subsample_count = subsamples_.size();
|
||||||
|
if (subsample_count == 0)
|
||||||
|
return iv_.size();
|
||||||
|
|
||||||
|
return iv_.size() + sizeof(subsample_count) +
|
||||||
|
subsample_count * kSubsampleEntrySize;
|
||||||
|
}
|
||||||
|
|
||||||
size_t FrameCENCInfo::GetTotalSizeOfSubsamples() const {
|
size_t FrameCENCInfo::GetTotalSizeOfSubsamples() const {
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (size_t i = 0; i < subsamples.size(); i++) {
|
for (size_t i = 0; i < subsamples_.size(); ++i) {
|
||||||
size += subsamples[i].clear_bytes + subsamples[i].cypher_bytes;
|
size += subsamples_[i].clear_bytes + subsamples_[i].cypher_bytes;
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,20 +11,37 @@
|
||||||
#include "media/base/decrypt_config.h"
|
#include "media/base/decrypt_config.h"
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
|
||||||
|
|
||||||
class BufferReader;
|
class BufferReader;
|
||||||
|
class BufferWriter;
|
||||||
|
|
||||||
struct FrameCENCInfo {
|
namespace mp4 {
|
||||||
uint8 iv[16];
|
|
||||||
std::vector<SubsampleEntry> subsamples;
|
|
||||||
|
|
||||||
|
class FrameCENCInfo {
|
||||||
|
public:
|
||||||
FrameCENCInfo();
|
FrameCENCInfo();
|
||||||
|
explicit FrameCENCInfo(const std::vector<uint8>& iv);
|
||||||
~FrameCENCInfo();
|
~FrameCENCInfo();
|
||||||
bool Parse(int iv_size, BufferReader* r);
|
|
||||||
size_t GetTotalSizeOfSubsamples() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
bool Parse(uint8 iv_size, BufferReader* reader);
|
||||||
|
void Write(BufferWriter* writer) const;
|
||||||
|
size_t ComputeSize() const;
|
||||||
|
size_t GetTotalSizeOfSubsamples() const;
|
||||||
|
|
||||||
|
void AddSubsample(const SubsampleEntry& subsample) {
|
||||||
|
subsamples_.push_back(subsample);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint8>& iv() const { return iv_; }
|
||||||
|
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8> iv_;
|
||||||
|
std::vector<SubsampleEntry> subsamples_;
|
||||||
|
|
||||||
|
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
|
||||||
|
// generated copy constructor and assignment operator.
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace mp4
|
} // namespace mp4
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "media/mp4/chunk_info_iterator.h"
|
#include "media/mp4/chunk_info_iterator.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
#include "base/memory/scoped_ptr.h"
|
#include "base/memory/scoped_ptr.h"
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
|
@ -18,8 +19,8 @@ namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
const uint32 kNumChunks = 100;
|
const uint32 kNumChunks = 100;
|
||||||
const ChunkInfo kChunkInfos[] =
|
const ChunkInfo kChunkInfos[] = {
|
||||||
{{1, 8, 1}, {9, 5, 1}, {25, 7, 2}, {48, 63, 2}, {80, 2, 1}};
|
{1, 8, 1}, {9, 5, 1}, {25, 7, 2}, {48, 63, 2}, {80, 2, 1}};
|
||||||
|
|
||||||
class ChunkInfoIteratorTest : public testing::Test {
|
class ChunkInfoIteratorTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
|
@ -33,9 +34,8 @@ class ChunkInfoIteratorTest : public testing::Test {
|
||||||
uint32 next_first_chunk =
|
uint32 next_first_chunk =
|
||||||
(i == length - 1) ? kNumChunks + 1 : kChunkInfos[i + 1].first_chunk;
|
(i == length - 1) ? kNumChunks + 1 : kChunkInfos[i + 1].first_chunk;
|
||||||
for (; chunk_index < next_first_chunk; ++chunk_index) {
|
for (; chunk_index < next_first_chunk; ++chunk_index) {
|
||||||
ChunkProperty chunk = {kChunkInfos[i].samples_per_chunk, kChunkInfos[i].sample_description_index};
|
ChunkProperty chunk = {kChunkInfos[i].samples_per_chunk,
|
||||||
//chunk.samples_per_chunk = kChunkInfos[i].samples_per_chunk;
|
kChunkInfos[i].sample_description_index};
|
||||||
//chunk.sample_description_index = kChunkInfos[i].sample_description_index;
|
|
||||||
chunk_info_table_.push_back(chunk);
|
chunk_info_table_.push_back(chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,36 @@
|
||||||
#include "media/mp4/es_descriptor.h"
|
#include "media/mp4/es_descriptor.h"
|
||||||
|
|
||||||
#include "media/base/bit_reader.h"
|
#include "media/base/bit_reader.h"
|
||||||
|
#include "media/base/buffer_writer.h"
|
||||||
#include "media/mp4/rcheck.h"
|
#include "media/mp4/rcheck.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// ISO/IEC 14496-1:2004 Section 7.2.6.6 Table 6: StreamType values.
|
||||||
|
enum StreamType {
|
||||||
|
kForbiddenStreamType = 0x00,
|
||||||
|
kObjectDescriptorStreamType = 0x01,
|
||||||
|
kClockReferenceStreamType = 0x02,
|
||||||
|
kSceneDescriptionStreamType = 0x03,
|
||||||
|
kVisualStreamType = 0x04,
|
||||||
|
kAudioStreamType = 0x05,
|
||||||
|
kMPEG7StreamType = 0x06,
|
||||||
|
kIPMPStreamType = 0x07,
|
||||||
|
kObjectContentInfoStreamType = 0x08,
|
||||||
|
kMPEGJStreamType = 0x09,
|
||||||
|
kInteractionStream = 0x0A,
|
||||||
|
kIPMPToolStreamType = 0x0B,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ISO/IEC 14496-1:2004 Section 7.3.2.3 Table 12: ISO SL Config Descriptor.
|
||||||
|
enum SLPredefinedTags {
|
||||||
|
kSLPredefinedNull = 0x01,
|
||||||
|
kSLPredefinedMP4 = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
// The elementary stream size is specific by up to 4 bytes.
|
// The elementary stream size is specific by up to 4 bytes.
|
||||||
// The MSB of a byte indicates if there are more bytes for the size.
|
// The MSB of a byte indicates if there are more bytes for the size.
|
||||||
static bool ReadESSize(media::BitReader* reader, uint32* size) {
|
bool ReadESSize(media::BitReader* reader, uint32* size) {
|
||||||
uint8 msb;
|
uint8 msb;
|
||||||
uint8 byte;
|
uint8 byte;
|
||||||
|
|
||||||
|
@ -27,18 +52,18 @@ static bool ReadESSize(media::BitReader* reader, uint32* size) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Descryptor Header Size: 1 byte tag and 1 byte size (we don't support
|
||||||
|
// multi-bytes size for now).
|
||||||
|
const size_t kHeaderSize = 2;
|
||||||
|
const size_t kMaxDecoderSpecificInfoSize = 64;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
// static
|
ESDescriptor::ESDescriptor() : esid_(0), object_type_(kForbidden) {}
|
||||||
bool ESDescriptor::IsAAC(ObjectType object_type) {
|
|
||||||
return object_type == kISO_14496_3 || object_type == kISO_13818_7_AAC_LC;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESDescriptor::ESDescriptor()
|
|
||||||
: object_type_(kForbidden) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ESDescriptor::~ESDescriptor() {}
|
ESDescriptor::~ESDescriptor() {}
|
||||||
|
|
||||||
|
@ -55,7 +80,7 @@ bool ESDescriptor::Parse(const std::vector<uint8>& data) {
|
||||||
RCHECK(tag == kESDescrTag);
|
RCHECK(tag == kESDescrTag);
|
||||||
RCHECK(ReadESSize(&reader, &size));
|
RCHECK(ReadESSize(&reader, &size));
|
||||||
|
|
||||||
RCHECK(reader.ReadBits(16, &dummy)); // ES_ID
|
RCHECK(reader.ReadBits(16, &esid_)); // ES_ID
|
||||||
RCHECK(reader.ReadBits(1, &stream_dependency_flag));
|
RCHECK(reader.ReadBits(1, &stream_dependency_flag));
|
||||||
RCHECK(reader.ReadBits(1, &url_flag));
|
RCHECK(reader.ReadBits(1, &url_flag));
|
||||||
RCHECK(!url_flag); // We don't support url flag
|
RCHECK(!url_flag); // We don't support url flag
|
||||||
|
@ -72,14 +97,6 @@ bool ESDescriptor::Parse(const std::vector<uint8>& data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectType ESDescriptor::object_type() const {
|
|
||||||
return object_type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<uint8>& ESDescriptor::decoder_specific_info() const {
|
|
||||||
return decoder_specific_info_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ESDescriptor::ParseDecoderConfigDescriptor(BitReader* reader) {
|
bool ESDescriptor::ParseDecoderConfigDescriptor(BitReader* reader) {
|
||||||
uint8 tag;
|
uint8 tag;
|
||||||
uint32 size;
|
uint32 size;
|
||||||
|
@ -98,6 +115,7 @@ bool ESDescriptor::ParseDecoderConfigDescriptor(BitReader* reader) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESDescriptor::ParseDecoderSpecificInfo(BitReader* reader) {
|
bool ESDescriptor::ParseDecoderSpecificInfo(BitReader* reader) {
|
||||||
|
DCHECK(reader);
|
||||||
uint8 tag;
|
uint8 tag;
|
||||||
uint32 size;
|
uint32 size;
|
||||||
|
|
||||||
|
@ -112,6 +130,65 @@ bool ESDescriptor::ParseDecoderSpecificInfo(BitReader* reader) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESDescriptor::Write(BufferWriter* writer) const {
|
||||||
|
// TODO(kqyang): Consider writing Descriptor classes.
|
||||||
|
// ElementaryStreamDescriptor, DecoderConfigDescriptor, SLConfigDescriptor,
|
||||||
|
// DecoderSpecificInfoDescriptor.
|
||||||
|
DCHECK(writer);
|
||||||
|
CHECK_LT(decoder_specific_info_.size(), kMaxDecoderSpecificInfoSize);
|
||||||
|
|
||||||
|
const std::vector<uint8> kEmptyDecodingBufferSize(3, 0);
|
||||||
|
const uint32 kUnknownBitrate = 0;
|
||||||
|
const uint8 kNoEsFlags = 0;
|
||||||
|
|
||||||
|
const uint8 decoder_specific_info_size = decoder_specific_info_.size();
|
||||||
|
|
||||||
|
// 6 bit stream type. The last bit is reserved with 1.
|
||||||
|
const uint8 stream_type = (kAudioStreamType << 2) | 1;
|
||||||
|
const uint8 decoder_config_size = decoder_specific_info_size + kHeaderSize +
|
||||||
|
sizeof(uint8) + // object_type_.
|
||||||
|
sizeof(stream_type) +
|
||||||
|
kEmptyDecodingBufferSize.size() +
|
||||||
|
sizeof(kUnknownBitrate) * 2;
|
||||||
|
|
||||||
|
const uint8 sl_config_size = sizeof(uint8); // predefined.
|
||||||
|
const uint8 es_size = decoder_config_size + kHeaderSize + sl_config_size +
|
||||||
|
kHeaderSize + sizeof(esid_) + sizeof(kNoEsFlags);
|
||||||
|
|
||||||
|
writer->AppendInt(static_cast<uint8>(kESDescrTag));
|
||||||
|
writer->AppendInt(es_size);
|
||||||
|
writer->AppendInt(esid_);
|
||||||
|
writer->AppendInt(kNoEsFlags);
|
||||||
|
|
||||||
|
writer->AppendInt(static_cast<uint8>(kDecoderConfigDescrTag));
|
||||||
|
writer->AppendInt(decoder_config_size);
|
||||||
|
writer->AppendInt(static_cast<uint8>(object_type_));
|
||||||
|
writer->AppendInt(stream_type);
|
||||||
|
writer->AppendVector(kEmptyDecodingBufferSize);
|
||||||
|
writer->AppendInt(kUnknownBitrate); // max_bitrate.
|
||||||
|
writer->AppendInt(kUnknownBitrate); // avg_bitrate.
|
||||||
|
|
||||||
|
writer->AppendInt(static_cast<uint8>(kDecoderSpecificInfoTag));
|
||||||
|
writer->AppendInt(decoder_specific_info_size);
|
||||||
|
writer->AppendVector(decoder_specific_info_);
|
||||||
|
|
||||||
|
writer->AppendInt(static_cast<uint8>(kSLConfigTag));
|
||||||
|
writer->AppendInt(sl_config_size);
|
||||||
|
writer->AppendInt(static_cast<uint8>(kSLPredefinedMP4));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ESDescriptor::ComputeSize() const {
|
||||||
|
// A bit magical. Refer to ESDescriptor::Write for details.
|
||||||
|
const uint8 decoder_specific_info_size = decoder_specific_info_.size();
|
||||||
|
const uint8 decoder_config_size = decoder_specific_info_size + kHeaderSize +
|
||||||
|
sizeof(uint8) * 5 + sizeof(uint32) * 2;
|
||||||
|
const uint8 sl_config_size = sizeof(uint8);
|
||||||
|
const uint8 es_size = decoder_config_size + kHeaderSize +
|
||||||
|
sl_config_size + kHeaderSize +
|
||||||
|
sizeof(esid_) + sizeof(uint8);
|
||||||
|
return es_size + kHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mp4
|
} // namespace mp4
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class BitReader;
|
class BitReader;
|
||||||
|
class BufferWriter;
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
|
@ -29,27 +30,44 @@ enum ObjectType {
|
||||||
// Please refer to ISO 14496 Part 1 7.2.6.5 for more details.
|
// Please refer to ISO 14496 Part 1 7.2.6.5 for more details.
|
||||||
class ESDescriptor {
|
class ESDescriptor {
|
||||||
public:
|
public:
|
||||||
// Utility function to check if the given object type is AAC.
|
|
||||||
static bool IsAAC(ObjectType object_type);
|
|
||||||
|
|
||||||
ESDescriptor();
|
ESDescriptor();
|
||||||
~ESDescriptor();
|
~ESDescriptor();
|
||||||
|
|
||||||
bool Parse(const std::vector<uint8>& data);
|
bool Parse(const std::vector<uint8>& data);
|
||||||
|
void Write(BufferWriter* writer) const;
|
||||||
|
size_t ComputeSize() const;
|
||||||
|
|
||||||
ObjectType object_type() const;
|
uint16 esid() const { return esid_; }
|
||||||
const std::vector<uint8>& decoder_specific_info() const;
|
void set_esid(uint16 esid) { esid_ = esid; }
|
||||||
|
|
||||||
|
ObjectType object_type() const { return object_type_; }
|
||||||
|
void set_object_type(ObjectType object_type) { object_type_ = object_type; }
|
||||||
|
|
||||||
|
const std::vector<uint8>& decoder_specific_info() const {
|
||||||
|
return decoder_specific_info_;
|
||||||
|
}
|
||||||
|
void set_decoder_specific_info(
|
||||||
|
const std::vector<uint8>& decoder_specific_info) {
|
||||||
|
decoder_specific_info_ = decoder_specific_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the stream is AAC.
|
||||||
|
bool IsAAC() const {
|
||||||
|
return object_type_ == kISO_14496_3 || object_type_ == kISO_13818_7_AAC_LC;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum Tag {
|
enum Tag {
|
||||||
kESDescrTag = 0x03,
|
kESDescrTag = 0x03,
|
||||||
kDecoderConfigDescrTag = 0x04,
|
kDecoderConfigDescrTag = 0x04,
|
||||||
kDecoderSpecificInfoTag = 0x05
|
kDecoderSpecificInfoTag = 0x05,
|
||||||
|
kSLConfigTag = 0x06,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ParseDecoderConfigDescriptor(BitReader* reader);
|
bool ParseDecoderConfigDescriptor(BitReader* reader);
|
||||||
bool ParseDecoderSpecificInfo(BitReader* reader);
|
bool ParseDecoderSpecificInfo(BitReader* reader);
|
||||||
|
|
||||||
|
uint16 esid_; // Elementary Stream ID.
|
||||||
ObjectType object_type_;
|
ObjectType object_type_;
|
||||||
std::vector<uint8> decoder_specific_info_;
|
std::vector<uint8> decoder_specific_info_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#define RCHECK(x) \
|
#define RCHECK(x) \
|
||||||
do { \
|
do { \
|
||||||
if (!(x)) { \
|
if (!(x)) { \
|
||||||
DLOG(ERROR) << "Failure while parsing MP4: " << #x; \
|
LOG(ERROR) << "Failure while processing MP4: " << #x; \
|
||||||
return false; \
|
return false; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "media/base/buffer_reader.h"
|
||||||
#include "media/mp4/chunk_info_iterator.h"
|
#include "media/mp4/chunk_info_iterator.h"
|
||||||
#include "media/mp4/composition_offset_iterator.h"
|
#include "media/mp4/composition_offset_iterator.h"
|
||||||
#include "media/mp4/decoding_time_iterator.h"
|
#include "media/mp4/decoding_time_iterator.h"
|
||||||
|
@ -90,12 +91,12 @@ static void PopulateSampleInfo(const TrackExtends& trex,
|
||||||
uint32 flags;
|
uint32 flags;
|
||||||
if (i < trun.sample_flags.size()) {
|
if (i < trun.sample_flags.size()) {
|
||||||
flags = trun.sample_flags[i];
|
flags = trun.sample_flags[i];
|
||||||
} else if (tfhd.flags & kDefaultSampleFlagsPresentMask) {
|
} else if (tfhd.flags & TrackFragmentHeader::kDefaultSampleFlagsPresentMask) {
|
||||||
flags = tfhd.default_sample_flags;
|
flags = tfhd.default_sample_flags;
|
||||||
} else {
|
} else {
|
||||||
flags = trex.default_sample_flags;
|
flags = trex.default_sample_flags;
|
||||||
}
|
}
|
||||||
sample_info->is_keyframe = !(flags & kNonKeySampleMask);
|
sample_info->is_keyframe = !(flags & TrackFragmentHeader::kNonKeySampleMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In well-structured encrypted media, each track run will be immediately
|
// In well-structured encrypted media, each track run will be immediately
|
||||||
|
@ -396,6 +397,7 @@ bool TrackRunIterator::AuxInfoNeedsToBeCached() {
|
||||||
return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0;
|
return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(kqyang): Revisit later. We might not need to cache cenc info.
|
||||||
// This implementation currently only caches CENC auxiliary info.
|
// This implementation currently only caches CENC auxiliary info.
|
||||||
bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
|
bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
|
||||||
RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
|
RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
|
||||||
|
@ -525,8 +527,9 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
||||||
const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
|
const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
|
||||||
DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
|
DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
|
||||||
|
|
||||||
if (!cenc_info.subsamples.empty() && (cenc_info.GetTotalSizeOfSubsamples() !=
|
const size_t total_size_of_subsamples = cenc_info.GetTotalSizeOfSubsamples();
|
||||||
static_cast<size_t>(sample_size()))) {
|
if (total_size_of_subsamples != 0 &&
|
||||||
|
total_size_of_subsamples != static_cast<size_t>(sample_size())) {
|
||||||
LOG(ERROR) << "Incorrect CENC subsample size.";
|
LOG(ERROR) << "Incorrect CENC subsample size.";
|
||||||
return scoped_ptr<DecryptConfig>();
|
return scoped_ptr<DecryptConfig>();
|
||||||
}
|
}
|
||||||
|
@ -534,9 +537,9 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
||||||
const std::vector<uint8>& kid = track_encryption().default_kid;
|
const std::vector<uint8>& kid = track_encryption().default_kid;
|
||||||
return scoped_ptr<DecryptConfig>(new DecryptConfig(
|
return scoped_ptr<DecryptConfig>(new DecryptConfig(
|
||||||
std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
|
std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
|
||||||
std::string(cenc_info.iv.begin(), cenc_info.iv.end()),
|
std::string(cenc_info.iv().begin(), cenc_info.iv().end()),
|
||||||
0, // No offset to start of media data in MP4 using CENC.
|
0, // No offset to start of media data in MP4 using CENC.
|
||||||
cenc_info.subsamples));
|
cenc_info.subsamples()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mp4
|
} // namespace mp4
|
||||||
|
|
|
@ -82,7 +82,8 @@ class TrackRunIteratorTest : public testing::Test {
|
||||||
moof.tracks.resize(2);
|
moof.tracks.resize(2);
|
||||||
moof.tracks[0].decode_time.decode_time = 0;
|
moof.tracks[0].decode_time.decode_time = 0;
|
||||||
moof.tracks[0].header.track_id = 1;
|
moof.tracks[0].header.track_id = 1;
|
||||||
moof.tracks[0].header.flags = kDefaultSampleFlagsPresentMask;
|
moof.tracks[0].header.flags =
|
||||||
|
TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
|
||||||
moof.tracks[0].header.default_sample_duration = 1024;
|
moof.tracks[0].header.default_sample_duration = 1024;
|
||||||
moof.tracks[0].header.default_sample_size = 4;
|
moof.tracks[0].header.default_sample_size = 4;
|
||||||
moof.tracks[0].runs.resize(2);
|
moof.tracks[0].runs.resize(2);
|
||||||
|
@ -103,7 +104,8 @@ class TrackRunIteratorTest : public testing::Test {
|
||||||
SetAscending(&moof.tracks[1].runs[0].sample_durations);
|
SetAscending(&moof.tracks[1].runs[0].sample_durations);
|
||||||
moof.tracks[1].runs[0].sample_flags.resize(10);
|
moof.tracks[1].runs[0].sample_flags.resize(10);
|
||||||
for (size_t i = 1; i < moof.tracks[1].runs[0].sample_flags.size(); i++) {
|
for (size_t i = 1; i < moof.tracks[1].runs[0].sample_flags.size(); i++) {
|
||||||
moof.tracks[1].runs[0].sample_flags[i] = kNonKeySampleMask;
|
moof.tracks[1].runs[0].sample_flags[i] =
|
||||||
|
TrackFragmentHeader::kNonKeySampleMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return moof;
|
return moof;
|
||||||
|
@ -211,7 +213,8 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
|
||||||
TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
|
TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
|
||||||
moov_.extends.tracks[0].default_sample_duration = 50;
|
moov_.extends.tracks[0].default_sample_duration = 50;
|
||||||
moov_.extends.tracks[0].default_sample_size = 3;
|
moov_.extends.tracks[0].default_sample_size = 3;
|
||||||
moov_.extends.tracks[0].default_sample_flags = kNonKeySampleMask;
|
moov_.extends.tracks[0].default_sample_flags =
|
||||||
|
TrackFragmentHeader::kNonKeySampleMask;
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
moof.tracks[0].header.flags = 0;
|
moof.tracks[0].header.flags = 0;
|
||||||
|
@ -233,8 +236,10 @@ TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
|
||||||
// defaults for all subsequent samples
|
// defaults for all subsequent samples
|
||||||
iter_.reset(new TrackRunIterator(&moov_));
|
iter_.reset(new TrackRunIterator(&moov_));
|
||||||
MovieFragment moof = CreateFragment();
|
MovieFragment moof = CreateFragment();
|
||||||
moof.tracks[1].header.flags = kDefaultSampleFlagsPresentMask;
|
moof.tracks[1].header.flags =
|
||||||
moof.tracks[1].header.default_sample_flags = kNonKeySampleMask;
|
TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
|
||||||
|
moof.tracks[1].header.default_sample_flags =
|
||||||
|
TrackFragmentHeader::kNonKeySampleMask;
|
||||||
moof.tracks[1].runs[0].sample_flags.resize(1);
|
moof.tracks[1].runs[0].sample_flags.resize(1);
|
||||||
ASSERT_TRUE(iter_->Init(moof));
|
ASSERT_TRUE(iter_->Init(moof));
|
||||||
iter_->AdvanceRun();
|
iter_->AdvanceRun();
|
||||||
|
|
Loading…
Reference in New Issue