Add 'sbgp' and 'sgpd' box definitions
sbgp: Sample to Group box sgpd: Sample Group Descripton box They are required for DASH Live support. Change-Id: I2f3b55843c5148db60c7c3a6891ba825b59934ce
This commit is contained in:
parent
beac7bae62
commit
2c8418fd22
|
@ -1595,6 +1595,121 @@ uint32 TrackFragmentRun::ComputeSize() {
|
|||
return atom_size;
|
||||
}
|
||||
|
||||
SampleToGroup::SampleToGroup() : grouping_type(0), grouping_type_parameter(0) {}
|
||||
SampleToGroup::~SampleToGroup() {}
|
||||
FourCC SampleToGroup::BoxType() const { return FOURCC_SBGP; }
|
||||
|
||||
bool SampleToGroup::ReadWrite(BoxBuffer* buffer) {
|
||||
RCHECK(FullBox::ReadWrite(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 '" << grouping_type << "' is not supported.";
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 count = entries.size();
|
||||
RCHECK(buffer->ReadWriteUInt32(&count));
|
||||
entries.resize(count);
|
||||
for (uint32 i = 0; i < count; ++i) {
|
||||
RCHECK(buffer->ReadWriteUInt32(&entries[i].sample_count) &&
|
||||
buffer->ReadWriteUInt32(&entries[i].group_description_index));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 SampleToGroup::ComputeSize() {
|
||||
// This box is optional. Skip it if it is not used.
|
||||
atom_size = 0;
|
||||
if (!entries.empty()) {
|
||||
atom_size = kFullBoxSize + sizeof(grouping_type) +
|
||||
(version == 1 ? sizeof(grouping_type_parameter) : 0) +
|
||||
sizeof(uint32) + entries.size() * sizeof(entries[0]);
|
||||
}
|
||||
return atom_size;
|
||||
}
|
||||
|
||||
CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry()
|
||||
: is_encrypted(false), iv_size(0) {
|
||||
}
|
||||
CencSampleEncryptionInfoEntry::~CencSampleEncryptionInfoEntry() {};
|
||||
|
||||
SampleGroupDescription::SampleGroupDescription() : grouping_type(0) {}
|
||||
SampleGroupDescription::~SampleGroupDescription() {}
|
||||
FourCC SampleGroupDescription::BoxType() const { return FOURCC_SGPD; }
|
||||
|
||||
bool SampleGroupDescription::ReadWrite(BoxBuffer* buffer) {
|
||||
RCHECK(FullBox::ReadWrite(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 kKeyIdSize = 16;
|
||||
const size_t kEntrySize = sizeof(uint32) + kKeyIdSize;
|
||||
uint32 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 count = entries.size();
|
||||
RCHECK(buffer->ReadWriteUInt32(&count));
|
||||
entries.resize(count);
|
||||
for (uint32 i = 0; i < count; ++i) {
|
||||
if (version == 1) {
|
||||
if (buffer->Reading() && default_length == 0) {
|
||||
uint32 description_length = 0;
|
||||
RCHECK(buffer->ReadWriteUInt32(&description_length));
|
||||
RCHECK(description_length == kEntrySize);
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer->Reading())
|
||||
RCHECK(entries[i].key_id.size() == kKeyIdSize);
|
||||
|
||||
uint8 flag = entries[i].is_encrypted ? 1 : 0;
|
||||
RCHECK(buffer->IgnoreBytes(2) && // reserved.
|
||||
buffer->ReadWriteUInt8(&flag) &&
|
||||
buffer->ReadWriteUInt8(&entries[i].iv_size) &&
|
||||
buffer->ReadWriteVector(&entries[i].key_id, kKeyIdSize));
|
||||
|
||||
if (buffer->Reading()) {
|
||||
entries[i].is_encrypted = (flag != 0);
|
||||
if (entries[i].is_encrypted) {
|
||||
RCHECK(entries[i].iv_size == 8 || entries[i].iv_size == 16);
|
||||
} else {
|
||||
RCHECK(entries[i].iv_size == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 SampleGroupDescription::ComputeSize() {
|
||||
// This box is optional. Skip it if it is not used.
|
||||
atom_size = 0;
|
||||
if (!entries.empty()) {
|
||||
const size_t kKeyIdSize = 16;
|
||||
const size_t kEntrySize = sizeof(uint32) + kKeyIdSize;
|
||||
atom_size = kFullBoxSize + sizeof(grouping_type) +
|
||||
(version == 1 ? sizeof(uint32) : 0) +
|
||||
sizeof(uint32) + entries.size() * kEntrySize;
|
||||
}
|
||||
return atom_size;
|
||||
}
|
||||
|
||||
TrackFragment::TrackFragment() {}
|
||||
TrackFragment::~TrackFragment() {}
|
||||
FourCC TrackFragment::BoxType() const { return FOURCC_TRAF; }
|
||||
|
@ -1608,9 +1723,46 @@ bool TrackFragment::ReadWrite(BoxBuffer* buffer) {
|
|||
if (buffer->Reading()) {
|
||||
DCHECK(buffer->reader());
|
||||
RCHECK(buffer->reader()->TryReadChildren(&runs));
|
||||
|
||||
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));
|
||||
}
|
||||
if (sample_to_group.grouping_type == FOURCC_SEIG) {
|
||||
// SampleGroupDescription box can appear in either 'moov...stbl' or
|
||||
// 'moov.traf'. The first case is not supported for now, so we require
|
||||
// a companion SampleGroupDescription box to coexist with the
|
||||
// SampleToGroup box.
|
||||
if (sample_group_description.grouping_type != FOURCC_SEIG) {
|
||||
NOTIMPLEMENTED()
|
||||
<< "SampleGroupDescription box in 'moov' is not supported.";
|
||||
return false;
|
||||
}
|
||||
for (std::vector<SampleToGroupEntry>::iterator it =
|
||||
sample_to_group.entries.begin();
|
||||
it != sample_to_group.entries.end();
|
||||
++it) {
|
||||
if ((it->group_description_index & 0x10000) == 0) {
|
||||
NOTIMPLEMENTED()
|
||||
<< "SampleGroupDescription box in 'moov' is not supported.";
|
||||
return false;
|
||||
}
|
||||
it->group_description_index &= 0x0FFFF;
|
||||
RCHECK(it->group_description_index <=
|
||||
sample_group_description.entries.size());
|
||||
}
|
||||
} else {
|
||||
RCHECK(sample_group_description.grouping_type != FOURCC_SEIG);
|
||||
}
|
||||
} else {
|
||||
for (uint32 i = 0; i < runs.size(); ++i)
|
||||
RCHECK(runs[i].ReadWrite(buffer));
|
||||
RCHECK(buffer->TryReadWriteChild(&sample_to_group) &&
|
||||
buffer->TryReadWriteChild(&sample_group_description));
|
||||
}
|
||||
return buffer->TryReadWriteChild(&auxiliary_size) &&
|
||||
buffer->TryReadWriteChild(&auxiliary_offset);
|
||||
|
@ -1618,6 +1770,8 @@ bool TrackFragment::ReadWrite(BoxBuffer* buffer) {
|
|||
|
||||
uint32 TrackFragment::ComputeSize() {
|
||||
atom_size = kBoxSize + header.ComputeSize() + decode_time.ComputeSize() +
|
||||
sample_to_group.ComputeSize() +
|
||||
sample_group_description.ComputeSize() +
|
||||
auxiliary_size.ComputeSize() + auxiliary_offset.ComputeSize();
|
||||
for (uint32 i = 0; i < runs.size(); ++i)
|
||||
atom_size += runs[i].ComputeSize();
|
||||
|
|
|
@ -492,14 +492,50 @@ struct TrackFragmentRun : FullBox {
|
|||
std::vector<int32> sample_composition_time_offsets;
|
||||
};
|
||||
|
||||
struct SampleToGroupEntry {
|
||||
enum GroupDescriptionIndexBase {
|
||||
kTrackGroupDescriptionIndexBase = 0,
|
||||
kTrackFragmentGroupDescriptionIndexBase = 0x10000,
|
||||
};
|
||||
|
||||
uint32 sample_count;
|
||||
uint32 group_description_index;
|
||||
};
|
||||
|
||||
struct SampleToGroup : FullBox {
|
||||
DECLARE_BOX_METHODS(SampleToGroup);
|
||||
|
||||
uint32 grouping_type;
|
||||
uint32 grouping_type_parameter; // Version 1 only.
|
||||
std::vector<SampleToGroupEntry> entries;
|
||||
};
|
||||
|
||||
struct CencSampleEncryptionInfoEntry {
|
||||
CencSampleEncryptionInfoEntry();
|
||||
~CencSampleEncryptionInfoEntry();
|
||||
|
||||
bool is_encrypted;
|
||||
uint8 iv_size;
|
||||
std::vector<uint8> key_id;
|
||||
};
|
||||
|
||||
struct SampleGroupDescription : FullBox {
|
||||
DECLARE_BOX_METHODS(SampleGroupDescription);
|
||||
|
||||
uint32 grouping_type;
|
||||
std::vector<CencSampleEncryptionInfoEntry> entries;
|
||||
};
|
||||
|
||||
struct TrackFragment : Box {
|
||||
DECLARE_BOX_METHODS(TrackFragment);
|
||||
|
||||
TrackFragmentHeader header;
|
||||
std::vector<TrackFragmentRun> runs;
|
||||
TrackFragmentDecodeTime decode_time;
|
||||
SampleAuxiliaryInformationOffset auxiliary_offset;
|
||||
SampleToGroup sample_to_group;
|
||||
SampleGroupDescription sample_group_description;
|
||||
SampleAuxiliaryInformationSize auxiliary_size;
|
||||
SampleAuxiliaryInformationOffset auxiliary_offset;
|
||||
};
|
||||
|
||||
struct MovieFragment : Box {
|
||||
|
|
|
@ -303,6 +303,32 @@ 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_encrypted == rhs.is_encrypted &&
|
||||
lhs.iv_size == rhs.iv_size &&
|
||||
lhs.key_id == rhs.key_id;
|
||||
}
|
||||
|
||||
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 &&
|
||||
|
|
|
@ -103,7 +103,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
void Modify(Box* box) {}
|
||||
|
||||
// Is this box optional?
|
||||
bool IsOptional(Box* box) { return false; }
|
||||
bool IsOptional(const Box* box) { return false; }
|
||||
|
||||
// Non-full box does not have version field.
|
||||
uint8 GetAndClearVersion(Box* box) { return 0; }
|
||||
|
@ -625,6 +625,37 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
trun->version = 1;
|
||||
}
|
||||
|
||||
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[0].sample_count = 1212;
|
||||
sbgp->entries[0].group_description_index = 0x10001;
|
||||
}
|
||||
|
||||
void Modify(SampleToGroup* sbgp) {
|
||||
sbgp->entries.resize(1);
|
||||
sbgp->entries[0].group_description_index = 0x10001;
|
||||
}
|
||||
|
||||
void Fill(SampleGroupDescription* sgpd) {
|
||||
sgpd->grouping_type = FOURCC_SEIG;
|
||||
sgpd->entries.resize(2);
|
||||
sgpd->entries[0].is_encrypted = true;
|
||||
sgpd->entries[0].iv_size = 8;
|
||||
sgpd->entries[0].key_id.assign(kData16Bytes,
|
||||
kData16Bytes + arraysize(kData16Bytes));
|
||||
sgpd->entries[1].is_encrypted = false;
|
||||
sgpd->entries[1].iv_size = 0;
|
||||
sgpd->entries[1].key_id.resize(16);
|
||||
}
|
||||
|
||||
void Modify(SampleGroupDescription* sbgp) {
|
||||
sbgp->entries.resize(1);
|
||||
sbgp->entries[0].key_id[4] = 88;
|
||||
}
|
||||
|
||||
void Fill(TrackFragment* traf) {
|
||||
Fill(&traf->header);
|
||||
traf->runs.resize(1);
|
||||
|
@ -680,18 +711,20 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
sidx->version = 1;
|
||||
}
|
||||
|
||||
bool IsOptional(SampleAuxiliaryInformationOffset* box) { return true; }
|
||||
bool IsOptional(SampleAuxiliaryInformationSize* box) { return true; }
|
||||
bool IsOptional(ProtectionSchemeInfo* box) { return true; }
|
||||
bool IsOptional(EditList* box) { return true; }
|
||||
bool IsOptional(Edit* box) { return true; }
|
||||
bool IsOptional(AVCDecoderConfigurationRecord* box) { return true; }
|
||||
bool IsOptional(PixelAspectRatioBox* box) { return true; }
|
||||
bool IsOptional(ElementaryStreamDescriptor* box) { return true; }
|
||||
bool IsOptional(CompositionTimeToSample* box) { return true; }
|
||||
bool IsOptional(SyncSample* box) { return true; }
|
||||
bool IsOptional(MovieExtendsHeader* box) { return true; }
|
||||
bool IsOptional(MovieExtends* box) { return true; }
|
||||
bool IsOptional(const SampleAuxiliaryInformationOffset* box) { return true; }
|
||||
bool IsOptional(const SampleAuxiliaryInformationSize* box) { return true; }
|
||||
bool IsOptional(const ProtectionSchemeInfo* box) { return true; }
|
||||
bool IsOptional(const EditList* box) { return true; }
|
||||
bool IsOptional(const Edit* box) { return true; }
|
||||
bool IsOptional(const AVCDecoderConfigurationRecord* box) { return true; }
|
||||
bool IsOptional(const PixelAspectRatioBox* box) { return true; }
|
||||
bool IsOptional(const ElementaryStreamDescriptor* box) { return true; }
|
||||
bool IsOptional(const CompositionTimeToSample* box) { return true; }
|
||||
bool IsOptional(const SyncSample* 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; }
|
||||
|
||||
protected:
|
||||
scoped_ptr<BufferWriter> buffer_;
|
||||
|
@ -749,9 +782,15 @@ typedef testing::Types<
|
|||
MovieFragment,
|
||||
SegmentIndex> Boxes;
|
||||
|
||||
TYPED_TEST_CASE(BoxDefinitionsTestGeneral, Boxes);
|
||||
// GTEST support a maximum of 50 types in the template list, so we have to
|
||||
// break it into two groups.
|
||||
typedef testing::Types<
|
||||
SampleToGroup,
|
||||
SampleGroupDescription> Boxes2;
|
||||
|
||||
TYPED_TEST(BoxDefinitionsTestGeneral, WriteReadbackCompare) {
|
||||
TYPED_TEST_CASE_P(BoxDefinitionsTestGeneral);
|
||||
|
||||
TYPED_TEST_P(BoxDefinitionsTestGeneral, WriteReadbackCompare) {
|
||||
TypeParam box;
|
||||
LOG(INFO) << "Processing " << FourCCToString(box.BoxType());
|
||||
this->Fill(&box);
|
||||
|
@ -762,7 +801,7 @@ TYPED_TEST(BoxDefinitionsTestGeneral, WriteReadbackCompare) {
|
|||
ASSERT_EQ(box, box_readback);
|
||||
}
|
||||
|
||||
TYPED_TEST(BoxDefinitionsTestGeneral, WriteModifyWrite) {
|
||||
TYPED_TEST_P(BoxDefinitionsTestGeneral, WriteModifyWrite) {
|
||||
TypeParam box;
|
||||
LOG(INFO) << "Processing " << FourCCToString(box.BoxType());
|
||||
this->Fill(&box);
|
||||
|
@ -783,7 +822,7 @@ TYPED_TEST(BoxDefinitionsTestGeneral, WriteModifyWrite) {
|
|||
ASSERT_EQ(box, box_readback);
|
||||
}
|
||||
|
||||
TYPED_TEST(BoxDefinitionsTestGeneral, Empty) {
|
||||
TYPED_TEST_P(BoxDefinitionsTestGeneral, Empty) {
|
||||
TypeParam box;
|
||||
LOG(INFO) << "Processing " << FourCCToString(box.BoxType());
|
||||
if (this->IsOptional(&box)) {
|
||||
|
@ -793,6 +832,18 @@ TYPED_TEST(BoxDefinitionsTestGeneral, Empty) {
|
|||
}
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(BoxDefinitionsTestGeneral,
|
||||
WriteReadbackCompare,
|
||||
WriteModifyWrite,
|
||||
Empty);
|
||||
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(BoxDefinitionTypedTests,
|
||||
BoxDefinitionsTestGeneral,
|
||||
Boxes);
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(BoxDefinitionTypedTests2,
|
||||
BoxDefinitionsTestGeneral,
|
||||
Boxes2);
|
||||
|
||||
// Test other cases of box input.
|
||||
class BoxDefinitionsTest : public BoxDefinitionsTestGeneral<Box> {};
|
||||
|
||||
|
|
|
@ -56,9 +56,12 @@ enum FourCC {
|
|||
FOURCC_PSSH = 0x70737368,
|
||||
FOURCC_SAIO = 0x7361696f,
|
||||
FOURCC_SAIZ = 0x7361697a,
|
||||
FOURCC_SBGP = 0x73626770,
|
||||
FOURCC_SCHI = 0x73636869,
|
||||
FOURCC_SCHM = 0x7363686d,
|
||||
FOURCC_SDTP = 0x73647470,
|
||||
FOURCC_SEIG = 0x73656967,
|
||||
FOURCC_SGPD = 0x73677064,
|
||||
FOURCC_SIDX = 0x73696478,
|
||||
FOURCC_SINF = 0x73696e66,
|
||||
FOURCC_SKIP = 0x736b6970,
|
||||
|
|
Loading…
Reference in New Issue