Add support for AudioRollRecoveryEntry
This is part of the effort to support Opus in iso-bmff #83. Changes in this CL: - Add support for multiple SampleGroupDescription and SampleToGroup boxes in TrackFragment box; - Add support for SampleGroupDescription and SampleToGroup boxes in SampleTable box; - Add support for AudioRollRecoveryEntry in Sample Group and 'roll' sample grouping type; - Also fix a bug that default length was not set correctly in SampleGroupDescription when constant iv is used. Change-Id: I7e31386ea6cd17a8ee5e1dca4c1a90937d51368f
This commit is contained in:
parent
cf4a2447c1
commit
940cc43de2
|
@ -82,6 +82,7 @@ enum FourCC : uint32_t {
|
|||
FOURCC_pdin = 0x7064696e,
|
||||
FOURCC_prft = 0x70726674,
|
||||
FOURCC_pssh = 0x70737368,
|
||||
FOURCC_roll = 0x726f6c6c,
|
||||
FOURCC_saio = 0x7361696f,
|
||||
FOURCC_saiz = 0x7361697a,
|
||||
FOURCC_sbgp = 0x73626770,
|
||||
|
|
|
@ -921,6 +921,187 @@ uint32_t SyncSample::ComputeSizeInternal() {
|
|||
sizeof(uint32_t) * sample_number.size();
|
||||
}
|
||||
|
||||
CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry()
|
||||
: is_protected(0),
|
||||
per_sample_iv_size(0),
|
||||
crypt_byte_block(0),
|
||||
skip_byte_block(0) {}
|
||||
CencSampleEncryptionInfoEntry::~CencSampleEncryptionInfoEntry() {};
|
||||
|
||||
bool CencSampleEncryptionInfoEntry::ReadWrite(BoxBuffer* buffer) {
|
||||
if (!buffer->Reading()) {
|
||||
if (key_id.size() != kCencKeyIdSize) {
|
||||
LOG(WARNING) << "CENC defines key id length of " << kCencKeyIdSize
|
||||
<< " bytes; got " << key_id.size()
|
||||
<< ". Resized accordingly.";
|
||||
key_id.resize(kCencKeyIdSize);
|
||||
}
|
||||
RCHECK(crypt_byte_block < 16 && skip_byte_block < 16);
|
||||
}
|
||||
|
||||
RCHECK(buffer->IgnoreBytes(1)); // reserved.
|
||||
|
||||
uint8_t pattern = crypt_byte_block << 4 | skip_byte_block;
|
||||
RCHECK(buffer->ReadWriteUInt8(&pattern));
|
||||
crypt_byte_block = pattern >> 4;
|
||||
skip_byte_block = pattern & 0x0F;
|
||||
|
||||
RCHECK(buffer->ReadWriteUInt8(&is_protected) &&
|
||||
buffer->ReadWriteUInt8(&per_sample_iv_size) &&
|
||||
buffer->ReadWriteVector(&key_id, kCencKeyIdSize));
|
||||
|
||||
if (is_protected == 1) {
|
||||
if (per_sample_iv_size == 0) { // For constant iv.
|
||||
uint8_t constant_iv_size = constant_iv.size();
|
||||
RCHECK(buffer->ReadWriteUInt8(&constant_iv_size));
|
||||
RCHECK(constant_iv_size == 8 || constant_iv_size == 16);
|
||||
RCHECK(buffer->ReadWriteVector(&constant_iv, constant_iv_size));
|
||||
} else {
|
||||
RCHECK(per_sample_iv_size == 8 || per_sample_iv_size == 16);
|
||||
DCHECK(constant_iv.empty());
|
||||
}
|
||||
} else {
|
||||
// Expect |is_protected| to be 0, i.e. not protected. Other values of
|
||||
// |is_protected| is not supported.
|
||||
RCHECK(is_protected == 0);
|
||||
RCHECK(per_sample_iv_size == 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t CencSampleEncryptionInfoEntry::ComputeSize() const {
|
||||
return sizeof(uint32_t) + kCencKeyIdSize +
|
||||
(constant_iv.empty() ? 0 : (sizeof(uint8_t) + constant_iv.size()));
|
||||
}
|
||||
|
||||
AudioRollRecoveryEntry::AudioRollRecoveryEntry(): roll_distance(0) {}
|
||||
AudioRollRecoveryEntry::~AudioRollRecoveryEntry() {}
|
||||
|
||||
bool AudioRollRecoveryEntry::ReadWrite(BoxBuffer* buffer) {
|
||||
RCHECK(buffer->ReadWriteInt16(&roll_distance));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t AudioRollRecoveryEntry::ComputeSize() const {
|
||||
return sizeof(roll_distance);
|
||||
}
|
||||
|
||||
SampleGroupDescription::SampleGroupDescription() : grouping_type(0) {}
|
||||
SampleGroupDescription::~SampleGroupDescription() {}
|
||||
FourCC SampleGroupDescription::BoxType() const { return FOURCC_sgpd; }
|
||||
|
||||
bool SampleGroupDescription::ReadWriteInternal(BoxBuffer* buffer) {
|
||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->ReadWriteUInt32(&grouping_type));
|
||||
|
||||
switch (grouping_type) {
|
||||
case FOURCC_seig:
|
||||
return ReadWriteEntries(buffer, &cenc_sample_encryption_info_entries);
|
||||
case FOURCC_roll:
|
||||
return ReadWriteEntries(buffer, &audio_roll_recovery_entries);
|
||||
default:
|
||||
DCHECK(buffer->Reading());
|
||||
DLOG(WARNING) << "Sample group '" << grouping_type
|
||||
<< "' is not supported.";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SampleGroupDescription::ReadWriteEntries(BoxBuffer* buffer,
|
||||
std::vector<T>* entries) {
|
||||
uint32_t default_length = 0;
|
||||
if (!buffer->Reading()) {
|
||||
DCHECK(!entries->empty());
|
||||
default_length = (*entries)[0].ComputeSize();
|
||||
DCHECK_NE(default_length, 0u);
|
||||
}
|
||||
if (version == 1)
|
||||
RCHECK(buffer->ReadWriteUInt32(&default_length));
|
||||
if (version >= 2) {
|
||||
NOTIMPLEMENTED() << "Unsupported SampleGroupDescriptionBox 'sgpd' version "
|
||||
<< static_cast<int>(version);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t count = entries->size();
|
||||
RCHECK(buffer->ReadWriteUInt32(&count));
|
||||
RCHECK(count != 0);
|
||||
entries->resize(count);
|
||||
|
||||
for (T& entry : *entries) {
|
||||
if (version == 1) {
|
||||
uint32_t description_length = default_length;
|
||||
if (buffer->Reading() && default_length == 0)
|
||||
RCHECK(buffer->ReadWriteUInt32(&description_length));
|
||||
RCHECK(entry.ReadWrite(buffer));
|
||||
RCHECK(entry.ComputeSize() == description_length);
|
||||
} else {
|
||||
RCHECK(entry.ReadWrite(buffer));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t SampleGroupDescription::ComputeSizeInternal() {
|
||||
// Version 0 is obsoleted, so always generate version 1 box.
|
||||
version = 1;
|
||||
size_t entries_size = 0;
|
||||
switch (grouping_type) {
|
||||
case FOURCC_seig:
|
||||
for (const auto& entry : cenc_sample_encryption_info_entries)
|
||||
entries_size += entry.ComputeSize();
|
||||
break;
|
||||
case FOURCC_roll:
|
||||
for (const auto& entry : audio_roll_recovery_entries)
|
||||
entries_size += entry.ComputeSize();
|
||||
break;
|
||||
}
|
||||
// This box is optional. Skip it if it is not used.
|
||||
if (entries_size == 0)
|
||||
return 0;
|
||||
return HeaderSize() + sizeof(grouping_type) +
|
||||
(version == 1 ? sizeof(uint32_t) : 0) + sizeof(uint32_t) +
|
||||
entries_size;
|
||||
}
|
||||
|
||||
SampleToGroup::SampleToGroup() : grouping_type(0), grouping_type_parameter(0) {}
|
||||
SampleToGroup::~SampleToGroup() {}
|
||||
FourCC SampleToGroup::BoxType() const { return FOURCC_sbgp; }
|
||||
|
||||
bool SampleToGroup::ReadWriteInternal(BoxBuffer* buffer) {
|
||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->ReadWriteUInt32(&grouping_type));
|
||||
if (version == 1)
|
||||
RCHECK(buffer->ReadWriteUInt32(&grouping_type_parameter));
|
||||
|
||||
if (grouping_type != FOURCC_seig && grouping_type != FOURCC_roll) {
|
||||
DCHECK(buffer->Reading());
|
||||
DLOG(WARNING) << "Sample group "
|
||||
<< FourCCToString(static_cast<FourCC>(grouping_type))
|
||||
<< " is not supported.";
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t count = entries.size();
|
||||
RCHECK(buffer->ReadWriteUInt32(&count));
|
||||
entries.resize(count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
RCHECK(buffer->ReadWriteUInt32(&entries[i].sample_count) &&
|
||||
buffer->ReadWriteUInt32(&entries[i].group_description_index));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t SampleToGroup::ComputeSizeInternal() {
|
||||
// This box is optional. Skip it if it is not used.
|
||||
if (entries.empty())
|
||||
return 0;
|
||||
return HeaderSize() + sizeof(grouping_type) +
|
||||
(version == 1 ? sizeof(grouping_type_parameter) : 0) +
|
||||
sizeof(uint32_t) + entries.size() * sizeof(entries[0]);
|
||||
}
|
||||
|
||||
SampleTable::SampleTable() {}
|
||||
SampleTable::~SampleTable() {}
|
||||
FourCC SampleTable::BoxType() const { return FOURCC_stbl; }
|
||||
|
@ -961,15 +1142,30 @@ bool SampleTable::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
buffer->ReadWriteChild(&chunk_large_offset));
|
||||
}
|
||||
RCHECK(buffer->TryReadWriteChild(&sync_sample));
|
||||
if (buffer->Reading()) {
|
||||
RCHECK(buffer->reader()->TryReadChildren(&sample_group_descriptions) &&
|
||||
buffer->reader()->TryReadChildren(&sample_to_groups));
|
||||
} else {
|
||||
for (auto& sample_group_description : sample_group_descriptions)
|
||||
RCHECK(buffer->ReadWriteChild(&sample_group_description));
|
||||
for (auto& sample_to_group : sample_to_groups)
|
||||
RCHECK(buffer->ReadWriteChild(&sample_to_group));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t SampleTable::ComputeSizeInternal() {
|
||||
return HeaderSize() + description.ComputeSize() +
|
||||
uint32_t box_size =
|
||||
HeaderSize() + description.ComputeSize() +
|
||||
decoding_time_to_sample.ComputeSize() +
|
||||
composition_time_to_sample.ComputeSize() +
|
||||
sample_to_chunk.ComputeSize() + sample_size.ComputeSize() +
|
||||
chunk_large_offset.ComputeSize() + sync_sample.ComputeSize();
|
||||
composition_time_to_sample.ComputeSize() + sample_to_chunk.ComputeSize() +
|
||||
sample_size.ComputeSize() + chunk_large_offset.ComputeSize() +
|
||||
sync_sample.ComputeSize();
|
||||
for (auto& sample_group_description : sample_group_descriptions)
|
||||
box_size += sample_group_description.ComputeSize();
|
||||
for (auto& sample_to_group : sample_to_groups)
|
||||
box_size += sample_to_group.ComputeSize();
|
||||
return box_size;
|
||||
}
|
||||
|
||||
EditList::EditList() {}
|
||||
|
@ -2199,152 +2395,6 @@ uint32_t TrackFragmentRun::ComputeSizeInternal() {
|
|||
return box_size;
|
||||
}
|
||||
|
||||
SampleToGroup::SampleToGroup() : grouping_type(0), grouping_type_parameter(0) {}
|
||||
SampleToGroup::~SampleToGroup() {}
|
||||
FourCC SampleToGroup::BoxType() const { return FOURCC_sbgp; }
|
||||
|
||||
bool SampleToGroup::ReadWriteInternal(BoxBuffer* buffer) {
|
||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->ReadWriteUInt32(&grouping_type));
|
||||
if (version == 1)
|
||||
RCHECK(buffer->ReadWriteUInt32(&grouping_type_parameter));
|
||||
|
||||
if (grouping_type != FOURCC_seig) {
|
||||
DCHECK(buffer->Reading());
|
||||
DLOG(WARNING) << "Sample group "
|
||||
<< FourCCToString(static_cast<FourCC>(grouping_type))
|
||||
<< " is not supported.";
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t count = entries.size();
|
||||
RCHECK(buffer->ReadWriteUInt32(&count));
|
||||
entries.resize(count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
RCHECK(buffer->ReadWriteUInt32(&entries[i].sample_count) &&
|
||||
buffer->ReadWriteUInt32(&entries[i].group_description_index));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t SampleToGroup::ComputeSizeInternal() {
|
||||
// This box is optional. Skip it if it is not used.
|
||||
if (entries.empty())
|
||||
return 0;
|
||||
return HeaderSize() + sizeof(grouping_type) +
|
||||
(version == 1 ? sizeof(grouping_type_parameter) : 0) +
|
||||
sizeof(uint32_t) + entries.size() * sizeof(entries[0]);
|
||||
}
|
||||
|
||||
CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry()
|
||||
: is_protected(0),
|
||||
per_sample_iv_size(0),
|
||||
crypt_byte_block(0),
|
||||
skip_byte_block(0) {}
|
||||
CencSampleEncryptionInfoEntry::~CencSampleEncryptionInfoEntry() {};
|
||||
|
||||
SampleGroupDescription::SampleGroupDescription() : grouping_type(0) {}
|
||||
SampleGroupDescription::~SampleGroupDescription() {}
|
||||
FourCC SampleGroupDescription::BoxType() const { return FOURCC_sgpd; }
|
||||
|
||||
bool SampleGroupDescription::ReadWriteInternal(BoxBuffer* buffer) {
|
||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->ReadWriteUInt32(&grouping_type));
|
||||
|
||||
if (grouping_type != FOURCC_seig) {
|
||||
DCHECK(buffer->Reading());
|
||||
DLOG(WARNING) << "Sample group '" << grouping_type << "' is not supported.";
|
||||
return true;
|
||||
}
|
||||
|
||||
const size_t kEntrySize = sizeof(uint32_t) + kCencKeyIdSize;
|
||||
uint32_t default_length = 0;
|
||||
if (version == 1) {
|
||||
if (buffer->Reading()) {
|
||||
RCHECK(buffer->ReadWriteUInt32(&default_length));
|
||||
RCHECK(default_length == 0 || default_length >= kEntrySize);
|
||||
} else {
|
||||
default_length = kEntrySize;
|
||||
RCHECK(buffer->ReadWriteUInt32(&default_length));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t count = entries.size();
|
||||
RCHECK(buffer->ReadWriteUInt32(&count));
|
||||
entries.resize(count);
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (version == 1) {
|
||||
if (buffer->Reading() && default_length == 0) {
|
||||
uint32_t description_length = 0;
|
||||
RCHECK(buffer->ReadWriteUInt32(&description_length));
|
||||
RCHECK(description_length >= kEntrySize);
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer->Reading()) {
|
||||
if (entries[i].key_id.size() != kCencKeyIdSize) {
|
||||
LOG(WARNING) << "CENC defines key id length of " << kCencKeyIdSize
|
||||
<< " bytes; got " << entries[i].key_id.size()
|
||||
<< ". Resized accordingly.";
|
||||
entries[i].key_id.resize(kCencKeyIdSize);
|
||||
}
|
||||
RCHECK(entries[i].crypt_byte_block < 16 &&
|
||||
entries[i].skip_byte_block < 16);
|
||||
}
|
||||
|
||||
RCHECK(buffer->IgnoreBytes(1)); // reserved.
|
||||
|
||||
uint8_t pattern =
|
||||
entries[i].crypt_byte_block << 4 | entries[i].skip_byte_block;
|
||||
RCHECK(buffer->ReadWriteUInt8(&pattern));
|
||||
entries[i].crypt_byte_block = pattern >> 4;
|
||||
entries[i].skip_byte_block = pattern & 0x0F;
|
||||
|
||||
RCHECK(buffer->ReadWriteUInt8(&entries[i].is_protected) &&
|
||||
buffer->ReadWriteUInt8(&entries[i].per_sample_iv_size) &&
|
||||
buffer->ReadWriteVector(&entries[i].key_id, kCencKeyIdSize));
|
||||
|
||||
if (entries[i].is_protected == 1) {
|
||||
if (entries[i].per_sample_iv_size == 0) { // For constant iv.
|
||||
uint8_t constant_iv_size = entries[i].constant_iv.size();
|
||||
RCHECK(buffer->ReadWriteUInt8(&constant_iv_size));
|
||||
RCHECK(constant_iv_size == 8 || constant_iv_size == 16);
|
||||
RCHECK(
|
||||
buffer->ReadWriteVector(&entries[i].constant_iv, constant_iv_size));
|
||||
} else {
|
||||
RCHECK(entries[i].per_sample_iv_size == 8 ||
|
||||
entries[i].per_sample_iv_size == 16);
|
||||
RCHECK(entries[i].constant_iv.empty());
|
||||
}
|
||||
} else {
|
||||
// Expect |is_protected| to be 0, i.e. not protected. Other values of
|
||||
// |is_protected| is not supported.
|
||||
RCHECK(entries[i].is_protected == 0);
|
||||
RCHECK(entries[i].per_sample_iv_size == 0);
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t SampleGroupDescription::ComputeSizeInternal() {
|
||||
// Version 0 is obsoleted, so always generate version 1 box.
|
||||
version = 1;
|
||||
// This box is optional. Skip it if it is not used.
|
||||
if (entries.empty())
|
||||
return 0;
|
||||
size_t entries_size = 0;
|
||||
for (const auto& entry : entries) {
|
||||
entries_size += sizeof(uint32_t) + kCencKeyIdSize +
|
||||
(entry.constant_iv.empty()
|
||||
? 0
|
||||
: (sizeof(uint8_t) + entry.constant_iv.size()));
|
||||
}
|
||||
return HeaderSize() + sizeof(grouping_type) +
|
||||
(version == 1 ? sizeof(uint32_t) : 0) + sizeof(uint32_t) +
|
||||
entries_size;
|
||||
}
|
||||
|
||||
TrackFragment::TrackFragment() : decode_time_absent(false) {}
|
||||
TrackFragment::~TrackFragment() {}
|
||||
FourCC TrackFragment::BoxType() const { return FOURCC_traf; }
|
||||
|
@ -2358,27 +2408,18 @@ bool TrackFragment::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
decode_time_absent = !buffer->reader()->ChildExist(&decode_time);
|
||||
if (!decode_time_absent)
|
||||
RCHECK(buffer->ReadWriteChild(&decode_time));
|
||||
RCHECK(buffer->reader()->TryReadChildren(&runs));
|
||||
|
||||
// There could be multiple SampleGroupDescription and SampleToGroup boxes
|
||||
// with different grouping types. For common encryption, the relevant
|
||||
// grouping type is 'seig'. Continue reading until 'seig' is found, or
|
||||
// until running out of child boxes.
|
||||
while (sample_to_group.grouping_type != FOURCC_seig &&
|
||||
buffer->reader()->ChildExist(&sample_to_group)) {
|
||||
RCHECK(buffer->reader()->ReadChild(&sample_to_group));
|
||||
}
|
||||
while (sample_group_description.grouping_type != FOURCC_seig &&
|
||||
buffer->reader()->ChildExist(&sample_group_description)) {
|
||||
RCHECK(buffer->reader()->ReadChild(&sample_group_description));
|
||||
}
|
||||
RCHECK(buffer->reader()->TryReadChildren(&runs) &&
|
||||
buffer->reader()->TryReadChildren(&sample_group_descriptions) &&
|
||||
buffer->reader()->TryReadChildren(&sample_to_groups));
|
||||
} else {
|
||||
if (!decode_time_absent)
|
||||
RCHECK(buffer->ReadWriteChild(&decode_time));
|
||||
for (uint32_t i = 0; i < runs.size(); ++i)
|
||||
RCHECK(buffer->ReadWriteChild(&runs[i]));
|
||||
RCHECK(buffer->TryReadWriteChild(&sample_to_group) &&
|
||||
buffer->TryReadWriteChild(&sample_group_description));
|
||||
for (uint32_t i = 0; i < sample_to_groups.size(); ++i)
|
||||
RCHECK(buffer->ReadWriteChild(&sample_to_groups[i]));
|
||||
for (uint32_t i = 0; i < sample_group_descriptions.size(); ++i)
|
||||
RCHECK(buffer->ReadWriteChild(&sample_group_descriptions[i]));
|
||||
}
|
||||
return buffer->TryReadWriteChild(&auxiliary_size) &&
|
||||
buffer->TryReadWriteChild(&auxiliary_offset) &&
|
||||
|
@ -2388,11 +2429,14 @@ bool TrackFragment::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
uint32_t TrackFragment::ComputeSizeInternal() {
|
||||
uint32_t box_size =
|
||||
HeaderSize() + header.ComputeSize() + decode_time.ComputeSize() +
|
||||
sample_to_group.ComputeSize() + sample_group_description.ComputeSize() +
|
||||
auxiliary_size.ComputeSize() + auxiliary_offset.ComputeSize() +
|
||||
sample_encryption.ComputeSize();
|
||||
for (uint32_t i = 0; i < runs.size(); ++i)
|
||||
box_size += runs[i].ComputeSize();
|
||||
for (uint32_t i = 0; i < sample_group_descriptions.size(); ++i)
|
||||
box_size += sample_group_descriptions[i].ComputeSize();
|
||||
for (uint32_t i = 0; i < sample_to_groups.size(); ++i)
|
||||
box_size += sample_to_groups[i].ComputeSize();
|
||||
return box_size;
|
||||
}
|
||||
|
||||
|
|
|
@ -468,6 +468,67 @@ struct SyncSample : FullBox {
|
|||
std::vector<uint32_t> sample_number;
|
||||
};
|
||||
|
||||
struct CencSampleEncryptionInfoEntry {
|
||||
CencSampleEncryptionInfoEntry();
|
||||
~CencSampleEncryptionInfoEntry();
|
||||
|
||||
bool ReadWrite(BoxBuffer* buffer);
|
||||
uint32_t ComputeSize() const;
|
||||
|
||||
uint8_t is_protected;
|
||||
uint8_t per_sample_iv_size;
|
||||
std::vector<uint8_t> key_id;
|
||||
|
||||
// For pattern-based encryption.
|
||||
uint8_t crypt_byte_block;
|
||||
uint8_t skip_byte_block;
|
||||
|
||||
// Present only if |is_protected == 1 && per_sample_iv_size == 0|.
|
||||
std::vector<uint8_t> constant_iv;
|
||||
};
|
||||
|
||||
struct AudioRollRecoveryEntry {
|
||||
AudioRollRecoveryEntry();
|
||||
~AudioRollRecoveryEntry();
|
||||
|
||||
bool ReadWrite(BoxBuffer* buffer);
|
||||
uint32_t ComputeSize() const;
|
||||
|
||||
int16_t roll_distance;
|
||||
};
|
||||
|
||||
struct SampleGroupDescription : FullBox {
|
||||
DECLARE_BOX_METHODS(SampleGroupDescription);
|
||||
|
||||
template <typename T>
|
||||
bool ReadWriteEntries(BoxBuffer* buffer, std::vector<T>* entries);
|
||||
|
||||
uint32_t grouping_type;
|
||||
// Only present if grouping_type == 'seig'.
|
||||
std::vector<CencSampleEncryptionInfoEntry>
|
||||
cenc_sample_encryption_info_entries;
|
||||
// Only present if grouping_type == 'roll'.
|
||||
std::vector<AudioRollRecoveryEntry> audio_roll_recovery_entries;
|
||||
};
|
||||
|
||||
struct SampleToGroupEntry {
|
||||
enum GroupDescriptionIndexBase {
|
||||
kTrackGroupDescriptionIndexBase = 0,
|
||||
kTrackFragmentGroupDescriptionIndexBase = 0x10000,
|
||||
};
|
||||
|
||||
uint32_t sample_count;
|
||||
uint32_t group_description_index;
|
||||
};
|
||||
|
||||
struct SampleToGroup : FullBox {
|
||||
DECLARE_BOX_METHODS(SampleToGroup);
|
||||
|
||||
uint32_t grouping_type;
|
||||
uint32_t grouping_type_parameter; // Version 1 only.
|
||||
std::vector<SampleToGroupEntry> entries;
|
||||
};
|
||||
|
||||
struct SampleTable : Box {
|
||||
DECLARE_BOX_METHODS(SampleTable);
|
||||
|
||||
|
@ -481,6 +542,8 @@ struct SampleTable : Box {
|
|||
// ChunkLargeOffset.
|
||||
ChunkLargeOffset chunk_large_offset;
|
||||
SyncSample sync_sample;
|
||||
std::vector<SampleGroupDescription> sample_group_descriptions;
|
||||
std::vector<SampleToGroup> sample_to_groups;
|
||||
};
|
||||
|
||||
struct MediaHeader : FullBox {
|
||||
|
@ -654,47 +717,6 @@ struct TrackFragmentRun : FullBox {
|
|||
std::vector<int64_t> sample_composition_time_offsets;
|
||||
};
|
||||
|
||||
struct SampleToGroupEntry {
|
||||
enum GroupDescriptionIndexBase {
|
||||
kTrackGroupDescriptionIndexBase = 0,
|
||||
kTrackFragmentGroupDescriptionIndexBase = 0x10000,
|
||||
};
|
||||
|
||||
uint32_t sample_count;
|
||||
uint32_t group_description_index;
|
||||
};
|
||||
|
||||
struct SampleToGroup : FullBox {
|
||||
DECLARE_BOX_METHODS(SampleToGroup);
|
||||
|
||||
uint32_t grouping_type;
|
||||
uint32_t grouping_type_parameter; // Version 1 only.
|
||||
std::vector<SampleToGroupEntry> entries;
|
||||
};
|
||||
|
||||
struct CencSampleEncryptionInfoEntry {
|
||||
CencSampleEncryptionInfoEntry();
|
||||
~CencSampleEncryptionInfoEntry();
|
||||
|
||||
uint8_t is_protected;
|
||||
uint8_t per_sample_iv_size;
|
||||
std::vector<uint8_t> key_id;
|
||||
|
||||
// For pattern-based encryption.
|
||||
uint8_t crypt_byte_block;
|
||||
uint8_t skip_byte_block;
|
||||
|
||||
// Present only if |is_protected == 1 && per_sample_iv_size == 0|.
|
||||
std::vector<uint8_t> constant_iv;
|
||||
};
|
||||
|
||||
struct SampleGroupDescription : FullBox {
|
||||
DECLARE_BOX_METHODS(SampleGroupDescription);
|
||||
|
||||
uint32_t grouping_type;
|
||||
std::vector<CencSampleEncryptionInfoEntry> entries;
|
||||
};
|
||||
|
||||
struct TrackFragment : Box {
|
||||
DECLARE_BOX_METHODS(TrackFragment);
|
||||
|
||||
|
@ -702,8 +724,8 @@ struct TrackFragment : Box {
|
|||
std::vector<TrackFragmentRun> runs;
|
||||
bool decode_time_absent;
|
||||
TrackFragmentDecodeTime decode_time;
|
||||
SampleToGroup sample_to_group;
|
||||
SampleGroupDescription sample_group_description;
|
||||
std::vector<SampleGroupDescription> sample_group_descriptions;
|
||||
std::vector<SampleToGroup> sample_to_groups;
|
||||
SampleAuxiliaryInformationSize auxiliary_size;
|
||||
SampleAuxiliaryInformationOffset auxiliary_offset;
|
||||
SampleEncryption sample_encryption;
|
||||
|
|
|
@ -156,6 +156,42 @@ inline bool operator==(const SyncSample& lhs, const SyncSample& rhs) {
|
|||
return lhs.sample_number == rhs.sample_number;
|
||||
}
|
||||
|
||||
inline bool operator==(const CencSampleEncryptionInfoEntry& lhs,
|
||||
const CencSampleEncryptionInfoEntry& rhs) {
|
||||
return lhs.is_protected == rhs.is_protected &&
|
||||
lhs.per_sample_iv_size == rhs.per_sample_iv_size &&
|
||||
lhs.key_id == rhs.key_id &&
|
||||
lhs.crypt_byte_block == rhs.crypt_byte_block &&
|
||||
lhs.skip_byte_block == rhs.skip_byte_block &&
|
||||
lhs.constant_iv == rhs.constant_iv;
|
||||
}
|
||||
|
||||
inline bool operator==(const AudioRollRecoveryEntry& lhs,
|
||||
const AudioRollRecoveryEntry& rhs) {
|
||||
return lhs.roll_distance == rhs.roll_distance;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleGroupDescription& lhs,
|
||||
const SampleGroupDescription& rhs) {
|
||||
return lhs.grouping_type == rhs.grouping_type &&
|
||||
lhs.cenc_sample_encryption_info_entries ==
|
||||
rhs.cenc_sample_encryption_info_entries &&
|
||||
lhs.audio_roll_recovery_entries == rhs.audio_roll_recovery_entries;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleToGroupEntry& lhs,
|
||||
const SampleToGroupEntry& rhs) {
|
||||
return lhs.sample_count == rhs.sample_count &&
|
||||
lhs.group_description_index == rhs.group_description_index;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleToGroup& lhs,
|
||||
const SampleToGroup& rhs) {
|
||||
return lhs.grouping_type == rhs.grouping_type &&
|
||||
lhs.grouping_type_parameter == rhs.grouping_type_parameter &&
|
||||
lhs.entries == rhs.entries;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleTable& lhs, const SampleTable& rhs) {
|
||||
return lhs.description == rhs.description &&
|
||||
lhs.decoding_time_to_sample == rhs.decoding_time_to_sample &&
|
||||
|
@ -163,7 +199,9 @@ inline bool operator==(const SampleTable& lhs, const SampleTable& rhs) {
|
|||
lhs.sample_to_chunk == rhs.sample_to_chunk &&
|
||||
lhs.sample_size == rhs.sample_size &&
|
||||
lhs.chunk_large_offset == rhs.chunk_large_offset &&
|
||||
lhs.sync_sample == rhs.sync_sample;
|
||||
lhs.sync_sample == rhs.sync_sample &&
|
||||
lhs.sample_group_descriptions == rhs.sample_group_descriptions &&
|
||||
lhs.sample_to_groups == rhs.sample_to_groups;
|
||||
}
|
||||
|
||||
inline bool operator==(const EditListEntry& lhs, const EditListEntry& rhs) {
|
||||
|
@ -385,35 +423,6 @@ inline bool operator==(const TrackFragmentRun& lhs,
|
|||
rhs.sample_composition_time_offsets;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleToGroupEntry& lhs,
|
||||
const SampleToGroupEntry& rhs) {
|
||||
return lhs.sample_count == rhs.sample_count &&
|
||||
lhs.group_description_index == rhs.group_description_index;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleToGroup& lhs,
|
||||
const SampleToGroup& rhs) {
|
||||
return lhs.grouping_type == rhs.grouping_type &&
|
||||
lhs.grouping_type_parameter == rhs.grouping_type_parameter &&
|
||||
lhs.entries == rhs.entries;
|
||||
}
|
||||
|
||||
inline bool operator==(const CencSampleEncryptionInfoEntry& lhs,
|
||||
const CencSampleEncryptionInfoEntry& rhs) {
|
||||
return lhs.is_protected == rhs.is_protected &&
|
||||
lhs.per_sample_iv_size == rhs.per_sample_iv_size &&
|
||||
lhs.key_id == rhs.key_id &&
|
||||
lhs.crypt_byte_block == rhs.crypt_byte_block &&
|
||||
lhs.skip_byte_block == rhs.skip_byte_block &&
|
||||
lhs.constant_iv == rhs.constant_iv;
|
||||
}
|
||||
|
||||
inline bool operator==(const SampleGroupDescription& lhs,
|
||||
const SampleGroupDescription& rhs) {
|
||||
return lhs.grouping_type == rhs.grouping_type &&
|
||||
lhs.entries == rhs.entries;
|
||||
}
|
||||
|
||||
inline bool operator==(const TrackFragment& lhs, const TrackFragment& rhs) {
|
||||
return lhs.header == rhs.header && lhs.runs == rhs.runs &&
|
||||
lhs.decode_time == rhs.decode_time &&
|
||||
|
|
|
@ -567,6 +567,45 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
|
||||
void Modify(SyncSample* stss) { stss->sample_number.pop_back(); }
|
||||
|
||||
void Fill(SampleGroupDescription* sgpd) {
|
||||
sgpd->grouping_type = FOURCC_seig;
|
||||
sgpd->cenc_sample_encryption_info_entries.resize(2);
|
||||
sgpd->cenc_sample_encryption_info_entries[0].is_protected = 1;
|
||||
sgpd->cenc_sample_encryption_info_entries[0].per_sample_iv_size = 8;
|
||||
sgpd->cenc_sample_encryption_info_entries[0].key_id.assign(
|
||||
kData16Bytes, kData16Bytes + arraysize(kData16Bytes));
|
||||
sgpd->cenc_sample_encryption_info_entries[0].crypt_byte_block = 3;
|
||||
sgpd->cenc_sample_encryption_info_entries[0].skip_byte_block = 7;
|
||||
sgpd->cenc_sample_encryption_info_entries[1].is_protected = 0;
|
||||
sgpd->cenc_sample_encryption_info_entries[1].per_sample_iv_size = 0;
|
||||
sgpd->cenc_sample_encryption_info_entries[1].key_id.resize(16);
|
||||
sgpd->version = 1;
|
||||
}
|
||||
|
||||
void Modify(SampleGroupDescription* sgpd) {
|
||||
sgpd->cenc_sample_encryption_info_entries.resize(1);
|
||||
sgpd->cenc_sample_encryption_info_entries[0].is_protected = 1;
|
||||
sgpd->cenc_sample_encryption_info_entries[0].per_sample_iv_size = 0;
|
||||
sgpd->cenc_sample_encryption_info_entries[0].constant_iv.assign(
|
||||
kData16Bytes, kData16Bytes + arraysize(kData16Bytes));
|
||||
sgpd->cenc_sample_encryption_info_entries[0].key_id.resize(16);
|
||||
}
|
||||
|
||||
void Fill(SampleToGroup* sbgp) {
|
||||
sbgp->grouping_type = FOURCC_seig;
|
||||
sbgp->entries.resize(2);
|
||||
sbgp->entries[0].sample_count = 3;
|
||||
sbgp->entries[0].group_description_index = 0x10002;
|
||||
sbgp->entries[1].sample_count = 1212;
|
||||
sbgp->entries[1].group_description_index = 0x10001;
|
||||
}
|
||||
|
||||
void Modify(SampleToGroup* sbgp) {
|
||||
sbgp->entries.resize(1);
|
||||
sbgp->entries[0].sample_count = 5;
|
||||
sbgp->entries[0].group_description_index = 0x10001;
|
||||
}
|
||||
|
||||
void Fill(SampleTable* stbl) {
|
||||
Fill(&stbl->description);
|
||||
Fill(&stbl->decoding_time_to_sample);
|
||||
|
@ -575,11 +614,17 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
Fill(&stbl->sample_size);
|
||||
Fill(&stbl->chunk_large_offset);
|
||||
Fill(&stbl->sync_sample);
|
||||
stbl->sample_group_descriptions.resize(1);
|
||||
Fill(&stbl->sample_group_descriptions[0]);
|
||||
stbl->sample_to_groups.resize(1);
|
||||
Fill(&stbl->sample_to_groups[0]);
|
||||
}
|
||||
|
||||
void Modify(SampleTable* stbl) {
|
||||
Modify(&stbl->chunk_large_offset);
|
||||
Modify(&stbl->sync_sample);
|
||||
stbl->sample_group_descriptions.clear();
|
||||
stbl->sample_to_groups.clear();
|
||||
}
|
||||
|
||||
void Fill(MediaHeader* mdhd) {
|
||||
|
@ -768,47 +813,6 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
trun->version = 0;
|
||||
}
|
||||
|
||||
void Fill(SampleToGroup* sbgp) {
|
||||
sbgp->grouping_type = FOURCC_seig;
|
||||
sbgp->entries.resize(2);
|
||||
sbgp->entries[0].sample_count = 3;
|
||||
sbgp->entries[0].group_description_index = 0x10002;
|
||||
sbgp->entries[1].sample_count = 1212;
|
||||
sbgp->entries[1].group_description_index = 0x10001;
|
||||
}
|
||||
|
||||
void Modify(SampleToGroup* sbgp) {
|
||||
sbgp->entries.resize(1);
|
||||
sbgp->entries[0].sample_count = 5;
|
||||
sbgp->entries[0].group_description_index = 0x10001;
|
||||
}
|
||||
|
||||
void Fill(SampleGroupDescription* sgpd) {
|
||||
sgpd->grouping_type = FOURCC_seig;
|
||||
sgpd->entries.resize(3);
|
||||
sgpd->entries[0].is_protected = 1;
|
||||
sgpd->entries[0].per_sample_iv_size = 8;
|
||||
sgpd->entries[0].key_id.assign(kData16Bytes,
|
||||
kData16Bytes + arraysize(kData16Bytes));
|
||||
sgpd->entries[0].crypt_byte_block = 3;
|
||||
sgpd->entries[0].skip_byte_block = 7;
|
||||
sgpd->entries[1].is_protected = 0;
|
||||
sgpd->entries[1].per_sample_iv_size = 0;
|
||||
sgpd->entries[1].key_id.resize(16);
|
||||
sgpd->entries[2].is_protected = 1;
|
||||
sgpd->entries[2].per_sample_iv_size = 0;
|
||||
sgpd->entries[2].constant_iv.assign(kData16Bytes,
|
||||
kData16Bytes + arraysize(kData16Bytes));
|
||||
sgpd->entries[2].key_id.resize(16);
|
||||
sgpd->version = 1;
|
||||
}
|
||||
|
||||
void Modify(SampleGroupDescription* sgpd) {
|
||||
sgpd->entries.resize(1);
|
||||
sgpd->entries[0].key_id[4] = 88;
|
||||
sgpd->version = 1;
|
||||
}
|
||||
|
||||
void Fill(TrackFragment* traf) {
|
||||
Fill(&traf->header);
|
||||
traf->runs.resize(1);
|
||||
|
@ -821,8 +825,19 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
void Modify(TrackFragment* traf) {
|
||||
Modify(&traf->header);
|
||||
Modify(&traf->decode_time);
|
||||
Fill(&traf->sample_to_group);
|
||||
Fill(&traf->sample_group_description);
|
||||
|
||||
traf->sample_group_descriptions.resize(2);
|
||||
Fill(&traf->sample_group_descriptions[0]);
|
||||
traf->sample_group_descriptions[1].grouping_type = FOURCC_roll;
|
||||
traf->sample_group_descriptions[1].audio_roll_recovery_entries.resize(1);
|
||||
traf->sample_group_descriptions[1]
|
||||
.audio_roll_recovery_entries[0]
|
||||
.roll_distance = -10;
|
||||
|
||||
traf->sample_to_groups.resize(2);
|
||||
Fill(&traf->sample_to_groups[0]);
|
||||
Modify(&traf->sample_to_groups[1]);
|
||||
traf->sample_to_groups[1].grouping_type = FOURCC_roll;
|
||||
}
|
||||
|
||||
void Fill(MovieFragment* moof) {
|
||||
|
@ -951,10 +966,10 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
bool IsOptional(const WebVTTSourceLabelBox* box) { return true; }
|
||||
bool IsOptional(const CompositionTimeToSample* box) { return true; }
|
||||
bool IsOptional(const SyncSample* box) { return true; }
|
||||
bool IsOptional(const SampleGroupDescription* box) { return true; }
|
||||
bool IsOptional(const SampleToGroup* box) { return true; }
|
||||
bool IsOptional(const MovieExtendsHeader* box) { return true; }
|
||||
bool IsOptional(const MovieExtends* box) { return true; }
|
||||
bool IsOptional(const SampleToGroup* box) { return true; }
|
||||
bool IsOptional(const SampleGroupDescription* box) { return true; }
|
||||
bool IsOptional(const CueSourceIDBox* box) { return true; }
|
||||
bool IsOptional(const CueIDBox* box) { return true; }
|
||||
bool IsOptional(const CueTimeBox* box) { return true; }
|
||||
|
@ -1006,6 +1021,8 @@ typedef testing::Types<FileType,
|
|||
ChunkLargeOffset,
|
||||
ChunkOffset,
|
||||
SyncSample,
|
||||
SampleGroupDescription,
|
||||
SampleToGroup,
|
||||
SampleTable>
|
||||
Boxes;
|
||||
typedef testing::Types<MediaHeader,
|
||||
|
@ -1029,8 +1046,6 @@ typedef testing::Types<MediaHeader,
|
|||
TrackFragment,
|
||||
MovieFragment,
|
||||
SegmentIndex,
|
||||
SampleToGroup,
|
||||
SampleGroupDescription,
|
||||
CueSourceIDBox,
|
||||
CueTimeBox,
|
||||
CueIDBox,
|
||||
|
|
|
@ -67,7 +67,7 @@ EncryptingFragmenter::EncryptingFragmenter(
|
|||
FourCC protection_scheme,
|
||||
uint8_t crypt_byte_block,
|
||||
uint8_t skip_byte_block)
|
||||
: Fragmenter(traf),
|
||||
: Fragmenter(info, traf),
|
||||
info_(info),
|
||||
encryption_key_(encryption_key.Pass()),
|
||||
nalu_length_size_(GetNaluLengthSize(*info)),
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace mp4 {
|
|||
/// EncryptingFragmenter generates MP4 fragments with sample encrypted.
|
||||
class EncryptingFragmenter : public Fragmenter {
|
||||
public:
|
||||
/// @param info contains stream information.
|
||||
/// @param traf points to a TrackFragment box.
|
||||
/// @param encryption_key contains the encryption parameters.
|
||||
/// @param clear_time specifies clear lead duration in units of the current
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <limits>
|
||||
|
||||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/base/audio_stream_info.h"
|
||||
#include "packager/media/base/media_sample.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
|
||||
|
@ -18,10 +19,18 @@ namespace mp4 {
|
|||
|
||||
namespace {
|
||||
const int64_t kInvalidTime = std::numeric_limits<int64_t>::max();
|
||||
|
||||
uint64_t GetSeekPreroll(const StreamInfo& stream_info) {
|
||||
if (stream_info.stream_type() != kStreamAudio) return 0;
|
||||
const AudioStreamInfo& audio_stream_info =
|
||||
static_cast<const AudioStreamInfo&>(stream_info);
|
||||
return audio_stream_info.seek_preroll_ns();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Fragmenter::Fragmenter(TrackFragment* traf)
|
||||
Fragmenter::Fragmenter(scoped_refptr<StreamInfo> info, TrackFragment* traf)
|
||||
: traf_(traf),
|
||||
seek_preroll_(GetSeekPreroll(*info)),
|
||||
fragment_initialized_(false),
|
||||
fragment_finalized_(false),
|
||||
fragment_duration_(0),
|
||||
|
@ -80,6 +89,8 @@ Status Fragmenter::InitializeFragment(int64_t first_sample_dts) {
|
|||
traf_->runs.clear();
|
||||
traf_->runs.resize(1);
|
||||
traf_->runs[0].flags = TrackFragmentRun::kDataOffsetPresentMask;
|
||||
traf_->sample_group_descriptions.clear();
|
||||
traf_->sample_to_groups.clear();
|
||||
traf_->header.sample_description_index = 1; // 1-based.
|
||||
traf_->header.flags = TrackFragmentHeader::kDefaultBaseIsMoofMask |
|
||||
TrackFragmentHeader::kSampleDescriptionIndexPresentMask;
|
||||
|
@ -113,6 +124,35 @@ void Fragmenter::FinalizeFragment() {
|
|||
traf_->runs[0].flags |= TrackFragmentRun::kSampleFlagsPresentMask;
|
||||
}
|
||||
|
||||
// Add SampleToGroup boxes. A SampleToGroup box with grouping type of 'roll'
|
||||
// needs to be added if there is seek preroll, referencing sample group
|
||||
// description in track level; Also need to add SampleToGroup boxes
|
||||
// correponding to every SampleGroupDescription boxes, referencing sample
|
||||
// group description in fragment level.
|
||||
DCHECK_EQ(traf_->sample_to_groups.size(), 0u);
|
||||
if (seek_preroll_ > 0) {
|
||||
traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
|
||||
SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
|
||||
sample_to_group.grouping_type = FOURCC_roll;
|
||||
|
||||
sample_to_group.entries.resize(1);
|
||||
SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
|
||||
sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
|
||||
sample_to_group_entry.group_description_index =
|
||||
SampleToGroupEntry::kTrackGroupDescriptionIndexBase + 1;
|
||||
}
|
||||
for (const auto& sample_group_description : traf_->sample_group_descriptions) {
|
||||
traf_->sample_to_groups.resize(traf_->sample_to_groups.size() + 1);
|
||||
SampleToGroup& sample_to_group = traf_->sample_to_groups.back();
|
||||
sample_to_group.grouping_type = sample_group_description.grouping_type;
|
||||
|
||||
sample_to_group.entries.resize(1);
|
||||
SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
|
||||
sample_to_group_entry.sample_count = traf_->runs[0].sample_count;
|
||||
sample_to_group_entry.group_description_index =
|
||||
SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
|
||||
}
|
||||
|
||||
fragment_finalized_ = true;
|
||||
fragment_initialized_ = false;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace media {
|
|||
|
||||
class BufferWriter;
|
||||
class MediaSample;
|
||||
class StreamInfo;
|
||||
|
||||
namespace mp4 {
|
||||
|
||||
|
@ -29,8 +30,9 @@ struct TrackFragment;
|
|||
/// box and corresponding 'mdat' box.
|
||||
class Fragmenter {
|
||||
public:
|
||||
/// @param info contains stream information.
|
||||
/// @param traf points to a TrackFragment box.
|
||||
Fragmenter(TrackFragment* traf);
|
||||
Fragmenter(scoped_refptr<StreamInfo> info, TrackFragment* traf);
|
||||
|
||||
virtual ~Fragmenter();
|
||||
|
||||
|
@ -74,6 +76,7 @@ class Fragmenter {
|
|||
bool StartsWithSAP();
|
||||
|
||||
TrackFragment* traf_;
|
||||
uint64_t seek_preroll_;
|
||||
bool fragment_initialized_;
|
||||
bool fragment_finalized_;
|
||||
uint64_t fragment_duration_;
|
||||
|
|
|
@ -107,9 +107,15 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
|||
// i.e. there is at most one key for the fragment. So there should be only
|
||||
// one entry in SampleGroupDescription box and one entry in SampleToGroup box.
|
||||
// Fill in SampleGroupDescription box information.
|
||||
traf()->sample_group_description.grouping_type = FOURCC_seig;
|
||||
traf()->sample_group_description.entries.resize(1);
|
||||
auto& sample_group_entry = traf()->sample_group_description.entries[0];
|
||||
traf()->sample_group_descriptions.resize(
|
||||
traf()->sample_group_descriptions.size() + 1);
|
||||
SampleGroupDescription& sample_group_description =
|
||||
traf()->sample_group_descriptions.back();
|
||||
sample_group_description.grouping_type = FOURCC_seig;
|
||||
|
||||
sample_group_description.cenc_sample_encryption_info_entries.resize(1);
|
||||
CencSampleEncryptionInfoEntry& sample_group_entry =
|
||||
sample_group_description.cenc_sample_encryption_info_entries.back();
|
||||
sample_group_entry.is_protected = 1;
|
||||
if (protection_scheme() == FOURCC_cbcs) {
|
||||
// For 'cbcs' scheme, Constant IVs SHALL be used.
|
||||
|
@ -122,23 +128,9 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
|
|||
sample_group_entry.skip_byte_block = skip_byte_block();
|
||||
sample_group_entry.key_id = encryption_key()->key_id;
|
||||
|
||||
// Fill in SampleToGroup box information.
|
||||
traf()->sample_to_group.grouping_type = FOURCC_seig;
|
||||
traf()->sample_to_group.entries.resize(1);
|
||||
// sample_count is adjusted in |FinalizeFragment| later.
|
||||
traf()->sample_to_group.entries[0].group_description_index =
|
||||
SampleToGroupEntry::kTrackFragmentGroupDescriptionIndexBase + 1;
|
||||
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
void KeyRotationFragmenter::FinalizeFragmentForEncryption() {
|
||||
EncryptingFragmenter::FinalizeFragmentForEncryption();
|
||||
DCHECK_EQ(1u, traf()->sample_to_group.entries.size());
|
||||
traf()->sample_to_group.entries[0].sample_count =
|
||||
traf()->auxiliary_size.sample_count;
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
|
|
@ -23,6 +23,7 @@ struct MovieFragment;
|
|||
class KeyRotationFragmenter : public EncryptingFragmenter {
|
||||
public:
|
||||
/// @param moof points to a MovieFragment box.
|
||||
/// @param info contains stream information.
|
||||
/// @param traf points to a TrackFragment box.
|
||||
/// @param encryption_key_source points to the source which generates
|
||||
/// encryption keys.
|
||||
|
@ -57,7 +58,6 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
|
|||
/// @name Fragmenter implementation overrides.
|
||||
/// @{
|
||||
Status PrepareFragmentForEncryption(bool enable_encryption) override;
|
||||
void FinalizeFragmentForEncryption() override;
|
||||
/// @}
|
||||
|
||||
private:
|
||||
|
|
|
@ -440,6 +440,34 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Extract possible seek preroll.
|
||||
uint64_t seek_preroll_ns = 0;
|
||||
for (const auto& sample_group_description :
|
||||
track->media.information.sample_table.sample_group_descriptions) {
|
||||
if (sample_group_description.grouping_type != FOURCC_roll)
|
||||
continue;
|
||||
const auto& audio_roll_recovery_entries =
|
||||
sample_group_description.audio_roll_recovery_entries;
|
||||
if (audio_roll_recovery_entries.size() != 1) {
|
||||
LOG(WARNING) << "Unexpected number of entries in "
|
||||
"SampleGroupDescription table with grouping type "
|
||||
"'roll'.";
|
||||
break;
|
||||
}
|
||||
const int16_t roll_distance_in_samples =
|
||||
audio_roll_recovery_entries[0].roll_distance;
|
||||
if (roll_distance_in_samples < 0) {
|
||||
RCHECK(sampling_frequency != 0);
|
||||
seek_preroll_ns = kNanosecondsPerSecond *
|
||||
(-roll_distance_in_samples) / sampling_frequency;
|
||||
} else {
|
||||
LOG(WARNING)
|
||||
<< "Roll distance is supposed to be negative, but seeing "
|
||||
<< roll_distance_in_samples;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const bool is_encrypted =
|
||||
entry.sinf.info.track_encryption.default_is_protected == 1;
|
||||
DVLOG(1) << "is_audio_track_encrypted_: " << is_encrypted;
|
||||
|
@ -453,7 +481,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
entry.samplesize,
|
||||
num_channels,
|
||||
sampling_frequency,
|
||||
0 /* seek preroll */,
|
||||
seek_preroll_ns,
|
||||
codec_delay_ns,
|
||||
max_bitrate,
|
||||
avg_bitrate,
|
||||
|
|
|
@ -287,10 +287,41 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
|||
audio.channelcount = audio_info->num_channels();
|
||||
audio.samplesize = audio_info->sample_bits();
|
||||
audio.samplerate = audio_info->sampling_frequency();
|
||||
SampleDescription& sample_description =
|
||||
trak->media.information.sample_table.description;
|
||||
SampleTable& sample_table = trak->media.information.sample_table;
|
||||
SampleDescription& sample_description = sample_table.description;
|
||||
sample_description.type = kAudio;
|
||||
sample_description.audio_entries.push_back(audio);
|
||||
|
||||
// Opus requires at least one sample group description box and at least one
|
||||
// sample to group box with grouping type 'roll' within sample table box.
|
||||
if (audio_info->codec() == kCodecOpus) {
|
||||
sample_table.sample_group_descriptions.resize(1);
|
||||
SampleGroupDescription& sample_group_description =
|
||||
sample_table.sample_group_descriptions.back();
|
||||
sample_group_description.grouping_type = FOURCC_roll;
|
||||
sample_group_description.audio_roll_recovery_entries.resize(1);
|
||||
// The roll distance is expressed in sample units and always takes negative
|
||||
// values.
|
||||
const uint64_t kNanosecondsPerSecond = 1000000000ull;
|
||||
sample_group_description.audio_roll_recovery_entries[0].roll_distance =
|
||||
-(audio_info->seek_preroll_ns() * audio.samplerate +
|
||||
kNanosecondsPerSecond / 2) /
|
||||
kNanosecondsPerSecond;
|
||||
|
||||
sample_table.sample_to_groups.resize(1);
|
||||
SampleToGroup& sample_to_group = sample_table.sample_to_groups.back();
|
||||
sample_to_group.grouping_type = FOURCC_roll;
|
||||
|
||||
sample_to_group.entries.resize(1);
|
||||
SampleToGroupEntry& sample_to_group_entry = sample_to_group.entries.back();
|
||||
// All samples are in track fragments.
|
||||
sample_to_group_entry.sample_count = 0;
|
||||
sample_to_group_entry.group_description_index =
|
||||
SampleToGroupEntry::kTrackGroupDescriptionIndexBase + 1;
|
||||
} else if (audio_info->seek_preroll_ns() != 0) {
|
||||
LOG(WARNING) << "Unexpected seek preroll for codec " << audio_info->codec();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool MP4Muxer::GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end) {
|
||||
|
|
|
@ -170,7 +170,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
sidx_->reference_id = i + 1;
|
||||
}
|
||||
if (!encryption_key_source) {
|
||||
fragmenters_[i] = new Fragmenter(&moof_->tracks[i]);
|
||||
fragmenters_[i] = new Fragmenter(streams[i]->info(), &moof_->tracks[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue