Define SampleEncryption 'senc' box
Issue #41 Change-Id: Ib28c079209f3a58ecd8d7b86d720a1c8c774b669
This commit is contained in:
parent
814d2414e3
commit
0c870b7c02
|
@ -13,7 +13,7 @@ namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
Box::Box() : atom_size(0) {}
|
Box::Box() : box_size_(0) {}
|
||||||
Box::~Box() {}
|
Box::~Box() {}
|
||||||
|
|
||||||
bool Box::Parse(BoxReader* reader) {
|
bool Box::Parse(BoxReader* reader) {
|
||||||
|
@ -24,21 +24,21 @@ bool Box::Parse(BoxReader* reader) {
|
||||||
|
|
||||||
void Box::Write(BufferWriter* writer) {
|
void Box::Write(BufferWriter* writer) {
|
||||||
DCHECK(writer);
|
DCHECK(writer);
|
||||||
// Compute and update atom_size.
|
// Compute and update box size.
|
||||||
uint32_t size = ComputeSize();
|
uint32_t size = ComputeSize();
|
||||||
DCHECK_EQ(size, this->atom_size);
|
DCHECK_EQ(size, box_size_);
|
||||||
|
|
||||||
size_t buffer_size_before_write = writer->Size();
|
size_t buffer_size_before_write = writer->Size();
|
||||||
BoxBuffer buffer(writer);
|
BoxBuffer buffer(writer);
|
||||||
CHECK(ReadWriteInternal(&buffer));
|
CHECK(ReadWriteInternal(&buffer));
|
||||||
DCHECK_EQ(this->atom_size, writer->Size() - buffer_size_before_write);
|
DCHECK_EQ(box_size_, writer->Size() - buffer_size_before_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Box::WriteHeader(BufferWriter* writer) {
|
void Box::WriteHeader(BufferWriter* writer) {
|
||||||
DCHECK(writer);
|
DCHECK(writer);
|
||||||
// Compute and update atom_size.
|
// Compute and update box size.
|
||||||
uint32_t size = ComputeSize();
|
uint32_t size = ComputeSize();
|
||||||
DCHECK_EQ(size, this->atom_size);
|
DCHECK_EQ(size, box_size_);
|
||||||
|
|
||||||
size_t buffer_size_before_write = writer->Size();
|
size_t buffer_size_before_write = writer->Size();
|
||||||
BoxBuffer buffer(writer);
|
BoxBuffer buffer(writer);
|
||||||
|
@ -47,8 +47,8 @@ void Box::WriteHeader(BufferWriter* writer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Box::ComputeSize() {
|
uint32_t Box::ComputeSize() {
|
||||||
this->atom_size = ComputeSizeInternal();
|
box_size_ = ComputeSizeInternal();
|
||||||
return this->atom_size;
|
return box_size_;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Box::HeaderSize() const {
|
uint32_t Box::HeaderSize() const {
|
||||||
|
@ -61,7 +61,7 @@ bool Box::ReadWriteHeaderInternal(BoxBuffer* buffer) {
|
||||||
if (buffer->Reading()) {
|
if (buffer->Reading()) {
|
||||||
// Skip for read mode, which is handled already in BoxReader.
|
// Skip for read mode, which is handled already in BoxReader.
|
||||||
} else {
|
} else {
|
||||||
CHECK(buffer->ReadWriteUInt32(&this->atom_size));
|
CHECK(buffer->ReadWriteUInt32(&box_size_));
|
||||||
FourCC fourcc = BoxType();
|
FourCC fourcc = BoxType();
|
||||||
CHECK(buffer->ReadWriteFourCC(&fourcc));
|
CHECK(buffer->ReadWriteFourCC(&fourcc));
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,24 +50,28 @@ struct Box {
|
||||||
/// @return box type.
|
/// @return box type.
|
||||||
virtual FourCC BoxType() const = 0;
|
virtual FourCC BoxType() const = 0;
|
||||||
|
|
||||||
|
/// @return The size of result box including child boxes. Note that this
|
||||||
|
// function expects that ComputeSize has been invoked already.
|
||||||
|
uint32_t box_size() { return box_size_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Read/write mp4 box header. Note that this function expects box size
|
/// Read/write mp4 box header. Note that this function expects that
|
||||||
/// updated already.
|
/// ComputeSize has been invoked already.
|
||||||
/// @return true on success, false otherwise.
|
/// @return true on success, false otherwise.
|
||||||
virtual bool ReadWriteHeaderInternal(BoxBuffer* buffer);
|
virtual bool ReadWriteHeaderInternal(BoxBuffer* buffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class BoxBuffer;
|
friend class BoxBuffer;
|
||||||
// Read/write the mp4 box from/to BoxBuffer. Note that this function expects
|
// Read/write the mp4 box from/to BoxBuffer. Note that this function expects
|
||||||
// box size updated already.
|
// that ComputeSize has been invoked already.
|
||||||
virtual bool ReadWriteInternal(BoxBuffer* buffer) = 0;
|
virtual bool ReadWriteInternal(BoxBuffer* buffer) = 0;
|
||||||
// Compute the size of this box. A value of 0 should be returned if the box
|
// Compute the size of this box. A value of 0 should be returned if the box
|
||||||
// should not be written. Note that this function won't update box size.
|
// should not be written. Note that this function won't update box size.
|
||||||
virtual uint32_t ComputeSizeInternal() = 0;
|
virtual uint32_t ComputeSizeInternal() = 0;
|
||||||
|
|
||||||
// We don't support 64-bit atom sizes. 32-bit should be large enough for our
|
// We don't support 64-bit box sizes. 32-bit should be large enough for our
|
||||||
// current needs.
|
// current needs.
|
||||||
uint32_t atom_size;
|
uint32_t box_size_;
|
||||||
|
|
||||||
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
|
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
|
||||||
// generated copy constructor and assignment operator.
|
// generated copy constructor and assignment operator.
|
||||||
|
|
|
@ -167,7 +167,7 @@ class BoxBuffer {
|
||||||
if (reader_)
|
if (reader_)
|
||||||
return reader_->ReadChild(box);
|
return reader_->ReadChild(box);
|
||||||
// The box is mandatory, i.e. the box size should not be 0.
|
// The box is mandatory, i.e. the box size should not be 0.
|
||||||
DCHECK_NE(0u, box->atom_size);
|
DCHECK_NE(0u, box->box_size());
|
||||||
CHECK(box->ReadWriteInternal(this));
|
CHECK(box->ReadWriteInternal(this));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ class BoxBuffer {
|
||||||
if (reader_)
|
if (reader_)
|
||||||
return reader_->TryReadChild(box);
|
return reader_->TryReadChild(box);
|
||||||
// The box is optional, i.e. it can be skipped if the box size is 0.
|
// The box is optional, i.e. it can be skipped if the box size is 0.
|
||||||
if (box->atom_size != 0)
|
if (box->box_size() != 0)
|
||||||
CHECK(box->ReadWriteInternal(this));
|
CHECK(box->ReadWriteInternal(this));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,12 @@ const char kVpcCompressorName[] = "\012VPC Coding";
|
||||||
// at once.
|
// at once.
|
||||||
const int kCueSourceIdNotSet = -1;
|
const int kCueSourceIdNotSet = -1;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
// Utility functions to check if the 64bit integers can fit in 32bit integer.
|
// Utility functions to check if the 64bit integers can fit in 32bit integer.
|
||||||
bool IsFitIn32Bits(uint64_t a) {
|
bool IsFitIn32Bits(uint64_t a) {
|
||||||
return a <= std::numeric_limits<uint32_t>::max();
|
return a <= std::numeric_limits<uint32_t>::max();
|
||||||
|
@ -183,6 +189,143 @@ uint32_t SampleAuxiliaryInformationSize::ComputeSizeInternal() {
|
||||||
(default_sample_info_size == 0 ? sample_info_sizes.size() : 0);
|
(default_sample_info_size == 0 ? sample_info_sizes.size() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SampleEncryptionEntry::SampleEncryptionEntry() {}
|
||||||
|
SampleEncryptionEntry::~SampleEncryptionEntry() {}
|
||||||
|
|
||||||
|
bool SampleEncryptionEntry::ReadWrite(uint8_t iv_size,
|
||||||
|
bool has_subsamples,
|
||||||
|
BoxBuffer* buffer) {
|
||||||
|
DCHECK(IsIvSizeValid(iv_size));
|
||||||
|
DCHECK(buffer);
|
||||||
|
|
||||||
|
RCHECK(buffer->ReadWriteVector(&initialization_vector, iv_size));
|
||||||
|
|
||||||
|
if (!has_subsamples) {
|
||||||
|
subsamples.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t subsample_count = subsamples.size();
|
||||||
|
RCHECK(buffer->ReadWriteUInt16(&subsample_count));
|
||||||
|
RCHECK(subsample_count > 0);
|
||||||
|
subsamples.resize(subsample_count);
|
||||||
|
for (auto& subsample : subsamples) {
|
||||||
|
RCHECK(buffer->ReadWriteUInt16(&subsample.clear_bytes) &&
|
||||||
|
buffer->ReadWriteUInt32(&subsample.cipher_bytes));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SampleEncryptionEntry::ParseFromBuffer(uint8_t iv_size,
|
||||||
|
bool has_subsamples,
|
||||||
|
BufferReader* reader) {
|
||||||
|
DCHECK(IsIvSizeValid(iv_size));
|
||||||
|
DCHECK(reader);
|
||||||
|
|
||||||
|
initialization_vector.resize(iv_size);
|
||||||
|
RCHECK(reader->ReadToVector(&initialization_vector, iv_size));
|
||||||
|
|
||||||
|
if (!has_subsamples) {
|
||||||
|
subsamples.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t subsample_count;
|
||||||
|
RCHECK(reader->Read2(&subsample_count));
|
||||||
|
RCHECK(subsample_count > 0);
|
||||||
|
subsamples.resize(subsample_count);
|
||||||
|
for (auto& subsample : subsamples) {
|
||||||
|
RCHECK(reader->Read2(&subsample.clear_bytes) &&
|
||||||
|
reader->Read4(&subsample.cipher_bytes));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SampleEncryptionEntry::ComputeSize() const {
|
||||||
|
const uint32_t subsample_entry_size = sizeof(uint16_t) + sizeof(uint32_t);
|
||||||
|
const uint16_t subsample_count = subsamples.size();
|
||||||
|
return initialization_vector.size() +
|
||||||
|
(subsample_count > 0 ? (sizeof(subsample_count) +
|
||||||
|
subsample_entry_size * subsample_count)
|
||||||
|
: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SampleEncryptionEntry::GetTotalSizeOfSubsamples() const {
|
||||||
|
uint32_t size = 0;
|
||||||
|
for (uint32_t i = 0; i < subsamples.size(); ++i)
|
||||||
|
size += subsamples[i].clear_bytes + subsamples[i].cipher_bytes;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleEncryption::SampleEncryption() : iv_size(0) {}
|
||||||
|
SampleEncryption::~SampleEncryption() {}
|
||||||
|
FourCC SampleEncryption::BoxType() const { return FOURCC_SENC; }
|
||||||
|
|
||||||
|
bool SampleEncryption::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
|
RCHECK(ReadWriteHeaderInternal(buffer));
|
||||||
|
|
||||||
|
// If we don't know |iv_size|, store sample encryption data to parse later
|
||||||
|
// after we know iv_size.
|
||||||
|
if (buffer->Reading() && iv_size == 0) {
|
||||||
|
RCHECK(buffer->ReadWriteVector(&sample_encryption_data,
|
||||||
|
buffer->Size() - buffer->Pos()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsIvSizeValid(iv_size)) {
|
||||||
|
LOG(ERROR) << "IV_size can only be 8 or 16, but seeing " << iv_size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t sample_count = sample_encryption_entries.size();
|
||||||
|
RCHECK(buffer->ReadWriteUInt32(&sample_count));
|
||||||
|
|
||||||
|
sample_encryption_entries.resize(sample_count);
|
||||||
|
for (auto& sample_encryption_entry : sample_encryption_entries) {
|
||||||
|
RCHECK(sample_encryption_entry.ReadWrite(
|
||||||
|
iv_size, flags & kUseSubsampleEncryption, buffer));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SampleEncryption::ComputeSizeInternal() {
|
||||||
|
const uint32_t sample_count = sample_encryption_entries.size();
|
||||||
|
if (sample_count == 0) {
|
||||||
|
// Sample encryption box is optional. Skip it if it is empty.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DCHECK(IsIvSizeValid(iv_size));
|
||||||
|
uint32_t box_size = HeaderSize() + sizeof(sample_count);
|
||||||
|
if (flags & kUseSubsampleEncryption) {
|
||||||
|
for (const SampleEncryptionEntry& sample_encryption_entry :
|
||||||
|
sample_encryption_entries) {
|
||||||
|
box_size += sample_encryption_entry.ComputeSize();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
box_size += sample_count * iv_size;
|
||||||
|
}
|
||||||
|
return box_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SampleEncryption::ParseFromSampleEncryptionData(
|
||||||
|
size_t iv_size,
|
||||||
|
std::vector<SampleEncryptionEntry>* sample_encryption_entries) const {
|
||||||
|
DCHECK(IsIvSizeValid(iv_size));
|
||||||
|
|
||||||
|
BufferReader reader(vector_as_array(&sample_encryption_data),
|
||||||
|
sample_encryption_data.size());
|
||||||
|
uint32_t sample_count = 0;
|
||||||
|
RCHECK(reader.Read4(&sample_count));
|
||||||
|
|
||||||
|
sample_encryption_entries->resize(sample_count);
|
||||||
|
for (auto& sample_encryption_entry : *sample_encryption_entries) {
|
||||||
|
RCHECK(sample_encryption_entry.ParseFromBuffer(
|
||||||
|
iv_size, flags & kUseSubsampleEncryption, &reader));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
OriginalFormat::OriginalFormat() : format(FOURCC_NULL) {}
|
OriginalFormat::OriginalFormat() : format(FOURCC_NULL) {}
|
||||||
OriginalFormat::~OriginalFormat() {}
|
OriginalFormat::~OriginalFormat() {}
|
||||||
FourCC OriginalFormat::BoxType() const { return FOURCC_FRMA; }
|
FourCC OriginalFormat::BoxType() const { return FOURCC_FRMA; }
|
||||||
|
@ -1440,7 +1583,8 @@ bool Track::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
buffer->PrepareChildren() &&
|
buffer->PrepareChildren() &&
|
||||||
buffer->ReadWriteChild(&header) &&
|
buffer->ReadWriteChild(&header) &&
|
||||||
buffer->ReadWriteChild(&media) &&
|
buffer->ReadWriteChild(&media) &&
|
||||||
buffer->TryReadWriteChild(&edit));
|
buffer->TryReadWriteChild(&edit) &&
|
||||||
|
buffer->TryReadWriteChild(&sample_encryption));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1921,14 +2065,16 @@ bool TrackFragment::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
buffer->TryReadWriteChild(&sample_group_description));
|
buffer->TryReadWriteChild(&sample_group_description));
|
||||||
}
|
}
|
||||||
return buffer->TryReadWriteChild(&auxiliary_size) &&
|
return buffer->TryReadWriteChild(&auxiliary_size) &&
|
||||||
buffer->TryReadWriteChild(&auxiliary_offset);
|
buffer->TryReadWriteChild(&auxiliary_offset) &&
|
||||||
|
buffer->TryReadWriteChild(&sample_encryption);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TrackFragment::ComputeSizeInternal() {
|
uint32_t TrackFragment::ComputeSizeInternal() {
|
||||||
uint32_t box_size =
|
uint32_t box_size =
|
||||||
HeaderSize() + header.ComputeSize() + decode_time.ComputeSize() +
|
HeaderSize() + header.ComputeSize() + decode_time.ComputeSize() +
|
||||||
sample_to_group.ComputeSize() + sample_group_description.ComputeSize() +
|
sample_to_group.ComputeSize() + sample_group_description.ComputeSize() +
|
||||||
auxiliary_size.ComputeSize() + auxiliary_offset.ComputeSize();
|
auxiliary_size.ComputeSize() + auxiliary_offset.ComputeSize() +
|
||||||
|
sample_encryption.ComputeSize();
|
||||||
for (uint32_t i = 0; i < runs.size(); ++i)
|
for (uint32_t i = 0; i < runs.size(); ++i)
|
||||||
box_size += runs[i].ComputeSize();
|
box_size += runs[i].ComputeSize();
|
||||||
return box_size;
|
return box_size;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "packager/media/base/decrypt_config.h"
|
||||||
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
|
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
|
||||||
#include "packager/media/formats/mp4/box.h"
|
#include "packager/media/formats/mp4/box.h"
|
||||||
#include "packager/media/formats/mp4/es_descriptor.h"
|
#include "packager/media/formats/mp4/es_descriptor.h"
|
||||||
|
@ -75,6 +76,60 @@ struct SampleAuxiliaryInformationSize : FullBox {
|
||||||
std::vector<uint8_t> sample_info_sizes;
|
std::vector<uint8_t> sample_info_sizes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SampleEncryptionEntry {
|
||||||
|
SampleEncryptionEntry();
|
||||||
|
~SampleEncryptionEntry();
|
||||||
|
/// Read/Write SampleEncryptionEntry.
|
||||||
|
/// @param iv_size specifies the size of initialization vector.
|
||||||
|
/// @param has_subsamples indicates whether this sample encryption entry
|
||||||
|
/// constains subsamples.
|
||||||
|
/// @param buffer points to the box buffer for reading or writing.
|
||||||
|
/// @return true on success, false otherwise.
|
||||||
|
bool ReadWrite(uint8_t iv_size,
|
||||||
|
bool has_subsamples,
|
||||||
|
BoxBuffer* buffer);
|
||||||
|
/// Parse SampleEncryptionEntry from buffer.
|
||||||
|
/// @param iv_size specifies the size of initialization vector.
|
||||||
|
/// @param has_subsamples indicates whether this sample encryption entry
|
||||||
|
/// constains subsamples.
|
||||||
|
/// @param reader points to the buffer reader. Cannot be NULL.
|
||||||
|
/// @return true on success, false otherwise.
|
||||||
|
bool ParseFromBuffer(uint8_t iv_size,
|
||||||
|
bool has_subsamples,
|
||||||
|
BufferReader* reader);
|
||||||
|
/// @return The size of the structure in bytes when it is stored.
|
||||||
|
uint32_t ComputeSize() const;
|
||||||
|
/// @return The accumulated size of subsamples. Returns 0 if there is no
|
||||||
|
/// subsamples.
|
||||||
|
uint32_t GetTotalSizeOfSubsamples() const;
|
||||||
|
|
||||||
|
std::vector<uint8_t> initialization_vector;
|
||||||
|
std::vector<SubsampleEntry> subsamples;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SampleEncryption : FullBox {
|
||||||
|
enum SampleEncryptionFlags {
|
||||||
|
kUseSubsampleEncryption = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_BOX_METHODS(SampleEncryption);
|
||||||
|
/// Parse from @a sample_encryption_data.
|
||||||
|
/// @param iv_size specifies the size of initialization vector.
|
||||||
|
/// @param[out] sample_encryption_entries receives parsed sample encryption
|
||||||
|
/// entries.
|
||||||
|
/// @return true on success, false otherwise.
|
||||||
|
bool ParseFromSampleEncryptionData(
|
||||||
|
size_t iv_size,
|
||||||
|
std::vector<SampleEncryptionEntry>* sample_encryption_entries) const;
|
||||||
|
|
||||||
|
/// We may not know @a iv_size before reading this box. In this case, we will
|
||||||
|
/// store sample encryption data for parsing later when @a iv_size is known.
|
||||||
|
std::vector<uint8_t> sample_encryption_data;
|
||||||
|
|
||||||
|
size_t iv_size;
|
||||||
|
std::vector<SampleEncryptionEntry> sample_encryption_entries;
|
||||||
|
};
|
||||||
|
|
||||||
struct OriginalFormat : Box {
|
struct OriginalFormat : Box {
|
||||||
DECLARE_BOX_METHODS(OriginalFormat);
|
DECLARE_BOX_METHODS(OriginalFormat);
|
||||||
|
|
||||||
|
@ -258,6 +313,8 @@ struct SampleDescription : FullBox {
|
||||||
DECLARE_BOX_METHODS(SampleDescription);
|
DECLARE_BOX_METHODS(SampleDescription);
|
||||||
|
|
||||||
TrackType type;
|
TrackType type;
|
||||||
|
// TODO(kqyang): Clean up the code to have one single member, e.g. by creating
|
||||||
|
// SampleEntry struct, std::vector<SampleEntry> sample_entries.
|
||||||
std::vector<VideoSampleEntry> video_entries;
|
std::vector<VideoSampleEntry> video_entries;
|
||||||
std::vector<AudioSampleEntry> audio_entries;
|
std::vector<AudioSampleEntry> audio_entries;
|
||||||
std::vector<WVTTSampleEntry> wvtt_entries;
|
std::vector<WVTTSampleEntry> wvtt_entries;
|
||||||
|
@ -428,6 +485,7 @@ struct Track : Box {
|
||||||
TrackHeader header;
|
TrackHeader header;
|
||||||
Media media;
|
Media media;
|
||||||
Edit edit;
|
Edit edit;
|
||||||
|
SampleEncryption sample_encryption;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MovieExtendsHeader : FullBox {
|
struct MovieExtendsHeader : FullBox {
|
||||||
|
@ -569,6 +627,7 @@ struct TrackFragment : Box {
|
||||||
SampleGroupDescription sample_group_description;
|
SampleGroupDescription sample_group_description;
|
||||||
SampleAuxiliaryInformationSize auxiliary_size;
|
SampleAuxiliaryInformationSize auxiliary_size;
|
||||||
SampleAuxiliaryInformationOffset auxiliary_offset;
|
SampleAuxiliaryInformationOffset auxiliary_offset;
|
||||||
|
SampleEncryption sample_encryption;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MovieFragment : Box {
|
struct MovieFragment : Box {
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
inline bool operator==(const SubsampleEntry& lhs, const SubsampleEntry& rhs) {
|
||||||
|
return lhs.clear_bytes == rhs.clear_bytes &&
|
||||||
|
lhs.cipher_bytes == rhs.cipher_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
inline bool operator==(const FileType& lhs, const FileType& rhs) {
|
inline bool operator==(const FileType& lhs, const FileType& rhs) {
|
||||||
|
@ -38,6 +44,18 @@ inline bool operator==(const SampleAuxiliaryInformationSize& lhs,
|
||||||
lhs.sample_info_sizes == rhs.sample_info_sizes;
|
lhs.sample_info_sizes == rhs.sample_info_sizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const SampleEncryptionEntry& lhs,
|
||||||
|
const SampleEncryptionEntry& rhs) {
|
||||||
|
return lhs.initialization_vector == rhs.initialization_vector &&
|
||||||
|
lhs.subsamples == rhs.subsamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const SampleEncryption& lhs,
|
||||||
|
const SampleEncryption& rhs) {
|
||||||
|
return lhs.iv_size == rhs.iv_size &&
|
||||||
|
lhs.sample_encryption_entries == rhs.sample_encryption_entries;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator==(const OriginalFormat& lhs, const OriginalFormat& rhs) {
|
inline bool operator==(const OriginalFormat& lhs, const OriginalFormat& rhs) {
|
||||||
return lhs.format == rhs.format;
|
return lhs.format == rhs.format;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +292,7 @@ inline bool operator==(const Media& lhs, const Media& rhs) {
|
||||||
|
|
||||||
inline bool operator==(const Track& lhs, const Track& rhs) {
|
inline bool operator==(const Track& lhs, const Track& rhs) {
|
||||||
return lhs.header == rhs.header && lhs.media == rhs.media &&
|
return lhs.header == rhs.header && lhs.media == rhs.media &&
|
||||||
lhs.edit == rhs.edit;
|
lhs.edit == rhs.edit && lhs.sample_encryption == rhs.sample_encryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const MovieExtendsHeader& lhs,
|
inline bool operator==(const MovieExtendsHeader& lhs,
|
||||||
|
@ -360,7 +378,8 @@ inline bool operator==(const TrackFragment& lhs, const TrackFragment& rhs) {
|
||||||
return lhs.header == rhs.header && lhs.runs == rhs.runs &&
|
return lhs.header == rhs.header && lhs.runs == rhs.runs &&
|
||||||
lhs.decode_time == rhs.decode_time &&
|
lhs.decode_time == rhs.decode_time &&
|
||||||
lhs.auxiliary_offset == rhs.auxiliary_offset &&
|
lhs.auxiliary_offset == rhs.auxiliary_offset &&
|
||||||
lhs.auxiliary_size == rhs.auxiliary_size;
|
lhs.auxiliary_size == rhs.auxiliary_size &&
|
||||||
|
lhs.sample_encryption == rhs.sample_encryption;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const MovieFragment& lhs, const MovieFragment& rhs) {
|
inline bool operator==(const MovieFragment& lhs, const MovieFragment& rhs) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
namespace {
|
namespace {
|
||||||
|
const uint8_t kData8Bytes[] = {3, 4, 5, 6, 7, 8, 9, 0};
|
||||||
const uint8_t kData16Bytes[] = {8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8};
|
const uint8_t kData16Bytes[] = {8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8};
|
||||||
const uint8_t kData4[] = {1, 5, 4, 3, 15};
|
const uint8_t kData4[] = {1, 5, 4, 3, 15};
|
||||||
const uint8_t kData8[] = {1, 8, 42, 98, 156};
|
const uint8_t kData8[] = {1, 8, 42, 98, 156};
|
||||||
|
@ -161,6 +162,30 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
saiz->sample_info_sizes.clear();
|
saiz->sample_info_sizes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Fill(SampleEncryption* senc) {
|
||||||
|
senc->iv_size = 8;
|
||||||
|
senc->flags = SampleEncryption::kUseSubsampleEncryption;
|
||||||
|
senc->sample_encryption_entries.resize(2);
|
||||||
|
senc->sample_encryption_entries[0].initialization_vector.assign(
|
||||||
|
kData8Bytes, kData8Bytes + arraysize(kData8Bytes));
|
||||||
|
senc->sample_encryption_entries[0].subsamples.resize(2);
|
||||||
|
senc->sample_encryption_entries[0].subsamples[0].clear_bytes = 17;
|
||||||
|
senc->sample_encryption_entries[0].subsamples[0].cipher_bytes = 3456;
|
||||||
|
senc->sample_encryption_entries[0].subsamples[1].clear_bytes = 1543;
|
||||||
|
senc->sample_encryption_entries[0].subsamples[1].cipher_bytes = 0;
|
||||||
|
senc->sample_encryption_entries[1] = senc->sample_encryption_entries[0];
|
||||||
|
senc->sample_encryption_entries[1].subsamples[0].clear_bytes = 0;
|
||||||
|
senc->sample_encryption_entries[1].subsamples[0].cipher_bytes = 15;
|
||||||
|
senc->sample_encryption_entries[1].subsamples[1].clear_bytes = 1988;
|
||||||
|
senc->sample_encryption_entries[1].subsamples[1].cipher_bytes = 8765;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Modify(SampleEncryption* senc) {
|
||||||
|
senc->flags = 0;
|
||||||
|
senc->sample_encryption_entries.resize(1);
|
||||||
|
senc->sample_encryption_entries[0].subsamples.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void Fill(OriginalFormat* frma) { frma->format = FOURCC_AVC1; }
|
void Fill(OriginalFormat* frma) { frma->format = FOURCC_AVC1; }
|
||||||
|
|
||||||
void Modify(OriginalFormat* frma) { frma->format = FOURCC_MP4A; }
|
void Modify(OriginalFormat* frma) { frma->format = FOURCC_MP4A; }
|
||||||
|
@ -817,6 +842,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
|
|
||||||
bool IsOptional(const SampleAuxiliaryInformationOffset* box) { return true; }
|
bool IsOptional(const SampleAuxiliaryInformationOffset* box) { return true; }
|
||||||
bool IsOptional(const SampleAuxiliaryInformationSize* box) { return true; }
|
bool IsOptional(const SampleAuxiliaryInformationSize* box) { return true; }
|
||||||
|
bool IsOptional(const SampleEncryption* box) { return true; }
|
||||||
bool IsOptional(const ProtectionSchemeInfo* box) { return true; }
|
bool IsOptional(const ProtectionSchemeInfo* box) { return true; }
|
||||||
bool IsOptional(const EditList* box) { return true; }
|
bool IsOptional(const EditList* box) { return true; }
|
||||||
bool IsOptional(const Edit* box) { return true; }
|
bool IsOptional(const Edit* box) { return true; }
|
||||||
|
@ -1066,6 +1092,48 @@ TEST_F(BoxDefinitionsTest, TrackFragmentRun_NoSampleSize) {
|
||||||
ASSERT_EQ(trun, trun_readback);
|
ASSERT_EQ(trun, trun_readback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(BoxDefinitionsTest, SampleEncryptionIsOptional) {
|
||||||
|
SampleEncryption senc;
|
||||||
|
EXPECT_EQ(0u, senc.ComputeSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BoxDefinitionsTest, SampleEncryptionWithIvKnownWhenReading) {
|
||||||
|
SampleEncryption senc;
|
||||||
|
Fill(&senc);
|
||||||
|
senc.Write(buffer_.get());
|
||||||
|
|
||||||
|
SampleEncryption senc_readback;
|
||||||
|
senc_readback.iv_size = senc.iv_size;
|
||||||
|
|
||||||
|
ASSERT_TRUE(ReadBack(&senc_readback));
|
||||||
|
EXPECT_EQ(0u, senc_readback.sample_encryption_data.size());
|
||||||
|
EXPECT_NE(0u, senc_readback.sample_encryption_entries.size());
|
||||||
|
ASSERT_EQ(senc, senc_readback);
|
||||||
|
|
||||||
|
Modify(&senc);
|
||||||
|
senc.Write(buffer_.get());
|
||||||
|
ASSERT_TRUE(ReadBack(&senc_readback));
|
||||||
|
ASSERT_EQ(senc, senc_readback);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BoxDefinitionsTest, SampleEncryptionWithIvUnknownWhenReading) {
|
||||||
|
SampleEncryption senc;
|
||||||
|
Fill(&senc);
|
||||||
|
senc.Write(buffer_.get());
|
||||||
|
|
||||||
|
SampleEncryption senc_readback;
|
||||||
|
senc_readback.iv_size = 0;
|
||||||
|
|
||||||
|
ASSERT_TRUE(ReadBack(&senc_readback));
|
||||||
|
EXPECT_NE(0u, senc_readback.sample_encryption_data.size());
|
||||||
|
EXPECT_EQ(0u, senc_readback.sample_encryption_entries.size());
|
||||||
|
|
||||||
|
std::vector<SampleEncryptionEntry> sample_encryption_entries;
|
||||||
|
ASSERT_TRUE(senc_readback.ParseFromSampleEncryptionData(
|
||||||
|
senc.iv_size, &sample_encryption_entries));
|
||||||
|
ASSERT_EQ(senc.sample_encryption_entries, sample_encryption_entries);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mp4
|
} // namespace mp4
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace edash_packager
|
} // namespace edash_packager
|
||||||
|
|
|
@ -73,6 +73,7 @@ enum FourCC {
|
||||||
FOURCC_SCHM = 0x7363686d,
|
FOURCC_SCHM = 0x7363686d,
|
||||||
FOURCC_SDTP = 0x73647470,
|
FOURCC_SDTP = 0x73647470,
|
||||||
FOURCC_SEIG = 0x73656967,
|
FOURCC_SEIG = 0x73656967,
|
||||||
|
FOURCC_SENC = 0x73656e63,
|
||||||
FOURCC_SGPD = 0x73677064,
|
FOURCC_SGPD = 0x73677064,
|
||||||
FOURCC_SIDX = 0x73696478,
|
FOURCC_SIDX = 0x73696478,
|
||||||
FOURCC_SINF = 0x73696e66,
|
FOURCC_SINF = 0x73696e66,
|
||||||
|
|
|
@ -74,8 +74,8 @@ class MP4MediaParser : public MediaParser {
|
||||||
uint8_t* buffer,
|
uint8_t* buffer,
|
||||||
size_t buffer_size);
|
size_t buffer_size);
|
||||||
|
|
||||||
// To retain proper framing, each 'mdat' atom must be read; to limit memory
|
// To retain proper framing, each 'mdat' box must be read; to limit memory
|
||||||
// usage, the atom's data needs to be discarded incrementally as frames are
|
// usage, the box's data needs to be discarded incrementally as frames are
|
||||||
// extracted from the stream. This function discards data from the stream up
|
// extracted from the stream. This function discards data from the stream up
|
||||||
// to |offset|, updating the |mdat_tail_| value so that framing can be
|
// to |offset|, updating the |mdat_tail_| value so that framing can be
|
||||||
// retained after all 'mdat' information has been read.
|
// retained after all 'mdat' information has been read.
|
||||||
|
|
|
@ -42,7 +42,7 @@ class MP4Muxer : public Muxer {
|
||||||
Status DoAddSample(const MediaStream* stream,
|
Status DoAddSample(const MediaStream* stream,
|
||||||
scoped_refptr<MediaSample> sample) override;
|
scoped_refptr<MediaSample> sample) override;
|
||||||
|
|
||||||
// Generate Audio/Video Track atom.
|
// Generate Audio/Video Track box.
|
||||||
void InitializeTrak(const StreamInfo* info, Track* trak);
|
void InitializeTrak(const StreamInfo* info, Track* trak);
|
||||||
void GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
void GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
||||||
Track* trak,
|
Track* trak,
|
||||||
|
|
Loading…
Reference in New Issue