// 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 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(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 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 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 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(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 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 {}; 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