From 57474b31d623e3d339f814028d8130cc2ce7c536 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Tue, 26 Nov 2013 17:52:13 -0800 Subject: [PATCH] Implement mp4 box read/write. The box may be optimized if necessary during writing. Change-Id: I7a46e72a0bcbeacb23085a87e1f0df3a826e4da7 --- media/mp4/box_definitions.cc | 1697 ++++++++++++++++++++++++---------- media/mp4/box_definitions.h | 225 ++++- media/mp4/fourccs.h | 6 + 3 files changed, 1410 insertions(+), 518 deletions(-) diff --git a/media/mp4/box_definitions.cc b/media/mp4/box_definitions.cc index 08728dbde9..9339786704 100644 --- a/media/mp4/box_definitions.cc +++ b/media/mp4/box_definitions.cc @@ -6,141 +6,252 @@ #include "base/logging.h" #include "media/base/bit_reader.h" -#include "media/mp4/es_descriptor.h" +#include "media/mp4/box_buffer.h" #include "media/mp4/rcheck.h" +namespace { +const uint32 kFourCCSize = 4; +// Additional 32-bit size. We don't support 64-bit size. +const uint32 kBoxSize = kFourCCSize + sizeof(uint32); +// Additional 1-byte version and 3-byte flags. +const uint32 kFullBoxSize = kBoxSize + 4; + +// 9 uint32 in big endian formatted array. +const uint8 kUnityMatrix[] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0, 0, 0}; + +// Default entries for HandlerReference box. +const char kVideoHandlerName[] = "VideoHandler"; +const char kAudioHandlerName[] = "SoundHandler"; + +// Default values for VideoSampleEntry box. +const uint32 kVideoResolution = 0x00480000; // 72 dpi. +const uint16 kVideoFrameCount = 1; +const uint16 kVideoDepth = 0x0018; + +bool IsFitIn32Bits(uint64 a) { return a <= kuint32max; } +bool IsFitIn32Bits(int64 a) { return a <= kint32max && a >= kint32min; } +bool IsFitIn32Bits(uint64 a, uint64 b) { + return IsFitIn32Bits(a) && IsFitIn32Bits(b); +} +bool IsFitIn32Bits(uint64 a, int64 b) { + return IsFitIn32Bits(a) && IsFitIn32Bits(b); +} +bool IsFitIn32Bits(uint64 a, uint64 b, uint64 c) { + return IsFitIn32Bits(a) && IsFitIn32Bits(b) && IsFitIn32Bits(c); +} + +} // namespace + namespace media { namespace mp4 { -FileType::FileType() {} +FileType::FileType() : major_brand(FOURCC_NULL), minor_version(0) {} FileType::~FileType() {} FourCC FileType::BoxType() const { return FOURCC_FTYP; } -bool FileType::Parse(BoxReader* reader) { - RCHECK(reader->ReadFourCC(&major_brand) && reader->Read4(&minor_version)); - size_t num_brands = (reader->size() - reader->pos()) / sizeof(FourCC); - return reader->SkipBytes(sizeof(FourCC) * num_brands); // compatible_brands +bool FileType::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->ReadWriteFourCC(&major_brand) && + buffer->ReadWriteUInt32(&minor_version)); + size_t num_brands; + if (buffer->Reading()) { + num_brands = (buffer->Size() - buffer->Pos()) / sizeof(FourCC); + compatible_brands.resize(num_brands); + } else { + num_brands = compatible_brands.size(); + } + for (size_t i = 0; i < num_brands; ++i) + RCHECK(buffer->ReadWriteFourCC(&compatible_brands[i])); + return true; +} + +uint32 FileType::ComputeSize() { + atom_size = kBoxSize + kFourCCSize + sizeof(minor_version) + + kFourCCSize * compatible_brands.size(); + return atom_size; +} + +SegmentType::SegmentType() {} +SegmentType::~SegmentType() {} +FourCC SegmentType::BoxType() const { return FOURCC_STYP; } + +bool SegmentType::ReadWrite(BoxBuffer* buffer) { + return FileType::ReadWrite(buffer); +} + +uint32 SegmentType::ComputeSize() { + return FileType::ComputeSize(); } ProtectionSystemSpecificHeader::ProtectionSystemSpecificHeader() {} ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {} FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; } -bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) { - // Validate the box's contents and hang on to the system ID. - uint32 size; - RCHECK(reader->ReadFullBoxHeader() && - reader->ReadVec(&system_id, 16) && - reader->Read4(&size) && - reader->HasBytes(size)); +bool ProtectionSystemSpecificHeader::ReadWrite(BoxBuffer* buffer) { + uint32 size = data.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteVector(&system_id, 16) && + buffer->ReadWriteUInt32(&size) && + buffer->ReadWriteVector(&data, size)); - // Copy the entire box, including the header, for passing to EME as initData. - DCHECK(raw_box.empty()); - raw_box.assign(reader->data(), reader->data() + reader->size()); + if (buffer->Reading()) { + // Copy the entire box, including the header, for passing to EME as + // initData. + DCHECK(raw_box.empty()); + BoxReader* reader = buffer->reader(); + DCHECK(reader); + raw_box.assign(reader->data(), reader->data() + reader->size()); + } return true; } +uint32 ProtectionSystemSpecificHeader::ComputeSize() { + atom_size = kFullBoxSize + system_id.size() + sizeof(uint32) + data.size(); + return atom_size; +} + SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() {} SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() {} FourCC SampleAuxiliaryInformationOffset::BoxType() const { return FOURCC_SAIO; } -bool SampleAuxiliaryInformationOffset::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); - if (reader->flags() & 1) - RCHECK(reader->SkipBytes(8)); +bool SampleAuxiliaryInformationOffset::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); + if (flags & 1) + RCHECK(buffer->IgnoreBytes(8)); // aux_info_type and parameter. - uint32 count; - RCHECK(reader->Read4(&count) && - reader->HasBytes(count * (reader->version() == 1 ? 8 : 4))); + uint32 count = offsets.size(); + RCHECK(buffer->ReadWriteUInt32(&count)); offsets.resize(count); - for (uint32 i = 0; i < count; i++) { - if (reader->version() == 1) { - RCHECK(reader->Read8(&offsets[i])); - } else { - RCHECK(reader->Read4Into8(&offsets[i])); - } - } + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + for (uint32 i = 0; i < count; ++i) + RCHECK(buffer->ReadWriteUInt64NBytes(&offsets[i], num_bytes)); return true; } +uint32 SampleAuxiliaryInformationOffset::ComputeSize() { + // This box is optional. Skip it if it is empty. + atom_size = 0; + if (offsets.size() != 0) { + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + atom_size = kFullBoxSize + sizeof(uint32) + num_bytes * offsets.size(); + } + return atom_size; +} + SampleAuxiliaryInformationSize::SampleAuxiliaryInformationSize() - : default_sample_info_size(0), sample_count(0) { -} + : default_sample_info_size(0), sample_count(0) {} SampleAuxiliaryInformationSize::~SampleAuxiliaryInformationSize() {} FourCC SampleAuxiliaryInformationSize::BoxType() const { return FOURCC_SAIZ; } -bool SampleAuxiliaryInformationSize::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); - if (reader->flags() & 1) - RCHECK(reader->SkipBytes(8)); +bool SampleAuxiliaryInformationSize::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); + if (flags & 1) + RCHECK(buffer->IgnoreBytes(8)); - RCHECK(reader->Read1(&default_sample_info_size) && - reader->Read4(&sample_count)); + RCHECK(buffer->ReadWriteUInt8(&default_sample_info_size) && + buffer->ReadWriteUInt32(&sample_count)); if (default_sample_info_size == 0) - return reader->ReadVec(&sample_info_sizes, sample_count); + RCHECK(buffer->ReadWriteVector(&sample_info_sizes, sample_count)); return true; } +uint32 SampleAuxiliaryInformationSize::ComputeSize() { + // This box is optional. Skip it if it is empty. + atom_size = 0; + if (sample_count != 0) { + atom_size = kFullBoxSize + sizeof(default_sample_info_size) + + sizeof(sample_count) + + (default_sample_info_size == 0 ? sample_info_sizes.size() : 0); + } + return atom_size; +} + OriginalFormat::OriginalFormat() : format(FOURCC_NULL) {} OriginalFormat::~OriginalFormat() {} FourCC OriginalFormat::BoxType() const { return FOURCC_FRMA; } -bool OriginalFormat::Parse(BoxReader* reader) { - return reader->ReadFourCC(&format); +bool OriginalFormat::ReadWrite(BoxBuffer* buffer) { + return Box::ReadWrite(buffer) && buffer->ReadWriteFourCC(&format); +} + +uint32 OriginalFormat::ComputeSize() { + atom_size = kBoxSize + kFourCCSize; + return atom_size; } SchemeType::SchemeType() : type(FOURCC_NULL), version(0) {} SchemeType::~SchemeType() {} FourCC SchemeType::BoxType() const { return FOURCC_SCHM; } -bool SchemeType::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader() && - reader->ReadFourCC(&type) && - reader->Read4(&version)); +bool SchemeType::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteFourCC(&type) && + buffer->ReadWriteUInt32(&version)); return true; } +uint32 SchemeType::ComputeSize() { + atom_size = kFullBoxSize + kFourCCSize + sizeof(version); + return atom_size; +} + TrackEncryption::TrackEncryption() - : is_encrypted(false), default_iv_size(0) { -} + : is_encrypted(false), default_iv_size(0), default_kid(16, 0) {} TrackEncryption::~TrackEncryption() {} FourCC TrackEncryption::BoxType() const { return FOURCC_TENC; } -bool TrackEncryption::Parse(BoxReader* reader) { - uint8 flag; - RCHECK(reader->ReadFullBoxHeader() && - reader->SkipBytes(2) && - reader->Read1(&flag) && - reader->Read1(&default_iv_size) && - reader->ReadVec(&default_kid, 16)); - is_encrypted = (flag != 0); - if (is_encrypted) { - RCHECK(default_iv_size == 8 || default_iv_size == 16); - } else { - RCHECK(default_iv_size == 0); +bool TrackEncryption::ReadWrite(BoxBuffer* buffer) { + uint8 flag = is_encrypted ? 1 : 0; + RCHECK(FullBox::ReadWrite(buffer) && + buffer->IgnoreBytes(2) && // reserved. + buffer->ReadWriteUInt8(&flag) && + buffer->ReadWriteUInt8(&default_iv_size) && + buffer->ReadWriteVector(&default_kid, 16)); + if (buffer->Reading()) { + is_encrypted = (flag != 0); + if (is_encrypted) { + RCHECK(default_iv_size == 8 || default_iv_size == 16); + } else { + RCHECK(default_iv_size == 0); + } } return true; } +uint32 TrackEncryption::ComputeSize() { + atom_size = kFullBoxSize + sizeof(uint32) + default_kid.size(); + return atom_size; +} + SchemeInfo::SchemeInfo() {} SchemeInfo::~SchemeInfo() {} FourCC SchemeInfo::BoxType() const { return FOURCC_SCHI; } -bool SchemeInfo::Parse(BoxReader* reader) { - return reader->ScanChildren() && reader->ReadChild(&track_encryption); +bool SchemeInfo::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && buffer->PrepareChildren() && + buffer->ReadWriteChild(&track_encryption)); + return true; +} + +uint32 SchemeInfo::ComputeSize() { + atom_size = kBoxSize + track_encryption.ComputeSize(); + return atom_size; } ProtectionSchemeInfo::ProtectionSchemeInfo() {} ProtectionSchemeInfo::~ProtectionSchemeInfo() {} FourCC ProtectionSchemeInfo::BoxType() const { return FOURCC_SINF; } -bool ProtectionSchemeInfo::Parse(BoxReader* reader) { - RCHECK(reader->ScanChildren() && - reader->ReadChild(&format) && - reader->ReadChild(&type)); +bool ProtectionSchemeInfo::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&format) && + buffer->ReadWriteChild(&type)); if (type.type == FOURCC_CENC) - RCHECK(reader->ReadChild(&info)); + RCHECK(buffer->ReadWriteChild(&info)); // Other protection schemes are silently ignored. Since the protection scheme // type can't be determined until this box is opened, we return 'true' for // non-CENC protection scheme types. It is the parent box's responsibility to @@ -148,209 +259,312 @@ bool ProtectionSchemeInfo::Parse(BoxReader* reader) { return true; } +uint32 ProtectionSchemeInfo::ComputeSize() { + // Skip sinf box if it is not initialized. + atom_size = 0; + if (format.format != FOURCC_NULL) { + atom_size = kBoxSize + format.ComputeSize() + type.ComputeSize() + + info.ComputeSize(); + } + return atom_size; +} + MovieHeader::MovieHeader() : creation_time(0), modification_time(0), timescale(0), duration(0), - rate(-1), - volume(-1), + rate(1 << 16), + volume(1 << 8), next_track_id(0) {} MovieHeader::~MovieHeader() {} FourCC MovieHeader::BoxType() const { return FOURCC_MVHD; } -bool MovieHeader::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); +bool MovieHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); - if (reader->version() == 1) { - RCHECK(reader->Read8(&creation_time) && - reader->Read8(&modification_time) && - reader->Read4(×cale) && - reader->Read8(&duration)); - } else { - RCHECK(reader->Read4Into8(&creation_time) && - reader->Read4Into8(&modification_time) && - reader->Read4(×cale) && - reader->Read4Into8(&duration)); - } + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) && + buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) && + buffer->ReadWriteUInt32(×cale) && + buffer->ReadWriteUInt64NBytes(&duration, num_bytes)); - RCHECK(reader->Read4s(&rate) && - reader->Read2s(&volume) && - reader->SkipBytes(10) && // reserved - reader->SkipBytes(36) && // matrix - reader->SkipBytes(24) && // predefined zero - reader->Read4(&next_track_id)); + std::vector matrix(kUnityMatrix, + kUnityMatrix + arraysize(kUnityMatrix)); + RCHECK(buffer->ReadWriteInt32(&rate) && + buffer->ReadWriteInt16(&volume) && + buffer->IgnoreBytes(10) && // reserved + buffer->ReadWriteVector(&matrix, matrix.size()) && + buffer->IgnoreBytes(24) && // predefined zero + buffer->ReadWriteUInt32(&next_track_id)); return true; } +uint32 MovieHeader::ComputeSize() { + version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1; + atom_size = kFullBoxSize + sizeof(uint32) * (1 + version) * 3 + + sizeof(timescale) + sizeof(rate) + sizeof(volume) + + sizeof(next_track_id) + sizeof(kUnityMatrix) + 10 + + 24; // 10 bytes reserved, 24 bytes predefined. + return atom_size; +} + TrackHeader::TrackHeader() : creation_time(0), modification_time(0), track_id(0), duration(0), - layer(-1), - alternate_group(-1), + layer(0), + alternate_group(0), volume(-1), width(0), - height(0) {} + height(0) { + flags = kTrackEnabled | kTrackInMovie; +} TrackHeader::~TrackHeader() {} FourCC TrackHeader::BoxType() const { return FOURCC_TKHD; } -bool TrackHeader::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); - if (reader->version() == 1) { - RCHECK(reader->Read8(&creation_time) && - reader->Read8(&modification_time) && - reader->Read4(&track_id) && - reader->SkipBytes(4) && // reserved - reader->Read8(&duration)); - } else { - RCHECK(reader->Read4Into8(&creation_time) && - reader->Read4Into8(&modification_time) && - reader->Read4(&track_id) && - reader->SkipBytes(4) && // reserved - reader->Read4Into8(&duration)); - } +bool TrackHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); - RCHECK(reader->SkipBytes(8) && // reserved - reader->Read2s(&layer) && - reader->Read2s(&alternate_group) && - reader->Read2s(&volume) && - reader->SkipBytes(2) && // reserved - reader->SkipBytes(36) && // matrix - reader->Read4(&width) && - reader->Read4(&height)); + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) && + buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) && + buffer->ReadWriteUInt32(&track_id) && + buffer->IgnoreBytes(4) && // reserved + buffer->ReadWriteUInt64NBytes(&duration, num_bytes)); + + if (!buffer->Reading()) { + // Set default value for volume, if track is audio, 0x100 else 0. + if (volume == -1) + volume = (width != 0 && height != 0) ? 0 : 0x100; + // Convert integer to 16.16 fix point. + width <<= 16; + height <<= 16; + } + std::vector matrix(kUnityMatrix, + kUnityMatrix + arraysize(kUnityMatrix)); + RCHECK(buffer->IgnoreBytes(8) && // reserved + buffer->ReadWriteInt16(&layer) && + buffer->ReadWriteInt16(&alternate_group) && + buffer->ReadWriteInt16(&volume) && + buffer->IgnoreBytes(2) && // reserved + buffer->ReadWriteVector(&matrix, matrix.size()) && + buffer->ReadWriteUInt32(&width) && + buffer->ReadWriteUInt32(&height)); + // Convert 16.16 fixed point to integer. width >>= 16; height >>= 16; return true; } +uint32 TrackHeader::ComputeSize() { + version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1; + atom_size = kFullBoxSize + sizeof(track_id) + + sizeof(uint32) * (1 + version) * 3 + sizeof(layer) + + sizeof(alternate_group) + sizeof(volume) + sizeof(width) + + sizeof(height) + sizeof(kUnityMatrix) + 14; // 14 bytes reserved. + return atom_size; +} + SampleDescription::SampleDescription() : type(kInvalid) {} SampleDescription::~SampleDescription() {} FourCC SampleDescription::BoxType() const { return FOURCC_STSD; } -bool SampleDescription::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->SkipBytes(4) && - reader->Read4(&count)); - video_entries.clear(); - audio_entries.clear(); +bool SampleDescription::ReadWrite(BoxBuffer* buffer) { + uint32 count = 0; + if (type == kVideo) + count = video_entries.size(); + else + count = audio_entries.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); - // Note: this value is preset before scanning begins. See comments in the - // Parse(Media*) function. - if (type == kVideo) { - RCHECK(reader->ReadAllChildren(&video_entries)); - } else if (type == kAudio) { - RCHECK(reader->ReadAllChildren(&audio_entries)); + if (buffer->Reading()) { + BoxReader* reader = buffer->reader(); + DCHECK(reader); + video_entries.clear(); + audio_entries.clear(); + // Note: this value is preset before scanning begins. See comments in the + // Parse(Media*) function. + if (type == kVideo) { + RCHECK(reader->ReadAllChildren(&video_entries)); + RCHECK(video_entries.size() == count); + } else if (type == kAudio) { + RCHECK(reader->ReadAllChildren(&audio_entries)); + RCHECK(audio_entries.size() == count); + } + } else { + DCHECK_LT(0, count); + if (type == kVideo) { + for (uint32 i = 0; i < count; ++i) + RCHECK(video_entries[i].ReadWrite(buffer)); + } else if (type == kAudio) { + for (uint32 i = 0; i < count; ++i) + RCHECK(audio_entries[i].ReadWrite(buffer)); + } else { + NOTIMPLEMENTED(); + } } return true; } +uint32 SampleDescription::ComputeSize() { + atom_size = kFullBoxSize + sizeof(uint32); + if (type == kVideo) { + for (uint32 i = 0; i < video_entries.size(); ++i) + atom_size += video_entries[i].ComputeSize(); + } else if (type == kAudio) { + for (uint32 i = 0; i < audio_entries.size(); ++i) + atom_size += audio_entries[i].ComputeSize(); + } + return atom_size; +} + DecodingTimeToSample::DecodingTimeToSample() {} DecodingTimeToSample::~DecodingTimeToSample() {} FourCC DecodingTimeToSample::BoxType() const { return FOURCC_STTS; } -bool DecodingTimeToSample::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&count)); +bool DecodingTimeToSample::ReadWrite(BoxBuffer* buffer) { + uint32 count = decoding_time.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); decoding_time.resize(count); - for (int i = 0; i < count; ++i) { - RCHECK(reader->Read4(&decoding_time[i].sample_count) && - reader->Read4(&decoding_time[i].sample_delta)); + for (uint32 i = 0; i < count; ++i) { + RCHECK(buffer->ReadWriteUInt32(&decoding_time[i].sample_count) && + buffer->ReadWriteUInt32(&decoding_time[i].sample_delta)); } return true; } +uint32 DecodingTimeToSample::ComputeSize() { + atom_size = kFullBoxSize + sizeof(uint32) + + sizeof(DecodingTime) * decoding_time.size(); + return atom_size; +} + CompositionTimeToSample::CompositionTimeToSample() {} CompositionTimeToSample::~CompositionTimeToSample() {} FourCC CompositionTimeToSample::BoxType() const { return FOURCC_CTTS; } -bool CompositionTimeToSample::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&count)); +bool CompositionTimeToSample::ReadWrite(BoxBuffer* buffer) { + uint32 count = composition_offset.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); composition_offset.resize(count); - for (int i = 0; i < count; ++i) { - RCHECK(reader->Read4(&composition_offset[i].sample_count) && - reader->Read4s(&composition_offset[i].sample_offset)); + for (uint32 i = 0; i < count; ++i) { + RCHECK(buffer->ReadWriteUInt32(&composition_offset[i].sample_count) && + buffer->ReadWriteInt32(&composition_offset[i].sample_offset)); } return true; } +uint32 CompositionTimeToSample::ComputeSize() { + // Version 1 to support signed offset. + version = 1; + // This box is optional. Skip it if it is empty. + atom_size = 0; + if (!composition_offset.empty()) { + atom_size = kFullBoxSize + sizeof(uint32) + + sizeof(CompositionOffset) * composition_offset.size(); + } + return atom_size; +} + SampleToChunk::SampleToChunk() {} SampleToChunk::~SampleToChunk() {} FourCC SampleToChunk::BoxType() const { return FOURCC_STSC; } -bool SampleToChunk::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&count)); +bool SampleToChunk::ReadWrite(BoxBuffer* buffer) { + uint32 count = chunk_info.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); chunk_info.resize(count); - for (int i = 0; i < count; ++i) { - RCHECK(reader->Read4(&chunk_info[i].first_chunk) && - reader->Read4(&chunk_info[i].samples_per_chunk) && - reader->Read4(&chunk_info[i].sample_description_index)); + for (uint32 i = 0; i < count; ++i) { + RCHECK(buffer->ReadWriteUInt32(&chunk_info[i].first_chunk) && + buffer->ReadWriteUInt32(&chunk_info[i].samples_per_chunk) && + buffer->ReadWriteUInt32(&chunk_info[i].sample_description_index)); // first_chunk values are always increasing. - DCHECK(i == 0 ? chunk_info[i].first_chunk == 1 + RCHECK(i == 0 ? chunk_info[i].first_chunk == 1 : chunk_info[i].first_chunk > chunk_info[i - 1].first_chunk); } return true; } +uint32 SampleToChunk::ComputeSize() { + atom_size = + kFullBoxSize + sizeof(uint32) + sizeof(ChunkInfo) * chunk_info.size(); + return atom_size; +} + SampleSize::SampleSize() : sample_size(0), sample_count(0) {} SampleSize::~SampleSize() {} FourCC SampleSize::BoxType() const { return FOURCC_STSZ; } -bool SampleSize::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&sample_size) && - reader->Read4(&sample_count)); +bool SampleSize::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&sample_size) && + buffer->ReadWriteUInt32(&sample_count)); if (sample_size == 0) { - sizes.resize(sample_count); - for (int i = 0; i < sample_count; ++i) - RCHECK(reader->Read4(&sizes[i])); + if (buffer->Reading()) + sizes.resize(sample_count); + else + DCHECK(sample_count == sizes.size()); + for (uint32 i = 0; i < sample_count; ++i) + RCHECK(buffer->ReadWriteUInt32(&sizes[i])); } return true; } -CompactSampleSize::CompactSampleSize() : SampleSize() {} +uint32 SampleSize::ComputeSize() { + atom_size = kFullBoxSize + sizeof(sample_size) + sizeof(sample_count) + + (sample_size == 0 ? sizeof(uint32) * sizes.size() : 0); + return atom_size; +} + +CompactSampleSize::CompactSampleSize() : field_size(0) {} CompactSampleSize::~CompactSampleSize() {} FourCC CompactSampleSize::BoxType() const { return FOURCC_STZ2; } -bool CompactSampleSize::Parse(BoxReader* reader) { - uint8 field_size; - RCHECK(reader->ReadFullBoxHeader() && - reader->SkipBytes(3) && - reader->Read1(&field_size) && - reader->Read4(&sample_count)); +bool CompactSampleSize::ReadWrite(BoxBuffer* buffer) { + uint32 sample_count = sizes.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->IgnoreBytes(3) && + buffer->ReadWriteUInt8(&field_size) && + buffer->ReadWriteUInt32(&sample_count)); // Reserve one more entry if field size is 4 bits. sizes.resize(sample_count + (field_size == 4 ? 1 : 0)); switch (field_size) { case 4: - for (int i = 0; i < sample_count; ++i) { + for (uint32 i = 0; i < sample_count; ++i) { uint8 size; - RCHECK(reader->Read1(&size)); - sizes[i] = size >> 4; - sizes[++i] = size & 0x0F; + if (!buffer->Reading()) { + DCHECK(sizes[i] < 16 && sizes[i+1] < 16); + size = (sizes[i] << 4) | (++i < sample_count ? sizes[i] : 0); + } + RCHECK(buffer->ReadWriteUInt8(&size)); + if (buffer->Reading()) { + sizes[i] = size >> 4; + sizes[++i] = size & 0x0F; + } } break; case 8: - for (int i = 0; i < sample_count; ++i) { - uint8 size; - RCHECK(reader->Read1(&size)); + for (uint32 i = 0; i < sample_count; ++i) { + uint8 size = sizes[i]; + RCHECK(buffer->ReadWriteUInt8(&size)); sizes[i] = size; } break; case 16: - for (int i = 0; i < sample_count; ++i) { - uint16 size; - RCHECK(reader->Read2(&size)); + for (uint32 i = 0; i < sample_count; ++i) { + uint16 size = sizes[i]; + RCHECK(buffer->ReadWriteUInt16(&size)); sizes[i] = size; } break; @@ -361,141 +575,249 @@ bool CompactSampleSize::Parse(BoxReader* reader) { return true; } +uint32 CompactSampleSize::ComputeSize() { + atom_size = kFullBoxSize + sizeof(uint32) + sizeof(uint32) + + (field_size * sizes.size() + 7) / 8; + return atom_size; +} + ChunkOffset::ChunkOffset() {} ChunkOffset::~ChunkOffset() {} FourCC ChunkOffset::BoxType() const { return FOURCC_STCO; } -bool ChunkOffset::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&count)); +bool ChunkOffset::ReadWrite(BoxBuffer* buffer) { + uint32 count = offsets.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); offsets.resize(count); - for (int i = 0; i < count; ++i) - RCHECK(reader->Read4Into8(&offsets[i])); + for (uint32 i = 0; i < count; ++i) + RCHECK(buffer->ReadWriteUInt64NBytes(&offsets[i], sizeof(uint32))); return true; } +uint32 ChunkOffset::ComputeSize() { + atom_size = kFullBoxSize + sizeof(uint32) + sizeof(uint32) * offsets.size(); + return atom_size; +} + ChunkLargeOffset::ChunkLargeOffset() {} ChunkLargeOffset::~ChunkLargeOffset() {} FourCC ChunkLargeOffset::BoxType() const { return FOURCC_CO64; } -bool ChunkLargeOffset::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&count)); +bool ChunkLargeOffset::ReadWrite(BoxBuffer* buffer) { + uint32 count = offsets.size(); + + if (!buffer->Reading()) { + // Switch to ChunkOffset box if it is able to fit in 32 bits offset. + if (count == 0 || IsFitIn32Bits(offsets[count - 1])) { + ChunkOffset stco; + stco.offsets.swap(offsets); + DCHECK(buffer->writer()); + stco.Write(buffer->writer()); + stco.offsets.swap(offsets); + return true; + } + } + + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); offsets.resize(count); - for (int i = 0; i < count; ++i) - RCHECK(reader->Read8(&offsets[i])); + for (uint32 i = 0; i < count; ++i) + RCHECK(buffer->ReadWriteUInt64(&offsets[i])); return true; } +uint32 ChunkLargeOffset::ComputeSize() { + uint32 count = offsets.size(); + int use_large_offset = + (count > 0 && !IsFitIn32Bits(offsets[count - 1])) ? 1 : 0; + atom_size = kFullBoxSize + sizeof(count) + + sizeof(uint32) * (1 + use_large_offset) * offsets.size(); + return atom_size; +} + SyncSample::SyncSample() {} SyncSample::~SyncSample() {} FourCC SyncSample::BoxType() const { return FOURCC_STSS; } -bool SyncSample::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&count)); +bool SyncSample::ReadWrite(BoxBuffer* buffer) { + uint32 count = sample_number.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&count)); sample_number.resize(count); - for (int i = 0; i < count; ++i) - RCHECK(reader->Read4(&sample_number[i])); + for (uint32 i = 0; i < count; ++i) + RCHECK(buffer->ReadWriteUInt32(&sample_number[i])); return true; } +uint32 SyncSample::ComputeSize() { + // Sync sample box is optional. Skip it if it is empty. + atom_size = 0; + if (!sample_number.empty()) { + atom_size = + kFullBoxSize + sizeof(uint32) + sizeof(uint32) * sample_number.size(); + } + return atom_size; +} + SampleTable::SampleTable() {} SampleTable::~SampleTable() {} FourCC SampleTable::BoxType() const { return FOURCC_STBL; } -bool SampleTable::Parse(BoxReader* reader) { - RCHECK(reader->ScanChildren() && - reader->ReadChild(&description) && - reader->ReadChild(&decoding_time_to_sample) && - reader->MaybeReadChild(&composition_time_to_sample) && - reader->MaybeReadChild(&sync_sample) && - reader->ReadChild(&sample_to_chunk)); +bool SampleTable::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&description) && + buffer->ReadWriteChild(&decoding_time_to_sample) && + buffer->TryReadWriteChild(&composition_time_to_sample) && + buffer->ReadWriteChild(&sample_to_chunk)); - // Either SampleSize or CompactSampleSize must present. - if (reader->ChildExist(&sample_size)) { - RCHECK(reader->ReadChild(&sample_size)); - } else { - CompactSampleSize compact_sample_size; - RCHECK(reader->ReadChild(&compact_sample_size)); - sample_size.sample_size = compact_sample_size.sample_size; - sample_size.sample_count = compact_sample_size.sample_count; - sample_size.sizes.swap(compact_sample_size.sizes); - } + if (buffer->Reading()) { + BoxReader* reader = buffer->reader(); + DCHECK(reader); - // Either ChunkOffset or ChunkLargeOffset must present. - if (reader->ChildExist(&chunk_offset)) { - RCHECK(reader->ReadChild(&chunk_offset)); + // Either SampleSize or CompactSampleSize must present. + if (reader->ChildExist(&sample_size)) { + RCHECK(reader->ReadChild(&sample_size)); + } else { + CompactSampleSize compact_sample_size; + RCHECK(reader->ReadChild(&compact_sample_size)); + sample_size.sample_size = 0; + sample_size.sample_count = compact_sample_size.sizes.size(); + sample_size.sizes.swap(compact_sample_size.sizes); + } + + // Either ChunkOffset or ChunkLargeOffset must present. + if (reader->ChildExist(&chunk_large_offset)) { + RCHECK(reader->ReadChild(&chunk_large_offset)); + } else { + ChunkOffset chunk_offset; + RCHECK(reader->ReadChild(&chunk_offset)); + chunk_large_offset.offsets.swap(chunk_offset.offsets); + } } else { - ChunkLargeOffset chunk_large_offset; - RCHECK(reader->ReadChild(&chunk_large_offset)); - chunk_offset.offsets.swap(chunk_large_offset.offsets); + RCHECK(sample_size.ReadWrite(buffer) && + chunk_large_offset.ReadWrite(buffer)); } + RCHECK(buffer->TryReadWriteChild(&sync_sample)); return true; } +uint32 SampleTable::ComputeSize() { + atom_size = kBoxSize + description.ComputeSize() + + decoding_time_to_sample.ComputeSize() + + composition_time_to_sample.ComputeSize() + + sample_to_chunk.ComputeSize() + sample_size.ComputeSize() + + chunk_large_offset.ComputeSize() + sync_sample.ComputeSize(); + return atom_size; +} + EditList::EditList() {} EditList::~EditList() {} FourCC EditList::BoxType() const { return FOURCC_ELST; } -bool EditList::Parse(BoxReader* reader) { - uint32 count; - RCHECK(reader->ReadFullBoxHeader() && reader->Read4(&count)); - - if (reader->version() == 1) { - RCHECK(reader->HasBytes(count * 20)); - } else { - RCHECK(reader->HasBytes(count * 12)); - } +bool EditList::ReadWrite(BoxBuffer* buffer) { + uint32 count = edits.size(); + RCHECK(FullBox::ReadWrite(buffer) && buffer->ReadWriteUInt32(&count)); edits.resize(count); - for (std::vector::iterator edit = edits.begin(); - edit != edits.end(); ++edit) { - if (reader->version() == 1) { - RCHECK(reader->Read8(&edit->segment_duration) && - reader->Read8s(&edit->media_time)); - } else { - RCHECK(reader->Read4Into8(&edit->segment_duration) && - reader->Read4sInto8s(&edit->media_time)); - } - RCHECK(reader->Read2s(&edit->media_rate_integer) && - reader->Read2s(&edit->media_rate_fraction)); + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + for (uint32 i = 0; i < count; ++i) { + RCHECK( + buffer->ReadWriteUInt64NBytes(&edits[i].segment_duration, num_bytes) && + buffer->ReadWriteInt64NBytes(&edits[i].media_time, num_bytes) && + buffer->ReadWriteInt16(&edits[i].media_rate_integer) && + buffer->ReadWriteInt16(&edits[i].media_rate_fraction)); } return true; } +uint32 EditList::ComputeSize() { + // EditList box is optional. Skip it if it is empty. + atom_size = 0; + if (edits.empty()) + return 0; + + version = 0; + for (uint32 i = 0; i < edits.size(); ++i) { + if (!IsFitIn32Bits(edits[i].segment_duration, edits[i].media_time)) { + version = 1; + break; + } + } + atom_size = + kFullBoxSize + sizeof(uint32) + + (sizeof(uint32) * (1 + version) * 2 + sizeof(int16) * 2) * edits.size(); + return atom_size; +} + Edit::Edit() {} Edit::~Edit() {} FourCC Edit::BoxType() const { return FOURCC_EDTS; } -bool Edit::Parse(BoxReader* reader) { - return reader->ScanChildren() && reader->ReadChild(&list); +bool Edit::ReadWrite(BoxBuffer* buffer) { + return Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&list); +} + +uint32 Edit::ComputeSize() { + // Edit box is optional. Skip it if it is empty. + atom_size = 0; + if (!list.edits.empty()) + atom_size = kBoxSize + list.ComputeSize(); + return atom_size; } HandlerReference::HandlerReference() : type(kInvalid) {} HandlerReference::~HandlerReference() {} FourCC HandlerReference::BoxType() const { return FOURCC_HDLR; } -bool HandlerReference::Parse(BoxReader* reader) { +bool HandlerReference::ReadWrite(BoxBuffer* buffer) { FourCC hdlr_type; - RCHECK(reader->SkipBytes(8) && reader->ReadFourCC(&hdlr_type)); - // Note: remaining fields in box ignored - if (hdlr_type == FOURCC_VIDE) { - type = kVideo; - } else if (hdlr_type == FOURCC_SOUN) { - type = kAudio; + std::vector handler_name; + if (!buffer->Reading()) { + if (type == kVideo) { + hdlr_type = FOURCC_VIDE; + handler_name.assign(kVideoHandlerName, + kVideoHandlerName + arraysize(kVideoHandlerName)); + } else if (type == kAudio) { + hdlr_type = FOURCC_SOUN; + handler_name.assign(kAudioHandlerName, + kAudioHandlerName + arraysize(kAudioHandlerName)); + } else { + NOTIMPLEMENTED(); + } + } + RCHECK(FullBox::ReadWrite(buffer) && + buffer->IgnoreBytes(4) && // predefined. + buffer->ReadWriteFourCC(&hdlr_type)); + if (buffer->Reading()) { + // Note: for reading, remaining fields in box ignored. + if (hdlr_type == FOURCC_VIDE) { + type = kVideo; + } else if (hdlr_type == FOURCC_SOUN) { + type = kAudio; + } else { + type = kInvalid; + } } else { - type = kInvalid; + RCHECK(buffer->IgnoreBytes(12) && // reserved. + buffer->ReadWriteVector(&handler_name, handler_name.size())); } return true; } +uint32 HandlerReference::ComputeSize() { + atom_size = + kFullBoxSize + kFourCCSize + 16 + // 16 bytes Reserved + (type == kVideo ? sizeof(kVideoHandlerName) : sizeof(kAudioHandlerName)); + return atom_size; +} + AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord() : version(0), profile_indication(0), @@ -506,11 +828,16 @@ AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord() AVCDecoderConfigurationRecord::~AVCDecoderConfigurationRecord() {} FourCC AVCDecoderConfigurationRecord::BoxType() const { return FOURCC_AVCC; } -bool AVCDecoderConfigurationRecord::Parse(BoxReader* reader) { - RCHECK(reader->ReadVec(&data, reader->size() - 8)); - - BufferReader buffer_reader(&data[0], data.size()); - return ParseData(&buffer_reader); +bool AVCDecoderConfigurationRecord::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer)); + if (buffer->Reading()) { + RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos())); + BufferReader buffer_reader(&data[0], data.size()); + return ParseData(&buffer_reader); + } else { + RCHECK(buffer->ReadWriteVector(&data, data.size())); + } + return true; } bool AVCDecoderConfigurationRecord::ParseData(BufferReader* reader) { @@ -532,7 +859,7 @@ bool AVCDecoderConfigurationRecord::ParseData(BufferReader* reader) { for (int i = 0; i < num_sps; i++) { uint16 sps_length; RCHECK(reader->Read2(&sps_length) && - reader->ReadVec(&sps_list[i], sps_length)); + reader->ReadToVector(&sps_list[i], sps_length)); } uint8 num_pps; @@ -542,223 +869,450 @@ bool AVCDecoderConfigurationRecord::ParseData(BufferReader* reader) { for (int i = 0; i < num_pps; i++) { uint16 pps_length; RCHECK(reader->Read2(&pps_length) && - reader->ReadVec(&pps_list[i], pps_length)); + reader->ReadToVector(&pps_list[i], pps_length)); } return true; } -PixelAspectRatioBox::PixelAspectRatioBox() : h_spacing(1), v_spacing(1) {} +uint32 AVCDecoderConfigurationRecord::ComputeSize() { + atom_size = 0; + if (!data.empty()) + atom_size = kBoxSize + data.size(); + return atom_size; +} + +PixelAspectRatioBox::PixelAspectRatioBox() : h_spacing(0), v_spacing(0) {} PixelAspectRatioBox::~PixelAspectRatioBox() {} FourCC PixelAspectRatioBox::BoxType() const { return FOURCC_PASP; } -bool PixelAspectRatioBox::Parse(BoxReader* reader) { - RCHECK(reader->Read4(&h_spacing) && - reader->Read4(&v_spacing)); +bool PixelAspectRatioBox::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&h_spacing) && + buffer->ReadWriteUInt32(&v_spacing)); return true; } +uint32 PixelAspectRatioBox::ComputeSize() { + // This box is optional. Skip it if it is not initialized. + atom_size = 0; + if (h_spacing != 0 || v_spacing != 0) { + // Both values must be positive. + DCHECK(h_spacing != 0 && v_spacing != 0); + atom_size = kBoxSize + sizeof(h_spacing) + sizeof(v_spacing); + } + return atom_size; +} + VideoSampleEntry::VideoSampleEntry() - : format(FOURCC_NULL), - data_reference_index(0), - width(0), - height(0) {} + : format(FOURCC_NULL), data_reference_index(1), width(0), height(0) {} VideoSampleEntry::~VideoSampleEntry() {} FourCC VideoSampleEntry::BoxType() const { - DCHECK(false) << "VideoSampleEntry should be parsed according to the " - << "handler type recovered in its Media ancestor."; + LOG(ERROR) << "VideoSampleEntry should be parsed according to the " + << "handler type recovered in its Media ancestor."; return FOURCC_NULL; } -bool VideoSampleEntry::Parse(BoxReader* reader) { - format = reader->type(); - RCHECK(reader->SkipBytes(6) && - reader->Read2(&data_reference_index) && - reader->SkipBytes(16) && - reader->Read2(&width) && - reader->Read2(&height) && - reader->SkipBytes(50)); +bool VideoSampleEntry::ReadWrite(BoxBuffer* buffer) { + if (buffer->Reading()) { + DCHECK(buffer->reader()); + format = buffer->reader()->type(); + } else { + RCHECK(buffer->ReadWriteUInt32(&atom_size) && + buffer->ReadWriteFourCC(&format)); + } - RCHECK(reader->ScanChildren() && - reader->MaybeReadChild(&pixel_aspect)); + uint32 video_resolution = kVideoResolution; + uint16 video_frame_count = kVideoFrameCount; + uint16 video_depth = kVideoDepth; + int16 predefined = -1; + RCHECK(buffer->IgnoreBytes(6) && // reserved. + buffer->ReadWriteUInt16(&data_reference_index) && + buffer->IgnoreBytes(16) && // predefined 0. + buffer->ReadWriteUInt16(&width) && + buffer->ReadWriteUInt16(&height) && + buffer->ReadWriteUInt32(&video_resolution) && + buffer->ReadWriteUInt32(&video_resolution) && + buffer->IgnoreBytes(4) && // reserved. + buffer->ReadWriteUInt16(&video_frame_count) && + buffer->IgnoreBytes(32) && // comparessor_name. + buffer->ReadWriteUInt16(&video_depth) && + buffer->ReadWriteInt16(&predefined)); + + RCHECK(buffer->PrepareChildren() && + buffer->TryReadWriteChild(&pixel_aspect)); if (format == FOURCC_ENCV) { - // Continue scanning until a recognized protection scheme is found, or until - // we run out of protection schemes. - while (sinf.type.type != FOURCC_CENC) { - if (!reader->ReadChild(&sinf)) - return false; + if (buffer->Reading()) { + // Continue scanning until a recognized protection scheme is found, + // or until we run out of protection schemes. + while (sinf.type.type != FOURCC_CENC) { + if (!buffer->ReadWriteChild(&sinf)) + return false; + } + } else { + RCHECK(buffer->ReadWriteChild(&sinf)); } } if (format == FOURCC_AVC1 || (format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) { - RCHECK(reader->ReadChild(&avcc)); + RCHECK(buffer->ReadWriteChild(&avcc)); } return true; } -ElementaryStreamDescriptor::ElementaryStreamDescriptor() - : object_type(kForbidden) {} - -ElementaryStreamDescriptor::~ElementaryStreamDescriptor() {} - -FourCC ElementaryStreamDescriptor::BoxType() const { - return FOURCC_ESDS; +uint32 VideoSampleEntry::ComputeSize() { + atom_size = kBoxSize + sizeof(data_reference_index) + sizeof(width) + + sizeof(height) + sizeof(kVideoResolution) * 2 + + sizeof(kVideoFrameCount) + sizeof(kVideoDepth) + + pixel_aspect.ComputeSize() + sinf.ComputeSize() + + avcc.ComputeSize() + 32 + // 32 bytes comparessor_name. + 6 + 4 + 16 + 2; // 6 + 4 bytes reserved, 16 + 2 bytes predefined. + return atom_size; } -bool ElementaryStreamDescriptor::Parse(BoxReader* reader) { - std::vector data; - ESDescriptor es_desc; - - RCHECK(reader->ReadFullBoxHeader()); - RCHECK(reader->ReadVec(&data, reader->size() - reader->pos())); - RCHECK(es_desc.Parse(data)); - - object_type = es_desc.object_type(); - - RCHECK(aac.Parse(es_desc.decoder_specific_info())); +ElementaryStreamDescriptor::ElementaryStreamDescriptor() {} +ElementaryStreamDescriptor::~ElementaryStreamDescriptor() {} +FourCC ElementaryStreamDescriptor::BoxType() const { return FOURCC_ESDS; } +bool ElementaryStreamDescriptor::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); + if (buffer->Reading()) { + std::vector data; + RCHECK(buffer->ReadWriteVector(&data, buffer->Size() - buffer->Pos())); + RCHECK(es_descriptor.Parse(data)); + if (es_descriptor.IsAAC()) + RCHECK(aac.Parse(es_descriptor.decoder_specific_info())); + } else { + DCHECK(buffer->writer()); + es_descriptor.Write(buffer->writer()); + } return true; } +uint32 ElementaryStreamDescriptor::ComputeSize() { + // This box is optional. Skip it if not initialized. + atom_size = 0; + if (es_descriptor.object_type() != kForbidden) + atom_size = kFullBoxSize + es_descriptor.ComputeSize(); + return atom_size; +} + AudioSampleEntry::AudioSampleEntry() : format(FOURCC_NULL), - data_reference_index(0), - channelcount(0), - samplesize(0), + data_reference_index(1), + channelcount(2), + samplesize(16), samplerate(0) {} AudioSampleEntry::~AudioSampleEntry() {} FourCC AudioSampleEntry::BoxType() const { - DCHECK(false) << "AudioSampleEntry should be parsed according to the " - << "handler type recovered in its Media ancestor."; + LOG(ERROR) << "AudioSampleEntry should be parsed according to the " + << "handler type recovered in its Media ancestor."; return FOURCC_NULL; } -bool AudioSampleEntry::Parse(BoxReader* reader) { - format = reader->type(); - RCHECK(reader->SkipBytes(6) && - reader->Read2(&data_reference_index) && - reader->SkipBytes(8) && - reader->Read2(&channelcount) && - reader->Read2(&samplesize) && - reader->SkipBytes(4) && - reader->Read4(&samplerate)); - // Convert from 16.16 fixed point to integer +bool AudioSampleEntry::ReadWrite(BoxBuffer* buffer) { + if (buffer->Reading()) { + DCHECK(buffer->reader()); + format = buffer->reader()->type(); + } else { + RCHECK(buffer->ReadWriteUInt32(&atom_size) && + buffer->ReadWriteFourCC(&format)); + } + + // Convert from integer to 16.16 fixed point for writing. + samplerate <<= 16; + RCHECK(buffer->IgnoreBytes(6) && // reserved. + buffer->ReadWriteUInt16(&data_reference_index) && + buffer->IgnoreBytes(8) && // reserved. + buffer->ReadWriteUInt16(&channelcount) && + buffer->ReadWriteUInt16(&samplesize) && + buffer->IgnoreBytes(4) && // predefined. + buffer->ReadWriteUInt32(&samplerate)); + // Convert from 16.16 fixed point to integer. samplerate >>= 16; - RCHECK(reader->ScanChildren()); + RCHECK(buffer->PrepareChildren()); if (format == FOURCC_ENCA) { - // Continue scanning until a recognized protection scheme is found, or until - // we run out of protection schemes. - while (sinf.type.type != FOURCC_CENC) { - if (!reader->ReadChild(&sinf)) - return false; + if (buffer->Reading()) { + // Continue scanning until a recognized protection scheme is found, + // or until we run out of protection schemes. + while (sinf.type.type != FOURCC_CENC) { + if (!buffer->ReadWriteChild(&sinf)) + return false; + } + } else { + RCHECK(buffer->ReadWriteChild(&sinf)); } } // ESDS is not valid in case of EAC3. - RCHECK(reader->MaybeReadChild(&esds)); + RCHECK(buffer->TryReadWriteChild(&esds)); return true; } +uint32 AudioSampleEntry::ComputeSize() { + atom_size = kBoxSize + sizeof(data_reference_index) + sizeof(channelcount) + + sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() + + esds.ComputeSize() + 6 + 8 + // 6 + 8 bytes reserved. + 4; // 4 bytes predefined. + return atom_size; +} + MediaHeader::MediaHeader() - : creation_time(0), - modification_time(0), - timescale(0), - duration(0) {} + : creation_time(0), modification_time(0), timescale(0), duration(0) { + language[0] = 0; +} MediaHeader::~MediaHeader() {} FourCC MediaHeader::BoxType() const { return FOURCC_MDHD; } -bool MediaHeader::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); +bool MediaHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); - if (reader->version() == 1) { - RCHECK(reader->Read8(&creation_time) && - reader->Read8(&modification_time) && - reader->Read4(×cale) && - reader->Read8(&duration)); + uint8 num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) && + buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) && + buffer->ReadWriteUInt32(×cale) && + buffer->ReadWriteUInt64NBytes(&duration, num_bytes)); + + if (buffer->Reading()) { + // Read language codes into temp first then use BitReader to read the + // values. ISO-639-2/T language code: unsigned int(5)[3] language (2 bytes). + std::vector temp; + RCHECK(buffer->ReadWriteVector(&temp, 2)); + + BitReader bit_reader(&temp[0], 2); + bit_reader.SkipBits(1); + for (int i = 0; i < 3; ++i) { + CHECK(bit_reader.ReadBits(5, &language[i])); + language[i] += 0x60; + } + language[3] = '\0'; } else { - RCHECK(reader->Read4Into8(&creation_time) && - reader->Read4Into8(&modification_time) && - reader->Read4(×cale) && - reader->Read4Into8(&duration)); + // Set up default language if it is not set. + const char kUndefinedLanguage[] = "und"; + if (language[0] == 0) + strcpy(language, kUndefinedLanguage); + + // Lang format: bit(1) pad, unsigned int(5)[3] language. + uint16 lang = 0; + for (int i = 0; i < 3; ++i) + lang |= (language[i] - 0x60) << ((2 - i) * 5); + RCHECK(buffer->ReadWriteUInt16(&lang)); } - // Read language codes into temp first then use BitReader to read the values. - // ISO-639-2/T language code: unsigned int(5)[3] language (2 bytes). - std::vector temp; - RCHECK(reader->ReadVec(&temp, 2)); + RCHECK(buffer->IgnoreBytes(2)); // predefined. + return true; +} - BitReader bit_reader(&temp[0], 2); - bit_reader.SkipBits(1); - for (int i = 0; i < 3; ++i) { - CHECK(bit_reader.ReadBits(5, &language[i])); - language[i] += 0x60; +uint32 MediaHeader::ComputeSize() { + version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1; + atom_size = kFullBoxSize + sizeof(timescale) + + sizeof(uint32) * (1 + version) * 3 + 2 + // 2 bytes language. + 2; // 2 bytes predefined. + return atom_size; +} + +VideoMediaHeader::VideoMediaHeader() + : graphicsmode(0), opcolor_red(0), opcolor_green(0), opcolor_blue(0) { + const uint32 kVideoMediaHeaderFlags = 1; + flags = kVideoMediaHeaderFlags; +} +VideoMediaHeader::~VideoMediaHeader() {} +FourCC VideoMediaHeader::BoxType() const { return FOURCC_VMHD; } +bool VideoMediaHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt16(&graphicsmode) && + buffer->ReadWriteUInt16(&opcolor_red) && + buffer->ReadWriteUInt16(&opcolor_green) && + buffer->ReadWriteUInt16(&opcolor_blue)); + return true; +} + +uint32 VideoMediaHeader::ComputeSize() { + atom_size = kFullBoxSize + sizeof(graphicsmode) + sizeof(opcolor_red) + + sizeof(opcolor_green) + sizeof(opcolor_blue); + return atom_size; +} + +SoundMediaHeader::SoundMediaHeader() : balance(0) {} +SoundMediaHeader::~SoundMediaHeader() {} +FourCC SoundMediaHeader::BoxType() const { return FOURCC_SMHD; } +bool SoundMediaHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt16(&balance) && + buffer->IgnoreBytes(2)); // reserved. + return true; +} + +uint32 SoundMediaHeader::ComputeSize() { + atom_size = kFullBoxSize + sizeof(balance) + sizeof(uint16); + return atom_size; +} + +DataEntryUrl::DataEntryUrl() { + const uint32 kDataEntryUrlFlags = 1; + flags = kDataEntryUrlFlags; +} +DataEntryUrl::~DataEntryUrl() {} +FourCC DataEntryUrl::BoxType() const { return FOURCC_URL; } +bool DataEntryUrl::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); + if (buffer->Reading()) { + RCHECK(buffer->ReadWriteVector(&location, buffer->Size() - buffer->Pos())); + } else { + RCHECK(buffer->ReadWriteVector(&location, location.size())); } - language[3] = '\0'; + return true; +} - return reader->SkipBytes(2); +uint32 DataEntryUrl::ComputeSize() { + atom_size = kBoxSize + sizeof(flags) + location.size(); + return atom_size; +} + +DataReference::DataReference() { + // Default 1 entry. + data_entry.resize(1); +} +DataReference::~DataReference() {} +FourCC DataReference::BoxType() const { return FOURCC_DREF; } +bool DataReference::ReadWrite(BoxBuffer* buffer) { + uint32 entry_count = data_entry.size(); + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&entry_count)); + data_entry.resize(entry_count); + RCHECK(buffer->PrepareChildren()); + for (uint32 i = 0; i < entry_count; ++i) + RCHECK(buffer->ReadWriteChild(&data_entry[i])); + return true; +} + +uint32 DataReference::ComputeSize() { + uint32 count = data_entry.size(); + atom_size = kFullBoxSize + sizeof(count); + for (uint32 i = 0; i < count; ++i) + atom_size += data_entry[i].ComputeSize(); + return atom_size; +} + +DataInformation::DataInformation() {} +DataInformation::~DataInformation() {} +FourCC DataInformation::BoxType() const { return FOURCC_DINF; } + +bool DataInformation::ReadWrite(BoxBuffer* buffer) { + return Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&dref); +} + +uint32 DataInformation::ComputeSize() { + atom_size = kBoxSize + dref.ComputeSize(); + return atom_size; } MediaInformation::MediaInformation() {} MediaInformation::~MediaInformation() {} FourCC MediaInformation::BoxType() const { return FOURCC_MINF; } -bool MediaInformation::Parse(BoxReader* reader) { - return reader->ScanChildren() && - reader->ReadChild(&sample_table); +bool MediaInformation::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&dinf) && + buffer->ReadWriteChild(&sample_table)); + if (sample_table.description.type == kVideo) + RCHECK(buffer->ReadWriteChild(&vmhd)); + else if (sample_table.description.type == kAudio) + RCHECK(buffer->ReadWriteChild(&smhd)); + else + NOTIMPLEMENTED(); + // Hint is not supported for now. + return true; +} + +uint32 MediaInformation::ComputeSize() { + atom_size = kBoxSize + dinf.ComputeSize() + sample_table.ComputeSize(); + if (sample_table.description.type == kVideo) + atom_size += vmhd.ComputeSize(); + else if (sample_table.description.type == kAudio) + atom_size += smhd.ComputeSize(); + return atom_size; } Media::Media() {} Media::~Media() {} FourCC Media::BoxType() const { return FOURCC_MDIA; } -bool Media::Parse(BoxReader* reader) { - RCHECK(reader->ScanChildren() && - reader->ReadChild(&header) && - reader->ReadChild(&handler)); - - // Maddeningly, the HandlerReference box specifies how to parse the - // SampleDescription box, making the latter the only box (of those that we - // support) which cannot be parsed correctly on its own (or even with - // information from its strict ancestor tree). We thus copy the handler type - // to the sample description box *before* parsing it to provide this - // information while parsing. - information.sample_table.description.type = handler.type; - RCHECK(reader->ReadChild(&information)); +bool Media::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&header) && + buffer->ReadWriteChild(&handler)); + if (buffer->Reading()) { + // Maddeningly, the HandlerReference box specifies how to parse the + // SampleDescription box, making the latter the only box (of those that we + // support) which cannot be parsed correctly on its own (or even with + // information from its strict ancestor tree). We thus copy the handler type + // to the sample description box *before* parsing it to provide this + // information while parsing. + information.sample_table.description.type = handler.type; + } else { + DCHECK_EQ(information.sample_table.description.type, handler.type); + } + RCHECK(buffer->ReadWriteChild(&information)); return true; } +uint32 Media::ComputeSize() { + atom_size = kBoxSize + header.ComputeSize() + handler.ComputeSize() + + information.ComputeSize(); + return atom_size; +} + Track::Track() {} Track::~Track() {} FourCC Track::BoxType() const { return FOURCC_TRAK; } -bool Track::Parse(BoxReader* reader) { - RCHECK(reader->ScanChildren() && - reader->ReadChild(&header) && - reader->ReadChild(&media) && - reader->MaybeReadChild(&edit)); +bool Track::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&header) && + buffer->ReadWriteChild(&media) && + buffer->TryReadWriteChild(&edit)); return true; } +uint32 Track::ComputeSize() { + atom_size = kBoxSize + header.ComputeSize() + media.ComputeSize() + + edit.ComputeSize(); + return atom_size; +} + MovieExtendsHeader::MovieExtendsHeader() : fragment_duration(0) {} MovieExtendsHeader::~MovieExtendsHeader() {} FourCC MovieExtendsHeader::BoxType() const { return FOURCC_MEHD; } -bool MovieExtendsHeader::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); - if (reader->version() == 1) { - RCHECK(reader->Read8(&fragment_duration)); - } else { - RCHECK(reader->Read4Into8(&fragment_duration)); - } +bool MovieExtendsHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + RCHECK(buffer->ReadWriteUInt64NBytes(&fragment_duration, num_bytes)); return true; } +uint32 MovieExtendsHeader::ComputeSize() { + atom_size = 0; + // This box is optional. Skip it if it is not used. + if (fragment_duration != 0) { + version = IsFitIn32Bits(fragment_duration) ? 0 : 1; + atom_size = kFullBoxSize + sizeof(uint32) * (1 + version); + } + return atom_size; +} + TrackExtends::TrackExtends() : track_id(0), default_sample_description_index(0), @@ -768,58 +1322,114 @@ TrackExtends::TrackExtends() TrackExtends::~TrackExtends() {} FourCC TrackExtends::BoxType() const { return FOURCC_TREX; } -bool TrackExtends::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&track_id) && - reader->Read4(&default_sample_description_index) && - reader->Read4(&default_sample_duration) && - reader->Read4(&default_sample_size) && - reader->Read4(&default_sample_flags)); +bool TrackExtends::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&track_id) && + buffer->ReadWriteUInt32(&default_sample_description_index) && + buffer->ReadWriteUInt32(&default_sample_duration) && + buffer->ReadWriteUInt32(&default_sample_size) && + buffer->ReadWriteUInt32(&default_sample_flags)); return true; } +uint32 TrackExtends::ComputeSize() { + atom_size = kFullBoxSize + sizeof(track_id) + + sizeof(default_sample_description_index) + + sizeof(default_sample_duration) + sizeof(default_sample_size) + + sizeof(default_sample_flags); + return atom_size; +} + MovieExtends::MovieExtends() {} MovieExtends::~MovieExtends() {} FourCC MovieExtends::BoxType() const { return FOURCC_MVEX; } -bool MovieExtends::Parse(BoxReader* reader) { - header.fragment_duration = 0; - return reader->ScanChildren() && - reader->MaybeReadChild(&header) && - reader->ReadChildren(&tracks); +bool MovieExtends::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->TryReadWriteChild(&header)); + if (buffer->Reading()) { + DCHECK(buffer->reader()); + RCHECK(buffer->reader()->ReadChildren(&tracks)); + } else { + for (uint32 i = 0; i < tracks.size(); ++i) + RCHECK(tracks[i].ReadWrite(buffer)); + } + return true; } -Movie::Movie() : fragmented(false) {} +uint32 MovieExtends::ComputeSize() { + // This box is optional. Skip it if it does not contain any track. + atom_size = 0; + if (tracks.size() != 0) { + atom_size = kBoxSize + header.ComputeSize(); + for (uint32 i = 0; i < tracks.size(); ++i) + atom_size += tracks[i].ComputeSize(); + } + return atom_size; +} + +Movie::Movie() {} Movie::~Movie() {} FourCC Movie::BoxType() const { return FOURCC_MOOV; } -bool Movie::Parse(BoxReader* reader) { - return reader->ScanChildren() && - reader->ReadChild(&header) && - reader->ReadChildren(&tracks) && - // Media Source specific: 'mvex' required - reader->MaybeReadChild(&extends) && - reader->MaybeReadChildren(&pssh); +bool Movie::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&header) && + buffer->TryReadWriteChild(&extends)); + if (buffer->Reading()) { + BoxReader* reader = buffer->reader(); + DCHECK(reader); + RCHECK(reader->ReadChildren(&tracks) && + reader->TryReadChildren(&pssh)); + } else { + for (uint32 i = 0; i < tracks.size(); ++i) + RCHECK(tracks[i].ReadWrite(buffer)); + for (uint32 i = 0; i < pssh.size(); ++i) + RCHECK(pssh[i].ReadWrite(buffer)); + } + return true; +} + +uint32 Movie::ComputeSize() { + atom_size = kBoxSize + header.ComputeSize() + extends.ComputeSize(); + for (uint32 i = 0; i < tracks.size(); ++i) + atom_size += tracks[i].ComputeSize(); + for (uint32 i = 0; i < pssh.size(); ++i) + atom_size += pssh[i].ComputeSize(); + return atom_size; } TrackFragmentDecodeTime::TrackFragmentDecodeTime() : decode_time(0) {} TrackFragmentDecodeTime::~TrackFragmentDecodeTime() {} FourCC TrackFragmentDecodeTime::BoxType() const { return FOURCC_TFDT; } -bool TrackFragmentDecodeTime::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader()); - if (reader->version() == 1) - return reader->Read8(&decode_time); - else - return reader->Read4Into8(&decode_time); +bool TrackFragmentDecodeTime::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer)); + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + RCHECK(buffer->ReadWriteUInt64NBytes(&decode_time, num_bytes)); + return true; +} + +uint32 TrackFragmentDecodeTime::ComputeSize() { + version = IsFitIn32Bits(decode_time) ? 0 : 1; + atom_size = kFullBoxSize + sizeof(uint32) * (1 + version); + return atom_size; } MovieFragmentHeader::MovieFragmentHeader() : sequence_number(0) {} MovieFragmentHeader::~MovieFragmentHeader() {} FourCC MovieFragmentHeader::BoxType() const { return FOURCC_MFHD; } -bool MovieFragmentHeader::Parse(BoxReader* reader) { - return reader->SkipBytes(4) && reader->Read4(&sequence_number); +bool MovieFragmentHeader::ReadWrite(BoxBuffer* buffer) { + return FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&sequence_number); +} + +uint32 MovieFragmentHeader::ComputeSize() { + atom_size = kFullBoxSize + sizeof(sequence_number); + return atom_size; } TrackFragmentHeader::TrackFragmentHeader() @@ -827,137 +1437,288 @@ TrackFragmentHeader::TrackFragmentHeader() sample_description_index(0), default_sample_duration(0), default_sample_size(0), - default_sample_flags(0), - has_default_sample_flags(false) {} + default_sample_flags(0) {} TrackFragmentHeader::~TrackFragmentHeader() {} FourCC TrackFragmentHeader::BoxType() const { return FOURCC_TFHD; } -bool TrackFragmentHeader::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader() && reader->Read4(&track_id)); +bool TrackFragmentHeader::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&track_id)); + // TODO(kqyang): we should support base-data-offset-present. // Media Source specific: reject tracks that set 'base-data-offset-present'. // Although the Media Source requires that 'default-base-is-moof' (14496-12 // Amendment 2) be set, we omit this check as many otherwise-valid files in // the wild don't set it. // - // RCHECK((flags & 0x020000) && !(flags & 0x1)); - RCHECK(!(reader->flags() & 0x1)); + // RCHECK((flags & kDefaultBaseIsMoofMask) && + // !(flags & kBaseDataOffsetPresentMask)); + RCHECK(!(flags & kDataOffsetPresentMask)); - if (reader->flags() & 0x2) { - RCHECK(reader->Read4(&sample_description_index)); - } else { + if (flags & kSampleDescriptionIndexPresentMask) { + RCHECK(buffer->ReadWriteUInt32(&sample_description_index)); + } else if (buffer->Reading()) { sample_description_index = 0; } - if (reader->flags() & 0x8) { - RCHECK(reader->Read4(&default_sample_duration)); - } else { + if (flags & kDefaultSampleDurationPresentMask) { + RCHECK(buffer->ReadWriteUInt32(&default_sample_duration)); + } else if (buffer->Reading()) { default_sample_duration = 0; } - if (reader->flags() & 0x10) { - RCHECK(reader->Read4(&default_sample_size)); - } else { + if (flags & kDefaultSampleSizePresentMask) { + RCHECK(buffer->ReadWriteUInt32(&default_sample_size)); + } else if (buffer->Reading()) { default_sample_size = 0; } - if (reader->flags() & 0x20) { - RCHECK(reader->Read4(&default_sample_flags)); - has_default_sample_flags = true; - } else { - has_default_sample_flags = false; - } - + if (flags & kDefaultSampleFlagsPresentMask) + RCHECK(buffer->ReadWriteUInt32(&default_sample_flags)); return true; } -TrackFragmentRun::TrackFragmentRun() - : sample_count(0), data_offset(0) {} +uint32 TrackFragmentHeader::ComputeSize() { + atom_size = kFullBoxSize + sizeof(track_id); + if (flags & kSampleDescriptionIndexPresentMask) + atom_size += sizeof(sample_description_index); + if (flags & kDefaultSampleDurationPresentMask) + atom_size += sizeof(default_sample_duration); + if (flags & kDefaultSampleSizePresentMask) + atom_size += sizeof(default_sample_size); + if (flags & kDefaultSampleFlagsPresentMask) + atom_size += sizeof(default_sample_flags); + return atom_size; +} + +TrackFragmentRun::TrackFragmentRun() : sample_count(0), data_offset(0) {} TrackFragmentRun::~TrackFragmentRun() {} FourCC TrackFragmentRun::BoxType() const { return FOURCC_TRUN; } -bool TrackFragmentRun::Parse(BoxReader* reader) { - RCHECK(reader->ReadFullBoxHeader() && - reader->Read4(&sample_count)); - const uint32 flags = reader->flags(); +bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&sample_count)); - bool data_offset_present = (flags & 0x1) != 0; - bool first_sample_flags_present = (flags & 0x4) != 0; - bool sample_duration_present = (flags & 0x100) != 0; - bool sample_size_present = (flags & 0x200) != 0; - bool sample_flags_present = (flags & 0x400) != 0; - bool sample_composition_time_offsets_present = (flags & 0x800) != 0; + bool data_offset_present = (flags & kDataOffsetPresentMask) != 0; + bool first_sample_flags_present = (flags & kFirstSampleFlagsPresentMask) != 0; + bool sample_duration_present = (flags & kSampleDurationPresentMask) != 0; + bool sample_size_present = (flags & kSampleSizePresentMask) != 0; + bool sample_flags_present = (flags & kSampleFlagsPresentMask) != 0; + bool sample_composition_time_offsets_present = + (flags & kSampleCompTimeOffsetsPresentMask) != 0; if (data_offset_present) { - RCHECK(reader->Read4(&data_offset)); + RCHECK(buffer->ReadWriteUInt32(&data_offset)); } else { - data_offset = 0; + // TODO(kqyang): this is incorrect. If the data-offset is not present, + // then the data for this run starts immediately after the data of the + // previous run, or at the base-data-offset defined by the track fragment + // header if this is the first run in a track fragment, If the data-offset + // is present, it is relative to the base-data-offset established in the + // track fragment header. + // data_offset = 0; + NOTIMPLEMENTED(); } uint32 first_sample_flags; - if (first_sample_flags_present) - RCHECK(reader->Read4(&first_sample_flags)); - int fields = sample_duration_present + sample_size_present + - sample_flags_present + sample_composition_time_offsets_present; - RCHECK(reader->HasBytes(fields * sample_count)); + if (buffer->Reading()) { + if (first_sample_flags_present) + RCHECK(buffer->ReadWriteUInt32(&first_sample_flags)); - if (sample_duration_present) - sample_durations.resize(sample_count); - if (sample_size_present) - sample_sizes.resize(sample_count); - if (sample_flags_present) - sample_flags.resize(sample_count); - if (sample_composition_time_offsets_present) - sample_composition_time_offsets.resize(sample_count); + if (sample_duration_present) + sample_durations.resize(sample_count); + if (sample_size_present) + sample_sizes.resize(sample_count); + if (sample_flags_present) + sample_flags.resize(sample_count); + if (sample_composition_time_offsets_present) + sample_composition_time_offsets.resize(sample_count); + } else { + if (first_sample_flags_present) { + first_sample_flags = sample_flags[0]; + DCHECK(sample_flags.size() == 1); + RCHECK(buffer->ReadWriteUInt32(&first_sample_flags)); + } + + if (sample_duration_present) + DCHECK(sample_durations.size() == sample_count); + if (sample_size_present) + DCHECK(sample_sizes.size() == sample_count); + if (sample_flags_present) + DCHECK(sample_flags.size() == sample_count); + if (sample_composition_time_offsets_present) + DCHECK(sample_composition_time_offsets.size() == sample_count); + } for (uint32 i = 0; i < sample_count; ++i) { if (sample_duration_present) - RCHECK(reader->Read4(&sample_durations[i])); + RCHECK(buffer->ReadWriteUInt32(&sample_durations[i])); if (sample_size_present) - RCHECK(reader->Read4(&sample_sizes[i])); + RCHECK(buffer->ReadWriteUInt32(&sample_sizes[i])); if (sample_flags_present) - RCHECK(reader->Read4(&sample_flags[i])); + RCHECK(buffer->ReadWriteUInt32(&sample_flags[i])); if (sample_composition_time_offsets_present) - RCHECK(reader->Read4s(&sample_composition_time_offsets[i])); + RCHECK(buffer->ReadWriteInt32(&sample_composition_time_offsets[i])); } - if (first_sample_flags_present) { - if (sample_flags.size() == 0) { - sample_flags.push_back(first_sample_flags); - } else { - sample_flags[0] = first_sample_flags; + if (buffer->Reading()) { + if (first_sample_flags_present) { + if (sample_flags.size() == 0) { + sample_flags.push_back(first_sample_flags); + } else { + sample_flags[0] = first_sample_flags; + } } } return true; } +uint32 TrackFragmentRun::ComputeSize() { + version = 1; // Version 1 to support signed offset. + atom_size = kFullBoxSize + sizeof(sample_count); + if (flags & kDataOffsetPresentMask) + atom_size += sizeof(data_offset); + if (flags & kFirstSampleFlagsPresentMask) + atom_size += sizeof(uint32); + uint32 fields = (flags & kSampleDurationPresentMask ? 1 : 0) + + (flags & kSampleSizePresentMask ? 1 : 0) + + (flags & kSampleFlagsPresentMask ? 1 : 0) + + (flags & kSampleCompTimeOffsetsPresentMask ? 1 : 0); + atom_size += fields * sizeof(uint32) * sample_count; + return atom_size; +} + TrackFragment::TrackFragment() {} TrackFragment::~TrackFragment() {} FourCC TrackFragment::BoxType() const { return FOURCC_TRAF; } -bool TrackFragment::Parse(BoxReader* reader) { - return reader->ScanChildren() && - reader->ReadChild(&header) && +bool TrackFragment::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&header) && // Media Source specific: 'tfdt' required - reader->ReadChild(&decode_time) && - reader->MaybeReadChildren(&runs) && - reader->MaybeReadChild(&auxiliary_offset) && - reader->MaybeReadChild(&auxiliary_size); + buffer->ReadWriteChild(&decode_time)); + if (buffer->Reading()) { + DCHECK(buffer->reader()); + RCHECK(buffer->reader()->TryReadChildren(&runs)); + } else { + for (uint32 i = 0; i < runs.size(); ++i) + RCHECK(runs[i].ReadWrite(buffer)); + } + return buffer->TryReadWriteChild(&auxiliary_size) && + buffer->TryReadWriteChild(&auxiliary_offset); +} + +uint32 TrackFragment::ComputeSize() { + atom_size = kBoxSize + header.ComputeSize() + decode_time.ComputeSize() + + auxiliary_size.ComputeSize() + auxiliary_offset.ComputeSize(); + for (uint32 i = 0; i < runs.size(); ++i) + atom_size += runs[i].ComputeSize(); + return atom_size; } MovieFragment::MovieFragment() {} MovieFragment::~MovieFragment() {} FourCC MovieFragment::BoxType() const { return FOURCC_MOOF; } -bool MovieFragment::Parse(BoxReader* reader) { - RCHECK(reader->ScanChildren() && - reader->ReadChild(&header) && - reader->ReadChildren(&tracks) && - reader->MaybeReadChildren(&pssh)); +bool MovieFragment::ReadWrite(BoxBuffer* buffer) { + RCHECK(Box::ReadWrite(buffer) && + buffer->PrepareChildren() && + buffer->ReadWriteChild(&header)); + if (buffer->Reading()) { + BoxReader* reader = buffer->reader(); + DCHECK(reader); + RCHECK(reader->ReadChildren(&tracks) && + reader->TryReadChildren(&pssh)); + } else { + for (uint32 i = 0; i < tracks.size(); ++i) + RCHECK(tracks[i].ReadWrite(buffer)); + for (uint32 i = 0; i < pssh.size(); ++i) + RCHECK(pssh[i].ReadWrite(buffer)); + } return true; } +uint32 MovieFragment::ComputeSize() { + atom_size = kBoxSize + header.ComputeSize(); + for (uint32 i = 0; i < tracks.size(); ++i) + atom_size += tracks[i].ComputeSize(); + for (uint32 i = 0; i < pssh.size(); ++i) + atom_size += pssh[i].ComputeSize(); + return atom_size; +} + +SegmentIndex::SegmentIndex() + : reference_id(0), + timescale(0), + earliest_presentation_time(0), + first_offset(0) {} +SegmentIndex::~SegmentIndex() {} +FourCC SegmentIndex::BoxType() const { return FOURCC_SIDX; } + +bool SegmentIndex::ReadWrite(BoxBuffer* buffer) { + RCHECK(FullBox::ReadWrite(buffer) && + buffer->ReadWriteUInt32(&reference_id) && + buffer->ReadWriteUInt32(×cale)); + + size_t num_bytes = (version == 1) ? sizeof(uint64) : sizeof(uint32); + RCHECK( + buffer->ReadWriteUInt64NBytes(&earliest_presentation_time, num_bytes) && + buffer->ReadWriteUInt64NBytes(&first_offset, num_bytes)); + + uint16 reference_count = references.size(); + RCHECK(buffer->IgnoreBytes(2) && // reserved. + buffer->ReadWriteUInt16(&reference_count)); + references.resize(reference_count); + + uint32 reference_type_size; + uint32 sap; + for (uint32 i = 0; i < reference_count; ++i) { + if (!buffer->Reading()) { + reference_type_size = references[i].referenced_size; + if (references[i].reference_type) + reference_type_size |= (1 << 31); + sap = (references[i].sap_type << 28) | references[i].sap_delta_time; + if (references[i].starts_with_sap) + sap |= (1 << 31); + } + RCHECK(buffer->ReadWriteUInt32(&reference_type_size) && + buffer->ReadWriteUInt32(&references[i].subsegment_duration) && + buffer->ReadWriteUInt32(&sap)); + if (buffer->Reading()) { + references[i].reference_type = (reference_type_size >> 31) ? true : false; + references[i].referenced_size = reference_type_size & ~(1 << 31); + references[i].starts_with_sap = (sap >> 31) ? true : false; + references[i].sap_type = + static_cast((sap >> 28) & 0x07); + references[i].sap_delta_time = sap & ~(0xF << 28); + } + } + return true; +} + +uint32 SegmentIndex::ComputeSize() { + version = IsFitIn32Bits(earliest_presentation_time, first_offset) ? 0 : 1; + atom_size = kFullBoxSize + sizeof(reference_id) + sizeof(timescale) + + sizeof(uint32) * (1 + version) * 2 + 2 * sizeof(uint16) + + 3 * sizeof(uint32) * references.size(); + return atom_size; +} + +MediaData::MediaData() : data_size(0) {} +MediaData::~MediaData() {} +FourCC MediaData::BoxType() const { return FOURCC_MDAT; } + +void MediaData::Write(BufferWriter* buffer) { + buffer->AppendInt(ComputeSize()); + buffer->AppendInt(static_cast(BoxType())); +} + +uint32 MediaData::ComputeSize() { + return kBoxSize + data_size; +} + } // namespace mp4 } // namespace media diff --git a/media/mp4/box_definitions.h b/media/mp4/box_definitions.h index b4240f1108..6ce0047bce 100644 --- a/media/mp4/box_definitions.h +++ b/media/mp4/box_definitions.h @@ -9,10 +9,14 @@ #include #include "media/mp4/aac.h" -#include "media/mp4/box_reader.h" +#include "media/mp4/box.h" +#include "media/mp4/es_descriptor.h" #include "media/mp4/fourccs.h" namespace media { + +class BufferReader; + namespace mp4 { enum TrackType { @@ -22,33 +26,42 @@ enum TrackType { kHint }; -#define DECLARE_BOX_METHODS(T) \ - T(); \ - virtual ~T(); \ - virtual bool Parse(BoxReader* reader) OVERRIDE; \ - virtual FourCC BoxType() const OVERRIDE; \ +class BoxBuffer; + +#define DECLARE_BOX_METHODS(T) \ + T(); \ + virtual ~T(); \ + virtual bool ReadWrite(BoxBuffer* buffer) OVERRIDE; \ + virtual FourCC BoxType() const OVERRIDE; \ + virtual uint32 ComputeSize() OVERRIDE; struct FileType : Box { DECLARE_BOX_METHODS(FileType); FourCC major_brand; uint32 minor_version; + std::vector compatible_brands; }; -struct ProtectionSystemSpecificHeader : Box { +struct SegmentType : FileType { + DECLARE_BOX_METHODS(SegmentType); +}; + +struct ProtectionSystemSpecificHeader : FullBox { DECLARE_BOX_METHODS(ProtectionSystemSpecificHeader); std::vector system_id; + std::vector data; std::vector raw_box; }; -struct SampleAuxiliaryInformationOffset : Box { +struct SampleAuxiliaryInformationOffset : FullBox { DECLARE_BOX_METHODS(SampleAuxiliaryInformationOffset); std::vector offsets; }; -struct SampleAuxiliaryInformationSize : Box { +struct SampleAuxiliaryInformationSize : FullBox { DECLARE_BOX_METHODS(SampleAuxiliaryInformationSize); uint8 default_sample_info_size; @@ -62,14 +75,14 @@ struct OriginalFormat : Box { FourCC format; }; -struct SchemeType : Box { +struct SchemeType : FullBox { DECLARE_BOX_METHODS(SchemeType); FourCC type; uint32 version; }; -struct TrackEncryption : Box { +struct TrackEncryption : FullBox { DECLARE_BOX_METHODS(TrackEncryption); // Note: this definition is specific to the CENC protection type. @@ -92,7 +105,7 @@ struct ProtectionSchemeInfo : Box { SchemeInfo info; }; -struct MovieHeader : Box { +struct MovieHeader : FullBox { DECLARE_BOX_METHODS(MovieHeader); uint64 creation_time; @@ -104,7 +117,13 @@ struct MovieHeader : Box { uint32 next_track_id; }; -struct TrackHeader : Box { +struct TrackHeader : FullBox { + enum TrackHeaderFlags { + kTrackEnabled = 0x000001, + kTrackInMovie = 0x000002, + kTrackInPreview = 0x000004, + }; + DECLARE_BOX_METHODS(TrackHeader); uint64 creation_time; @@ -125,7 +144,7 @@ struct EditListEntry { int16 media_rate_fraction; }; -struct EditList : Box { +struct EditList : FullBox { DECLARE_BOX_METHODS(EditList); std::vector edits; @@ -137,7 +156,7 @@ struct Edit : Box { EditList list; }; -struct HandlerReference : Box { +struct HandlerReference : FullBox { DECLARE_BOX_METHODS(HandlerReference); TrackType type; @@ -188,11 +207,11 @@ struct VideoSampleEntry : Box { AVCDecoderConfigurationRecord avcc; }; -struct ElementaryStreamDescriptor : Box { +struct ElementaryStreamDescriptor : FullBox { DECLARE_BOX_METHODS(ElementaryStreamDescriptor); - uint8 object_type; AAC aac; + ESDescriptor es_descriptor; }; struct AudioSampleEntry : Box { @@ -208,7 +227,7 @@ struct AudioSampleEntry : Box { ElementaryStreamDescriptor esds; }; -struct SampleDescription : Box { +struct SampleDescription : FullBox { DECLARE_BOX_METHODS(SampleDescription); TrackType type; @@ -222,7 +241,7 @@ struct DecodingTime { }; // stts. -struct DecodingTimeToSample : Box { +struct DecodingTimeToSample : FullBox { DECLARE_BOX_METHODS(DecodingTimeToSample); std::vector decoding_time; @@ -232,13 +251,13 @@ struct CompositionOffset { uint32 sample_count; // If version == 0, sample_offset is uint32; // If version == 1, sample_offset is int32. - // Let us always use signed version, which should work unless the offset + // Always use signed version, which should work unless the offset // exceeds 31 bits, which shouldn't happen. int32 sample_offset; }; // ctts. Optional. -struct CompositionTimeToSample : Box { +struct CompositionTimeToSample : FullBox { DECLARE_BOX_METHODS(CompositionTimeToSample); std::vector composition_offset; @@ -251,14 +270,14 @@ struct ChunkInfo { }; // stsc. -struct SampleToChunk : Box { +struct SampleToChunk : FullBox { DECLARE_BOX_METHODS(SampleToChunk); std::vector chunk_info; }; // stsz. -struct SampleSize : Box { +struct SampleSize : FullBox { DECLARE_BOX_METHODS(SampleSize); uint32 sample_size; @@ -267,27 +286,27 @@ struct SampleSize : Box { }; // stz2. -struct CompactSampleSize : SampleSize { +struct CompactSampleSize : FullBox { DECLARE_BOX_METHODS(CompactSampleSize); -}; -// stco. -struct ChunkOffset : Box { - DECLARE_BOX_METHODS(ChunkOffset); - - // Chunk byte offsets into mdat relative to the beginning of the file. - // Use 64 bits instead of 32 bits so it is large enough to hold - // ChunkLargeOffset data. - std::vector offsets; + uint8 field_size; + std::vector sizes; }; // co64. -struct ChunkLargeOffset : ChunkOffset { +struct ChunkLargeOffset : FullBox { DECLARE_BOX_METHODS(ChunkLargeOffset); + + std::vector offsets; +}; + +// stco. +struct ChunkOffset : ChunkLargeOffset { + DECLARE_BOX_METHODS(ChunkOffset); }; // stss. Optional. -struct SyncSample : Box { +struct SyncSample : FullBox { DECLARE_BOX_METHODS(SyncSample); std::vector sample_number; @@ -302,12 +321,13 @@ struct SampleTable : Box { SampleToChunk sample_to_chunk; // Either SampleSize or CompactSampleSize must present. Store in SampleSize. SampleSize sample_size; - // Either ChunkOffset or ChunkLargeOffset must present. Store in ChunkOffset. - ChunkOffset chunk_offset; + // Either ChunkOffset or ChunkLargeOffset must present. Store in + // ChunkLargeOffset. + ChunkLargeOffset chunk_large_offset; SyncSample sync_sample; }; -struct MediaHeader : Box { +struct MediaHeader : FullBox { DECLARE_BOX_METHODS(MediaHeader); uint64 creation_time; @@ -318,10 +338,48 @@ struct MediaHeader : Box { char language[4]; }; +struct VideoMediaHeader : FullBox { + DECLARE_BOX_METHODS(VideoMediaHeader); + + uint16 graphicsmode; + uint16 opcolor_red; + uint16 opcolor_green; + uint16 opcolor_blue; +}; + +struct SoundMediaHeader : FullBox { + DECLARE_BOX_METHODS(SoundMediaHeader); + + uint16 balance; +}; + +struct DataEntryUrl : FullBox { + DECLARE_BOX_METHODS(DataEntryUrl); + + std::vector location; +}; + +struct DataReference : FullBox { + DECLARE_BOX_METHODS(DataReference); + + // data entry can be either url or urn box. Fix to url box for now. + std::vector data_entry; +}; + +struct DataInformation : Box { + DECLARE_BOX_METHODS(DataInformation); + + DataReference dref; +}; + struct MediaInformation : Box { DECLARE_BOX_METHODS(MediaInformation); + DataInformation dinf; SampleTable sample_table; + // Exactly one specific meida header shall be present, vmhd, smhd, hmhd, nmhd. + VideoMediaHeader vmhd; + SoundMediaHeader smhd; }; struct Media : Box { @@ -340,13 +398,13 @@ struct Track : Box { Edit edit; }; -struct MovieExtendsHeader : Box { +struct MovieExtendsHeader : FullBox { DECLARE_BOX_METHODS(MovieExtendsHeader); uint64 fragment_duration; }; -struct TrackExtends : Box { +struct TrackExtends : FullBox { DECLARE_BOX_METHODS(TrackExtends); uint32 track_id; @@ -366,41 +424,64 @@ struct MovieExtends : Box { struct Movie : Box { DECLARE_BOX_METHODS(Movie); - bool fragmented; MovieHeader header; MovieExtends extends; std::vector tracks; std::vector pssh; }; -struct TrackFragmentDecodeTime : Box { +struct TrackFragmentDecodeTime : FullBox { DECLARE_BOX_METHODS(TrackFragmentDecodeTime); uint64 decode_time; }; -struct MovieFragmentHeader : Box { +struct MovieFragmentHeader : FullBox { DECLARE_BOX_METHODS(MovieFragmentHeader); uint32 sequence_number; }; -struct TrackFragmentHeader : Box { +struct TrackFragmentHeader : FullBox { + enum TrackFragmentFlagsMasks { + kDataOffsetPresentMask = 0x000001, + kSampleDescriptionIndexPresentMask = 0x000002, + kDefaultSampleDurationPresentMask = 0x000008, + kDefaultSampleSizePresentMask = 0x000010, + kDefaultSampleFlagsPresentMask = 0x000020, + kDurationIsEmptyMask = 0x010000, + kDefaultBaseIsMoofMask = 0x020000, + }; + + enum SampleFlagsMasks { + kReservedMask = 0xFC000000, + kSampleDependsOnMask = 0x03000000, + kSampleIsDependedOnMask = 0x00C00000, + kSampleHasRedundancyMask = 0x00300000, + kSamplePaddingValueMask = 0x000E0000, + kNonKeySampleMask = 0x00010000, + kSampleDegradationPriorityMask = 0x0000FFFF, + }; + DECLARE_BOX_METHODS(TrackFragmentHeader); uint32 track_id; - uint32 sample_description_index; uint32 default_sample_duration; uint32 default_sample_size; uint32 default_sample_flags; - - // As 'flags' might be all zero, we cannot use zeroness alone to identify - // when default_sample_flags wasn't specified, unlike the other values. - bool has_default_sample_flags; }; -struct TrackFragmentRun : Box { +struct TrackFragmentRun : FullBox { + enum TrackFragmentFlagsMasks { + kDataOffsetPresentMask = 0x000001, + kFirstSampleFlagsPresentMask = 0x000004, + kSampleDurationPresentMask = 0x000100, + kSampleSizePresentMask = 0x000200, + kSampleFlagsPresentMask = 0x000400, + kSampleCompTimeOffsetsPresentMask = 0x000800, + }; + DECLARE_BOX_METHODS(TrackFragmentRun); uint32 sample_count; @@ -429,6 +510,50 @@ struct MovieFragment : Box { std::vector pssh; }; +struct SegmentReference { + enum SAPType { + TypeUnknown = 0, + Type1 = 1, // T(ept) = T(dec) = T(sap) = T(ptf) + Type2 = 2, // T(ept) = T(dec) = T(sap) < T(ptf) + Type3 = 3, // T(ept) < T(dec) = T(sap) <= T(ptf) + Type4 = 4, // T(ept) <= T(ptf) < T(dec) = T(sap) + Type5 = 5, // T(ept) = T(dec) < T(sap) + Type6 = 6, // T(ept) < T(dec) < T(sap) + }; + + bool reference_type; + uint32 referenced_size; + uint32 subsegment_duration; + bool starts_with_sap; + SAPType sap_type; + uint32 sap_delta_time; + // We add this field to keep track of earliest_presentation_time in this + // subsegment. It is not part of SegmentReference. + uint64 earliest_presentation_time; +}; + +struct SegmentIndex : FullBox { + DECLARE_BOX_METHODS(SegmentIndex); + + uint32 reference_id; + uint32 timescale; + uint64 earliest_presentation_time; + uint64 first_offset; + std::vector references; +}; + +// The actual data is parsed and written separately, so we do not inherit it +// from Box. +struct MediaData { + MediaData(); + ~MediaData(); + void Write(BufferWriter* buffer_writer); + uint32 ComputeSize(); + FourCC BoxType() const; + + uint32 data_size; +}; + #undef DECLARE_BOX } // namespace mp4 diff --git a/media/mp4/fourccs.h b/media/mp4/fourccs.h index 982b6aa62e..9c3e2bd664 100644 --- a/media/mp4/fourccs.h +++ b/media/mp4/fourccs.h @@ -18,7 +18,9 @@ enum FourCC { FOURCC_CENC = 0x63656e63, FOURCC_CO64 = 0x636f3634, FOURCC_CTTS = 0x63747473, + FOURCC_DASH = 0x64617368, FOURCC_DINF = 0x64696e66, + FOURCC_DREF = 0x64726566, FOURCC_EAC3 = 0x65632d33, FOURCC_EDTS = 0x65647473, FOURCC_ELST = 0x656c7374, @@ -30,6 +32,7 @@ enum FourCC { FOURCC_FTYP = 0x66747970, FOURCC_HDLR = 0x68646c72, FOURCC_HINT = 0x68696e74, + FOURCC_ISO6 = 0x69736f36, FOURCC_IODS = 0x696f6473, FOURCC_MDAT = 0x6d646174, FOURCC_MDHD = 0x6d646864, @@ -42,6 +45,7 @@ enum FourCC { FOURCC_MINF = 0x6d696e66, FOURCC_MOOF = 0x6d6f6f66, FOURCC_MOOV = 0x6d6f6f76, + FOURCC_MP41 = 0x6d703431, FOURCC_MP4A = 0x6d703461, FOURCC_MP4V = 0x6d703476, FOURCC_MVEX = 0x6d766578, @@ -79,6 +83,8 @@ enum FourCC { FOURCC_TREX = 0x74726578, FOURCC_TRUN = 0x7472756e, FOURCC_UDTA = 0x75647461, + FOURCC_URL = 0x75726c20, + FOURCC_URN = 0x75726e20, FOURCC_UUID = 0x75756964, FOURCC_VIDE = 0x76696465, FOURCC_VMHD = 0x766d6864,