Chromium Code Reviews| Index: media/mp4/box_definitions.cc |
| diff --git a/media/mp4/box_definitions.cc b/media/mp4/box_definitions.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7a2964c56692c26958f80cdc18f04f35aa0c838f |
| --- /dev/null |
| +++ b/media/mp4/box_definitions.cc |
| @@ -0,0 +1,547 @@ |
| +// Copyright (c) 2012 The Chromium Authors. 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/logging.h" |
| +#include "media/mp4/box_reader.h" |
| +#include "media/mp4/rcheck.h" |
| +#include "media/mp4/fourccs.h" |
| + |
| +namespace media { |
| +namespace mp4 { |
| + |
| +bool Parse(BoxReader* r, FileType* type) { |
|
acolwell GONE FROM CHROMIUM
2012/06/06 17:32:39
nit: r -> reader here and elsewhere
strobe_
2012/06/07 16:33:54
I'd argue that in this file, the reduced visual de
acolwell GONE FROM CHROMIUM
2012/06/07 19:44:02
I'm unconvinced. :) Please change it here and in a
|
| + RCHECK(r->Read(&type->major_brand) && r->Read(&type->minor_version)); |
| + size_t num_brands = (r->size() - r->pos()) / sizeof(FourCC); |
| + return r->Skip(sizeof(FourCC) * num_brands); // compatible_brands |
| +} |
| + |
| +ProtectionSystemSpecificHeader::ProtectionSystemSpecificHeader() {} |
| +ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {} |
| + |
| +bool Parse(BoxReader* r, ProtectionSystemSpecificHeader* pssh) { |
| + uint32 size; |
| + return r->Skip(4) && |
| + r->Read(&pssh->system_id, 16) && |
| + r->Read(&size) && |
| + r->Read(&pssh->data, size); |
| +} |
| + |
| +SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() {} |
| +SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() {} |
| + |
| +bool Parse(BoxReader* r, SampleAuxiliaryInformationOffset* saio) { |
| + RCHECK(r->ReadFullBoxHeader()); |
| + if (r->flags() & 1) |
| + RCHECK(r->Skip(8)); |
| + |
| + uint32 count; |
| + RCHECK(r->Read(&count) && |
| + r->HasBytes(count * (r->version() == 1 ? 8 : 4))); |
| + saio->offsets.resize(count); |
| + |
| + for (uint32 i = 0; i < count; i++) { |
| + if (r->version() == 1) { |
| + RCHECK(r->Read(&saio->offsets[i])); |
| + } else { |
| + RCHECK(r->Read4(&saio->offsets[i])); |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +SampleAuxiliaryInformationSize::SampleAuxiliaryInformationSize() |
| + : default_sample_info_size(0), sample_count(0) {} |
|
acolwell GONE FROM CHROMIUM
2012/06/06 17:32:39
nit: } on next line
strobe_
2012/06/07 16:33:54
Done.
|
| + |
| +SampleAuxiliaryInformationSize::~SampleAuxiliaryInformationSize() {} |
| + |
| +bool Parse(BoxReader* r, SampleAuxiliaryInformationSize* saiz) { |
| + RCHECK(r->ReadFullBoxHeader()); |
| + if (r->flags() & 1) |
| + RCHECK(r->Skip(8)); |
| + |
| + RCHECK(r->Read(&saiz->default_sample_info_size) && |
| + r->Read(&saiz->sample_count)); |
| + if (saiz->default_sample_info_size == 0) |
| + return r->Read(&saiz->sample_info_sizes, saiz->sample_count); |
| + return true; |
| +} |
| + |
| +bool Parse(BoxReader* r, OriginalFormat* format) { |
| + return r->Read(&format->format); |
| +} |
| + |
| +bool Parse(BoxReader* r, SchemeType* schm) { |
| + RCHECK(r->Skip(4) && r->Read(&schm->type) && r->Read(&schm->version)); |
| + RCHECK(schm->type == FOURCC_CENC); |
| + return true; |
| +} |
| + |
| +TrackEncryption::TrackEncryption() |
| + : is_encrypted(false), default_iv_size(0) {} |
|
acolwell GONE FROM CHROMIUM
2012/06/06 17:32:39
nit: } on next line.
strobe_
2012/06/07 16:33:54
Done.
|
| +TrackEncryption::~TrackEncryption() {} |
| + |
| +bool Parse(BoxReader* r, TrackEncryption* tenc) { |
| + uint8 flag; |
| + RCHECK(r->Skip(2) && |
| + r->Read(&flag) && |
| + r->Read(&tenc->default_iv_size) && |
| + r->Read(&tenc->default_kid, 16)); |
| + tenc->is_encrypted = (flag != 0); |
| + if (tenc->is_encrypted) { |
| + RCHECK(tenc->default_iv_size == 8 || tenc->default_iv_size == 16); |
| + } else { |
| + RCHECK(tenc->default_iv_size == 0); |
| + } |
| + return true; |
| +} |
| + |
| +bool Parse(BoxReader* r, SchemeInfo* schi) { |
| + return r->ScanChildren() && r->ReadChild(&schi->track_encryption); |
| +} |
| + |
| +bool Parse(BoxReader* r, ProtectionSchemeInfo* sinf) { |
| + return r->ScanChildren() && |
| + r->ReadChild(&sinf->type) && |
| + r->ReadChild(&sinf->info); |
| +} |
| + |
| +bool Parse(BoxReader* r, MovieHeader* mvhd) { |
| + RCHECK(r->ReadFullBoxHeader()); |
| + |
| + if (r->version() == 1) { |
| + RCHECK(r->Read(&mvhd->creation_time) && |
| + r->Read(&mvhd->modification_time) && |
| + r->Read(&mvhd->timescale) && |
| + r->Read(&mvhd->duration)); |
| + } else { |
| + RCHECK(r->Read4(&mvhd->creation_time) && |
| + r->Read4(&mvhd->modification_time) && |
| + r->Read(&mvhd->timescale) && |
| + r->Read4(&mvhd->duration)); |
| + } |
| + |
| + RCHECK(r->Read(&mvhd->rate) && |
| + r->Read(&mvhd->volume) && |
| + r->Skip(10) && // reserved |
|
acolwell GONE FROM CHROMIUM
2012/06/06 17:32:39
WDYT about renaming this to SkipBytes so the units
strobe_
2012/06/07 16:33:54
Done.
|
| + r->Skip(36) && // matrix |
| + r->Skip(24) && // predefined zero |
| + r->Read(&mvhd->next_track_id)); |
| + return true; |
| +} |
| + |
| +TrackHeader::TrackHeader() {} |
| +TrackHeader::~TrackHeader() {} |
| + |
| + |
| +bool Parse(BoxReader* r, TrackHeader* tkhd) { |
| + RCHECK(r->ReadFullBoxHeader()); |
| + if (r->version() == 1) { |
| + RCHECK(r->Read(&tkhd->creation_time) && |
| + r->Read(&tkhd->modification_time) && |
| + r->Read(&tkhd->track_id) && |
| + r->Skip(4) && // reserved |
| + r->Read(&tkhd->duration)); |
| + } else { |
| + RCHECK(r->Read4(&tkhd->creation_time) && |
| + r->Read4(&tkhd->modification_time) && |
| + r->Read(&tkhd->track_id) && |
| + r->Skip(4) && // reserved |
| + r->Read4(&tkhd->duration)); |
| + } |
| + |
| + RCHECK(r->Skip(8) && // reserved |
| + r->Read(&tkhd->layer) && |
| + r->Read(&tkhd->alternate_group) && |
| + r->Read(&tkhd->volume) && |
| + r->Skip(2) && // reserved |
| + r->Skip(36) && // matrix |
| + r->Read(&tkhd->width) && |
| + r->Read(&tkhd->height)); |
| + tkhd->width >>= 16; |
| + tkhd->height >>= 16; |
| + return true; |
| +} |
| + |
| +SampleDescription::SampleDescription() {} |
| +SampleDescription::~SampleDescription() {} |
| + |
| +bool Parse(BoxReader* r, SampleDescription* desc) { |
| + uint32 count; |
| + RCHECK(r->Skip(4) && r->Read(&count) && r->ScanChildren()); |
| + desc->video_entries.clear(); |
| + desc->audio_entries.clear(); |
| + |
| + // Note: this value is preset before scanning begins. See comments in the |
| + // Parse(Media*) function. |
| + if (desc->type == kVideo) { |
| + RCHECK(r->ReadAllChildren(&desc->video_entries)); |
| + } else if (desc->type == kAudio) { |
| + RCHECK(r->ReadAllChildren(&desc->audio_entries)); |
| + } |
| + return true; |
| +} |
| + |
| +SampleTable::SampleTable() {} |
| +SampleTable::~SampleTable() {} |
| + |
| +bool Parse(BoxReader* r, SampleTable* table) { |
| + return r->ScanChildren() && |
| + r->ReadChild(&table->description); |
| +} |
| + |
| +EditList::EditList() {} |
| +EditList::~EditList() {} |
| + |
| +bool Parse(BoxReader* r, EditList* list) { |
| + uint32 count; |
| + RCHECK(r->ReadFullBoxHeader() && r->Read(&count)); |
| + |
| + if (r->version() == 1) { |
| + RCHECK(r->HasBytes(count * 20)); |
| + } else { |
| + RCHECK(r->HasBytes(count * 12)); |
| + } |
| + list->edits.resize(count); |
| + |
| + for (auto edit = list->edits.begin(); edit != list->edits.end(); ++edit) { |
| + if (r->version() == 1) { |
| + RCHECK(r->Read(&edit->segment_duration) && |
| + r->Read(&edit->media_time)); |
| + } else { |
| + RCHECK(r->Read4(&edit->segment_duration) && |
| + r->Read4(&edit->media_time)); |
| + } |
| + RCHECK(r->Read(&edit->media_rate_integer) && |
| + r->Read(&edit->media_rate_fraction)); |
| + } |
| + return true; |
| +} |
| + |
| +Edit::Edit() {} |
| +Edit::~Edit() {} |
| + |
| +bool Parse(BoxReader* r, Edit* box) { |
| + return r->ScanChildren() && r->ReadChild(&box->list); |
| +} |
| + |
| +bool Parse(BoxReader* r, HandlerReference* reference) { |
| + FourCC type; |
| + RCHECK(r->Skip(8) && r->Read(&type)); |
| + // Note: remaining fields in box ignored |
| + if (type == FOURCC_VIDE) { |
| + |
| + reference->type = kVideo; |
| + } else if (type == FOURCC_SOUN) { |
| + reference->type = kAudio; |
| + } else { |
| + reference->type = kInvalid; |
| + } |
| + return true; |
| +} |
| + |
| +SampleEntry::SampleEntry() {} |
| +SampleEntry::~SampleEntry() {} |
| + |
| +bool ParseSampleEntryHead(BoxReader* r, SampleEntry* entry) { |
| + entry->format = r->type(); |
| + return r->Skip(6) && r->Read(&entry->data_reference_index); |
| +} |
| + |
| +AudioSampleEntry::AudioSampleEntry() {} |
| +AudioSampleEntry::~AudioSampleEntry() {} |
| + |
| +bool Parse(BoxReader* r, AudioSampleEntry* entry) { |
| + RCHECK(ParseSampleEntryHead(r, entry) && |
| + r->Skip(8) && |
| + r->Read(&entry->channelcount) && |
| + r->Read(&entry->samplesize) && |
| + r->Skip(4) && |
| + r->Read(&entry->samplerate)); |
| + // Convert from 16.16 fixed point to integer |
| + entry->samplerate >>= 16; |
| + |
| + RCHECK(r->ScanChildren()); |
| + if (entry->format == FOURCC_ENCA) { |
| + RCHECK(r->ReadChild(&entry->sinf)); |
| + } |
| + return true; |
| +} |
| + |
| +VideoSampleEntry::VideoSampleEntry() {} |
| +VideoSampleEntry::~VideoSampleEntry() {} |
| + |
| +bool Parse(BoxReader* r, VideoSampleEntry* entry) { |
| + RCHECK(ParseSampleEntryHead(r, entry) && |
| + r->Skip(16) && |
| + r->Read(&entry->width) && |
| + r->Read(&entry->height) && |
| + r->Skip(50)); |
| + |
| + RCHECK(r->ScanChildren()); |
| + if (entry->format == FOURCC_ENCV) { |
| + RCHECK(r->ReadChild(&entry->sinf)); |
| + } |
| + |
| + // TODO(strobe): Widevine test files do not follow spec here; check disabled |
| + //if (entry->format == FOURCC_AVC1 || |
| + // (entry->format == FOURCC_ENCV && |
| + // entry->sinf.format.format == FOURCC_AVC1)) { |
| + RCHECK(r->ReadChild(&entry->avcc)); |
| + //} |
| + return true; |
| +} |
| + |
| +MediaHeader::MediaHeader() {} |
| +MediaHeader::~MediaHeader() {} |
| + |
| +bool Parse(BoxReader* r, MediaHeader* header) { |
| + RCHECK(r->ReadFullBoxHeader()); |
| + |
| + if (r->version() == 1) { |
| + RCHECK(r->Read(&header->creation_time) && |
| + r->Read(&header->modification_time) && |
| + r->Read(&header->timescale) && |
| + r->Read(&header->duration)); |
| + } else { |
| + RCHECK(r->Read4(&header->creation_time) && |
| + r->Read4(&header->modification_time) && |
| + r->Read(&header->timescale) && |
| + r->Read4(&header->duration)); |
| + } |
| + // Skip language information |
| + return r->Skip(4); |
| +} |
| + |
| +bool Parse(BoxReader* r, MediaInformation* information) { |
| + return r->ScanChildren() && |
| + r->ReadChild(&information->sample_table); |
| +} |
| + |
| +bool Parse(BoxReader* r, Media* media) { |
| + RCHECK(r->ScanChildren() && |
| + r->ReadChild(&media->header) && |
| + r->ReadChild(&media->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. |
| + media->information.sample_table.description.type = media->handler.type; |
| + RCHECK(r->ReadChild(&media->information)); |
| + return true; |
| +} |
| + |
| +bool Parse(BoxReader* r, Track* track) { |
| + RCHECK(r->ScanChildren() && |
| + r->ReadChild(&track->header) && |
| + r->ReadChild(&track->media) && |
| + r->MaybeReadChild(&track->edit)); |
| + return true; |
| +} |
| + |
| +bool Parse(BoxReader* r, MovieExtendsHeader* header) { |
| + RCHECK(r->Read(&header->fragment_duration)); |
| + return true; |
| +} |
| + |
| +bool Parse(BoxReader* r, TrackExtends* extends) { |
| + RCHECK(r->ReadFullBoxHeader() && |
| + r->Read(&extends->track_id) && |
| + r->Read(&extends->default_sample_description_index) && |
| + r->Read(&extends->default_sample_duration) && |
| + r->Read(&extends->default_sample_size) && |
| + r->Read(&extends->default_sample_flags)); |
| + return true; |
| +} |
| + |
| +MovieExtends::MovieExtends() {}; |
| +MovieExtends::~MovieExtends() {}; |
| + |
| +bool Parse(BoxReader* r, MovieExtends* extends) { |
| + extends->header.fragment_duration = 0; |
| + return r->ScanChildren() && |
| + r->MaybeReadChild(&extends->header) && |
| + // TODO(strobe): does not detect correspondence rule (trex must match |
| + // one-to-one with trak) |
| + r->ReadChildren(&extends->tracks); |
| +} |
| + |
| +Movie::Movie() {}; |
| +Movie::~Movie() {}; |
| + |
| +bool Parse(BoxReader* r, Movie* moov) { |
| + return r->ScanChildren() && |
| + r->ReadChild(&moov->header) && |
| + r->ReadChildren(&moov->tracks) && |
| + // Media Source specific: 'mvex' required |
| + r->ReadChild(&moov->extends) && |
| + r->MaybeReadChildren(&moov->pssh); |
| +} |
| + |
| +bool Parse(BoxReader* r, TrackFragmentDecodeTime* tfdt) { |
| + RCHECK(r->ReadFullBoxHeader()); |
| + if (r->version() == 1) |
| + return r->Read(&tfdt->decode_time); |
| + else |
| + return r->Read4(&tfdt->decode_time); |
| +} |
| + |
| +bool Parse(BoxReader* r, MovieFragmentHeader* mfhd) { |
| + return r->Skip(4) && r->Read(&mfhd->sequence_number); |
| +} |
| + |
| +bool Parse(BoxReader* r, TrackFragmentHeader* tfhd) { |
| + memset(tfhd, 0, sizeof(TrackFragmentHeader)); |
| + |
| + RCHECK(r->ReadFullBoxHeader() && r->Read(&tfhd->track_id)); |
| + |
| + // Media Source specific: we require that 'default-base-is-moof' (14496-12 |
| + // Amendment 2) be set, and reject tracks that set 'base-data-offset-present'. |
| + // |
| + // TODO(strobe): Test files do not have 'default-base-is-moof' set explicitly |
| + // (although they are structured as if it is set). Because this flag is hidden |
| + // in Amendment 2, it might not be set by all implementors. Should we reject |
| + // such files, or try them anyway? |
| + // |
| + // RCHECK((flags & 0x020000) && !(flags & 0x1)); |
| + RCHECK(!(r->flags() & 0x1)); |
| + |
| + if (r->flags() & 0x2) |
| + RCHECK(r->Skip(4)); // sample_description_index |
| + |
| + if (r->flags() & 0x8) |
| + RCHECK(r->Read(&tfhd->default_sample_duration)); |
| + |
| + if (r->flags() & 0x10) |
| + RCHECK(r->Read(&tfhd->default_sample_size)); |
| + |
| + if (r->flags() & 0x20) |
| + RCHECK(r->Read(&tfhd->default_sample_flags)); |
| + |
| + return true; |
| +} |
| + |
| +TrackFragmentRun::TrackFragmentRun() {} |
| +TrackFragmentRun::~TrackFragmentRun() {} |
| + |
| +bool Parse(BoxReader* r, TrackFragmentRun* trun) { |
| + RCHECK(r->ReadFullBoxHeader() && |
| + r->Read(&trun->sample_count)); |
| + const uint32 flags = r->flags(); |
| + |
| + bool data_offset_present = flags & 0x1; |
| + bool first_sample_flags_present = flags & 0x4; |
| + bool sample_duration_present = flags & 0x100; |
| + bool sample_size_present = flags & 0x200; |
| + bool sample_flags_present = flags & 0x400; |
| + bool sample_composition_time_offsets_present = flags & 0x800; |
| + |
| + if (data_offset_present) { |
| + RCHECK(r->Read(&trun->data_offset)); |
| + } else { |
| + trun->data_offset = 0; |
| + } |
| + |
| + uint32 first_sample_flags; |
| + if (first_sample_flags_present) |
| + RCHECK(r->Read(&first_sample_flags)); |
| + |
| + int fields = sample_duration_present + sample_size_present |
| + + sample_flags_present + sample_composition_time_offsets_present; |
|
acolwell GONE FROM CHROMIUM
2012/06/06 17:32:39
nit: + goes on the end of the previous line and th
strobe_
2012/06/07 16:33:54
Done.
|
| + RCHECK(r->HasBytes(fields * trun->sample_count)); |
| + |
| + if (sample_duration_present) |
| + trun->sample_durations.resize(trun->sample_count); |
| + if (sample_size_present) |
| + trun->sample_sizes.resize(trun->sample_count); |
| + if (sample_flags_present) |
| + trun->sample_flags.resize(trun->sample_count); |
| + if (sample_composition_time_offsets_present) |
| + trun->sample_composition_time_offsets.resize(trun->sample_count); |
| + |
| + for (uint32 i = 0; i < trun->sample_count; ++i) { |
| + if (sample_duration_present) |
| + RCHECK(r->Read(&trun->sample_durations[i])); |
| + if (sample_size_present) |
| + RCHECK(r->Read(&trun->sample_sizes[i])); |
| + if (sample_flags_present) |
| + RCHECK(r->Read(&trun->sample_flags[i])); |
| + if (sample_composition_time_offsets_present) |
| + RCHECK(r->Read(&trun->sample_composition_time_offsets[i])); |
| + } |
| + |
| + if (first_sample_flags_present) { |
| + if (trun->sample_flags.size() == 0) { |
| + trun->sample_flags.push_back(first_sample_flags); |
| + } else { |
| + trun->sample_flags[0] = first_sample_flags; |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +TrackFragment::TrackFragment() {} |
| +TrackFragment::~TrackFragment() {} |
| + |
| +bool Parse(BoxReader* r, TrackFragment* traf) { |
| + return r->ScanChildren() && |
| + r->ReadChild(&traf->header) && |
| + // Media Source specific: 'tfdt' required |
| + r->ReadChild(&traf->decode_time) && |
| + r->MaybeReadChildren(&traf->runs) && |
| + r->MaybeReadChild(&traf->auxiliary_offset) && |
| + r->MaybeReadChild(&traf->auxiliary_size); |
| +} |
| + |
| +MovieFragment::MovieFragment() {} |
| +MovieFragment::~MovieFragment() {} |
| + |
| +bool Parse(BoxReader* r, MovieFragment* moof) { |
| + RCHECK(r->ScanChildren() && |
| + r->ReadChild(&moof->header) && |
| + r->ReadChildren(&moof->tracks) && |
| + r->MaybeReadChildren(&moof->pssh)); |
| + return true; |
| +} |
| + |
| +bool Parse(BoxReader* r, SegmentIndex* sidx) { |
| + RCHECK(r->ReadFullBoxHeader() && |
| + r->Read(&sidx->reference_id) && |
| + r->Read(&sidx->timescale)); |
| + |
| + if (r->version() == 0) { |
| + RCHECK(r->Read4(&sidx->earliest_presentation_time) && |
| + r->Read4(&sidx->first_offset)); |
| + } else { |
| + RCHECK(r->Read(&sidx->earliest_presentation_time) && |
| + r->Read(&sidx->first_offset)); |
| + } |
| + |
| + uint32 reference_count; |
| + RCHECK(r->Skip(2) && // reserved |
| + r->Read(&reference_count) && |
| + r->HasBytes(12 * reference_count)); |
| + |
| + for (uint32 i = 0; i < reference_count; ++i) { |
| + uint32 flags; |
| + RCHECK(r->Read(&sidx->sizes[i]) && |
| + r->Read(&sidx->durations[i]) && |
| + r->Read(&flags)); |
| + |
| + uint32 type = flags / 0x10000000 % 8; |
| + |
| + RCHECK(!(sidx->sizes[i] & 0x8000000) != 0 || |
|
acolwell GONE FROM CHROMIUM
2012/06/06 17:32:39
What?!? A comment indicating what this is checking
|
| + (flags & 0x80000000) != 0x80000000 || |
| + (type != 0 && type != 1) || |
| + (flags % 0x10000000 != 0)); |
| + } |
| + return true; |
| +} |
| + |
| +} // namespace mp4 |
| +} // namespace media |