858 lines
25 KiB
C++
858 lines
25 KiB
C++
// Copyright (c) 2013 Google Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "media/mp4/box_definitions.h"
|
|
|
|
#include "base/memory/scoped_ptr.h"
|
|
#include "media/base/buffer_writer.h"
|
|
#include "media/mp4/box_definitions_comparison.h"
|
|
#include "media/mp4/box_reader.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace {
|
|
const uint8 kData16Bytes[] = {8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8};
|
|
const uint8 kData4[] = {1, 5, 4, 3, 15};
|
|
const uint8 kData8[] = {1, 8, 42, 98, 156};
|
|
const uint16 kData16[] = {1, 15, 45, 768, 60000};
|
|
const uint32 kData32[] = {1, 24, 99, 1234, 9000000};
|
|
const uint64 kData64[] = {1, 9000000, 12345678901234, 56780909090900};
|
|
const media::mp4::TrackType kSampleDescriptionTrackType = media::mp4::kVideo;
|
|
|
|
// 4-byte FourCC + 4-bytes size.
|
|
const uint32 kBoxSize = 8;
|
|
} // namespace
|
|
|
|
namespace media {
|
|
namespace mp4 {
|
|
|
|
template <typename T>
|
|
class BoxDefinitionsTestGeneral : public testing::Test {
|
|
public:
|
|
BoxDefinitionsTestGeneral() : buffer_(new BufferWriter) {}
|
|
|
|
BoxReader* CreateReader() {
|
|
// Create a fake skip box contains the buffer and Write it.
|
|
BufferWriter buffer;
|
|
buffer.Swap(buffer_.get());
|
|
uint32 skip_box_size = buffer.Size() + kBoxSize;
|
|
buffer_->AppendInt(skip_box_size);
|
|
buffer_->AppendInt(static_cast<uint32>(FOURCC_SKIP));
|
|
buffer_->AppendBuffer(buffer);
|
|
bool err = false;
|
|
return BoxReader::ReadTopLevelBox(buffer_->Buffer(), buffer_->Size(), &err);
|
|
}
|
|
|
|
bool ReadBack(Box* box) {
|
|
BoxReader* reader = CreateReader();
|
|
RCHECK(reader->ScanChildren() && reader->ReadChild(box));
|
|
return true;
|
|
}
|
|
|
|
// FourCC for VideoSampleEntry is not a constant, e.g. could be avc1, or encv.
|
|
bool ReadBack(VideoSampleEntry* video) {
|
|
BoxReader* reader = CreateReader();
|
|
std::vector<VideoSampleEntry> video_entries;
|
|
RCHECK(reader->ReadAllChildren(&video_entries));
|
|
RCHECK(video_entries.size() == 1);
|
|
*video = video_entries[0];
|
|
return true;
|
|
}
|
|
|
|
// FourCC for AudioSampleEntry is not a constant, e.g. could be mp4a, or enca.
|
|
bool ReadBack(AudioSampleEntry* audio) {
|
|
BoxReader* reader = CreateReader();
|
|
std::vector<AudioSampleEntry> audio_entries;
|
|
RCHECK(reader->ReadAllChildren(&audio_entries));
|
|
RCHECK(audio_entries.size() == 1);
|
|
*audio = audio_entries[0];
|
|
return true;
|
|
}
|
|
|
|
// SampleDescription cannot parse on its own. Its type parameter should
|
|
// be preset before scanning the box.
|
|
bool ReadBack(SampleDescription* stsd) {
|
|
stsd->type = kSampleDescriptionTrackType;
|
|
BoxReader* reader = CreateReader();
|
|
RCHECK(reader->ScanChildren() && reader->ReadChild(stsd));
|
|
return true;
|
|
}
|
|
|
|
// SampleTable contains SampleDescription, which cannot parse on its own.
|
|
bool ReadBack(SampleTable* stbl) {
|
|
stbl->description.type = kSampleDescriptionTrackType;
|
|
BoxReader* reader = CreateReader();
|
|
RCHECK(reader->ScanChildren() && reader->ReadChild(stbl));
|
|
return true;
|
|
}
|
|
|
|
// MediaInformation contains SampleDescription, which cannot parse on its own.
|
|
bool ReadBack(MediaInformation* minf) {
|
|
minf->sample_table.description.type = kSampleDescriptionTrackType;
|
|
BoxReader* reader = CreateReader();
|
|
RCHECK(reader->ScanChildren() && reader->ReadChild(minf));
|
|
return true;
|
|
}
|
|
|
|
// Fill the box with sample data.
|
|
void Fill(Box* box) {}
|
|
|
|
// Modify the box with another set of data.
|
|
void Modify(Box* box) {}
|
|
|
|
// Is this box optional?
|
|
bool IsOptional(Box* box) { return false; }
|
|
|
|
// Non-full box does not have version field.
|
|
uint8 GetAndClearVersion(Box* box) { return 0; }
|
|
|
|
// Get full box version and then reset it to 0.
|
|
uint8 GetAndClearVersion(FullBox* full_box) {
|
|
uint8 version = full_box->version;
|
|
full_box->version = 0;
|
|
return version;
|
|
}
|
|
|
|
void Fill(FileType* ftyp) {
|
|
ftyp->major_brand = FOURCC_DASH;
|
|
ftyp->minor_version = 567;
|
|
ftyp->compatible_brands.push_back(FOURCC_ISO6);
|
|
ftyp->compatible_brands.push_back(FOURCC_MP41);
|
|
ftyp->compatible_brands.push_back(FOURCC_AVC1);
|
|
}
|
|
|
|
void Modify(FileType* ftyp) {
|
|
ftyp->major_brand = FOURCC_MP41;
|
|
ftyp->compatible_brands.clear();
|
|
ftyp->compatible_brands.push_back(FOURCC_DASH);
|
|
}
|
|
|
|
void Fill(ProtectionSystemSpecificHeader* pssh) {
|
|
pssh->system_id.assign(kData16Bytes,
|
|
kData16Bytes + arraysize(kData16Bytes));
|
|
pssh->data.assign(kData8, kData8 + arraysize(kData8));
|
|
}
|
|
|
|
void Modify(ProtectionSystemSpecificHeader* pssh) {
|
|
pssh->system_id[2] *= 3;
|
|
pssh->data.assign(kData4, kData4 + arraysize(kData4));
|
|
}
|
|
|
|
void Fill(SampleAuxiliaryInformationOffset* saio) {
|
|
saio->offsets.assign(kData32, kData32 + arraysize(kData32));
|
|
}
|
|
|
|
void Modify(SampleAuxiliaryInformationOffset* saio) {
|
|
saio->offsets.push_back(23);
|
|
}
|
|
|
|
void Fill(SampleAuxiliaryInformationSize* saiz) {
|
|
saiz->default_sample_info_size = 0;
|
|
saiz->sample_info_sizes.assign(kData8, kData8 + arraysize(kData8));
|
|
saiz->sample_count = arraysize(kData8);
|
|
}
|
|
|
|
void Modify(SampleAuxiliaryInformationSize* saiz) {
|
|
saiz->default_sample_info_size = 15;
|
|
saiz->sample_info_sizes.clear();
|
|
}
|
|
|
|
void Fill(OriginalFormat* frma) { frma->format = FOURCC_AVC1; }
|
|
|
|
void Modify(OriginalFormat* frma) { frma->format = FOURCC_MP4A; }
|
|
|
|
void Fill(SchemeType* schm) {
|
|
schm->type = FOURCC_CENC;
|
|
schm->version = 12344;
|
|
}
|
|
|
|
void Modify(SchemeType* schm) { schm->version = 123; }
|
|
|
|
void Fill(TrackEncryption* tenc) {
|
|
tenc->is_encrypted = true;
|
|
tenc->default_iv_size = 8;
|
|
tenc->default_kid.assign(kData16Bytes,
|
|
kData16Bytes + arraysize(kData16Bytes));
|
|
}
|
|
|
|
void Modify(TrackEncryption* tenc) {
|
|
tenc->is_encrypted = false;
|
|
tenc->default_iv_size = 0;
|
|
}
|
|
|
|
void Fill(SchemeInfo* schi) { Fill(&schi->track_encryption); }
|
|
|
|
void Modify(SchemeInfo* schi) { Modify(&schi->track_encryption); }
|
|
|
|
void Fill(ProtectionSchemeInfo* sinf) {
|
|
Fill(&sinf->format);
|
|
Fill(&sinf->type);
|
|
Fill(&sinf->info);
|
|
}
|
|
|
|
void Modify(ProtectionSchemeInfo* sinf) {
|
|
Modify(&sinf->type);
|
|
Modify(&sinf->info);
|
|
}
|
|
|
|
void Fill(MovieHeader* mvhd) {
|
|
mvhd->creation_time = 1234;
|
|
mvhd->modification_time = 2456;
|
|
mvhd->timescale = 48000;
|
|
mvhd->duration = 96000;
|
|
mvhd->rate = 0x010000;
|
|
mvhd->volume = 0x0100;
|
|
mvhd->next_track_id = 1;
|
|
mvhd->version = 0;
|
|
}
|
|
|
|
void Modify(MovieHeader* mvhd) {
|
|
mvhd->duration = 234141324123;
|
|
mvhd->next_track_id = 3;
|
|
mvhd->version = 1;
|
|
}
|
|
|
|
void Fill(TrackHeader* tkhd) {
|
|
tkhd->creation_time = 34523443;
|
|
tkhd->modification_time = 34533443;
|
|
tkhd->track_id = 2;
|
|
tkhd->duration = 96000;
|
|
tkhd->layer = 1;
|
|
tkhd->alternate_group = 2;
|
|
tkhd->volume = 0;
|
|
tkhd->width = 800;
|
|
tkhd->height = 600;
|
|
tkhd->version = 0;
|
|
}
|
|
|
|
void Modify(TrackHeader* tkhd) {
|
|
tkhd->modification_time = 345388873443;
|
|
tkhd->volume = 0x0100;
|
|
tkhd->width = 0;
|
|
tkhd->height = 0;
|
|
tkhd->version = 1;
|
|
}
|
|
|
|
void Fill(EditList* elst) {
|
|
elst->edits.resize(2);
|
|
elst->edits[0].segment_duration = 100;
|
|
elst->edits[0].media_time = -1;
|
|
elst->edits[0].media_rate_integer = 1;
|
|
elst->edits[0].media_rate_fraction = 0;
|
|
elst->edits[1].segment_duration = 300;
|
|
elst->edits[1].media_time = 0;
|
|
elst->edits[1].media_rate_integer = 1;
|
|
elst->edits[1].media_rate_fraction = 0;
|
|
elst->version = 0;
|
|
}
|
|
|
|
void Modify(EditList* elst) {
|
|
elst->edits.resize(1);
|
|
elst->edits[0].segment_duration = 0;
|
|
elst->edits[0].media_time = 20364563456;
|
|
elst->version = 1;
|
|
}
|
|
|
|
void Fill(Edit* edts) { Fill(&edts->list); }
|
|
|
|
void Modify(Edit* edts) { Modify(&edts->list); }
|
|
|
|
void Fill(HandlerReference* hdlr) {
|
|
hdlr->type = kSampleDescriptionTrackType;
|
|
}
|
|
|
|
void Modify(HandlerReference* hdlr) { hdlr->type = kAudio; }
|
|
|
|
void Fill(PixelAspectRatioBox* pasp) {
|
|
pasp->h_spacing = 5;
|
|
pasp->v_spacing = 8;
|
|
}
|
|
|
|
void Modify(PixelAspectRatioBox* pasp) { pasp->v_spacing *= 8; }
|
|
|
|
void Fill(AVCDecoderConfigurationRecord* avcc) {
|
|
const uint8 kAvccData[] = {
|
|
0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x18, 0x67, 0x64, 0x00,
|
|
0x1f, 0xac, 0xd9, 0x40, 0x50, 0x05, 0xbb, 0x01, 0x10, 0x00, 0x00,
|
|
0x3e, 0x90, 0x00, 0x0e, 0xa6, 0x00, 0xf1, 0x83, 0x19, 0x60, 0x01,
|
|
0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0};
|
|
BufferReader buffer_reader(kAvccData, arraysize(kAvccData));
|
|
CHECK(avcc->ParseData(&buffer_reader));
|
|
avcc->data.assign(kAvccData, kAvccData + arraysize(kAvccData));
|
|
}
|
|
|
|
void Modify(AVCDecoderConfigurationRecord* avcc) {
|
|
const uint8 kAvccData[] = {
|
|
0x01, 0x64, 0x00, 0x1e, 0xff, 0xe1, 0x00, 0x19, 0x67, 0x64, 0x00,
|
|
0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00,
|
|
0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96,
|
|
0x01, 0x00, 0x05, 0x68, 0xeb, 0xec, 0xb2, 0x2c};
|
|
BufferReader buffer_reader(kAvccData, arraysize(kAvccData));
|
|
CHECK(avcc->ParseData(&buffer_reader));
|
|
avcc->data.assign(kAvccData, kAvccData + arraysize(kAvccData));
|
|
}
|
|
|
|
void Fill(VideoSampleEntry* encv) {
|
|
encv->format = FOURCC_ENCV;
|
|
encv->data_reference_index = 1;
|
|
encv->width = 800;
|
|
encv->height = 600;
|
|
Fill(&encv->pixel_aspect);
|
|
Fill(&encv->sinf);
|
|
Fill(&encv->avcc);
|
|
}
|
|
|
|
void Modify(VideoSampleEntry* encv) {
|
|
encv->height += 600;
|
|
Modify(&encv->avcc);
|
|
}
|
|
|
|
void Fill(ElementaryStreamDescriptor* esds) {
|
|
const uint8 kDecoderSpecificInfo[] = {18, 16};
|
|
esds->es_descriptor.set_esid(1);
|
|
esds->es_descriptor.set_object_type(kISO_14496_3);
|
|
std::vector<uint8> decoder_specific_info(
|
|
kDecoderSpecificInfo,
|
|
kDecoderSpecificInfo + sizeof(kDecoderSpecificInfo));
|
|
esds->es_descriptor.set_decoder_specific_info(decoder_specific_info);
|
|
}
|
|
|
|
void Modify(ElementaryStreamDescriptor* esds) {
|
|
esds->es_descriptor.set_esid(2);
|
|
}
|
|
|
|
void Fill(AudioSampleEntry* enca) {
|
|
enca->format = FOURCC_ENCA;
|
|
enca->data_reference_index = 2;
|
|
enca->channelcount = 5;
|
|
enca->samplesize = 16;
|
|
enca->samplerate = 44100;
|
|
Fill(&enca->sinf);
|
|
Fill(&enca->esds);
|
|
}
|
|
|
|
void Modify(AudioSampleEntry* enca) { enca->channelcount = 2; }
|
|
|
|
void Fill(SampleDescription* stsd) {
|
|
stsd->type = kSampleDescriptionTrackType;
|
|
stsd->video_entries.resize(1);
|
|
Fill(&stsd->video_entries[0]);
|
|
}
|
|
|
|
void Fill(DecodingTimeToSample* stts) {
|
|
stts->decoding_time.resize(2);
|
|
stts->decoding_time[0].sample_count = 3;
|
|
stts->decoding_time[0].sample_delta = 5;
|
|
stts->decoding_time[1].sample_count = 2;
|
|
stts->decoding_time[1].sample_delta = 9;
|
|
}
|
|
|
|
void Modify(DecodingTimeToSample* stts) {
|
|
stts->decoding_time.resize(3);
|
|
stts->decoding_time[2].sample_count = 9;
|
|
stts->decoding_time[2].sample_delta = 4;
|
|
}
|
|
|
|
void Fill(CompositionTimeToSample* ctts) {
|
|
ctts->composition_offset.resize(2);
|
|
ctts->composition_offset[0].sample_count = 3;
|
|
ctts->composition_offset[0].sample_offset = 5;
|
|
ctts->composition_offset[1].sample_count = 2;
|
|
ctts->composition_offset[1].sample_offset = 9;
|
|
ctts->version = 1;
|
|
}
|
|
|
|
void Modify(CompositionTimeToSample* ctts) {
|
|
ctts->composition_offset.resize(1);
|
|
ctts->composition_offset[0].sample_count = 6;
|
|
ctts->composition_offset[0].sample_offset = 1;
|
|
ctts->version = 1;
|
|
}
|
|
|
|
void Fill(SampleToChunk* stsc) {
|
|
stsc->chunk_info.resize(2);
|
|
stsc->chunk_info[0].first_chunk = 1;
|
|
stsc->chunk_info[0].samples_per_chunk = 5;
|
|
stsc->chunk_info[0].sample_description_index = 0;
|
|
stsc->chunk_info[1].first_chunk = 5;
|
|
stsc->chunk_info[1].samples_per_chunk = 2;
|
|
stsc->chunk_info[1].sample_description_index = 1;
|
|
}
|
|
|
|
void Modify(SampleToChunk* stsc) {
|
|
stsc->chunk_info.resize(4);
|
|
stsc->chunk_info[2].first_chunk = 7;
|
|
stsc->chunk_info[2].samples_per_chunk = 8;
|
|
stsc->chunk_info[2].sample_description_index = 1;
|
|
stsc->chunk_info[3].first_chunk = 9;
|
|
stsc->chunk_info[3].samples_per_chunk = 12;
|
|
stsc->chunk_info[3].sample_description_index = 0;
|
|
}
|
|
|
|
void Fill(SampleSize* stsz) {
|
|
stsz->sample_size = 0;
|
|
stsz->sizes.assign(kData8, kData8 + arraysize(kData8));
|
|
stsz->sample_count = arraysize(kData8);
|
|
}
|
|
|
|
void Modify(SampleSize* stsz) {
|
|
stsz->sample_size = 35;
|
|
stsz->sizes.clear();
|
|
}
|
|
|
|
void Fill(CompactSampleSize* stz2) {
|
|
stz2->field_size = 4;
|
|
stz2->sizes.assign(kData4, kData4 + arraysize(kData4));
|
|
}
|
|
|
|
void Modify(CompactSampleSize* stz2) {
|
|
stz2->field_size = 8;
|
|
stz2->sizes.assign(kData8, kData8 + arraysize(kData8));
|
|
}
|
|
|
|
void Fill(ChunkLargeOffset* co64) {
|
|
co64->offsets.assign(kData64, kData64 + arraysize(kData64));
|
|
}
|
|
|
|
void Modify(ChunkLargeOffset* co64) { co64->offsets.pop_back(); }
|
|
|
|
void Fill(ChunkOffset* stco) {
|
|
stco->offsets.assign(kData32, kData32 + arraysize(kData32));
|
|
}
|
|
|
|
void Modify(ChunkOffset* stco) { stco->offsets.push_back(10); }
|
|
|
|
void Fill(SyncSample* stss) {
|
|
stss->sample_number.assign(kData32, kData32 + arraysize(kData32));
|
|
}
|
|
|
|
void Modify(SyncSample* stss) { stss->sample_number.pop_back(); }
|
|
|
|
void Fill(SampleTable* stbl) {
|
|
Fill(&stbl->description);
|
|
Fill(&stbl->decoding_time_to_sample);
|
|
Fill(&stbl->composition_time_to_sample);
|
|
Fill(&stbl->sample_to_chunk);
|
|
Fill(&stbl->sample_size);
|
|
Fill(&stbl->chunk_large_offset);
|
|
Fill(&stbl->sync_sample);
|
|
}
|
|
|
|
void Modify(SampleTable* stbl) {
|
|
Modify(&stbl->chunk_large_offset);
|
|
Modify(&stbl->sync_sample);
|
|
}
|
|
|
|
void Fill(MediaHeader* mdhd) {
|
|
mdhd->creation_time = 124231432;
|
|
mdhd->modification_time = static_cast<uint64>(kuint32max) + 1;
|
|
mdhd->timescale = 50000;
|
|
mdhd->duration = 250000;
|
|
strcpy(mdhd->language, "abc");
|
|
mdhd->version = 1;
|
|
}
|
|
|
|
void Modify(MediaHeader* mdhd) {
|
|
mdhd->creation_time = 2;
|
|
mdhd->modification_time = kuint32max;
|
|
strcpy(mdhd->language, "und");
|
|
mdhd->version = 0;
|
|
}
|
|
|
|
void Fill(VideoMediaHeader* vmhd) {
|
|
vmhd->graphicsmode = 4123;
|
|
vmhd->opcolor_red = 323;
|
|
vmhd->opcolor_green = 2135;
|
|
vmhd->opcolor_blue = 2387;
|
|
}
|
|
|
|
void Modify(VideoMediaHeader* vmhd) { vmhd->graphicsmode *= 2; }
|
|
|
|
void Fill(SoundMediaHeader* smhd) { smhd->balance = 8762; }
|
|
|
|
void Modify(SoundMediaHeader* smhd) { smhd->balance /= 2; }
|
|
|
|
void Fill(DataEntryUrl* url) {
|
|
url->flags = 2;
|
|
url->location.assign(kData8, kData8 + arraysize(kData8));
|
|
}
|
|
|
|
void Modify(DataEntryUrl* url) {
|
|
url->flags += 1;
|
|
url->location.assign(kData4, kData4 + arraysize(kData4));
|
|
}
|
|
|
|
void Fill(DataReference* dref) {
|
|
dref->data_entry.resize(2);
|
|
Fill(&dref->data_entry[0]);
|
|
Fill(&dref->data_entry[1]);
|
|
dref->data_entry[1].location.assign(kData4, kData4 + arraysize(kData4));
|
|
}
|
|
|
|
void Modify(DataReference* dref) {
|
|
dref->data_entry.resize(3);
|
|
Fill(&dref->data_entry[2]);
|
|
dref->data_entry[2].location.push_back(100);
|
|
}
|
|
|
|
void Fill(DataInformation* dinf) { Fill(&dinf->dref); }
|
|
|
|
void Modify(DataInformation* dinf) { Modify(&dinf->dref); }
|
|
|
|
void Fill(MediaInformation* minf) {
|
|
Fill(&minf->dinf);
|
|
Fill(&minf->sample_table);
|
|
Fill(&minf->vmhd);
|
|
}
|
|
|
|
void Modify(MediaInformation* minf) {
|
|
Modify(&minf->dinf);
|
|
Modify(&minf->sample_table);
|
|
}
|
|
|
|
void Fill(Media* mdia) {
|
|
Fill(&mdia->header);
|
|
Fill(&mdia->handler);
|
|
Fill(&mdia->information);
|
|
}
|
|
|
|
void Modify(Media* mdia) { Modify(&mdia->information); }
|
|
|
|
void Fill(Track* trak) {
|
|
Fill(&trak->header);
|
|
Fill(&trak->media);
|
|
Fill(&trak->edit);
|
|
}
|
|
|
|
void Modify(Track* trak) { Modify(&trak->media); }
|
|
|
|
void Fill(MovieExtendsHeader* mehd) {
|
|
mehd->fragment_duration = 23489038090;
|
|
mehd->version = 1;
|
|
}
|
|
|
|
void Modify(MovieExtendsHeader* mehd) {
|
|
mehd->fragment_duration = 123456;
|
|
mehd->version = 0;
|
|
}
|
|
|
|
void Fill(TrackExtends* trex) {
|
|
trex->track_id = 2;
|
|
trex->default_sample_description_index = 3;
|
|
trex->default_sample_duration = 832;
|
|
trex->default_sample_size = 89723;
|
|
trex->default_sample_flags = 12;
|
|
}
|
|
|
|
void Modify(TrackExtends* trex) { trex->default_sample_size = 543; }
|
|
|
|
void Fill(MovieExtends* mvex) {
|
|
Fill(&mvex->header);
|
|
mvex->tracks.resize(2);
|
|
Fill(&mvex->tracks[0]);
|
|
mvex->tracks[1].track_id = 1;
|
|
mvex->tracks[1].default_sample_description_index = 13;
|
|
mvex->tracks[1].default_sample_duration = 97687;
|
|
mvex->tracks[1].default_sample_size = 1232;
|
|
mvex->tracks[1].default_sample_flags = 6;
|
|
}
|
|
|
|
void Modify(MovieExtends* mvex) { mvex->tracks.resize(1); }
|
|
|
|
void Fill(Movie* moov) {
|
|
Fill(&moov->header);
|
|
Fill(&moov->extends);
|
|
moov->tracks.resize(2);
|
|
Fill(&moov->tracks[0]);
|
|
Fill(&moov->tracks[1]);
|
|
}
|
|
|
|
void Modify(Movie* moov) { moov->tracks.resize(1); }
|
|
|
|
void Fill(TrackFragmentDecodeTime* tfdt) {
|
|
tfdt->decode_time = 234029673820;
|
|
tfdt->version = 1;
|
|
}
|
|
|
|
void Modify(TrackFragmentDecodeTime* tfdt) {
|
|
tfdt->decode_time = 4567;
|
|
tfdt->version = 0;
|
|
}
|
|
|
|
void Fill(MovieFragmentHeader* mfhd) { mfhd->sequence_number = 23235; }
|
|
|
|
void Modify(MovieFragmentHeader* mfhd) { mfhd->sequence_number = 67890; }
|
|
|
|
void Fill(TrackFragmentHeader* tfhd) {
|
|
tfhd->flags = TrackFragmentHeader::kSampleDescriptionIndexPresentMask |
|
|
TrackFragmentHeader::kDefaultSampleDurationPresentMask |
|
|
TrackFragmentHeader::kDefaultSampleSizePresentMask |
|
|
TrackFragmentHeader::kDefaultSampleFlagsPresentMask;
|
|
tfhd->track_id = 1;
|
|
tfhd->sample_description_index = 233;
|
|
tfhd->default_sample_duration = 42545;
|
|
tfhd->default_sample_size = 8765;
|
|
tfhd->default_sample_flags = 65;
|
|
}
|
|
|
|
void Modify(TrackFragmentHeader* tfhd) { tfhd->default_sample_size = 888; }
|
|
|
|
void Fill(TrackFragmentRun* trun) {
|
|
trun->flags = TrackFragmentRun::kDataOffsetPresentMask |
|
|
TrackFragmentRun::kSampleDurationPresentMask |
|
|
TrackFragmentRun::kSampleSizePresentMask |
|
|
TrackFragmentRun::kSampleFlagsPresentMask |
|
|
TrackFragmentRun::kSampleCompTimeOffsetsPresentMask;
|
|
trun->data_offset = 783246;
|
|
trun->sample_count = arraysize(kData32);
|
|
trun->sample_flags.assign(kData32, kData32 + arraysize(kData32));
|
|
trun->sample_sizes = trun->sample_flags;
|
|
trun->sample_sizes[0] += 1000;
|
|
trun->sample_durations = trun->sample_flags;
|
|
trun->sample_durations[1] += 2343;
|
|
trun->sample_composition_time_offsets.assign(kData32,
|
|
kData32 + arraysize(kData32));
|
|
trun->sample_composition_time_offsets[2] -= 89782;
|
|
trun->version = 1;
|
|
}
|
|
|
|
void Modify(TrackFragmentRun* trun) {
|
|
trun->flags |= TrackFragmentRun::kFirstSampleFlagsPresentMask;
|
|
trun->flags &= ~TrackFragmentRun::kSampleFlagsPresentMask;
|
|
trun->sample_flags.resize(1);
|
|
trun->version = 1;
|
|
}
|
|
|
|
void Fill(TrackFragment* traf) {
|
|
Fill(&traf->header);
|
|
traf->runs.resize(1);
|
|
Fill(&traf->runs[0]);
|
|
Fill(&traf->decode_time);
|
|
Fill(&traf->auxiliary_offset);
|
|
Fill(&traf->auxiliary_size);
|
|
}
|
|
|
|
void Modify(TrackFragment* traf) {
|
|
Modify(&traf->header);
|
|
Modify(&traf->decode_time);
|
|
}
|
|
|
|
void Fill(MovieFragment* moof) {
|
|
Fill(&moof->header);
|
|
moof->tracks.resize(1);
|
|
Fill(&moof->tracks[0]);
|
|
}
|
|
|
|
void Modify(MovieFragment* moof) {
|
|
moof->tracks.resize(2);
|
|
Fill(&moof->tracks[1]);
|
|
Modify(&moof->tracks[1]);
|
|
}
|
|
|
|
void Fill(SegmentIndex* sidx) {
|
|
sidx->reference_id = 3;
|
|
sidx->timescale = 56700;
|
|
sidx->earliest_presentation_time = 234;
|
|
sidx->first_offset = 876223;
|
|
sidx->references.resize(2);
|
|
sidx->references[0].reference_type = true;
|
|
sidx->references[0].referenced_size = 23424;
|
|
sidx->references[0].subsegment_duration = 9083423;
|
|
sidx->references[0].starts_with_sap = true;
|
|
sidx->references[0].sap_type = SegmentReference::Type1;
|
|
sidx->references[0].sap_delta_time = 2382;
|
|
sidx->references[1].reference_type = false;
|
|
sidx->references[1].referenced_size = 34572;
|
|
sidx->references[1].subsegment_duration = 7234323;
|
|
sidx->references[1].starts_with_sap = false;
|
|
sidx->references[1].sap_type = SegmentReference::Type5;
|
|
sidx->references[1].sap_delta_time = 53;
|
|
sidx->version = 0;
|
|
}
|
|
|
|
void Modify(SegmentIndex* sidx) {
|
|
sidx->earliest_presentation_time = 2348677865434;
|
|
sidx->references.resize(3);
|
|
sidx->references[2] = sidx->references[1];
|
|
sidx->references[2].subsegment_duration = 87662;
|
|
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; }
|
|
|
|
protected:
|
|
scoped_ptr<BufferWriter> buffer_;
|
|
};
|
|
|
|
typedef testing::Types<
|
|
FileType,
|
|
SegmentType,
|
|
// ProtectionSystemSpecificHeader, // Not fully parsed.
|
|
SampleAuxiliaryInformationOffset,
|
|
SampleAuxiliaryInformationSize,
|
|
OriginalFormat,
|
|
SchemeType,
|
|
TrackEncryption,
|
|
SchemeInfo,
|
|
ProtectionSchemeInfo,
|
|
MovieHeader,
|
|
TrackHeader,
|
|
EditList,
|
|
Edit,
|
|
HandlerReference,
|
|
AVCDecoderConfigurationRecord,
|
|
PixelAspectRatioBox,
|
|
VideoSampleEntry,
|
|
ElementaryStreamDescriptor,
|
|
AudioSampleEntry,
|
|
SampleDescription,
|
|
DecodingTimeToSample,
|
|
CompositionTimeToSample,
|
|
SampleToChunk,
|
|
SampleSize,
|
|
CompactSampleSize,
|
|
ChunkLargeOffset,
|
|
ChunkOffset,
|
|
SyncSample,
|
|
SampleTable,
|
|
MediaHeader,
|
|
VideoMediaHeader,
|
|
SoundMediaHeader,
|
|
DataEntryUrl,
|
|
DataReference,
|
|
DataInformation,
|
|
MediaInformation,
|
|
Media,
|
|
Track,
|
|
MovieExtendsHeader,
|
|
TrackExtends,
|
|
MovieExtends,
|
|
Movie,
|
|
TrackFragmentDecodeTime,
|
|
MovieFragmentHeader,
|
|
TrackFragmentHeader,
|
|
TrackFragmentRun,
|
|
TrackFragment,
|
|
MovieFragment,
|
|
SegmentIndex> Boxes;
|
|
|
|
TYPED_TEST_CASE(BoxDefinitionsTestGeneral, Boxes);
|
|
|
|
TYPED_TEST(BoxDefinitionsTestGeneral, WriteReadbackCompare) {
|
|
TypeParam box;
|
|
LOG(INFO) << "Processing " << FourCCToString(box.BoxType());
|
|
Fill(&box);
|
|
box.Write(this->buffer_.get());
|
|
|
|
TypeParam box_readback;
|
|
ASSERT_TRUE(ReadBack(&box_readback));
|
|
ASSERT_EQ(box, box_readback);
|
|
}
|
|
|
|
TYPED_TEST(BoxDefinitionsTestGeneral, WriteModifyWrite) {
|
|
TypeParam box;
|
|
LOG(INFO) << "Processing " << FourCCToString(box.BoxType());
|
|
Fill(&box);
|
|
// Save the expected version set earlier in function |Fill|, then clear
|
|
// the version, expecting box.Write set version as expected.
|
|
uint8 version = GetAndClearVersion(&box);
|
|
box.Write(this->buffer_.get());
|
|
EXPECT_EQ(version, GetAndClearVersion(&box));
|
|
|
|
this->buffer_->Clear();
|
|
Modify(&box);
|
|
version = GetAndClearVersion(&box);
|
|
box.Write(this->buffer_.get());
|
|
EXPECT_EQ(version, GetAndClearVersion(&box));
|
|
|
|
TypeParam box_readback;
|
|
ASSERT_TRUE(ReadBack(&box_readback));
|
|
ASSERT_EQ(box, box_readback);
|
|
}
|
|
|
|
TYPED_TEST(BoxDefinitionsTestGeneral, Empty) {
|
|
TypeParam box;
|
|
LOG(INFO) << "Processing " << FourCCToString(box.BoxType());
|
|
if (IsOptional(&box)) {
|
|
ASSERT_EQ(0, box.ComputeSize());
|
|
} else {
|
|
ASSERT_NE(0, box.ComputeSize());
|
|
}
|
|
}
|
|
|
|
// Test other cases of box input.
|
|
class BoxDefinitionsTest : public BoxDefinitionsTestGeneral<Box> {};
|
|
|
|
TEST_F(BoxDefinitionsTest, ProtectionSystemSpecificHeader) {
|
|
ProtectionSystemSpecificHeader pssh;
|
|
Fill(&pssh);
|
|
pssh.Write(this->buffer_.get());
|
|
|
|
ProtectionSystemSpecificHeader pssh_readback;
|
|
ASSERT_TRUE(ReadBack(&pssh_readback));
|
|
// PSSH does not parse data.
|
|
ASSERT_EQ(pssh.system_id, pssh_readback.system_id);
|
|
}
|
|
|
|
TEST_F(BoxDefinitionsTest, CompactSampleSize_FieldSize16) {
|
|
CompactSampleSize stz2;
|
|
stz2.field_size = 16;
|
|
stz2.sizes.assign(kData16, kData16 + arraysize(kData16));
|
|
stz2.Write(this->buffer_.get());
|
|
|
|
CompactSampleSize stz2_readback;
|
|
ASSERT_TRUE(ReadBack(&stz2_readback));
|
|
ASSERT_EQ(stz2, stz2_readback);
|
|
}
|
|
|
|
TEST_F(BoxDefinitionsTest, ChunkLargeOffsetSmallOffset) {
|
|
ChunkLargeOffset co64;
|
|
co64.offsets.assign(kData32, kData32 + arraysize(kData32));
|
|
co64.Write(this->buffer_.get());
|
|
|
|
// The data is stored in ChunkOffset box instead.
|
|
ChunkOffset stco;
|
|
ASSERT_TRUE(ReadBack(&stco));
|
|
ASSERT_EQ(co64, stco);
|
|
}
|
|
|
|
TEST_F(BoxDefinitionsTest, TrackFragmentHeader_NoSampleSize) {
|
|
TrackFragmentHeader tfhd;
|
|
Fill(&tfhd);
|
|
tfhd.flags &= ~TrackFragmentHeader::kDefaultSampleSizePresentMask;
|
|
tfhd.Write(this->buffer_.get());
|
|
|
|
TrackFragmentHeader tfhd_readback;
|
|
ASSERT_TRUE(ReadBack(&tfhd_readback));
|
|
EXPECT_EQ(0, tfhd_readback.default_sample_size);
|
|
tfhd.default_sample_size = 0;
|
|
ASSERT_EQ(tfhd, tfhd_readback);
|
|
}
|
|
|
|
TEST_F(BoxDefinitionsTest, TrackFragmentRun_NoSampleSize) {
|
|
TrackFragmentRun trun;
|
|
Fill(&trun);
|
|
trun.flags &= ~TrackFragmentRun::kSampleSizePresentMask;
|
|
trun.Write(this->buffer_.get());
|
|
|
|
TrackFragmentRun trun_readback;
|
|
ASSERT_TRUE(ReadBack(&trun_readback));
|
|
EXPECT_TRUE(trun_readback.sample_sizes.empty());
|
|
trun.sample_sizes.clear();
|
|
ASSERT_EQ(trun, trun_readback);
|
|
}
|
|
|
|
} // namespace mp4
|
|
} // namespace media
|