shaka-packager/packager/media/formats/mp4/box_definitions.cc

2557 lines
83 KiB
C++
Raw Normal View History

// 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 "packager/media/formats/mp4/box_definitions.h"
#include <limits>
#include "packager/base/logging.h"
#include "packager/media/base/bit_reader.h"
#include "packager/media/base/macros.h"
#include "packager/media/base/rcheck.h"
#include "packager/media/formats/mp4/box_buffer.h"
namespace {
const uint32_t kFourCCSize = 4;
// Key Id size as defined in CENC spec.
const uint32_t kCencKeyIdSize = 16;
// 9 uint32_t in big endian formatted array.
const uint8_t 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";
const char kTextHandlerName[] = "TextHandler";
// Default values for VideoSampleEntry box.
const uint32_t kVideoResolution = 0x00480000; // 72 dpi.
const uint16_t kVideoFrameCount = 1;
const uint16_t kVideoDepth = 0x0018;
const uint32_t kCompressorNameSize = 32u;
const char kAvcCompressorName[] = "\012AVC Coding";
const char kHevcCompressorName[] = "\013HEVC Coding";
const char kVpcCompressorName[] = "\012VPC Coding";
// Using negative value as "not set". It is very unlikely that 2^31 cues happen
// at once.
const int kCueSourceIdNotSet = -1;
// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
// 64-bit (8-byte) or 128-bit (16-byte).
bool IsIvSizeValid(size_t iv_size) {
return iv_size == 8 || iv_size == 16;
}
// Default values to construct the following fields in ddts box. Values are set
// according to FFMPEG.
// bit(2) FrameDuration; // 3 = 4096
// bit(5) StreamConstruction; // 18
// bit(1) CoreLFEPresent; // 0 = none
// bit(6) CoreLayout; // 31 = ignore core layout
// bit(14) CoreSize; // 0
// bit(1) StereoDownmix // 0 = none
// bit(3) RepresentationType; // 4
// bit(16) ChannelLayout; // 0xf = 5.1 channel layout.
// bit(1) MultiAssetFlag // 0 = single asset
// bit(1) LBRDurationMod // 0 = ignore
// bit(1) ReservedBoxPresent // 0 = none
// bit(5) Reserved // 0
const uint8_t kDdtsExtraData[] = {0xe4, 0x7c, 0, 4, 0, 0x0f, 0};
// ID3v2 header: http://id3.org/id3v2.4.0-structure
const uint32_t kID3v2HeaderSize = 10;
const char kID3v2Identifier[] = "ID3";
const uint16_t kID3v2Version = 0x0400; // id3v2.4.0
// Utility functions to check if the 64bit integers can fit in 32bit integer.
bool IsFitIn32Bits(uint64_t a) {
return a <= std::numeric_limits<uint32_t>::max();
}
bool IsFitIn32Bits(int64_t a) {
return a <= std::numeric_limits<int32_t>::max() &&
a >= std::numeric_limits<int32_t>::min();
}
template <typename T1, typename T2>
bool IsFitIn32Bits(T1 a1, T2 a2) {
return IsFitIn32Bits(a1) && IsFitIn32Bits(a2);
}
template <typename T1, typename T2, typename T3>
bool IsFitIn32Bits(T1 a1, T2 a2, T3 a3) {
return IsFitIn32Bits(a1) && IsFitIn32Bits(a2) && IsFitIn32Bits(a3);
}
} // namespace
namespace edash_packager {
namespace media {
namespace mp4 {
namespace {
TrackType FourCCToTrackType(FourCC fourcc) {
switch (fourcc) {
case FOURCC_vide:
return kVideo;
case FOURCC_soun:
return kAudio;
case FOURCC_text:
return kText;
default:
return kInvalid;
}
}
FourCC TrackTypeToFourCC(TrackType track_type) {
switch (track_type) {
case kVideo:
return FOURCC_vide;
case kAudio:
return FOURCC_soun;
case kText:
return FOURCC_text;
default:
return FOURCC_NULL;
}
}
} // namespace
FileType::FileType() : major_brand(FOURCC_NULL), minor_version(0) {}
FileType::~FileType() {}
FourCC FileType::BoxType() const { return FOURCC_ftyp; }
bool FileType::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteFourCC(&major_brand) &&
buffer->ReadWriteUInt32(&minor_version));
size_t num_brands;
if (buffer->Reading()) {
RCHECK(buffer->BytesLeft() % sizeof(FourCC) == 0);
num_brands = buffer->BytesLeft() / 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_t FileType::ComputeSizeInternal() {
return HeaderSize() + kFourCCSize + sizeof(minor_version) +
kFourCCSize * compatible_brands.size();
}
FourCC SegmentType::BoxType() const { return FOURCC_styp; }
ProtectionSystemSpecificHeader::ProtectionSystemSpecificHeader() {}
ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {}
FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_pssh; }
bool ProtectionSystemSpecificHeader::ReadWriteInternal(BoxBuffer* buffer) {
if (buffer->Reading()) {
BoxReader* reader = buffer->reader();
DCHECK(reader);
raw_box.assign(reader->data(), reader->data() + reader->size());
} else {
DCHECK(!raw_box.empty());
buffer->writer()->AppendVector(raw_box);
}
return true;
}
uint32_t ProtectionSystemSpecificHeader::ComputeSizeInternal() {
return raw_box.size();
}
SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() {}
SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() {}
FourCC SampleAuxiliaryInformationOffset::BoxType() const { return FOURCC_saio; }
bool SampleAuxiliaryInformationOffset::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
if (flags & 1)
RCHECK(buffer->IgnoreBytes(8)); // aux_info_type and parameter.
uint32_t count = offsets.size();
RCHECK(buffer->ReadWriteUInt32(&count));
offsets.resize(count);
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteUInt64NBytes(&offsets[i], num_bytes));
return true;
}
uint32_t SampleAuxiliaryInformationOffset::ComputeSizeInternal() {
// This box is optional. Skip it if it is empty.
if (offsets.size() == 0)
return 0;
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
return HeaderSize() + sizeof(uint32_t) + num_bytes * offsets.size();
}
SampleAuxiliaryInformationSize::SampleAuxiliaryInformationSize()
: default_sample_info_size(0), sample_count(0) {}
SampleAuxiliaryInformationSize::~SampleAuxiliaryInformationSize() {}
FourCC SampleAuxiliaryInformationSize::BoxType() const { return FOURCC_saiz; }
bool SampleAuxiliaryInformationSize::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
if (flags & 1)
RCHECK(buffer->IgnoreBytes(8));
RCHECK(buffer->ReadWriteUInt8(&default_sample_info_size) &&
buffer->ReadWriteUInt32(&sample_count));
if (default_sample_info_size == 0)
RCHECK(buffer->ReadWriteVector(&sample_info_sizes, sample_count));
return true;
}
uint32_t SampleAuxiliaryInformationSize::ComputeSizeInternal() {
// This box is optional. Skip it if it is empty.
if (sample_count == 0)
return 0;
return HeaderSize() + sizeof(default_sample_info_size) +
sizeof(sample_count) +
(default_sample_info_size == 0 ? sample_info_sizes.size() : 0);
}
SampleEncryptionEntry::SampleEncryptionEntry() {}
SampleEncryptionEntry::~SampleEncryptionEntry() {}
bool SampleEncryptionEntry::ReadWrite(uint8_t iv_size,
bool has_subsamples,
BoxBuffer* buffer) {
DCHECK(IsIvSizeValid(iv_size));
DCHECK(buffer);
RCHECK(buffer->ReadWriteVector(&initialization_vector, iv_size));
if (!has_subsamples) {
subsamples.clear();
return true;
}
uint16_t subsample_count = subsamples.size();
RCHECK(buffer->ReadWriteUInt16(&subsample_count));
RCHECK(subsample_count > 0);
subsamples.resize(subsample_count);
for (auto& subsample : subsamples) {
RCHECK(buffer->ReadWriteUInt16(&subsample.clear_bytes) &&
buffer->ReadWriteUInt32(&subsample.cipher_bytes));
}
return true;
}
bool SampleEncryptionEntry::ParseFromBuffer(uint8_t iv_size,
bool has_subsamples,
BufferReader* reader) {
DCHECK(IsIvSizeValid(iv_size));
DCHECK(reader);
initialization_vector.resize(iv_size);
RCHECK(reader->ReadToVector(&initialization_vector, iv_size));
if (!has_subsamples) {
subsamples.clear();
return true;
}
uint16_t subsample_count;
RCHECK(reader->Read2(&subsample_count));
RCHECK(subsample_count > 0);
subsamples.resize(subsample_count);
for (auto& subsample : subsamples) {
RCHECK(reader->Read2(&subsample.clear_bytes) &&
reader->Read4(&subsample.cipher_bytes));
}
return true;
}
uint32_t SampleEncryptionEntry::ComputeSize() const {
const uint32_t subsample_entry_size = sizeof(uint16_t) + sizeof(uint32_t);
const uint16_t subsample_count = subsamples.size();
return initialization_vector.size() +
(subsample_count > 0 ? (sizeof(subsample_count) +
subsample_entry_size * subsample_count)
: 0);
}
uint32_t SampleEncryptionEntry::GetTotalSizeOfSubsamples() const {
uint32_t size = 0;
for (uint32_t i = 0; i < subsamples.size(); ++i)
size += subsamples[i].clear_bytes + subsamples[i].cipher_bytes;
return size;
}
SampleEncryption::SampleEncryption() : iv_size(0) {}
SampleEncryption::~SampleEncryption() {}
FourCC SampleEncryption::BoxType() const { return FOURCC_senc; }
bool SampleEncryption::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
// If we don't know |iv_size|, store sample encryption data to parse later
// after we know iv_size.
if (buffer->Reading() && iv_size == 0) {
RCHECK(
buffer->ReadWriteVector(&sample_encryption_data, buffer->BytesLeft()));
return true;
}
if (!IsIvSizeValid(iv_size)) {
LOG(ERROR) << "IV_size can only be 8 or 16, but seeing " << iv_size;
return false;
}
uint32_t sample_count = sample_encryption_entries.size();
RCHECK(buffer->ReadWriteUInt32(&sample_count));
sample_encryption_entries.resize(sample_count);
for (auto& sample_encryption_entry : sample_encryption_entries) {
RCHECK(sample_encryption_entry.ReadWrite(
iv_size, flags & kUseSubsampleEncryption, buffer));
}
return true;
}
uint32_t SampleEncryption::ComputeSizeInternal() {
const uint32_t sample_count = sample_encryption_entries.size();
if (sample_count == 0) {
// Sample encryption box is optional. Skip it if it is empty.
return 0;
}
DCHECK(IsIvSizeValid(iv_size));
uint32_t box_size = HeaderSize() + sizeof(sample_count);
if (flags & kUseSubsampleEncryption) {
for (const SampleEncryptionEntry& sample_encryption_entry :
sample_encryption_entries) {
box_size += sample_encryption_entry.ComputeSize();
}
} else {
box_size += sample_count * iv_size;
}
return box_size;
}
bool SampleEncryption::ParseFromSampleEncryptionData(
size_t iv_size,
std::vector<SampleEncryptionEntry>* sample_encryption_entries) const {
DCHECK(IsIvSizeValid(iv_size));
BufferReader reader(sample_encryption_data.data(),
sample_encryption_data.size());
uint32_t sample_count = 0;
RCHECK(reader.Read4(&sample_count));
sample_encryption_entries->resize(sample_count);
for (auto& sample_encryption_entry : *sample_encryption_entries) {
RCHECK(sample_encryption_entry.ParseFromBuffer(
iv_size, flags & kUseSubsampleEncryption, &reader));
}
return true;
}
OriginalFormat::OriginalFormat() : format(FOURCC_NULL) {}
OriginalFormat::~OriginalFormat() {}
FourCC OriginalFormat::BoxType() const { return FOURCC_frma; }
bool OriginalFormat::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteHeaderInternal(buffer) && buffer->ReadWriteFourCC(&format);
}
uint32_t OriginalFormat::ComputeSizeInternal() {
return HeaderSize() + kFourCCSize;
}
SchemeType::SchemeType() : type(FOURCC_NULL), version(0) {}
SchemeType::~SchemeType() {}
FourCC SchemeType::BoxType() const { return FOURCC_schm; }
bool SchemeType::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteFourCC(&type) &&
buffer->ReadWriteUInt32(&version));
return true;
}
uint32_t SchemeType::ComputeSizeInternal() {
return HeaderSize() + kFourCCSize + sizeof(version);
}
TrackEncryption::TrackEncryption()
: is_encrypted(false), default_iv_size(0), default_kid(16, 0) {}
TrackEncryption::~TrackEncryption() {}
FourCC TrackEncryption::BoxType() const { return FOURCC_tenc; }
bool TrackEncryption::ReadWriteInternal(BoxBuffer* buffer) {
if (!buffer->Reading()) {
if (default_kid.size() != kCencKeyIdSize) {
LOG(WARNING) << "CENC defines key id length of " << kCencKeyIdSize
<< " bytes; got " << default_kid.size()
<< ". Resized accordingly.";
default_kid.resize(kCencKeyIdSize);
}
}
uint8_t flag = is_encrypted ? 1 : 0;
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->IgnoreBytes(2) && // reserved.
buffer->ReadWriteUInt8(&flag) &&
buffer->ReadWriteUInt8(&default_iv_size) &&
buffer->ReadWriteVector(&default_kid, kCencKeyIdSize));
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_t TrackEncryption::ComputeSizeInternal() {
return HeaderSize() + sizeof(uint32_t) + kCencKeyIdSize;
}
SchemeInfo::SchemeInfo() {}
SchemeInfo::~SchemeInfo() {}
FourCC SchemeInfo::BoxType() const { return FOURCC_schi; }
bool SchemeInfo::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&track_encryption));
return true;
}
uint32_t SchemeInfo::ComputeSizeInternal() {
return HeaderSize() + track_encryption.ComputeSize();
}
ProtectionSchemeInfo::ProtectionSchemeInfo() {}
ProtectionSchemeInfo::~ProtectionSchemeInfo() {}
FourCC ProtectionSchemeInfo::BoxType() const { return FOURCC_sinf; }
bool ProtectionSchemeInfo::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&format) &&
buffer->ReadWriteChild(&type));
if (type.type == FOURCC_cenc || type.type == FOURCC_cbc1)
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
// ensure that this scheme type is a supported one.
return true;
}
uint32_t ProtectionSchemeInfo::ComputeSizeInternal() {
// Skip sinf box if it is not initialized.
if (format.format == FOURCC_NULL)
return 0;
return HeaderSize() + format.ComputeSize() + type.ComputeSize() +
info.ComputeSize();
}
MovieHeader::MovieHeader()
: creation_time(0),
modification_time(0),
timescale(0),
duration(0),
rate(1 << 16),
volume(1 << 8),
next_track_id(0) {}
MovieHeader::~MovieHeader() {}
FourCC MovieHeader::BoxType() const { return FOURCC_mvhd; }
bool MovieHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) &&
buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) &&
buffer->ReadWriteUInt32(&timescale) &&
buffer->ReadWriteUInt64NBytes(&duration, num_bytes));
std::vector<uint8_t> 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_t MovieHeader::ComputeSizeInternal() {
version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1;
return HeaderSize() + sizeof(uint32_t) * (1 + version) * 3 +
sizeof(timescale) + sizeof(rate) + sizeof(volume) +
sizeof(next_track_id) + sizeof(kUnityMatrix) + 10 +
24; // 10 bytes reserved, 24 bytes predefined.
}
TrackHeader::TrackHeader()
: creation_time(0),
modification_time(0),
track_id(0),
duration(0),
layer(0),
alternate_group(0),
volume(-1),
width(0),
height(0) {
flags = kTrackEnabled | kTrackInMovie;
}
TrackHeader::~TrackHeader() {}
FourCC TrackHeader::BoxType() const { return FOURCC_tkhd; }
bool TrackHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
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;
}
std::vector<uint8_t> 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));
return true;
}
uint32_t TrackHeader::ComputeSizeInternal() {
version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1;
return HeaderSize() + sizeof(track_id) +
sizeof(uint32_t) * (1 + version) * 3 + sizeof(layer) +
sizeof(alternate_group) + sizeof(volume) + sizeof(width) +
sizeof(height) + sizeof(kUnityMatrix) + 14; // 14 bytes reserved.
}
SampleDescription::SampleDescription() : type(kInvalid) {}
SampleDescription::~SampleDescription() {}
FourCC SampleDescription::BoxType() const { return FOURCC_stsd; }
bool SampleDescription::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = 0;
switch (type) {
case kVideo:
count = video_entries.size();
break;
case kAudio:
count = audio_entries.size();
break;
case kText:
count = text_entries.size();
break;
default:
NOTIMPLEMENTED() << "SampleDecryption type " << type
<< " is not handled. Skipping.";
}
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
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 if (type == kText) {
RCHECK(reader->ReadAllChildren(&text_entries));
RCHECK(text_entries.size() == count);
}
} else {
DCHECK_LT(0u, count);
if (type == kVideo) {
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteChild(&video_entries[i]));
} else if (type == kAudio) {
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteChild(&audio_entries[i]));
} else if (type == kText) {
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteChild(&text_entries[i]));
} else {
NOTIMPLEMENTED();
}
}
return true;
}
uint32_t SampleDescription::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + sizeof(uint32_t);
if (type == kVideo) {
for (uint32_t i = 0; i < video_entries.size(); ++i)
box_size += video_entries[i].ComputeSize();
} else if (type == kAudio) {
for (uint32_t i = 0; i < audio_entries.size(); ++i)
box_size += audio_entries[i].ComputeSize();
} else if (type == kText) {
for (uint32_t i = 0; i < text_entries.size(); ++i)
box_size += text_entries[i].ComputeSize();
}
return box_size;
}
DecodingTimeToSample::DecodingTimeToSample() {}
DecodingTimeToSample::~DecodingTimeToSample() {}
FourCC DecodingTimeToSample::BoxType() const { return FOURCC_stts; }
bool DecodingTimeToSample::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = decoding_time.size();
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
decoding_time.resize(count);
for (uint32_t i = 0; i < count; ++i) {
RCHECK(buffer->ReadWriteUInt32(&decoding_time[i].sample_count) &&
buffer->ReadWriteUInt32(&decoding_time[i].sample_delta));
}
return true;
}
uint32_t DecodingTimeToSample::ComputeSizeInternal() {
return HeaderSize() + sizeof(uint32_t) +
sizeof(DecodingTime) * decoding_time.size();
}
CompositionTimeToSample::CompositionTimeToSample() {}
CompositionTimeToSample::~CompositionTimeToSample() {}
FourCC CompositionTimeToSample::BoxType() const { return FOURCC_ctts; }
bool CompositionTimeToSample::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = composition_offset.size();
if (!buffer->Reading()) {
// Determine whether version 0 or version 1 should be used.
// Use version 0 if possible, use version 1 if there is a negative
// sample_offset value.
version = 0;
for (uint32_t i = 0; i < count; ++i) {
if (composition_offset[i].sample_offset < 0) {
version = 1;
break;
}
}
}
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
composition_offset.resize(count);
for (uint32_t i = 0; i < count; ++i) {
RCHECK(buffer->ReadWriteUInt32(&composition_offset[i].sample_count));
if (version == 0) {
uint32_t sample_offset = composition_offset[i].sample_offset;
RCHECK(buffer->ReadWriteUInt32(&sample_offset));
composition_offset[i].sample_offset = sample_offset;
} else {
int32_t sample_offset = composition_offset[i].sample_offset;
RCHECK(buffer->ReadWriteInt32(&sample_offset));
composition_offset[i].sample_offset = sample_offset;
}
}
return true;
}
uint32_t CompositionTimeToSample::ComputeSizeInternal() {
// This box is optional. Skip it if it is empty.
if (composition_offset.empty())
return 0;
// Structure CompositionOffset contains |sample_offset| (uint32_t) and
// |sample_offset| (int64_t). The actual size of |sample_offset| is
// 4 bytes (uint32_t for version 0 and int32_t for version 1).
const uint32_t kCompositionOffsetSize = sizeof(uint32_t) * 2;
return HeaderSize() + sizeof(uint32_t) +
kCompositionOffsetSize * composition_offset.size();
}
SampleToChunk::SampleToChunk() {}
SampleToChunk::~SampleToChunk() {}
FourCC SampleToChunk::BoxType() const { return FOURCC_stsc; }
bool SampleToChunk::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = chunk_info.size();
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
chunk_info.resize(count);
for (uint32_t 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.
RCHECK(i == 0 ? chunk_info[i].first_chunk == 1
: chunk_info[i].first_chunk > chunk_info[i - 1].first_chunk);
}
return true;
}
uint32_t SampleToChunk::ComputeSizeInternal() {
return HeaderSize() + sizeof(uint32_t) +
sizeof(ChunkInfo) * chunk_info.size();
}
SampleSize::SampleSize() : sample_size(0), sample_count(0) {}
SampleSize::~SampleSize() {}
FourCC SampleSize::BoxType() const { return FOURCC_stsz; }
bool SampleSize::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&sample_size) &&
buffer->ReadWriteUInt32(&sample_count));
if (sample_size == 0) {
if (buffer->Reading())
sizes.resize(sample_count);
else
DCHECK(sample_count == sizes.size());
for (uint32_t i = 0; i < sample_count; ++i)
RCHECK(buffer->ReadWriteUInt32(&sizes[i]));
}
return true;
}
uint32_t SampleSize::ComputeSizeInternal() {
return HeaderSize() + sizeof(sample_size) + sizeof(sample_count) +
(sample_size == 0 ? sizeof(uint32_t) * sizes.size() : 0);
}
CompactSampleSize::CompactSampleSize() : field_size(0) {}
CompactSampleSize::~CompactSampleSize() {}
FourCC CompactSampleSize::BoxType() const { return FOURCC_stz2; }
bool CompactSampleSize::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t sample_count = sizes.size();
RCHECK(ReadWriteHeaderInternal(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), 0);
switch (field_size) {
case 4:
for (uint32_t i = 0; i < sample_count; i += 2) {
if (buffer->Reading()) {
uint8_t size = 0;
RCHECK(buffer->ReadWriteUInt8(&size));
sizes[i] = size >> 4;
sizes[i + 1] = size & 0x0F;
} else {
DCHECK_LT(sizes[i], 16u);
DCHECK_LT(sizes[i + 1], 16u);
uint8_t size = (sizes[i] << 4) | sizes[i + 1];
RCHECK(buffer->ReadWriteUInt8(&size));
}
}
break;
case 8:
for (uint32_t i = 0; i < sample_count; ++i) {
uint8_t size = sizes[i];
RCHECK(buffer->ReadWriteUInt8(&size));
sizes[i] = size;
}
break;
case 16:
for (uint32_t i = 0; i < sample_count; ++i) {
uint16_t size = sizes[i];
RCHECK(buffer->ReadWriteUInt16(&size));
sizes[i] = size;
}
break;
default:
RCHECK(false);
}
sizes.resize(sample_count);
return true;
}
uint32_t CompactSampleSize::ComputeSizeInternal() {
return HeaderSize() + sizeof(uint32_t) + sizeof(uint32_t) +
(field_size * sizes.size() + 7) / 8;
}
ChunkOffset::ChunkOffset() {}
ChunkOffset::~ChunkOffset() {}
FourCC ChunkOffset::BoxType() const { return FOURCC_stco; }
bool ChunkOffset::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = offsets.size();
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
offsets.resize(count);
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteUInt64NBytes(&offsets[i], sizeof(uint32_t)));
return true;
}
uint32_t ChunkOffset::ComputeSizeInternal() {
return HeaderSize() + sizeof(uint32_t) + sizeof(uint32_t) * offsets.size();
}
ChunkLargeOffset::ChunkLargeOffset() {}
ChunkLargeOffset::~ChunkLargeOffset() {}
FourCC ChunkLargeOffset::BoxType() const { return FOURCC_co64; }
bool ChunkLargeOffset::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t 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(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
offsets.resize(count);
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteUInt64(&offsets[i]));
return true;
}
uint32_t ChunkLargeOffset::ComputeSizeInternal() {
uint32_t count = offsets.size();
int use_large_offset =
(count > 0 && !IsFitIn32Bits(offsets[count - 1])) ? 1 : 0;
return HeaderSize() + sizeof(count) +
sizeof(uint32_t) * (1 + use_large_offset) * offsets.size();
}
SyncSample::SyncSample() {}
SyncSample::~SyncSample() {}
FourCC SyncSample::BoxType() const { return FOURCC_stss; }
bool SyncSample::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = sample_number.size();
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&count));
sample_number.resize(count);
for (uint32_t i = 0; i < count; ++i)
RCHECK(buffer->ReadWriteUInt32(&sample_number[i]));
return true;
}
uint32_t SyncSample::ComputeSizeInternal() {
// Sync sample box is optional. Skip it if it is empty.
if (sample_number.empty())
return 0;
return HeaderSize() + sizeof(uint32_t) +
sizeof(uint32_t) * sample_number.size();
}
SampleTable::SampleTable() {}
SampleTable::~SampleTable() {}
FourCC SampleTable::BoxType() const { return FOURCC_stbl; }
bool SampleTable::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&description) &&
buffer->ReadWriteChild(&decoding_time_to_sample) &&
buffer->TryReadWriteChild(&composition_time_to_sample) &&
buffer->ReadWriteChild(&sample_to_chunk));
if (buffer->Reading()) {
BoxReader* reader = buffer->reader();
DCHECK(reader);
// 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 {
RCHECK(buffer->ReadWriteChild(&sample_size) &&
buffer->ReadWriteChild(&chunk_large_offset));
}
RCHECK(buffer->TryReadWriteChild(&sync_sample));
return true;
}
uint32_t SampleTable::ComputeSizeInternal() {
return HeaderSize() + 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();
}
EditList::EditList() {}
EditList::~EditList() {}
FourCC EditList::BoxType() const { return FOURCC_elst; }
bool EditList::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t count = edits.size();
RCHECK(ReadWriteHeaderInternal(buffer) && buffer->ReadWriteUInt32(&count));
edits.resize(count);
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
for (uint32_t 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_t EditList::ComputeSizeInternal() {
// EditList box is optional. Skip it if it is empty.
if (edits.empty())
return 0;
version = 0;
for (uint32_t i = 0; i < edits.size(); ++i) {
if (!IsFitIn32Bits(edits[i].segment_duration, edits[i].media_time)) {
version = 1;
break;
}
}
return HeaderSize() + sizeof(uint32_t) +
(sizeof(uint32_t) * (1 + version) * 2 + sizeof(int16_t) * 2) *
edits.size();
}
Edit::Edit() {}
Edit::~Edit() {}
FourCC Edit::BoxType() const { return FOURCC_edts; }
bool Edit::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&list);
}
uint32_t Edit::ComputeSizeInternal() {
// Edit box is optional. Skip it if it is empty.
if (list.edits.empty())
return 0;
return HeaderSize() + list.ComputeSize();
}
HandlerReference::HandlerReference() : handler_type(FOURCC_NULL) {}
HandlerReference::~HandlerReference() {}
FourCC HandlerReference::BoxType() const { return FOURCC_hdlr; }
bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) {
std::vector<uint8_t> handler_name;
if (!buffer->Reading()) {
switch (handler_type) {
case FOURCC_vide:
handler_name.assign(kVideoHandlerName,
kVideoHandlerName + arraysize(kVideoHandlerName));
break;
case FOURCC_soun:
handler_name.assign(kAudioHandlerName,
kAudioHandlerName + arraysize(kAudioHandlerName));
break;
case FOURCC_text:
handler_name.assign(kTextHandlerName,
kTextHandlerName + arraysize(kTextHandlerName));
break;
case FOURCC_ID32:
break;
default:
NOTIMPLEMENTED();
return false;
}
}
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->IgnoreBytes(4) && // predefined.
buffer->ReadWriteFourCC(&handler_type));
if (!buffer->Reading()) {
RCHECK(buffer->IgnoreBytes(12) && // reserved.
buffer->ReadWriteVector(&handler_name, handler_name.size()));
}
return true;
}
uint32_t HandlerReference::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + kFourCCSize + 16; // 16 bytes Reserved
switch (handler_type) {
case FOURCC_vide:
box_size += sizeof(kVideoHandlerName);
break;
case FOURCC_soun:
box_size += sizeof(kAudioHandlerName);
break;
case FOURCC_text:
box_size += sizeof(kTextHandlerName);
break;
case FOURCC_ID32:
break;
default:
NOTIMPLEMENTED();
}
return box_size;
}
bool Language::ReadWrite(BoxBuffer* buffer) {
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<uint8_t> temp;
RCHECK(buffer->ReadWriteVector(&temp, 2));
BitReader bit_reader(&temp[0], 2);
bit_reader.SkipBits(1);
char language[3];
for (int i = 0; i < 3; ++i) {
CHECK(bit_reader.ReadBits(5, &language[i]));
language[i] += 0x60;
}
code.assign(language, 3);
} else {
// Set up default language if it is not set.
const char kUndefinedLanguage[] = "und";
if (code.empty())
code = kUndefinedLanguage;
DCHECK_EQ(code.size(), 3u);
// Lang format: bit(1) pad, unsigned int(5)[3] language.
uint16_t lang = 0;
for (int i = 0; i < 3; ++i)
lang |= (code[i] - 0x60) << ((2 - i) * 5);
RCHECK(buffer->ReadWriteUInt16(&lang));
}
return true;
}
uint32_t Language::ComputeSize() const {
// ISO-639-2/T language code: unsigned int(5)[3] language (2 bytes).
return 2;
}
bool PrivFrame::ReadWrite(BoxBuffer* buffer) {
FourCC fourcc = FOURCC_PRIV;
RCHECK(buffer->ReadWriteFourCC(&fourcc));
if (fourcc != FOURCC_PRIV) {
VLOG(1) << "Skip unrecognized id3 frame during read: "
<< FourCCToString(fourcc);
return true;
}
uint32_t frame_size = owner.size() + 1 + value.size();
// size should be encoded as synchsafe integer, which is not support here.
// We don't expect frame_size to be larger than 0x7F. Synchsafe integers less
// than 0x7F is encoded in the same way as normal integer.
DCHECK_LT(frame_size, 0x7Fu);
uint16_t flags = 0;
RCHECK(buffer->ReadWriteUInt32(&frame_size) &&
buffer->ReadWriteUInt16(&flags));
if (buffer->Reading()) {
std::string str;
RCHECK(buffer->ReadWriteString(&str, frame_size));
// |owner| is null terminated.
size_t pos = str.find('\0');
RCHECK(pos < str.size());
owner = str.substr(0, pos);
value = str.substr(pos + 1);
} else {
uint8_t byte = 0; // Null terminating byte between owner and value.
RCHECK(buffer->ReadWriteString(&owner, owner.size()) &&
buffer->ReadWriteUInt8(&byte) &&
buffer->ReadWriteString(&value, value.size()));
}
return true;
}
uint32_t PrivFrame::ComputeSize() const {
if (owner.empty() && value.empty())
return 0;
const uint32_t kFourCCSize = 4;
return kFourCCSize + sizeof(uint32_t) + sizeof(uint16_t) + owner.size() + 1 +
value.size();
}
ID3v2::ID3v2() {}
ID3v2::~ID3v2() {}
FourCC ID3v2::BoxType() const { return FOURCC_ID32; }
bool ID3v2::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
language.ReadWrite(buffer));
// Read/Write ID3v2 header
std::string id3v2_identifier = kID3v2Identifier;
uint16_t version = kID3v2Version;
// We only support PrivateFrame in ID3.
uint32_t data_size = private_frame.ComputeSize();
// size should be encoded as synchsafe integer, which is not support here.
// We don't expect data_size to be larger than 0x7F. Synchsafe integers less
// than 0x7F is encoded in the same way as normal integer.
DCHECK_LT(data_size, 0x7Fu);
uint8_t flags = 0;
RCHECK(buffer->ReadWriteString(&id3v2_identifier, id3v2_identifier.size()) &&
buffer->ReadWriteUInt16(&version) &&
buffer->ReadWriteUInt8(&flags) &&
buffer->ReadWriteUInt32(&data_size));
RCHECK(private_frame.ReadWrite(buffer));
return true;
}
uint32_t ID3v2::ComputeSizeInternal() {
uint32_t private_frame_size = private_frame.ComputeSize();
// Skip ID3v2 box generation if there is no private frame.
return private_frame_size == 0 ? 0 : HeaderSize() + language.ComputeSize() +
kID3v2HeaderSize +
private_frame_size;
}
Metadata::Metadata() {}
Metadata::~Metadata() {}
FourCC Metadata::BoxType() const {
return FOURCC_meta;
}
bool Metadata::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&handler) &&
buffer->TryReadWriteChild(&id3v2));
return true;
}
uint32_t Metadata::ComputeSizeInternal() {
uint32_t id3v2_size = id3v2.ComputeSize();
// Skip metadata box generation if there is no metadata box.
return id3v2_size == 0 ? 0
: HeaderSize() + handler.ComputeSize() + id3v2_size;
}
CodecConfigurationRecord::CodecConfigurationRecord() : box_type(FOURCC_NULL) {}
CodecConfigurationRecord::~CodecConfigurationRecord() {}
FourCC CodecConfigurationRecord::BoxType() const {
// CodecConfigurationRecord should be parsed according to format recovered in
// VideoSampleEntry. |box_type| is determined dynamically there.
return box_type;
}
bool CodecConfigurationRecord::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
if (buffer->Reading()) {
RCHECK(buffer->ReadWriteVector(&data, buffer->BytesLeft()));
} else {
RCHECK(buffer->ReadWriteVector(&data, data.size()));
}
return true;
}
uint32_t CodecConfigurationRecord::ComputeSizeInternal() {
if (data.empty())
return 0;
return HeaderSize() + data.size();
}
PixelAspectRatio::PixelAspectRatio() : h_spacing(0), v_spacing(0) {}
PixelAspectRatio::~PixelAspectRatio() {}
FourCC PixelAspectRatio::BoxType() const { return FOURCC_pasp; }
bool PixelAspectRatio::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&h_spacing) &&
buffer->ReadWriteUInt32(&v_spacing));
return true;
}
uint32_t PixelAspectRatio::ComputeSizeInternal() {
// This box is optional. Skip it if it is not initialized.
if (h_spacing == 0 && v_spacing == 0)
return 0;
// Both values must be positive.
DCHECK(h_spacing != 0 && v_spacing != 0);
return HeaderSize() + sizeof(h_spacing) + sizeof(v_spacing);
}
VideoSampleEntry::VideoSampleEntry()
: format(FOURCC_NULL), data_reference_index(1), width(0), height(0) {}
VideoSampleEntry::~VideoSampleEntry() {}
FourCC VideoSampleEntry::BoxType() const {
if (format == FOURCC_NULL) {
LOG(ERROR) << "VideoSampleEntry should be parsed according to the "
<< "handler type recovered in its Media ancestor.";
}
return format;
}
bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
std::vector<uint8_t> compressor_name;
if (buffer->Reading()) {
DCHECK(buffer->reader());
format = buffer->reader()->type();
} else {
RCHECK(ReadWriteHeaderInternal(buffer));
const FourCC actual_format = GetActualFormat();
switch (actual_format) {
case FOURCC_avc1:
compressor_name.assign(
kAvcCompressorName,
kAvcCompressorName + arraysize(kAvcCompressorName));
break;
case FOURCC_hev1:
case FOURCC_hvc1:
compressor_name.assign(
kHevcCompressorName,
kHevcCompressorName + arraysize(kHevcCompressorName));
break;
case FOURCC_vp08:
case FOURCC_vp09:
case FOURCC_vp10:
compressor_name.assign(
kVpcCompressorName,
kVpcCompressorName + arraysize(kVpcCompressorName));
break;
default:
LOG(ERROR) << FourCCToString(actual_format) << " is not supported.";
return false;
}
compressor_name.resize(kCompressorNameSize);
}
uint32_t video_resolution = kVideoResolution;
uint16_t video_frame_count = kVideoFrameCount;
uint16_t video_depth = kVideoDepth;
int16_t 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->ReadWriteVector(&compressor_name, kCompressorNameSize) &&
buffer->ReadWriteUInt16(&video_depth) &&
buffer->ReadWriteInt16(&predefined));
RCHECK(buffer->PrepareChildren());
if (format == FOURCC_encv) {
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 && sinf.type.type != FOURCC_cbc1) {
if (!buffer->ReadWriteChild(&sinf))
return false;
}
} else {
RCHECK(buffer->ReadWriteChild(&sinf));
}
}
const FourCC actual_format = GetActualFormat();
switch (actual_format) {
case FOURCC_avc1:
codec_config_record.box_type = FOURCC_avcC;
break;
case FOURCC_hev1:
case FOURCC_hvc1:
codec_config_record.box_type = FOURCC_hvcC;
break;
case FOURCC_vp08:
case FOURCC_vp09:
case FOURCC_vp10:
codec_config_record.box_type = FOURCC_vpcC;
break;
default:
LOG(ERROR) << FourCCToString(actual_format) << " is not supported.";
return false;
}
RCHECK(buffer->ReadWriteChild(&codec_config_record));
RCHECK(buffer->TryReadWriteChild(&pixel_aspect));
return true;
}
uint32_t VideoSampleEntry::ComputeSizeInternal() {
return HeaderSize() + sizeof(data_reference_index) + sizeof(width) +
sizeof(height) + sizeof(kVideoResolution) * 2 +
sizeof(kVideoFrameCount) + sizeof(kVideoDepth) +
pixel_aspect.ComputeSize() + sinf.ComputeSize() +
codec_config_record.ComputeSize() + kCompressorNameSize + 6 + 4 + 16 +
2; // 6 + 4 bytes reserved, 16 + 2 bytes predefined.
}
ElementaryStreamDescriptor::ElementaryStreamDescriptor() {}
ElementaryStreamDescriptor::~ElementaryStreamDescriptor() {}
FourCC ElementaryStreamDescriptor::BoxType() const { return FOURCC_esds; }
bool ElementaryStreamDescriptor::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
if (buffer->Reading()) {
std::vector<uint8_t> data;
RCHECK(buffer->ReadWriteVector(&data, buffer->BytesLeft()));
RCHECK(es_descriptor.Parse(data));
if (es_descriptor.IsAAC()) {
RCHECK(aac_audio_specific_config.Parse(
es_descriptor.decoder_specific_info()));
}
} else {
DCHECK(buffer->writer());
es_descriptor.Write(buffer->writer());
}
return true;
}
uint32_t ElementaryStreamDescriptor::ComputeSizeInternal() {
// This box is optional. Skip it if not initialized.
if (es_descriptor.object_type() == kForbidden)
return 0;
return HeaderSize() + es_descriptor.ComputeSize();
}
DTSSpecific::DTSSpecific()
: sampling_frequency(0),
max_bitrate(0),
avg_bitrate(0),
pcm_sample_depth(0) {}
DTSSpecific::~DTSSpecific() {}
FourCC DTSSpecific::BoxType() const { return FOURCC_ddts; }
bool DTSSpecific::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&sampling_frequency) &&
buffer->ReadWriteUInt32(&max_bitrate) &&
buffer->ReadWriteUInt32(&avg_bitrate) &&
buffer->ReadWriteUInt8(&pcm_sample_depth));
if (buffer->Reading()) {
RCHECK(buffer->ReadWriteVector(&extra_data, buffer->BytesLeft()));
} else {
if (extra_data.empty()) {
extra_data.assign(kDdtsExtraData,
kDdtsExtraData + sizeof(kDdtsExtraData));
}
RCHECK(buffer->ReadWriteVector(&extra_data, extra_data.size()));
}
return true;
}
uint32_t DTSSpecific::ComputeSizeInternal() {
// This box is optional. Skip it if not initialized.
if (sampling_frequency == 0)
return 0;
return HeaderSize() + sizeof(sampling_frequency) + sizeof(max_bitrate) +
sizeof(avg_bitrate) + sizeof(pcm_sample_depth) +
sizeof(kDdtsExtraData);
}
AC3Specific::AC3Specific() {}
AC3Specific::~AC3Specific() {}
FourCC AC3Specific::BoxType() const { return FOURCC_dac3; }
bool AC3Specific::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteVector(
&data, buffer->Reading() ? buffer->BytesLeft() : data.size()));
return true;
}
uint32_t AC3Specific::ComputeSizeInternal() {
// This box is optional. Skip it if not initialized.
if (data.empty())
return 0;
return HeaderSize() + data.size();
}
EC3Specific::EC3Specific() {}
EC3Specific::~EC3Specific() {}
FourCC EC3Specific::BoxType() const { return FOURCC_dec3; }
bool EC3Specific::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
uint32_t size = buffer->Reading() ? buffer->BytesLeft() : data.size();
RCHECK(buffer->ReadWriteVector(&data, size));
return true;
}
uint32_t EC3Specific::ComputeSizeInternal() {
// This box is optional. Skip it if not initialized.
if (data.empty())
return 0;
return HeaderSize() + data.size();
}
AudioSampleEntry::AudioSampleEntry()
: format(FOURCC_NULL),
data_reference_index(1),
channelcount(2),
samplesize(16),
samplerate(0) {}
AudioSampleEntry::~AudioSampleEntry() {}
FourCC AudioSampleEntry::BoxType() const {
if (format == FOURCC_NULL) {
LOG(ERROR) << "AudioSampleEntry should be parsed according to the "
<< "handler type recovered in its Media ancestor.";
}
return format;
}
bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
if (buffer->Reading()) {
DCHECK(buffer->reader());
format = buffer->reader()->type();
} else {
RCHECK(ReadWriteHeaderInternal(buffer));
}
// 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(buffer->PrepareChildren());
if (format == FOURCC_enca) {
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 && sinf.type.type != FOURCC_cbc1) {
if (!buffer->ReadWriteChild(&sinf))
return false;
}
} else {
RCHECK(buffer->ReadWriteChild(&sinf));
}
}
RCHECK(buffer->TryReadWriteChild(&esds));
RCHECK(buffer->TryReadWriteChild(&ddts));
RCHECK(buffer->TryReadWriteChild(&dac3));
RCHECK(buffer->TryReadWriteChild(&dec3));
return true;
}
uint32_t AudioSampleEntry::ComputeSizeInternal() {
return HeaderSize() + sizeof(data_reference_index) + sizeof(channelcount) +
sizeof(samplesize) + sizeof(samplerate) + sinf.ComputeSize() +
esds.ComputeSize() + ddts.ComputeSize() + dac3.ComputeSize() +
dec3.ComputeSize() +
6 + 8 + // 6 + 8 bytes reserved.
4; // 4 bytes predefined.
}
WebVTTConfigurationBox::WebVTTConfigurationBox() {}
WebVTTConfigurationBox::~WebVTTConfigurationBox() {}
FourCC WebVTTConfigurationBox::BoxType() const {
return FOURCC_vttC;
}
bool WebVTTConfigurationBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(
&config,
buffer->Reading() ? buffer->BytesLeft() : config.size());
}
uint32_t WebVTTConfigurationBox::ComputeSizeInternal() {
return HeaderSize() + config.size();
}
WebVTTSourceLabelBox::WebVTTSourceLabelBox() {}
WebVTTSourceLabelBox::~WebVTTSourceLabelBox() {}
FourCC WebVTTSourceLabelBox::BoxType() const {
return FOURCC_vlab;
}
bool WebVTTSourceLabelBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(&source_label, buffer->Reading()
? buffer->BytesLeft()
: source_label.size());
}
uint32_t WebVTTSourceLabelBox::ComputeSizeInternal() {
if (source_label.empty())
return 0;
return HeaderSize() + source_label.size();
}
TextSampleEntry::TextSampleEntry() : format(FOURCC_NULL) {}
TextSampleEntry::~TextSampleEntry() {}
FourCC TextSampleEntry::BoxType() const {
if (format == FOURCC_NULL) {
LOG(ERROR) << "TextSampleEntry should be parsed according to the "
<< "handler type recovered in its Media ancestor.";
}
return format;
}
bool TextSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
if (buffer->Reading()) {
DCHECK(buffer->reader());
format = buffer->reader()->type();
} else {
RCHECK(ReadWriteHeaderInternal(buffer));
}
RCHECK(buffer->IgnoreBytes(6) && // reserved for SampleEntry.
buffer->ReadWriteUInt16(&data_reference_index));
if (format == FOURCC_wvtt) {
// TODO(rkuroiwa): Handle the optional MPEG4BitRateBox.
RCHECK(buffer->PrepareChildren() &&
buffer->ReadWriteChild(&config) &&
buffer->ReadWriteChild(&label));
}
return true;
}
uint32_t TextSampleEntry::ComputeSizeInternal() {
// 6 for the (anonymous) reserved bytes for SampleEntry class.
return HeaderSize() + 6 + sizeof(data_reference_index) +
config.ComputeSize() + label.ComputeSize();
}
MediaHeader::MediaHeader()
: creation_time(0), modification_time(0), timescale(0), duration(0) {}
MediaHeader::~MediaHeader() {}
FourCC MediaHeader::BoxType() const { return FOURCC_mdhd; }
bool MediaHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
uint8_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) &&
buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) &&
buffer->ReadWriteUInt32(&timescale) &&
buffer->ReadWriteUInt64NBytes(&duration, num_bytes) &&
language.ReadWrite(buffer) &&
buffer->IgnoreBytes(2)); // predefined.
return true;
}
uint32_t MediaHeader::ComputeSizeInternal() {
version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1;
return HeaderSize() + sizeof(timescale) +
sizeof(uint32_t) * (1 + version) * 3 + language.ComputeSize() +
2; // 2 bytes predefined.
}
VideoMediaHeader::VideoMediaHeader()
: graphicsmode(0), opcolor_red(0), opcolor_green(0), opcolor_blue(0) {
const uint32_t kVideoMediaHeaderFlags = 1;
flags = kVideoMediaHeaderFlags;
}
VideoMediaHeader::~VideoMediaHeader() {}
FourCC VideoMediaHeader::BoxType() const { return FOURCC_vmhd; }
bool VideoMediaHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt16(&graphicsmode) &&
buffer->ReadWriteUInt16(&opcolor_red) &&
buffer->ReadWriteUInt16(&opcolor_green) &&
buffer->ReadWriteUInt16(&opcolor_blue));
return true;
}
uint32_t VideoMediaHeader::ComputeSizeInternal() {
return HeaderSize() + sizeof(graphicsmode) + sizeof(opcolor_red) +
sizeof(opcolor_green) + sizeof(opcolor_blue);
}
SoundMediaHeader::SoundMediaHeader() : balance(0) {}
SoundMediaHeader::~SoundMediaHeader() {}
FourCC SoundMediaHeader::BoxType() const { return FOURCC_smhd; }
bool SoundMediaHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt16(&balance) &&
buffer->IgnoreBytes(2)); // reserved.
return true;
}
uint32_t SoundMediaHeader::ComputeSizeInternal() {
return HeaderSize() + sizeof(balance) + sizeof(uint16_t);
}
SubtitleMediaHeader::SubtitleMediaHeader() {}
SubtitleMediaHeader::~SubtitleMediaHeader() {}
FourCC SubtitleMediaHeader::BoxType() const { return FOURCC_sthd; }
bool SubtitleMediaHeader::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteHeaderInternal(buffer);
}
uint32_t SubtitleMediaHeader::ComputeSizeInternal() {
return HeaderSize();
}
DataEntryUrl::DataEntryUrl() {
const uint32_t kDataEntryUrlFlags = 1;
flags = kDataEntryUrlFlags;
}
DataEntryUrl::~DataEntryUrl() {}
FourCC DataEntryUrl::BoxType() const { return FOURCC_url; }
bool DataEntryUrl::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
if (buffer->Reading()) {
RCHECK(buffer->ReadWriteVector(&location, buffer->BytesLeft()));
} else {
RCHECK(buffer->ReadWriteVector(&location, location.size()));
}
return true;
}
uint32_t DataEntryUrl::ComputeSizeInternal() {
return HeaderSize() + location.size();
}
DataReference::DataReference() {
// Default 1 entry.
data_entry.resize(1);
}
DataReference::~DataReference() {}
FourCC DataReference::BoxType() const { return FOURCC_dref; }
bool DataReference::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t entry_count = data_entry.size();
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&entry_count));
data_entry.resize(entry_count);
RCHECK(buffer->PrepareChildren());
for (uint32_t i = 0; i < entry_count; ++i)
RCHECK(buffer->ReadWriteChild(&data_entry[i]));
return true;
}
uint32_t DataReference::ComputeSizeInternal() {
uint32_t count = data_entry.size();
uint32_t box_size = HeaderSize() + sizeof(count);
for (uint32_t i = 0; i < count; ++i)
box_size += data_entry[i].ComputeSize();
return box_size;
}
DataInformation::DataInformation() {}
DataInformation::~DataInformation() {}
FourCC DataInformation::BoxType() const { return FOURCC_dinf; }
bool DataInformation::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&dref);
}
uint32_t DataInformation::ComputeSizeInternal() {
return HeaderSize() + dref.ComputeSize();
}
MediaInformation::MediaInformation() {}
MediaInformation::~MediaInformation() {}
FourCC MediaInformation::BoxType() const { return FOURCC_minf; }
bool MediaInformation::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&dinf) &&
buffer->ReadWriteChild(&sample_table));
switch (sample_table.description.type) {
case kVideo:
RCHECK(buffer->ReadWriteChild(&vmhd));
break;
case kAudio:
RCHECK(buffer->ReadWriteChild(&smhd));
break;
case kText:
RCHECK(buffer->TryReadWriteChild(&sthd));
break;
default:
NOTIMPLEMENTED();
}
// Hint is not supported for now.
return true;
}
uint32_t MediaInformation::ComputeSizeInternal() {
uint32_t box_size =
HeaderSize() + dinf.ComputeSize() + sample_table.ComputeSize();
switch (sample_table.description.type) {
case kVideo:
box_size += vmhd.ComputeSize();
break;
case kAudio:
box_size += smhd.ComputeSize();
break;
case kText:
box_size += sthd.ComputeSize();
break;
default:
NOTIMPLEMENTED();
}
return box_size;
}
Media::Media() {}
Media::~Media() {}
FourCC Media::BoxType() const { return FOURCC_mdia; }
bool Media::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header));
if (buffer->Reading()) {
RCHECK(buffer->ReadWriteChild(&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 =
FourCCToTrackType(handler.handler_type);
} else {
handler.handler_type =
TrackTypeToFourCC(information.sample_table.description.type);
RCHECK(handler.handler_type != FOURCC_NULL);
RCHECK(buffer->ReadWriteChild(&handler));
}
RCHECK(buffer->ReadWriteChild(&information));
return true;
}
uint32_t Media::ComputeSizeInternal() {
handler.handler_type =
TrackTypeToFourCC(information.sample_table.description.type);
return HeaderSize() + header.ComputeSize() + handler.ComputeSize() +
information.ComputeSize();
}
Track::Track() {}
Track::~Track() {}
FourCC Track::BoxType() const { return FOURCC_trak; }
bool Track::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header) &&
buffer->ReadWriteChild(&media) &&
buffer->TryReadWriteChild(&edit) &&
buffer->TryReadWriteChild(&sample_encryption));
return true;
}
uint32_t Track::ComputeSizeInternal() {
return HeaderSize() + header.ComputeSize() + media.ComputeSize() +
edit.ComputeSize();
}
MovieExtendsHeader::MovieExtendsHeader() : fragment_duration(0) {}
MovieExtendsHeader::~MovieExtendsHeader() {}
FourCC MovieExtendsHeader::BoxType() const { return FOURCC_mehd; }
bool MovieExtendsHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
RCHECK(buffer->ReadWriteUInt64NBytes(&fragment_duration, num_bytes));
return true;
}
uint32_t MovieExtendsHeader::ComputeSizeInternal() {
// This box is optional. Skip it if it is not used.
if (fragment_duration == 0)
return 0;
version = IsFitIn32Bits(fragment_duration) ? 0 : 1;
return HeaderSize() + sizeof(uint32_t) * (1 + version);
}
TrackExtends::TrackExtends()
: track_id(0),
default_sample_description_index(0),
default_sample_duration(0),
default_sample_size(0),
default_sample_flags(0) {}
TrackExtends::~TrackExtends() {}
FourCC TrackExtends::BoxType() const { return FOURCC_trex; }
bool TrackExtends::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(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_t TrackExtends::ComputeSizeInternal() {
return HeaderSize() + sizeof(track_id) +
sizeof(default_sample_description_index) +
sizeof(default_sample_duration) + sizeof(default_sample_size) +
sizeof(default_sample_flags);
}
MovieExtends::MovieExtends() {}
MovieExtends::~MovieExtends() {}
FourCC MovieExtends::BoxType() const { return FOURCC_mvex; }
bool MovieExtends::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->TryReadWriteChild(&header));
if (buffer->Reading()) {
DCHECK(buffer->reader());
RCHECK(buffer->reader()->ReadChildren(&tracks));
} else {
for (uint32_t i = 0; i < tracks.size(); ++i)
RCHECK(buffer->ReadWriteChild(&tracks[i]));
}
return true;
}
uint32_t MovieExtends::ComputeSizeInternal() {
// This box is optional. Skip it if it does not contain any track.
if (tracks.size() == 0)
return 0;
uint32_t box_size = HeaderSize() + header.ComputeSize();
for (uint32_t i = 0; i < tracks.size(); ++i)
box_size += tracks[i].ComputeSize();
return box_size;
}
Movie::Movie() {}
Movie::~Movie() {}
FourCC Movie::BoxType() const { return FOURCC_moov; }
bool Movie::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header) &&
buffer->TryReadWriteChild(&metadata) &&
buffer->TryReadWriteChild(&extends));
if (buffer->Reading()) {
BoxReader* reader = buffer->reader();
DCHECK(reader);
RCHECK(reader->ReadChildren(&tracks) &&
reader->TryReadChildren(&pssh));
} else {
for (uint32_t i = 0; i < tracks.size(); ++i)
RCHECK(buffer->ReadWriteChild(&tracks[i]));
for (uint32_t i = 0; i < pssh.size(); ++i)
RCHECK(buffer->ReadWriteChild(&pssh[i]));
}
return true;
}
uint32_t Movie::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + header.ComputeSize() +
metadata.ComputeSize() + extends.ComputeSize();
for (uint32_t i = 0; i < tracks.size(); ++i)
box_size += tracks[i].ComputeSize();
for (uint32_t i = 0; i < pssh.size(); ++i)
box_size += pssh[i].ComputeSize();
return box_size;
}
TrackFragmentDecodeTime::TrackFragmentDecodeTime() : decode_time(0) {}
TrackFragmentDecodeTime::~TrackFragmentDecodeTime() {}
FourCC TrackFragmentDecodeTime::BoxType() const { return FOURCC_tfdt; }
bool TrackFragmentDecodeTime::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
RCHECK(buffer->ReadWriteUInt64NBytes(&decode_time, num_bytes));
return true;
}
uint32_t TrackFragmentDecodeTime::ComputeSizeInternal() {
version = IsFitIn32Bits(decode_time) ? 0 : 1;
return HeaderSize() + sizeof(uint32_t) * (1 + version);
}
MovieFragmentHeader::MovieFragmentHeader() : sequence_number(0) {}
MovieFragmentHeader::~MovieFragmentHeader() {}
FourCC MovieFragmentHeader::BoxType() const { return FOURCC_mfhd; }
bool MovieFragmentHeader::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&sequence_number);
}
uint32_t MovieFragmentHeader::ComputeSizeInternal() {
return HeaderSize() + sizeof(sequence_number);
}
TrackFragmentHeader::TrackFragmentHeader()
: track_id(0),
sample_description_index(0),
default_sample_duration(0),
default_sample_size(0),
default_sample_flags(0) {}
TrackFragmentHeader::~TrackFragmentHeader() {}
FourCC TrackFragmentHeader::BoxType() const { return FOURCC_tfhd; }
bool TrackFragmentHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&track_id));
if (flags & kBaseDataOffsetPresentMask) {
// MSE requires 'default-base-is-moof' to be set and
// 'base-data-offset-present' not to be set. We omit these checks as some
// valid files in the wild don't follow these rules, though they use moof as
// base.
uint64_t base_data_offset;
RCHECK(buffer->ReadWriteUInt64(&base_data_offset));
DLOG(WARNING) << "base-data-offset-present is not expected. Assumes "
"default-base-is-moof.";
}
if (flags & kSampleDescriptionIndexPresentMask) {
RCHECK(buffer->ReadWriteUInt32(&sample_description_index));
} else if (buffer->Reading()) {
sample_description_index = 0;
}
if (flags & kDefaultSampleDurationPresentMask) {
RCHECK(buffer->ReadWriteUInt32(&default_sample_duration));
} else if (buffer->Reading()) {
default_sample_duration = 0;
}
if (flags & kDefaultSampleSizePresentMask) {
RCHECK(buffer->ReadWriteUInt32(&default_sample_size));
} else if (buffer->Reading()) {
default_sample_size = 0;
}
if (flags & kDefaultSampleFlagsPresentMask)
RCHECK(buffer->ReadWriteUInt32(&default_sample_flags));
return true;
}
uint32_t TrackFragmentHeader::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + sizeof(track_id);
if (flags & kSampleDescriptionIndexPresentMask)
box_size += sizeof(sample_description_index);
if (flags & kDefaultSampleDurationPresentMask)
box_size += sizeof(default_sample_duration);
if (flags & kDefaultSampleSizePresentMask)
box_size += sizeof(default_sample_size);
if (flags & kDefaultSampleFlagsPresentMask)
box_size += sizeof(default_sample_flags);
return box_size;
}
TrackFragmentRun::TrackFragmentRun() : sample_count(0), data_offset(0) {}
TrackFragmentRun::~TrackFragmentRun() {}
FourCC TrackFragmentRun::BoxType() const { return FOURCC_trun; }
bool TrackFragmentRun::ReadWriteInternal(BoxBuffer* buffer) {
if (!buffer->Reading()) {
// Determine whether version 0 or version 1 should be used.
// Use version 0 if possible, use version 1 if there is a negative
// sample_offset value.
version = 0;
if (flags & kSampleCompTimeOffsetsPresentMask) {
for (uint32_t i = 0; i < sample_count; ++i) {
if (sample_composition_time_offsets[i] < 0) {
version = 1;
break;
}
}
}
}
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&sample_count));
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(buffer->ReadWriteUInt32(&data_offset));
} else {
// NOTE: 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.
NOTIMPLEMENTED();
}
uint32_t first_sample_flags;
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);
} 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_t i = 0; i < sample_count; ++i) {
if (sample_duration_present)
RCHECK(buffer->ReadWriteUInt32(&sample_durations[i]));
if (sample_size_present)
RCHECK(buffer->ReadWriteUInt32(&sample_sizes[i]));
if (sample_flags_present)
RCHECK(buffer->ReadWriteUInt32(&sample_flags[i]));
if (sample_composition_time_offsets_present) {
if (version == 0) {
uint32_t sample_offset = sample_composition_time_offsets[i];
RCHECK(buffer->ReadWriteUInt32(&sample_offset));
sample_composition_time_offsets[i] = sample_offset;
} else {
int32_t sample_offset = sample_composition_time_offsets[i];
RCHECK(buffer->ReadWriteInt32(&sample_offset));
sample_composition_time_offsets[i] = sample_offset;
}
}
}
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_t TrackFragmentRun::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + sizeof(sample_count);
if (flags & kDataOffsetPresentMask)
box_size += sizeof(data_offset);
if (flags & kFirstSampleFlagsPresentMask)
box_size += sizeof(uint32_t);
uint32_t fields = (flags & kSampleDurationPresentMask ? 1 : 0) +
(flags & kSampleSizePresentMask ? 1 : 0) +
(flags & kSampleFlagsPresentMask ? 1 : 0) +
(flags & kSampleCompTimeOffsetsPresentMask ? 1 : 0);
box_size += fields * sizeof(uint32_t) * sample_count;
return box_size;
}
SampleToGroup::SampleToGroup() : grouping_type(0), grouping_type_parameter(0) {}
SampleToGroup::~SampleToGroup() {}
FourCC SampleToGroup::BoxType() const { return FOURCC_sbgp; }
bool SampleToGroup::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&grouping_type));
if (version == 1)
RCHECK(buffer->ReadWriteUInt32(&grouping_type_parameter));
if (grouping_type != FOURCC_seig) {
DCHECK(buffer->Reading());
DLOG(WARNING) << "Sample group "
<< FourCCToString(static_cast<FourCC>(grouping_type))
<< " is not supported.";
return true;
}
uint32_t count = entries.size();
RCHECK(buffer->ReadWriteUInt32(&count));
entries.resize(count);
for (uint32_t i = 0; i < count; ++i) {
RCHECK(buffer->ReadWriteUInt32(&entries[i].sample_count) &&
buffer->ReadWriteUInt32(&entries[i].group_description_index));
}
return true;
}
uint32_t SampleToGroup::ComputeSizeInternal() {
// This box is optional. Skip it if it is not used.
if (entries.empty())
return 0;
return HeaderSize() + sizeof(grouping_type) +
(version == 1 ? sizeof(grouping_type_parameter) : 0) +
sizeof(uint32_t) + entries.size() * sizeof(entries[0]);
}
CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry()
: is_encrypted(false), iv_size(0) {
}
CencSampleEncryptionInfoEntry::~CencSampleEncryptionInfoEntry() {};
SampleGroupDescription::SampleGroupDescription() : grouping_type(0) {}
SampleGroupDescription::~SampleGroupDescription() {}
FourCC SampleGroupDescription::BoxType() const { return FOURCC_sgpd; }
bool SampleGroupDescription::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&grouping_type));
if (grouping_type != FOURCC_seig) {
DCHECK(buffer->Reading());
DLOG(WARNING) << "Sample group '" << grouping_type << "' is not supported.";
return true;
}
const size_t kEntrySize = sizeof(uint32_t) + kCencKeyIdSize;
uint32_t default_length = 0;
if (version == 1) {
if (buffer->Reading()) {
RCHECK(buffer->ReadWriteUInt32(&default_length));
RCHECK(default_length == 0 || default_length >= kEntrySize);
} else {
default_length = kEntrySize;
RCHECK(buffer->ReadWriteUInt32(&default_length));
}
}
uint32_t count = entries.size();
RCHECK(buffer->ReadWriteUInt32(&count));
entries.resize(count);
for (uint32_t i = 0; i < count; ++i) {
if (version == 1) {
if (buffer->Reading() && default_length == 0) {
uint32_t description_length = 0;
RCHECK(buffer->ReadWriteUInt32(&description_length));
RCHECK(description_length >= kEntrySize);
}
}
if (!buffer->Reading()) {
if (entries[i].key_id.size() != kCencKeyIdSize) {
LOG(WARNING) << "CENC defines key id length of " << kCencKeyIdSize
<< " bytes; got " << entries[i].key_id.size()
<< ". Resized accordingly.";
entries[i].key_id.resize(kCencKeyIdSize);
}
}
uint8_t flag = entries[i].is_encrypted ? 1 : 0;
RCHECK(buffer->IgnoreBytes(2) && // reserved.
buffer->ReadWriteUInt8(&flag) &&
buffer->ReadWriteUInt8(&entries[i].iv_size) &&
buffer->ReadWriteVector(&entries[i].key_id, kCencKeyIdSize));
if (buffer->Reading()) {
entries[i].is_encrypted = (flag != 0);
if (entries[i].is_encrypted) {
RCHECK(entries[i].iv_size == 8 || entries[i].iv_size == 16);
} else {
RCHECK(entries[i].iv_size == 0);
}
}
}
return true;
}
uint32_t SampleGroupDescription::ComputeSizeInternal() {
// Version 0 is obsoleted, so always generate version 1 box.
version = 1;
// This box is optional. Skip it if it is not used.
if (entries.empty())
return 0;
const size_t kEntrySize = sizeof(uint32_t) + kCencKeyIdSize;
return HeaderSize() + sizeof(grouping_type) +
(version == 1 ? sizeof(uint32_t) : 0) + sizeof(uint32_t) +
entries.size() * kEntrySize;
}
TrackFragment::TrackFragment() : decode_time_absent(false) {}
TrackFragment::~TrackFragment() {}
FourCC TrackFragment::BoxType() const { return FOURCC_traf; }
bool TrackFragment::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header));
if (buffer->Reading()) {
DCHECK(buffer->reader());
decode_time_absent = !buffer->reader()->ChildExist(&decode_time);
if (!decode_time_absent)
RCHECK(buffer->ReadWriteChild(&decode_time));
RCHECK(buffer->reader()->TryReadChildren(&runs));
// There could be multiple SampleGroupDescription and SampleToGroup boxes
// with different grouping types. For common encryption, the relevant
// grouping type is 'seig'. Continue reading until 'seig' is found, or
// until running out of child boxes.
while (sample_to_group.grouping_type != FOURCC_seig &&
buffer->reader()->ChildExist(&sample_to_group)) {
RCHECK(buffer->reader()->ReadChild(&sample_to_group));
}
while (sample_group_description.grouping_type != FOURCC_seig &&
buffer->reader()->ChildExist(&sample_group_description)) {
RCHECK(buffer->reader()->ReadChild(&sample_group_description));
}
} else {
if (!decode_time_absent)
RCHECK(buffer->ReadWriteChild(&decode_time));
for (uint32_t i = 0; i < runs.size(); ++i)
RCHECK(buffer->ReadWriteChild(&runs[i]));
RCHECK(buffer->TryReadWriteChild(&sample_to_group) &&
buffer->TryReadWriteChild(&sample_group_description));
}
return buffer->TryReadWriteChild(&auxiliary_size) &&
buffer->TryReadWriteChild(&auxiliary_offset) &&
buffer->TryReadWriteChild(&sample_encryption);
}
uint32_t TrackFragment::ComputeSizeInternal() {
uint32_t box_size =
HeaderSize() + header.ComputeSize() + decode_time.ComputeSize() +
sample_to_group.ComputeSize() + sample_group_description.ComputeSize() +
auxiliary_size.ComputeSize() + auxiliary_offset.ComputeSize() +
sample_encryption.ComputeSize();
for (uint32_t i = 0; i < runs.size(); ++i)
box_size += runs[i].ComputeSize();
return box_size;
}
MovieFragment::MovieFragment() {}
MovieFragment::~MovieFragment() {}
FourCC MovieFragment::BoxType() const { return FOURCC_moof; }
bool MovieFragment::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(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_t i = 0; i < tracks.size(); ++i)
RCHECK(buffer->ReadWriteChild(&tracks[i]));
for (uint32_t i = 0; i < pssh.size(); ++i)
RCHECK(buffer->ReadWriteChild(&pssh[i]));
}
return true;
}
uint32_t MovieFragment::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + header.ComputeSize();
for (uint32_t i = 0; i < tracks.size(); ++i)
box_size += tracks[i].ComputeSize();
for (uint32_t i = 0; i < pssh.size(); ++i)
box_size += pssh[i].ComputeSize();
return box_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::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->ReadWriteUInt32(&reference_id) &&
buffer->ReadWriteUInt32(&timescale));
size_t num_bytes = (version == 1) ? sizeof(uint64_t) : sizeof(uint32_t);
RCHECK(
buffer->ReadWriteUInt64NBytes(&earliest_presentation_time, num_bytes) &&
buffer->ReadWriteUInt64NBytes(&first_offset, num_bytes));
uint16_t reference_count = references.size();
RCHECK(buffer->IgnoreBytes(2) && // reserved.
buffer->ReadWriteUInt16(&reference_count));
references.resize(reference_count);
uint32_t reference_type_size;
uint32_t sap;
for (uint32_t 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<SegmentReference::SAPType>((sap >> 28) & 0x07);
references[i].sap_delta_time = sap & ~(0xF << 28);
}
}
return true;
}
uint32_t SegmentIndex::ComputeSizeInternal() {
version = IsFitIn32Bits(earliest_presentation_time, first_offset) ? 0 : 1;
return HeaderSize() + sizeof(reference_id) + sizeof(timescale) +
sizeof(uint32_t) * (1 + version) * 2 + 2 * sizeof(uint16_t) +
3 * sizeof(uint32_t) * references.size();
}
MediaData::MediaData() : data_size(0) {}
MediaData::~MediaData() {}
FourCC MediaData::BoxType() const { return FOURCC_mdat; }
bool MediaData::ReadWriteInternal(BoxBuffer* buffer) {
NOTIMPLEMENTED() << "Actual data is parsed and written separately.";
return false;
}
uint32_t MediaData::ComputeSizeInternal() {
return HeaderSize() + data_size;
}
CueSourceIDBox::CueSourceIDBox() : source_id(kCueSourceIdNotSet) {}
CueSourceIDBox::~CueSourceIDBox() {}
FourCC CueSourceIDBox::BoxType() const { return FOURCC_vsid; }
bool CueSourceIDBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) && buffer->ReadWriteInt32(&source_id));
return true;
}
uint32_t CueSourceIDBox::ComputeSizeInternal() {
if (source_id == kCueSourceIdNotSet)
return 0;
return HeaderSize() + sizeof(source_id);
}
CueTimeBox::CueTimeBox() {}
CueTimeBox::~CueTimeBox() {}
FourCC CueTimeBox::BoxType() const {
return FOURCC_ctim;
}
bool CueTimeBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(
&cue_current_time,
buffer->Reading() ? buffer->BytesLeft() : cue_current_time.size());
}
uint32_t CueTimeBox::ComputeSizeInternal() {
if (cue_current_time.empty())
return 0;
return HeaderSize() + cue_current_time.size();
}
CueIDBox::CueIDBox() {}
CueIDBox::~CueIDBox() {}
FourCC CueIDBox::BoxType() const {
return FOURCC_iden;
}
bool CueIDBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(
&cue_id, buffer->Reading() ? buffer->BytesLeft() : cue_id.size());
}
uint32_t CueIDBox::ComputeSizeInternal() {
if (cue_id.empty())
return 0;
return HeaderSize() + cue_id.size();
}
CueSettingsBox::CueSettingsBox() {}
CueSettingsBox::~CueSettingsBox() {}
FourCC CueSettingsBox::BoxType() const {
return FOURCC_sttg;
}
bool CueSettingsBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(
&settings, buffer->Reading() ? buffer->BytesLeft() : settings.size());
}
uint32_t CueSettingsBox::ComputeSizeInternal() {
if (settings.empty())
return 0;
return HeaderSize() + settings.size();
}
CuePayloadBox::CuePayloadBox() {}
CuePayloadBox::~CuePayloadBox() {}
FourCC CuePayloadBox::BoxType() const {
return FOURCC_payl;
}
bool CuePayloadBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(
&cue_text, buffer->Reading() ? buffer->BytesLeft() : cue_text.size());
}
uint32_t CuePayloadBox::ComputeSizeInternal() {
return HeaderSize() + cue_text.size();
}
VTTEmptyCueBox::VTTEmptyCueBox() {}
VTTEmptyCueBox::~VTTEmptyCueBox() {}
FourCC VTTEmptyCueBox::BoxType() const {
return FOURCC_vtte;
}
bool VTTEmptyCueBox::ReadWriteInternal(BoxBuffer* buffer) {
return ReadWriteHeaderInternal(buffer);
}
uint32_t VTTEmptyCueBox::ComputeSizeInternal() {
return HeaderSize();
}
VTTAdditionalTextBox::VTTAdditionalTextBox() {}
VTTAdditionalTextBox::~VTTAdditionalTextBox() {}
FourCC VTTAdditionalTextBox::BoxType() const {
return FOURCC_vtta;
}
bool VTTAdditionalTextBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer));
return buffer->ReadWriteString(
&cue_additional_text,
buffer->Reading() ? buffer->BytesLeft() : cue_additional_text.size());
}
uint32_t VTTAdditionalTextBox::ComputeSizeInternal() {
return HeaderSize() + cue_additional_text.size();
}
VTTCueBox::VTTCueBox() {}
VTTCueBox::~VTTCueBox() {}
FourCC VTTCueBox::BoxType() const {
return FOURCC_vttc;
}
bool VTTCueBox::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&cue_source_id) &&
buffer->ReadWriteChild(&cue_id) &&
buffer->ReadWriteChild(&cue_time) &&
buffer->ReadWriteChild(&cue_settings) &&
buffer->ReadWriteChild(&cue_payload));
return true;
}
uint32_t VTTCueBox::ComputeSizeInternal() {
return HeaderSize() + cue_source_id.ComputeSize() + cue_id.ComputeSize() +
cue_time.ComputeSize() + cue_settings.ComputeSize() +
cue_payload.ComputeSize();
}
} // namespace mp4
} // namespace media
} // namespace edash_packager