MP4 Box unittest.

Change-Id: I1ed9b0108f5da4c52a3e33514ac4fd952f6616d0
This commit is contained in:
Kongqun Yang 2013-11-26 18:40:36 -08:00
parent 57474b31d6
commit 636baf9b21
2 changed files with 1194 additions and 0 deletions

View File

@ -0,0 +1,337 @@
// 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.
//
// Overloads operator== for mp4 boxes, mainly used for testing.
#ifndef MEDIA_MP4_BOX_DEFINITIONS_COMPARISON_H_
#define MEDIA_MP4_BOX_DEFINITIONS_COMPARISON_H_
#include "media/mp4/box_definitions.h"
namespace media {
namespace mp4 {
inline bool operator==(const FileType& lhs, const FileType& rhs) {
return lhs.major_brand == rhs.major_brand &&
lhs.minor_version == rhs.minor_version &&
lhs.compatible_brands == rhs.compatible_brands;
}
inline bool operator==(const ProtectionSystemSpecificHeader& lhs,
const ProtectionSystemSpecificHeader& rhs) {
return lhs.system_id == rhs.system_id && lhs.data == rhs.data;
}
inline bool operator==(const SampleAuxiliaryInformationOffset& lhs,
const SampleAuxiliaryInformationOffset& rhs) {
return lhs.offsets == rhs.offsets;
}
inline bool operator==(const SampleAuxiliaryInformationSize& lhs,
const SampleAuxiliaryInformationSize& rhs) {
return lhs.default_sample_info_size == rhs.default_sample_info_size &&
lhs.sample_count == rhs.sample_count &&
lhs.sample_info_sizes == rhs.sample_info_sizes;
}
inline bool operator==(const OriginalFormat& lhs, const OriginalFormat& rhs) {
return lhs.format == rhs.format;
}
inline bool operator==(const SchemeType& lhs, const SchemeType& rhs) {
return lhs.type == rhs.type && lhs.version == rhs.version;
}
inline bool operator==(const TrackEncryption& lhs, const TrackEncryption& rhs) {
return lhs.is_encrypted == rhs.is_encrypted &&
lhs.default_iv_size == rhs.default_iv_size &&
lhs.default_kid == rhs.default_kid;
}
inline bool operator==(const SchemeInfo& lhs, const SchemeInfo& rhs) {
return lhs.track_encryption == rhs.track_encryption;
}
inline bool operator==(const ProtectionSchemeInfo& lhs,
const ProtectionSchemeInfo& rhs) {
return lhs.format == rhs.format && lhs.type == rhs.type &&
lhs.info == rhs.info;
}
inline bool operator==(const MovieHeader& lhs, const MovieHeader& rhs) {
return lhs.creation_time == rhs.creation_time &&
lhs.modification_time == rhs.modification_time &&
lhs.timescale == rhs.timescale && lhs.duration == rhs.duration &&
lhs.rate == rhs.rate && lhs.volume == rhs.volume &&
lhs.next_track_id == rhs.next_track_id;
}
inline bool operator==(const TrackHeader& lhs, const TrackHeader& rhs) {
return lhs.creation_time == rhs.creation_time &&
lhs.modification_time == rhs.modification_time &&
lhs.track_id == rhs.track_id && lhs.duration == rhs.duration &&
lhs.layer == rhs.layer && lhs.alternate_group == rhs.alternate_group &&
lhs.volume == rhs.volume && lhs.width == rhs.width &&
lhs.height == rhs.height;
}
inline bool operator==(const SampleDescription& lhs,
const SampleDescription& rhs) {
return lhs.type == rhs.type && lhs.video_entries == rhs.video_entries &&
lhs.audio_entries == rhs.audio_entries;
}
inline bool operator==(const DecodingTime& lhs, const DecodingTime& rhs) {
return lhs.sample_count == rhs.sample_count &&
lhs.sample_delta == rhs.sample_delta;
}
inline bool operator==(const DecodingTimeToSample& lhs,
const DecodingTimeToSample& rhs) {
return lhs.decoding_time == rhs.decoding_time;
}
inline bool operator==(const CompositionOffset& lhs,
const CompositionOffset& rhs) {
return lhs.sample_count == rhs.sample_count &&
lhs.sample_offset == rhs.sample_offset;
}
inline bool operator==(const CompositionTimeToSample& lhs,
const CompositionTimeToSample& rhs) {
return lhs.composition_offset == rhs.composition_offset;
}
inline bool operator==(const ChunkInfo& lhs, const ChunkInfo& rhs) {
return lhs.first_chunk == rhs.first_chunk &&
lhs.samples_per_chunk == rhs.samples_per_chunk &&
lhs.sample_description_index == rhs.sample_description_index;
}
inline bool operator==(const SampleToChunk& lhs, const SampleToChunk& rhs) {
return lhs.chunk_info == rhs.chunk_info;
}
inline bool operator==(const SampleSize& lhs, const SampleSize& rhs) {
return lhs.sample_size == rhs.sample_size &&
lhs.sample_count == rhs.sample_count && lhs.sizes == rhs.sizes;
}
inline bool operator==(const CompactSampleSize& lhs,
const CompactSampleSize& rhs) {
return lhs.field_size == rhs.field_size && lhs.sizes == rhs.sizes;
}
inline bool operator==(const ChunkLargeOffset& lhs,
const ChunkLargeOffset& rhs) {
return lhs.offsets == rhs.offsets;
}
inline bool operator==(const SyncSample& lhs, const SyncSample& rhs) {
return lhs.sample_number == rhs.sample_number;
}
inline bool operator==(const SampleTable& lhs, const SampleTable& rhs) {
return lhs.description == rhs.description &&
lhs.decoding_time_to_sample == rhs.decoding_time_to_sample &&
lhs.composition_time_to_sample == rhs.composition_time_to_sample &&
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;
}
inline bool operator==(const EditListEntry& lhs, const EditListEntry& rhs) {
return lhs.segment_duration == rhs.segment_duration &&
lhs.media_time == rhs.media_time &&
lhs.media_rate_integer == rhs.media_rate_integer &&
lhs.media_rate_fraction == rhs.media_rate_fraction;
}
inline bool operator==(const EditList& lhs, const EditList& rhs) {
return lhs.edits == rhs.edits;
}
inline bool operator==(const Edit& lhs, const Edit& rhs) {
return lhs.list == rhs.list;
}
inline bool operator==(const HandlerReference& lhs,
const HandlerReference& rhs) {
return lhs.type == rhs.type;
}
inline bool operator==(const AVCDecoderConfigurationRecord& lhs,
const AVCDecoderConfigurationRecord& rhs) {
return lhs.data == rhs.data;
}
inline bool operator==(const PixelAspectRatioBox& lhs,
const PixelAspectRatioBox& rhs) {
return lhs.h_spacing == rhs.h_spacing && lhs.v_spacing == rhs.v_spacing;
}
inline bool operator==(const VideoSampleEntry& lhs,
const VideoSampleEntry& rhs) {
return lhs.format == rhs.format &&
lhs.data_reference_index == rhs.data_reference_index &&
lhs.width == rhs.width && lhs.height == rhs.height &&
lhs.pixel_aspect == rhs.pixel_aspect && lhs.sinf == rhs.sinf &&
lhs.avcc == rhs.avcc;
}
inline bool operator==(const ESDescriptor& lhs, const ESDescriptor& rhs) {
return lhs.esid() == rhs.esid() && lhs.object_type() == rhs.object_type() &&
lhs.decoder_specific_info() == rhs.decoder_specific_info();
}
inline bool operator==(const ElementaryStreamDescriptor& lhs,
const ElementaryStreamDescriptor& rhs) {
return lhs.es_descriptor == rhs.es_descriptor;
}
inline bool operator==(const AudioSampleEntry& lhs,
const AudioSampleEntry& rhs) {
return lhs.format == rhs.format &&
lhs.data_reference_index == rhs.data_reference_index &&
lhs.channelcount == rhs.channelcount &&
lhs.samplesize == rhs.samplesize && lhs.samplerate == rhs.samplerate &&
lhs.sinf == rhs.sinf && lhs.esds == rhs.esds;
}
inline bool operator==(const MediaHeader& lhs, const MediaHeader& rhs) {
return lhs.creation_time == rhs.creation_time &&
lhs.modification_time == rhs.modification_time &&
lhs.timescale == rhs.timescale && lhs.duration == rhs.duration &&
strcmp(lhs.language, rhs.language) == 0;
}
inline bool operator==(const VideoMediaHeader& lhs,
const VideoMediaHeader& rhs) {
return lhs.graphicsmode == rhs.graphicsmode &&
lhs.opcolor_red == rhs.opcolor_red &&
lhs.opcolor_green == rhs.opcolor_green &&
lhs.opcolor_blue == rhs.opcolor_blue;
}
inline bool operator==(const SoundMediaHeader& lhs,
const SoundMediaHeader& rhs) {
return lhs.balance == rhs.balance;
}
inline bool operator==(const DataEntryUrl& lhs, const DataEntryUrl& rhs) {
return lhs.flags == rhs.flags && lhs.location == rhs.location;
}
inline bool operator==(const DataReference& lhs, const DataReference& rhs) {
return lhs.data_entry == rhs.data_entry;
}
inline bool operator==(const DataInformation& lhs, const DataInformation& rhs) {
return lhs.dref == rhs.dref;
}
inline bool operator==(const MediaInformation& lhs,
const MediaInformation& rhs) {
return lhs.dinf == rhs.dinf && lhs.sample_table == rhs.sample_table &&
lhs.vmhd == rhs.vmhd && lhs.smhd == rhs.smhd;
}
inline bool operator==(const Media& lhs, const Media& rhs) {
return lhs.header == rhs.header && lhs.handler == rhs.handler &&
lhs.information == rhs.information;
}
inline bool operator==(const Track& lhs, const Track& rhs) {
return lhs.header == rhs.header && lhs.media == rhs.media &&
lhs.edit == rhs.edit;
}
inline bool operator==(const MovieExtendsHeader& lhs,
const MovieExtendsHeader& rhs) {
return lhs.fragment_duration == rhs.fragment_duration;
}
inline bool operator==(const TrackExtends& lhs, const TrackExtends& rhs) {
return lhs.track_id == rhs.track_id &&
lhs.default_sample_description_index ==
rhs.default_sample_description_index &&
lhs.default_sample_duration == rhs.default_sample_duration &&
lhs.default_sample_size == rhs.default_sample_size &&
lhs.default_sample_flags == rhs.default_sample_flags;
}
inline bool operator==(const MovieExtends& lhs, const MovieExtends& rhs) {
return lhs.header == rhs.header && lhs.tracks == rhs.tracks;
}
inline bool operator==(const Movie& lhs, const Movie& rhs) {
return lhs.header == rhs.header && lhs.extends == rhs.extends &&
lhs.tracks == rhs.tracks && lhs.pssh == rhs.pssh;
}
inline bool operator==(const TrackFragmentDecodeTime& lhs,
const TrackFragmentDecodeTime& rhs) {
return lhs.decode_time == rhs.decode_time;
}
inline bool operator==(const MovieFragmentHeader& lhs,
const MovieFragmentHeader& rhs) {
return lhs.sequence_number == rhs.sequence_number;
}
inline bool operator==(const TrackFragmentHeader& lhs,
const TrackFragmentHeader& rhs) {
return lhs.flags == rhs.flags && lhs.track_id == rhs.track_id &&
lhs.sample_description_index == rhs.sample_description_index &&
lhs.default_sample_duration == rhs.default_sample_duration &&
lhs.default_sample_size == rhs.default_sample_size &&
lhs.default_sample_flags == rhs.default_sample_flags;
}
inline bool operator==(const TrackFragmentRun& lhs,
const TrackFragmentRun& rhs) {
return lhs.flags == rhs.flags && lhs.sample_count == rhs.sample_count &&
lhs.data_offset == rhs.data_offset &&
lhs.sample_flags == rhs.sample_flags &&
lhs.sample_sizes == rhs.sample_sizes &&
lhs.sample_durations == rhs.sample_durations &&
lhs.sample_composition_time_offsets ==
rhs.sample_composition_time_offsets;
}
inline bool operator==(const TrackFragment& lhs, const TrackFragment& rhs) {
return lhs.header == rhs.header && lhs.runs == rhs.runs &&
lhs.decode_time == rhs.decode_time &&
lhs.auxiliary_offset == rhs.auxiliary_offset &&
lhs.auxiliary_size == rhs.auxiliary_size;
}
inline bool operator==(const MovieFragment& lhs, const MovieFragment& rhs) {
return lhs.header == rhs.header && lhs.tracks == rhs.tracks &&
lhs.pssh == rhs.pssh;
}
inline bool operator==(const SegmentReference& lhs,
const SegmentReference& rhs) {
return lhs.reference_type == rhs.reference_type &&
lhs.referenced_size == rhs.referenced_size &&
lhs.subsegment_duration == rhs.subsegment_duration &&
lhs.starts_with_sap == rhs.starts_with_sap &&
lhs.sap_type == rhs.sap_type &&
lhs.sap_delta_time == rhs.sap_delta_time;
}
inline bool operator==(const SegmentIndex& lhs, const SegmentIndex& rhs) {
return lhs.reference_id == rhs.reference_id &&
lhs.timescale == rhs.timescale &&
lhs.earliest_presentation_time == rhs.earliest_presentation_time &&
lhs.first_offset == rhs.first_offset &&
lhs.references == rhs.references;
}
} // namespace mp4
} // namespace media
#endif // MEDIA_MP4_BOX_DEFINITIONS_COMPARISON_H_

View File

@ -0,0 +1,857 @@
// 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