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:
Kongqun Yang 2013-11-26 18:23:30 -08:00
parent dccb069ffc
commit 01646837e1
12 changed files with 230 additions and 88 deletions

View File

@ -17,8 +17,6 @@ DecryptConfig::DecryptConfig(const std::string& key_id,
data_offset_(data_offset),
subsamples_(subsamples) {
CHECK_GT(key_id.size(), 0u);
CHECK(iv.size() == static_cast<size_t>(DecryptConfig::kDecryptionKeySize) ||
iv.empty());
CHECK_GE(data_offset, 0);
}

View File

@ -23,7 +23,7 @@ namespace media {
// result, and then copying each byte from the decrypted block over the
// position of the corresponding encrypted byte.
struct SubsampleEntry {
uint32 clear_bytes;
uint16 clear_bytes;
uint32 cypher_bytes;
};
@ -31,12 +31,10 @@ struct SubsampleEntry {
class DecryptConfig {
public:
// 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.
// |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
// head of the sample buffer before applying subsample information. A
// decrypted buffer will be shorter than an encrypted buffer by this amount.

View File

@ -37,8 +37,6 @@ AAC::~AAC() {
}
bool AAC::Parse(const std::vector<uint8>& data) {
codec_specific_data_ = data;
if (data.empty())
return false;

View File

@ -30,7 +30,6 @@ class AAC {
// The function will parse the data and get the ElementaryStreamDescriptor,
// then it will parse the ElementaryStreamDescriptor to get audio stream
// configurations.
// |data| is always copied to |codec_specific_data_|.
bool Parse(const std::vector<uint8>& data);
// Gets the output sample rate for the AAC stream.
@ -61,11 +60,6 @@ class AAC {
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().
static const size_t kADTSHeaderSize = 7;
@ -82,8 +76,6 @@ class AAC {
// Is Parametric Stereo on?
bool ps_present_;
std::vector<uint8> codec_specific_data_;
// The following variables store audio configuration information.
// They are based on the AAC specific configuration but can be overridden
// by extensions in elementary stream descriptor.

View File

@ -6,46 +6,82 @@
#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"
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 mp4 {
FrameCENCInfo::FrameCENCInfo() {}
FrameCENCInfo::FrameCENCInfo(const std::vector<uint8>& iv) : iv_(iv) {}
FrameCENCInfo::~FrameCENCInfo() {}
bool FrameCENCInfo::Parse(int iv_size, BufferReader* reader) {
const int kEntrySize = 6;
// Mandated by CENC spec
RCHECK(iv_size == 8 || iv_size == 16);
bool FrameCENCInfo::Parse(uint8 iv_size, BufferReader* reader) {
DCHECK(reader);
// Mandated by CENC spec.
RCHECK(IsIvSizeValid(iv_size));
memset(iv, 0, sizeof(iv));
for (int i = 0; i < iv_size; i++)
RCHECK(reader->Read1(&iv[i]));
iv_.resize(iv_size);
RCHECK(reader->ReadToVector(&iv_, iv_size));
if (!reader->HasBytes(1)) return true;
if (!reader->HasBytes(1))
return true;
uint16 subsample_count;
RCHECK(reader->Read2(&subsample_count) &&
reader->HasBytes(subsample_count * kEntrySize));
reader->HasBytes(subsample_count * kSubsampleEntrySize));
subsamples.resize(subsample_count);
for (int i = 0; i < subsample_count; i++) {
subsamples_.resize(subsample_count);
for (uint16 i = 0; i < subsample_count; ++i) {
uint16 clear_bytes;
uint32 cypher_bytes;
RCHECK(reader->Read2(&clear_bytes) &&
reader->Read4(&cypher_bytes));
subsamples[i].clear_bytes = clear_bytes;
subsamples[i].cypher_bytes = cypher_bytes;
subsamples_[i].clear_bytes = clear_bytes;
subsamples_[i].cypher_bytes = cypher_bytes;
}
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 size = 0;
for (size_t i = 0; i < subsamples.size(); i++) {
size += subsamples[i].clear_bytes + subsamples[i].cypher_bytes;
for (size_t i = 0; i < subsamples_.size(); ++i) {
size += subsamples_[i].clear_bytes + subsamples_[i].cypher_bytes;
}
return size;
}

View File

@ -11,20 +11,37 @@
#include "media/base/decrypt_config.h"
namespace media {
namespace mp4 {
class BufferReader;
class BufferWriter;
struct FrameCENCInfo {
uint8 iv[16];
std::vector<SubsampleEntry> subsamples;
namespace mp4 {
class FrameCENCInfo {
public:
FrameCENCInfo();
explicit FrameCENCInfo(const std::vector<uint8>& iv);
~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 media

View File

@ -4,6 +4,7 @@
#include "media/mp4/chunk_info_iterator.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -18,8 +19,8 @@ namespace media {
namespace mp4 {
const uint32 kNumChunks = 100;
const ChunkInfo kChunkInfos[] =
{{1, 8, 1}, {9, 5, 1}, {25, 7, 2}, {48, 63, 2}, {80, 2, 1}};
const ChunkInfo kChunkInfos[] = {
{1, 8, 1}, {9, 5, 1}, {25, 7, 2}, {48, 63, 2}, {80, 2, 1}};
class ChunkInfoIteratorTest : public testing::Test {
public:
@ -33,9 +34,8 @@ class ChunkInfoIteratorTest : public testing::Test {
uint32 next_first_chunk =
(i == length - 1) ? kNumChunks + 1 : kChunkInfos[i + 1].first_chunk;
for (; chunk_index < next_first_chunk; ++chunk_index) {
ChunkProperty chunk = {kChunkInfos[i].samples_per_chunk, kChunkInfos[i].sample_description_index};
//chunk.samples_per_chunk = kChunkInfos[i].samples_per_chunk;
//chunk.sample_description_index = kChunkInfos[i].sample_description_index;
ChunkProperty chunk = {kChunkInfos[i].samples_per_chunk,
kChunkInfos[i].sample_description_index};
chunk_info_table_.push_back(chunk);
}
}

View File

@ -5,11 +5,36 @@
#include "media/mp4/es_descriptor.h"
#include "media/base/bit_reader.h"
#include "media/base/buffer_writer.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 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 byte;
@ -27,18 +52,18 @@ static bool ReadESSize(media::BitReader* reader, uint32* size) {
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 mp4 {
// static
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() : esid_(0), object_type_(kForbidden) {}
ESDescriptor::~ESDescriptor() {}
@ -55,7 +80,7 @@ bool ESDescriptor::Parse(const std::vector<uint8>& data) {
RCHECK(tag == kESDescrTag);
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, &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;
}
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) {
uint8 tag;
uint32 size;
@ -98,6 +115,7 @@ bool ESDescriptor::ParseDecoderConfigDescriptor(BitReader* reader) {
}
bool ESDescriptor::ParseDecoderSpecificInfo(BitReader* reader) {
DCHECK(reader);
uint8 tag;
uint32 size;
@ -112,6 +130,65 @@ bool ESDescriptor::ParseDecoderSpecificInfo(BitReader* reader) {
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 media

View File

@ -12,6 +12,7 @@
namespace media {
class BitReader;
class BufferWriter;
namespace mp4 {
@ -19,9 +20,9 @@ namespace mp4 {
// objectTypeIndication Values. Only values currently in use are included.
enum ObjectType {
kForbidden = 0,
kISO_14496_3 = 0x40, // MPEG4 AAC
kISO_14496_3 = 0x40, // MPEG4 AAC
kISO_13818_7_AAC_LC = 0x67, // MPEG2 AAC-LC
kEAC3 = 0xa6 // Dolby Digital Plus
kEAC3 = 0xa6 // Dolby Digital Plus
};
// This class parse object type and decoder specific information from an
@ -29,27 +30,44 @@ enum ObjectType {
// Please refer to ISO 14496 Part 1 7.2.6.5 for more details.
class ESDescriptor {
public:
// Utility function to check if the given object type is AAC.
static bool IsAAC(ObjectType object_type);
ESDescriptor();
~ESDescriptor();
bool Parse(const std::vector<uint8>& data);
void Write(BufferWriter* writer) const;
size_t ComputeSize() const;
ObjectType object_type() const;
const std::vector<uint8>& decoder_specific_info() const;
uint16 esid() const { return esid_; }
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:
enum Tag {
kESDescrTag = 0x03,
kDecoderConfigDescrTag = 0x04,
kDecoderSpecificInfoTag = 0x05
kDecoderSpecificInfoTag = 0x05,
kSLConfigTag = 0x06,
};
bool ParseDecoderConfigDescriptor(BitReader* reader);
bool ParseDecoderSpecificInfo(BitReader* reader);
uint16 esid_; // Elementary Stream ID.
ObjectType object_type_;
std::vector<uint8> decoder_specific_info_;
};

View File

@ -7,12 +7,12 @@
#include "base/logging.h"
#define RCHECK(x) \
do { \
if (!(x)) { \
DLOG(ERROR) << "Failure while parsing MP4: " << #x; \
return false; \
} \
} while (0)
#define RCHECK(x) \
do { \
if (!(x)) { \
LOG(ERROR) << "Failure while processing MP4: " << #x; \
return false; \
} \
} while (0)
#endif // MEDIA_MP4_RCHECK_H_

View File

@ -6,6 +6,7 @@
#include <algorithm>
#include "media/base/buffer_reader.h"
#include "media/mp4/chunk_info_iterator.h"
#include "media/mp4/composition_offset_iterator.h"
#include "media/mp4/decoding_time_iterator.h"
@ -90,12 +91,12 @@ static void PopulateSampleInfo(const TrackExtends& trex,
uint32 flags;
if (i < trun.sample_flags.size()) {
flags = trun.sample_flags[i];
} else if (tfhd.flags & kDefaultSampleFlagsPresentMask) {
} else if (tfhd.flags & TrackFragmentHeader::kDefaultSampleFlagsPresentMask) {
flags = tfhd.default_sample_flags;
} else {
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
@ -396,6 +397,7 @@ bool TrackRunIterator::AuxInfoNeedsToBeCached() {
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.
bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_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];
DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
if (!cenc_info.subsamples.empty() && (cenc_info.GetTotalSizeOfSubsamples() !=
static_cast<size_t>(sample_size()))) {
const size_t total_size_of_subsamples = cenc_info.GetTotalSizeOfSubsamples();
if (total_size_of_subsamples != 0 &&
total_size_of_subsamples != static_cast<size_t>(sample_size())) {
LOG(ERROR) << "Incorrect CENC subsample size.";
return scoped_ptr<DecryptConfig>();
}
@ -534,9 +537,9 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
const std::vector<uint8>& kid = track_encryption().default_kid;
return scoped_ptr<DecryptConfig>(new DecryptConfig(
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.
cenc_info.subsamples));
cenc_info.subsamples()));
}
} // namespace mp4

View File

@ -82,7 +82,8 @@ class TrackRunIteratorTest : public testing::Test {
moof.tracks.resize(2);
moof.tracks[0].decode_time.decode_time = 0;
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_size = 4;
moof.tracks[0].runs.resize(2);
@ -103,7 +104,8 @@ class TrackRunIteratorTest : public testing::Test {
SetAscending(&moof.tracks[1].runs[0].sample_durations);
moof.tracks[1].runs[0].sample_flags.resize(10);
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;
@ -211,7 +213,8 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) {
TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) {
moov_.extends.tracks[0].default_sample_duration = 50;
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_));
MovieFragment moof = CreateFragment();
moof.tracks[0].header.flags = 0;
@ -233,8 +236,10 @@ TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) {
// defaults for all subsequent samples
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
moof.tracks[1].header.flags = kDefaultSampleFlagsPresentMask;
moof.tracks[1].header.default_sample_flags = kNonKeySampleMask;
moof.tracks[1].header.flags =
TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
moof.tracks[1].header.default_sample_flags =
TrackFragmentHeader::kNonKeySampleMask;
moof.tracks[1].runs[0].sample_flags.resize(1);
ASSERT_TRUE(iter_->Init(moof));
iter_->AdvanceRun();