| 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..4cb8e5522b379adf09920cd0e9997154f8a4c441
|
| --- /dev/null
|
| +++ b/media/mp4/box_definitions.cc
|
| @@ -0,0 +1,550 @@
|
| +// 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) {
|
| + RCHECK(r->Read(&type->major_brand) && r->Read(&type->minor_version));
|
| + size_t num_brands = (r->size() - r->pos()) / sizeof(FourCC);
|
| + return r->SkipBytes(sizeof(FourCC) * num_brands); // compatible_brands
|
| +}
|
| +
|
| +ProtectionSystemSpecificHeader::ProtectionSystemSpecificHeader() {}
|
| +ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {}
|
| +
|
| +bool Parse(BoxReader* r, ProtectionSystemSpecificHeader* pssh) {
|
| + uint32 size;
|
| + return r->SkipBytes(4) &&
|
| + r->ReadVec(&pssh->system_id, 16) &&
|
| + r->Read(&size) &&
|
| + r->ReadVec(&pssh->data, size);
|
| +}
|
| +
|
| +SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() {}
|
| +SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() {}
|
| +
|
| +bool Parse(BoxReader* r, SampleAuxiliaryInformationOffset* saio) {
|
| + RCHECK(r->ReadFullBoxHeader());
|
| + if (r->flags() & 1)
|
| + RCHECK(r->SkipBytes(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) {
|
| +}
|
| +
|
| +SampleAuxiliaryInformationSize::~SampleAuxiliaryInformationSize() {}
|
| +
|
| +bool Parse(BoxReader* r, SampleAuxiliaryInformationSize* saiz) {
|
| + RCHECK(r->ReadFullBoxHeader());
|
| + if (r->flags() & 1)
|
| + RCHECK(r->SkipBytes(8));
|
| +
|
| + RCHECK(r->Read(&saiz->default_sample_info_size) &&
|
| + r->Read(&saiz->sample_count));
|
| + if (saiz->default_sample_info_size == 0)
|
| + return r->ReadVec(&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->SkipBytes(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) {
|
| +}
|
| +
|
| +TrackEncryption::~TrackEncryption() {}
|
| +
|
| +bool Parse(BoxReader* r, TrackEncryption* tenc) {
|
| + uint8 flag;
|
| + RCHECK(r->SkipBytes(2) &&
|
| + r->Read(&flag) &&
|
| + r->Read(&tenc->default_iv_size) &&
|
| + r->ReadVec(&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->SkipBytes(10) && // reserved
|
| + r->SkipBytes(36) && // matrix
|
| + r->SkipBytes(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->SkipBytes(4) && // reserved
|
| + r->Read(&tkhd->duration));
|
| + } else {
|
| + RCHECK(r->Read4(&tkhd->creation_time) &&
|
| + r->Read4(&tkhd->modification_time) &&
|
| + r->Read(&tkhd->track_id) &&
|
| + r->SkipBytes(4) && // reserved
|
| + r->Read4(&tkhd->duration));
|
| + }
|
| +
|
| + RCHECK(r->SkipBytes(8) && // reserved
|
| + r->Read(&tkhd->layer) &&
|
| + r->Read(&tkhd->alternate_group) &&
|
| + r->Read(&tkhd->volume) &&
|
| + r->SkipBytes(2) && // reserved
|
| + r->SkipBytes(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->SkipBytes(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->SkipBytes(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->SkipBytes(6) && r->Read(&entry->data_reference_index);
|
| +}
|
| +
|
| +AudioSampleEntry::AudioSampleEntry() {}
|
| +AudioSampleEntry::~AudioSampleEntry() {}
|
| +
|
| +bool Parse(BoxReader* r, AudioSampleEntry* entry) {
|
| + RCHECK(ParseSampleEntryHead(r, entry) &&
|
| + r->SkipBytes(8) &&
|
| + r->Read(&entry->channelcount) &&
|
| + r->Read(&entry->samplesize) &&
|
| + r->SkipBytes(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->SkipBytes(16) &&
|
| + r->Read(&entry->width) &&
|
| + r->Read(&entry->height) &&
|
| + r->SkipBytes(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->SkipBytes(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->SkipBytes(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->SkipBytes(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;
|
| + 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->SkipBytes(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 ||
|
| + (flags & 0x80000000) != 0x80000000 ||
|
| + (type != 0 && type != 1) ||
|
| + (flags % 0x10000000 != 0));
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace mp4
|
| +} // namespace media
|
|
|