blob: 069f22bcd82386f0765561620671adc12b701c47 [file] [log] [blame]
// Copyright 2014 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/formats/mp4/box_definitions.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "base/big_endian.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_math.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "build/build_config.h"
#include "media/base/media_switches.h"
#include "media/base/media_util.h"
#include "media/base/video_types.h"
#include "media/base/video_util.h"
#include "media/formats/common/opus_constants.h"
#include "media/formats/mp4/es_descriptor.h"
#include "media/formats/mp4/rcheck.h"
#include "media/media_buildflags.h"
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
#include "media/formats/mp4/avc.h"
#include "media/video/h264_parser.h" // nogncheck
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
#include "media/formats/mp4/dolby_vision.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
#include "media/formats/mp4/hevc.h"
#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
namespace media {
namespace mp4 {
namespace {
const size_t kKeyIdSize = 16;
const size_t kFlacMetadataBlockStreaminfoSize = 34;
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
// Parse dvcC or dvvC box.
absl::optional<DOVIDecoderConfigurationRecord> ParseDOVIConfig(
BoxReader* reader) {
{
DolbyVisionConfiguration dvcc;
if (reader->HasChild(&dvcc) && reader->ReadChild(&dvcc)) {
DCHECK_LE(dvcc.dovi_config.dv_profile, 7);
return dvcc.dovi_config;
}
}
{
DolbyVisionConfiguration8 dvvc;
if (reader->HasChild(&dvvc) && reader->ReadChild(&dvvc)) {
DCHECK_GT(dvvc.dovi_config.dv_profile, 7);
return dvvc.dovi_config;
}
}
return absl::nullopt;
}
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
// Read color coordinate value as defined in the MasteringDisplayColorVolume
// ('mdcv') box. Each coordinate is a float encoded in uint16_t, with upper
// bound set to 50000.
bool ReadMdcvColorCoordinate(BoxReader* reader,
float* normalized_value_in_float) {
const float kColorCoordinateUpperBound = 50000.;
uint16_t value_in_uint16;
RCHECK(reader->Read2(&value_in_uint16));
float value_in_float = static_cast<float>(value_in_uint16);
if (value_in_float >= kColorCoordinateUpperBound) {
*normalized_value_in_float = 1.f;
return true;
}
*normalized_value_in_float = value_in_float / kColorCoordinateUpperBound;
return true;
}
// Read "fixed point" value as defined in the
// SMPTE2086MasteringDisplayMetadataBox ('SmDm') box. See
// https://www.webmproject.org/vp9/mp4/#data-types-and-fields
bool ReadFixedPoint16(float fixed_point_divisor,
BoxReader* reader,
float* normalized_value_in_float) {
uint16_t value;
RCHECK(reader->Read2(&value));
*normalized_value_in_float = value / fixed_point_divisor;
return true;
}
bool ReadFixedPoint32(float fixed_point_divisor,
BoxReader* reader,
float* normalized_value_in_float) {
uint32_t value;
RCHECK(reader->Read4(&value));
*normalized_value_in_float = value / fixed_point_divisor;
return true;
}
VideoColorSpace ConvertColorParameterInformationToColorSpace(
const ColorParameterInformation& info) {
auto primary_id =
static_cast<VideoColorSpace::PrimaryID>(info.colour_primaries);
auto transfer_id =
static_cast<VideoColorSpace::TransferID>(info.transfer_characteristics);
auto matrix_id =
static_cast<VideoColorSpace::MatrixID>(info.matrix_coefficients);
// Note that we don't check whether the embedded ids are valid. We rely on
// the underlying video decoder to reject any ids that it doesn't support.
return VideoColorSpace(primary_id, transfer_id, matrix_id,
info.full_range ? gfx::ColorSpace::RangeID::FULL
: gfx::ColorSpace::RangeID::LIMITED);
}
} // namespace
FileType::FileType() = default;
FileType::FileType(const FileType& other) = default;
FileType::~FileType() = default;
FourCC FileType::BoxType() const { return FOURCC_FTYP; }
bool FileType::Parse(BoxReader* reader) {
RCHECK(reader->ReadFourCC(&major_brand) && reader->Read4(&minor_version));
size_t num_brands = (reader->box_size() - reader->pos()) / sizeof(FourCC);
return reader->SkipBytes(sizeof(FourCC) * num_brands); // compatible_brands
}
ProtectionSystemSpecificHeader::ProtectionSystemSpecificHeader() = default;
ProtectionSystemSpecificHeader::ProtectionSystemSpecificHeader(
const ProtectionSystemSpecificHeader& other) = default;
ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() = default;
FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; }
bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) {
// Don't bother validating the box's contents.
// Copy the entire box, including the header, for passing to EME as initData.
DCHECK(raw_box.empty());
raw_box.assign(reader->buffer(), reader->buffer() + reader->box_size());
return true;
}
FullProtectionSystemSpecificHeader::FullProtectionSystemSpecificHeader() =
default;
FullProtectionSystemSpecificHeader::FullProtectionSystemSpecificHeader(
const FullProtectionSystemSpecificHeader& other) = default;
FullProtectionSystemSpecificHeader::~FullProtectionSystemSpecificHeader() =
default;
FourCC FullProtectionSystemSpecificHeader::BoxType() const {
return FOURCC_PSSH;
}
// The format of a 'pssh' box is as follows:
// unsigned int(32) size;
// unsigned int(32) type = "pssh";
// if (size==1) {
// unsigned int(64) largesize;
// } else if (size==0) {
// -- box extends to end of file
// }
// unsigned int(8) version;
// bit(24) flags;
// unsigned int(8)[16] SystemID;
// if (version > 0)
// {
// unsigned int(32) KID_count;
// {
// unsigned int(8)[16] KID;
// } [KID_count]
// }
// unsigned int(32) DataSize;
// unsigned int(8)[DataSize] Data;
bool FullProtectionSystemSpecificHeader::Parse(mp4::BoxReader* reader) {
RCHECK(reader->type() == BoxType() && reader->ReadFullBoxHeader());
// Only versions 0 and 1 of the 'pssh' boxes are supported. Any other
// versions are ignored.
RCHECK(reader->version() == 0 || reader->version() == 1);
RCHECK(reader->flags() == 0);
RCHECK(reader->ReadVec(&system_id, 16));
if (reader->version() > 0) {
uint32_t kid_count;
RCHECK(reader->Read4(&kid_count));
for (uint32_t i = 0; i < kid_count; ++i) {
std::vector<uint8_t> kid;
RCHECK(reader->ReadVec(&kid, 16));
key_ids.push_back(kid);
}
}
uint32_t data_size;
RCHECK(reader->Read4(&data_size));
RCHECK(reader->ReadVec(&data, data_size));
return true;
}
SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() = default;
SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset(
const SampleAuxiliaryInformationOffset& other) = default;
SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() = default;
FourCC SampleAuxiliaryInformationOffset::BoxType() const { return FOURCC_SAIO; }
bool SampleAuxiliaryInformationOffset::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->flags() & 1)
RCHECK(reader->SkipBytes(8));
uint32_t count;
RCHECK(reader->Read4(&count));
int bytes_per_offset = reader->version() == 1 ? 8 : 4;
// Cast |count| to size_t before multiplying to support maximum platform size.
base::CheckedNumeric<size_t> bytes_needed =
base::CheckMul(bytes_per_offset, static_cast<size_t>(count));
RCHECK_MEDIA_LOGGED(bytes_needed.IsValid(), reader->media_log(),
"Extreme SAIO count exceeds implementation limit.");
RCHECK(reader->HasBytes(bytes_needed.ValueOrDie()));
RCHECK(count <= offsets.max_size());
offsets.resize(count);
for (uint32_t i = 0; i < count; i++) {
if (reader->version() == 1) {
RCHECK(reader->Read8(&offsets[i]));
} else {
RCHECK(reader->Read4Into8(&offsets[i]));
}
}
return true;
}
SampleAuxiliaryInformationSize::SampleAuxiliaryInformationSize()
: default_sample_info_size(0), sample_count(0) {
}
SampleAuxiliaryInformationSize::SampleAuxiliaryInformationSize(
const SampleAuxiliaryInformationSize& other) = default;
SampleAuxiliaryInformationSize::~SampleAuxiliaryInformationSize() = default;
FourCC SampleAuxiliaryInformationSize::BoxType() const { return FOURCC_SAIZ; }
bool SampleAuxiliaryInformationSize::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->flags() & 1)
RCHECK(reader->SkipBytes(8));
RCHECK(reader->Read1(&default_sample_info_size) &&
reader->Read4(&sample_count));
if (default_sample_info_size == 0)
return reader->ReadVec(&sample_info_sizes, sample_count);
return true;
}
SampleEncryptionEntry::SampleEncryptionEntry() = default;
SampleEncryptionEntry::SampleEncryptionEntry(
const SampleEncryptionEntry& other) = default;
SampleEncryptionEntry::~SampleEncryptionEntry() = default;
bool SampleEncryptionEntry::Parse(BufferReader* reader,
uint8_t iv_size,
bool has_subsamples) {
// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
// 64-bit (8-byte) or 128-bit (16-byte). The 3rd Edition allows |iv_size|
// to be 0, for the case of a "constant IV". In this case, the existence of
// the constant IV must be ensured by the caller.
RCHECK(iv_size == 0 || iv_size == 8 || iv_size == 16);
memset(initialization_vector, 0, sizeof(initialization_vector));
for (uint8_t i = 0; i < iv_size; i++)
RCHECK(reader->Read1(initialization_vector + i));
if (!has_subsamples) {
subsamples.clear();
return true;
}
uint16_t subsample_count;
RCHECK(reader->Read2(&subsample_count));
RCHECK(subsample_count > 0);
RCHECK(subsample_count <= subsamples.max_size());
subsamples.resize(subsample_count);
for (SubsampleEntry& subsample : subsamples) {
uint16_t clear_bytes;
uint32_t cypher_bytes;
RCHECK(reader->Read2(&clear_bytes) && reader->Read4(&cypher_bytes));
subsample.clear_bytes = clear_bytes;
subsample.cypher_bytes = cypher_bytes;
}
return true;
}
bool SampleEncryptionEntry::GetTotalSizeOfSubsamples(size_t* total_size) const {
size_t size = 0;
for (const SubsampleEntry& subsample : subsamples) {
size += subsample.clear_bytes;
RCHECK(size >= subsample.clear_bytes); // overflow
size += subsample.cypher_bytes;
RCHECK(size >= subsample.cypher_bytes); // overflow
}
*total_size = size;
return true;
}
SampleEncryption::SampleEncryption() : use_subsample_encryption(false) {}
SampleEncryption::SampleEncryption(const SampleEncryption& other) = default;
SampleEncryption::~SampleEncryption() = default;
FourCC SampleEncryption::BoxType() const {
return FOURCC_SENC;
}
bool SampleEncryption::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
use_subsample_encryption = (reader->flags() & kUseSubsampleEncryption) != 0;
sample_encryption_data.assign(reader->buffer() + reader->pos(),
reader->buffer() + reader->box_size());
return true;
}
OriginalFormat::OriginalFormat() : format(FOURCC_NULL) {}
OriginalFormat::OriginalFormat(const OriginalFormat& other) = default;
OriginalFormat::~OriginalFormat() = default;
FourCC OriginalFormat::BoxType() const { return FOURCC_FRMA; }
bool OriginalFormat::Parse(BoxReader* reader) {
return reader->ReadFourCC(&format);
}
SchemeType::SchemeType() : type(FOURCC_NULL), version(0) {}
SchemeType::SchemeType(const SchemeType& other) = default;
SchemeType::~SchemeType() = default;
FourCC SchemeType::BoxType() const { return FOURCC_SCHM; }
bool SchemeType::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader() &&
reader->ReadFourCC(&type) &&
reader->Read4(&version));
return true;
}
TrackEncryption::TrackEncryption()
: is_encrypted(false),
default_iv_size(0),
default_crypt_byte_block(0),
default_skip_byte_block(0),
default_constant_iv_size(0) {}
TrackEncryption::TrackEncryption(const TrackEncryption& other) = default;
TrackEncryption::~TrackEncryption() = default;
FourCC TrackEncryption::BoxType() const { return FOURCC_TENC; }
bool TrackEncryption::Parse(BoxReader* reader) {
uint8_t flag;
uint8_t possible_pattern_info;
RCHECK(reader->ReadFullBoxHeader() &&
reader->SkipBytes(1) && // skip reserved byte
reader->Read1(&possible_pattern_info) && reader->Read1(&flag) &&
reader->Read1(&default_iv_size) &&
reader->ReadVec(&default_kid, kKeyIdSize));
is_encrypted = (flag != 0);
if (is_encrypted) {
if (reader->version() > 0) {
default_crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
default_skip_byte_block = possible_pattern_info & 0x0f;
}
if (default_iv_size == 0) {
RCHECK(reader->Read1(&default_constant_iv_size));
RCHECK(default_constant_iv_size == 8 || default_constant_iv_size == 16);
memset(default_constant_iv, 0, sizeof(default_constant_iv));
for (uint8_t i = 0; i < default_constant_iv_size; i++)
RCHECK(reader->Read1(default_constant_iv + i));
} else {
RCHECK(default_iv_size == 8 || default_iv_size == 16);
}
} else {
RCHECK(default_iv_size == 0);
}
return true;
}
SchemeInfo::SchemeInfo() = default;
SchemeInfo::SchemeInfo(const SchemeInfo& other) = default;
SchemeInfo::~SchemeInfo() = default;
FourCC SchemeInfo::BoxType() const { return FOURCC_SCHI; }
bool SchemeInfo::Parse(BoxReader* reader) {
return reader->ScanChildren() && reader->ReadChild(&track_encryption);
}
ProtectionSchemeInfo::ProtectionSchemeInfo() = default;
ProtectionSchemeInfo::ProtectionSchemeInfo(const ProtectionSchemeInfo& other) =
default;
ProtectionSchemeInfo::~ProtectionSchemeInfo() = default;
FourCC ProtectionSchemeInfo::BoxType() const { return FOURCC_SINF; }
bool ProtectionSchemeInfo::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() &&
reader->ReadChild(&format) &&
reader->ReadChild(&type));
if (HasSupportedScheme())
RCHECK(reader->ReadChild(&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
// unsupported protection schemes. It is the parent box's responsibility to
// ensure that this scheme type is a supported one.
return true;
}
bool ProtectionSchemeInfo::HasSupportedScheme() const {
FourCC four_cc = type.type;
return (four_cc == FOURCC_CENC || four_cc == FOURCC_CBCS);
}
bool ProtectionSchemeInfo::IsCbcsEncryptionScheme() const {
FourCC four_cc = type.type;
return (four_cc == FOURCC_CBCS);
}
MovieHeader::MovieHeader()
: version(0),
creation_time(0),
modification_time(0),
timescale(0),
duration(0),
rate(-1),
volume(-1),
next_track_id(0) {}
MovieHeader::MovieHeader(const MovieHeader& other) = default;
MovieHeader::~MovieHeader() = default;
FourCC MovieHeader::BoxType() const { return FOURCC_MVHD; }
bool MovieHeader::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
version = reader->version();
if (version == 1) {
RCHECK(reader->Read8(&creation_time) &&
reader->Read8(&modification_time) &&
reader->Read4(&timescale) &&
reader->Read8(&duration));
} else {
RCHECK(reader->Read4Into8(&creation_time) &&
reader->Read4Into8(&modification_time) &&
reader->Read4(&timescale) &&
reader->Read4Into8(&duration));
}
RCHECK_MEDIA_LOGGED(timescale > 0, reader->media_log(),
"Movie header's timescale must not be 0");
RCHECK(reader->Read4s(&rate) &&
reader->Read2s(&volume) &&
reader->SkipBytes(10) && // reserved
reader->ReadDisplayMatrix(display_matrix) &&
reader->SkipBytes(24) && // predefined zero
reader->Read4(&next_track_id));
return true;
}
TrackHeader::TrackHeader()
: creation_time(0),
modification_time(0),
track_id(0),
duration(0),
layer(-1),
alternate_group(-1),
volume(-1),
width(0),
height(0) {}
TrackHeader::TrackHeader(const TrackHeader& other) = default;
TrackHeader::~TrackHeader() = default;
FourCC TrackHeader::BoxType() const { return FOURCC_TKHD; }
bool TrackHeader::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->version() == 1) {
RCHECK(reader->Read8(&creation_time) &&
reader->Read8(&modification_time) &&
reader->Read4(&track_id) &&
reader->SkipBytes(4) && // reserved
reader->Read8(&duration));
} else {
RCHECK(reader->Read4Into8(&creation_time) &&
reader->Read4Into8(&modification_time) &&
reader->Read4(&track_id) &&
reader->SkipBytes(4) && // reserved
reader->Read4Into8(&duration));
}
RCHECK(reader->SkipBytes(8) && // reserved
reader->Read2s(&layer) &&
reader->Read2s(&alternate_group) &&
reader->Read2s(&volume) &&
reader->SkipBytes(2) && // reserved
reader->ReadDisplayMatrix(display_matrix) &&
reader->Read4(&width) &&
reader->Read4(&height));
// Round width and height to the nearest number.
// Note: width and height are fixed-point 16.16 values. The following code
// rounds a.1x to a + 1, and a.0x to a.
width >>= 15;
width += 1;
width >>= 1;
height >>= 15;
height += 1;
height >>= 1;
return true;
}
SampleDescription::SampleDescription() : type(kInvalid) {}
SampleDescription::SampleDescription(const SampleDescription& other) = default;
SampleDescription::~SampleDescription() = default;
FourCC SampleDescription::BoxType() const { return FOURCC_STSD; }
bool SampleDescription::Parse(BoxReader* reader) {
uint32_t count;
RCHECK(reader->SkipBytes(4) &&
reader->Read4(&count));
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));
} else if (type == kAudio) {
RCHECK(reader->ReadAllChildren(&audio_entries));
}
return true;
}
SampleTable::SampleTable() = default;
SampleTable::SampleTable(const SampleTable& other) = default;
SampleTable::~SampleTable() = default;
FourCC SampleTable::BoxType() const { return FOURCC_STBL; }
bool SampleTable::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() &&
reader->ReadChild(&description));
// There could be multiple SampleGroupDescription 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 (reader->HasChild(&sample_group_description)) {
RCHECK(reader->ReadChild(&sample_group_description));
if (sample_group_description.grouping_type == FOURCC_SEIG)
break;
sample_group_description.entries.clear();
}
return true;
}
EditList::EditList() = default;
EditList::EditList(const EditList& other) = default;
EditList::~EditList() = default;
FourCC EditList::BoxType() const { return FOURCC_ELST; }
bool EditList::Parse(BoxReader* reader) {
uint32_t count;
RCHECK(reader->ReadFullBoxHeader() && reader->Read4(&count));
const size_t bytes_per_edit = reader->version() == 1 ? 20 : 12;
// Cast |count| to size_t before multiplying to support maximum platform size.
base::CheckedNumeric<size_t> bytes_needed =
base::CheckMul(bytes_per_edit, static_cast<size_t>(count));
RCHECK_MEDIA_LOGGED(bytes_needed.IsValid(), reader->media_log(),
"Extreme ELST count exceeds implementation limit.");
RCHECK(reader->HasBytes(bytes_needed.ValueOrDie()));
RCHECK(count <= edits.max_size());
edits.resize(count);
for (auto edit = edits.begin(); edit != edits.end(); ++edit) {
if (reader->version() == 1) {
RCHECK(reader->Read8(&edit->segment_duration) &&
reader->Read8s(&edit->media_time));
} else {
RCHECK(reader->Read4Into8(&edit->segment_duration) &&
reader->Read4sInto8s(&edit->media_time));
}
RCHECK(reader->Read2s(&edit->media_rate_integer) &&
reader->Read2s(&edit->media_rate_fraction));
}
return true;
}
Edit::Edit() = default;
Edit::Edit(const Edit& other) = default;
Edit::~Edit() = default;
FourCC Edit::BoxType() const { return FOURCC_EDTS; }
bool Edit::Parse(BoxReader* reader) {
return reader->ScanChildren() && reader->ReadChild(&list);
}
HandlerReference::HandlerReference() : type(kInvalid) {}
HandlerReference::HandlerReference(const HandlerReference& other) = default;
HandlerReference::~HandlerReference() = default;
FourCC HandlerReference::BoxType() const { return FOURCC_HDLR; }
bool HandlerReference::Parse(BoxReader* reader) {
FourCC hdlr_type;
RCHECK(reader->ReadFullBoxHeader() && reader->SkipBytes(4) &&
reader->ReadFourCC(&hdlr_type) && reader->SkipBytes(12));
// Now we should be at the beginning of the |name| field of HDLR box. The
// |name| is a zero-terminated ASCII string in ISO BMFF, but it was a
// Pascal-style counted string in older QT/Mov formats. So we'll read the
// remaining box bytes first, then if the last one is zero, we strip the last
// zero byte, otherwise we'll string the first byte (containing the length of
// the Pascal-style string).
std::vector<uint8_t> name_bytes;
RCHECK(reader->ReadVec(&name_bytes, reader->box_size() - reader->pos()));
if (name_bytes.size() == 0) {
name = "";
} else if (name_bytes.back() == 0) {
// This is a zero-terminated C-style string, exclude the last byte.
name = std::string(name_bytes.begin(), name_bytes.end() - 1);
} else {
// Check that the length of the Pascal-style string is correct.
RCHECK(name_bytes[0] == (name_bytes.size() - 1));
// Skip the first byte, containing the length of the Pascal-string.
name = std::string(name_bytes.begin() + 1, name_bytes.end());
}
if (hdlr_type == FOURCC_VIDE) {
type = kVideo;
} else if (hdlr_type == FOURCC_SOUN) {
type = kAudio;
} else if (hdlr_type == FOURCC_META || hdlr_type == FOURCC_SUBT ||
hdlr_type == FOURCC_TEXT || hdlr_type == FOURCC_SBTL) {
// For purposes of detection, we include 'sbtl' handler here. Note, though
// that ISO-14496-12 and its 2012 Amendment 2, and the spec for sourcing
// inband tracks all reference only 'text' or 'subt', and 14496-30
// references only 'subt'. Yet ffmpeg can encode subtitles as 'sbtl'.
type = kText;
} else {
type = kInvalid;
}
return true;
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord()
: version(0),
profile_indication(0),
profile_compatibility(0),
avc_level(0),
length_size(0) {}
AVCDecoderConfigurationRecord::AVCDecoderConfigurationRecord(
const AVCDecoderConfigurationRecord& other) = default;
AVCDecoderConfigurationRecord::~AVCDecoderConfigurationRecord() = default;
FourCC AVCDecoderConfigurationRecord::BoxType() const { return FOURCC_AVCC; }
bool AVCDecoderConfigurationRecord::Parse(BoxReader* reader) {
return ParseInternal(reader, reader->media_log());
}
bool AVCDecoderConfigurationRecord::Parse(const uint8_t* data, int data_size) {
BufferReader reader(data, data_size);
// TODO(wolenetz): Questionable MediaLog usage, http://crbug.com/712310
NullMediaLog media_log;
return ParseInternal(&reader, &media_log);
}
bool AVCDecoderConfigurationRecord::ParseInternal(BufferReader* reader,
MediaLog* media_log) {
RCHECK(reader->Read1(&version) && version == 1 &&
reader->Read1(&profile_indication) &&
reader->Read1(&profile_compatibility) &&
reader->Read1(&avc_level));
uint8_t length_size_minus_one;
RCHECK(reader->Read1(&length_size_minus_one));
length_size = (length_size_minus_one & 0x3) + 1;
RCHECK(length_size != 3); // Only values of 1, 2, and 4 are valid.
uint8_t num_sps;
RCHECK(reader->Read1(&num_sps));
num_sps &= 0x1f;
sps_list.resize(num_sps);
for (int i = 0; i < num_sps; i++) {
uint16_t sps_length;
RCHECK(reader->Read2(&sps_length) &&
reader->ReadVec(&sps_list[i], sps_length));
RCHECK(sps_list[i].size() > 4);
}
uint8_t num_pps;
RCHECK(reader->Read1(&num_pps));
pps_list.resize(num_pps);
for (int i = 0; i < num_pps; i++) {
uint16_t pps_length;
RCHECK(reader->Read2(&pps_length) &&
reader->ReadVec(&pps_list[i], pps_length));
}
return true;
}
bool AVCDecoderConfigurationRecord::Serialize(
std::vector<uint8_t>& output) const {
// See ISO/IEC 14496-15 5.3.3.1.2 for the format description
constexpr uint8_t sps_list_size_mask = (1 << 5) - 1; // 5 bits
if (sps_list.size() > sps_list_size_mask)
return false;
constexpr uint8_t pps_list_size_mask = 0xff;
if (pps_list.size() > pps_list_size_mask)
return false;
if (length_size > 4)
return false;
// Calculating total size of the buffer we'll need for serialization
size_t expected_size =
1 + // configurationVersion
1 + // AVCProfileIndication
1 + // profile_compatibility
1 + // AVCLevelIndication
1 + // lengthSizeMinusOne
1 + // numOfSequenceParameterSets, i.e. length of sps_list
1; // numOfPictureParameterSets, i.e. length of pps_list
constexpr size_t max_vector_size = (1 << 16) - 1; // 2 bytes
for (auto& sps : sps_list) {
expected_size += 2; // 2 bytes for sequenceParameterSetLength
if (sps.size() > max_vector_size)
return false;
expected_size += sps.size();
}
for (auto& pps : pps_list) {
expected_size += 2; // 2 bytes for pictureParameterSetLength;
if (pps.size() > max_vector_size)
return false;
expected_size += pps.size();
}
output.clear();
output.resize(expected_size);
base::BigEndianWriter writer(reinterpret_cast<char*>(output.data()),
output.size());
bool result = true;
// configurationVersion
result &= writer.WriteU8(version);
// AVCProfileIndication
result &= writer.WriteU8(profile_indication);
// profile_compatibility
result &= writer.WriteU8(profile_compatibility);
// AVCLevelIndication
result &= writer.WriteU8(avc_level);
// lengthSizeMinusOne
uint8_t length_size_minus_one = (length_size - 1) | 0xfc;
result &= writer.WriteU8(length_size_minus_one);
// numOfSequenceParameterSets
uint8_t sps_size = sps_list.size() | ~sps_list_size_mask;
result &= writer.WriteU8(sps_size);
// sequenceParameterSetNALUnits
for (auto& sps : sps_list) {
result &= writer.WriteU16(sps.size());
writer.WriteBytes(sps.data(), sps.size());
}
// numOfPictureParameterSets
uint8_t pps_size = pps_list.size();
result &= writer.WriteU8(pps_size);
// pictureParameterSetNALUnit
for (auto& pps : pps_list) {
result &= writer.WriteU16(pps.size());
writer.WriteBytes(pps.data(), pps.size());
}
return result;
}
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
VPCodecConfigurationRecord::VPCodecConfigurationRecord()
: profile(VIDEO_CODEC_PROFILE_UNKNOWN) {}
VPCodecConfigurationRecord::VPCodecConfigurationRecord(
const VPCodecConfigurationRecord& other) = default;
VPCodecConfigurationRecord::~VPCodecConfigurationRecord() = default;
FourCC VPCodecConfigurationRecord::BoxType() const {
return FOURCC_VPCC;
}
bool VPCodecConfigurationRecord::Parse(BoxReader* reader) {
uint8_t profile_indication = 0;
RCHECK(reader->ReadFullBoxHeader() && reader->Read1(&profile_indication));
// The remaining fields are not parsed as we don't care about them for now.
switch (profile_indication) {
case 0:
profile = VP9PROFILE_PROFILE0;
break;
case 1:
profile = VP9PROFILE_PROFILE1;
break;
case 2:
profile = VP9PROFILE_PROFILE2;
break;
case 3:
profile = VP9PROFILE_PROFILE3;
break;
default:
MEDIA_LOG(ERROR, reader->media_log())
<< "Unsupported VP9 profile: 0x" << std::hex
<< static_cast<uint32_t>(profile_indication);
return false;
}
RCHECK(reader->Read1(&level));
uint8_t depth_chroma_full_range;
RCHECK(reader->Read1(&depth_chroma_full_range));
uint8_t primary_id;
RCHECK(reader->Read1(&primary_id));
uint8_t transfer_id;
RCHECK(reader->Read1(&transfer_id));
uint8_t matrix_id;
RCHECK(reader->Read1(&matrix_id));
color_space = VideoColorSpace(primary_id, transfer_id, matrix_id,
depth_chroma_full_range & 1
? gfx::ColorSpace::RangeID::FULL
: gfx::ColorSpace::RangeID::LIMITED);
return true;
}
#if BUILDFLAG(ENABLE_AV1_DECODER)
AV1CodecConfigurationRecord::AV1CodecConfigurationRecord()
: profile(VIDEO_CODEC_PROFILE_UNKNOWN) {}
AV1CodecConfigurationRecord::AV1CodecConfigurationRecord(
const AV1CodecConfigurationRecord& other) = default;
AV1CodecConfigurationRecord::~AV1CodecConfigurationRecord() = default;
FourCC AV1CodecConfigurationRecord::BoxType() const {
return FOURCC_AV1C;
}
// Parse the AV1CodecConfigurationRecord, which has the following format:
// unsigned int (1) marker = 1;
// unsigned int (7) version = 1;
// unsigned int (3) seq_profile;
// unsigned int (5) seq_level_idx_0;
// unsigned int (1) seq_tier_0;
// unsigned int (1) high_bitdepth;
// unsigned int (1) twelve_bit;
// unsigned int (1) monochrome;
// unsigned int (1) chroma_subsampling_x;
// unsigned int (1) chroma_subsampling_y;
// unsigned int (2) chroma_sample_position;
// unsigned int (3) reserved = 0;
//
// unsigned int (1) initial_presentation_delay_present;
// if (initial_presentation_delay_present) {
// unsigned int (4) initial_presentation_delay_minus_one;
// } else {
// unsigned int (4) reserved = 0;
// }
//
// unsigned int (8)[] configOBUs;
bool AV1CodecConfigurationRecord::Parse(BoxReader* reader) {
uint8_t av1c_byte = 0;
RCHECK(reader->Read1(&av1c_byte));
const uint8_t av1c_marker = av1c_byte >> 7;
if (!av1c_marker) {
MEDIA_LOG(ERROR, reader->media_log()) << "Unsupported av1C: marker unset.";
return false;
}
const uint8_t av1c_version = av1c_byte & 0b01111111;
if (av1c_version != 1) {
MEDIA_LOG(ERROR, reader->media_log())
<< "Unsupported av1C: unexpected version number: " << av1c_version;
return false;
}
RCHECK(reader->Read1(&av1c_byte));
const uint8_t seq_profile = av1c_byte >> 5;
switch (seq_profile) {
case 0:
profile = AV1PROFILE_PROFILE_MAIN;
break;
case 1:
profile = AV1PROFILE_PROFILE_HIGH;
break;
case 2:
profile = AV1PROFILE_PROFILE_PRO;
break;
default:
MEDIA_LOG(ERROR, reader->media_log())
<< "Unsupported av1C: unknown profile 0x" << std::hex << seq_profile;
return false;
}
// The remaining fields are ignored since we don't care about them yet.
return true;
}
#endif // BUILDFLAG(ENABLE_AV1_DECODER)
PixelAspectRatioBox::PixelAspectRatioBox() : h_spacing(1), v_spacing(1) {}
PixelAspectRatioBox::PixelAspectRatioBox(const PixelAspectRatioBox& other) =
default;
PixelAspectRatioBox::~PixelAspectRatioBox() = default;
FourCC PixelAspectRatioBox::BoxType() const { return FOURCC_PASP; }
bool PixelAspectRatioBox::Parse(BoxReader* reader) {
RCHECK(reader->Read4(&h_spacing) && reader->Read4(&v_spacing));
return true;
}
ColorParameterInformation::ColorParameterInformation() = default;
ColorParameterInformation::ColorParameterInformation(
const ColorParameterInformation& other) = default;
ColorParameterInformation::~ColorParameterInformation() = default;
FourCC ColorParameterInformation::BoxType() const {
return FOURCC_COLR;
}
bool ColorParameterInformation::Parse(BoxReader* reader) {
fully_parsed = false;
FourCC type;
RCHECK(reader->ReadFourCC(&type));
if (type != FOURCC_NCLX) {
// Ignore currently unsupported color information metadata parsing.
// TODO: Support 'nclc', 'rICC', and 'prof'.
return true;
}
uint8_t full_range_byte;
RCHECK(reader->Read2(&colour_primaries) &&
reader->Read2(&transfer_characteristics) &&
reader->Read2(&matrix_coefficients) &&
reader->Read1(&full_range_byte));
full_range = full_range_byte & 0x80;
fully_parsed = true;
return true;
}
MasteringDisplayColorVolume::MasteringDisplayColorVolume() = default;
MasteringDisplayColorVolume::MasteringDisplayColorVolume(
const MasteringDisplayColorVolume& other) = default;
MasteringDisplayColorVolume::~MasteringDisplayColorVolume() = default;
FourCC MasteringDisplayColorVolume::BoxType() const {
return FOURCC_MDCV;
}
bool MasteringDisplayColorVolume::Parse(BoxReader* reader) {
// Technically the color coordinates may be in any order. The spec recommends
// GBR and it is assumed that the color coordinates are in such order.
constexpr float kUnitOfMasteringLuminance = 10000;
RCHECK(ReadMdcvColorCoordinate(reader, &display_primaries_gx) &&
ReadMdcvColorCoordinate(reader, &display_primaries_gy) &&
ReadMdcvColorCoordinate(reader, &display_primaries_bx) &&
ReadMdcvColorCoordinate(reader, &display_primaries_by) &&
ReadMdcvColorCoordinate(reader, &display_primaries_rx) &&
ReadMdcvColorCoordinate(reader, &display_primaries_ry) &&
ReadMdcvColorCoordinate(reader, &white_point_x) &&
ReadMdcvColorCoordinate(reader, &white_point_y) &&
ReadFixedPoint32(kUnitOfMasteringLuminance, reader,
&max_display_mastering_luminance) &&
ReadFixedPoint32(kUnitOfMasteringLuminance, reader,
&min_display_mastering_luminance));
return true;
}
FourCC SMPTE2086MasteringDisplayMetadataBox::BoxType() const {
return FOURCC_SMDM;
}
bool SMPTE2086MasteringDisplayMetadataBox::Parse(BoxReader* reader) {
constexpr float kColorCoordinateUnit = 1 << 16;
constexpr float kLuminanceMaxUnit = 1 << 8;
constexpr float kLuminanceMinUnit = 1 << 14;
RCHECK(reader->ReadFullBoxHeader());
// Technically the color coordinates may be in any order. The spec recommends
// RGB and it is assumed that the color coordinates are in such order.
RCHECK(
ReadFixedPoint16(kColorCoordinateUnit, reader, &display_primaries_rx) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &display_primaries_ry) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &display_primaries_gx) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &display_primaries_gy) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &display_primaries_bx) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &display_primaries_by) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &white_point_x) &&
ReadFixedPoint16(kColorCoordinateUnit, reader, &white_point_y) &&
ReadFixedPoint32(kLuminanceMaxUnit, reader,
&max_display_mastering_luminance) &&
ReadFixedPoint32(kLuminanceMinUnit, reader,
&min_display_mastering_luminance));
return true;
}
ContentLightLevelInformation::ContentLightLevelInformation() = default;
ContentLightLevelInformation::ContentLightLevelInformation(
const ContentLightLevelInformation& other) = default;
ContentLightLevelInformation::~ContentLightLevelInformation() = default;
FourCC ContentLightLevelInformation::BoxType() const {
return FOURCC_CLLI;
}
bool ContentLightLevelInformation::Parse(BoxReader* reader) {
return reader->Read2(&max_content_light_level) &&
reader->Read2(&max_pic_average_light_level);
}
bool ContentLightLevel::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
return ContentLightLevelInformation::Parse(reader);
}
FourCC ContentLightLevel::BoxType() const {
return FOURCC_COLL;
}
VideoSampleEntry::VideoSampleEntry()
: format(FOURCC_NULL),
data_reference_index(0),
width(0),
height(0),
video_codec(VideoCodec::kUnknown),
video_codec_profile(VIDEO_CODEC_PROFILE_UNKNOWN),
video_codec_level(kNoVideoCodecLevel) {}
VideoSampleEntry::VideoSampleEntry(const VideoSampleEntry& other) = default;
VideoSampleEntry::~VideoSampleEntry() = default;
FourCC VideoSampleEntry::BoxType() const {
DCHECK(false) << "VideoSampleEntry should be parsed according to the "
<< "handler type recovered in its Media ancestor.";
return FOURCC_NULL;
}
bool VideoSampleEntry::Parse(BoxReader* reader) {
format = reader->type();
RCHECK(reader->SkipBytes(6) &&
reader->Read2(&data_reference_index) &&
reader->SkipBytes(16) &&
reader->Read2(&width) &&
reader->Read2(&height) &&
reader->SkipBytes(50));
RCHECK(reader->ScanChildren());
if (reader->HasChild(&pixel_aspect)) {
RCHECK(reader->MaybeReadChild(&pixel_aspect));
}
if (format == FOURCC_ENCV) {
// Continue scanning until a recognized protection scheme is found, or until
// we run out of protection schemes.
while (!sinf.HasSupportedScheme()) {
if (!reader->ReadChild(&sinf))
return false;
}
}
const FourCC actual_format =
format == FOURCC_ENCV ? sinf.format.format : format;
switch (actual_format) {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case FOURCC_AVC1:
case FOURCC_AVC3: {
DVLOG(2) << __func__ << " reading AVCDecoderConfigurationRecord (avcC)";
std::unique_ptr<AVCDecoderConfigurationRecord> avcConfig(
new AVCDecoderConfigurationRecord());
RCHECK(reader->ReadChild(avcConfig.get()));
video_codec = VideoCodec::kH264;
video_codec_profile = H264Parser::ProfileIDCToVideoCodecProfile(
avcConfig->profile_indication);
frame_bitstream_converter =
base::MakeRefCounted<AVCBitstreamConverter>(std::move(avcConfig));
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
// It can be Dolby Vision stream if there is DVCC box.
auto dv_config = ParseDOVIConfig(reader);
if (dv_config.has_value()) {
DVLOG(2) << __func__ << " reading DolbyVisionConfiguration (dvcC/dvvC)";
video_codec = VideoCodec::kDolbyVision;
video_codec_profile = dv_config->codec_profile;
video_codec_level = dv_config->dv_level;
}
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
break;
}
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case FOURCC_HEV1:
case FOURCC_HVC1: {
DVLOG(2) << __func__ << " parsing HEVCDecoderConfigurationRecord (hvcC)";
std::unique_ptr<HEVCDecoderConfigurationRecord> hevcConfig(
new HEVCDecoderConfigurationRecord());
RCHECK(reader->ReadChild(hevcConfig.get()));
video_codec = VideoCodec::kHEVC;
video_codec_profile = hevcConfig->GetVideoProfile();
frame_bitstream_converter =
base::MakeRefCounted<HEVCBitstreamConverter>(std::move(hevcConfig));
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
// It can be Dolby Vision stream if there is DVCC box.
auto dv_config = ParseDOVIConfig(reader);
if (dv_config.has_value()) {
DVLOG(2) << __func__ << " reading DolbyVisionConfiguration (dvcC/dvvC)";
video_codec = VideoCodec::kDolbyVision;
video_codec_profile = dv_config->codec_profile;
video_codec_level = dv_config->dv_level;
}
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
break;
}
#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
case FOURCC_DVA1:
case FOURCC_DVAV: {
DVLOG(2) << __func__ << " reading AVCDecoderConfigurationRecord (avcC)";
std::unique_ptr<AVCDecoderConfigurationRecord> avcConfig(
new AVCDecoderConfigurationRecord());
RCHECK(reader->ReadChild(avcConfig.get()));
frame_bitstream_converter =
base::MakeRefCounted<AVCBitstreamConverter>(std::move(avcConfig));
DVLOG(2) << __func__ << " reading DolbyVisionConfiguration (dvcC/dvvC)";
auto dv_config = ParseDOVIConfig(reader);
RCHECK(dv_config.has_value());
video_codec = VideoCodec::kDolbyVision;
video_codec_profile = dv_config->codec_profile;
video_codec_level = dv_config->dv_level;
break;
}
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case FOURCC_DVH1:
case FOURCC_DVHE: {
DVLOG(2) << __func__ << " reading HEVCDecoderConfigurationRecord (hvcC)";
std::unique_ptr<HEVCDecoderConfigurationRecord> hevcConfig(
new HEVCDecoderConfigurationRecord());
RCHECK(reader->ReadChild(hevcConfig.get()));
frame_bitstream_converter =
base::MakeRefCounted<HEVCBitstreamConverter>(std::move(hevcConfig));
DVLOG(2) << __func__ << " reading DolbyVisionConfiguration (dvcC/dvvC)";
auto dv_config = ParseDOVIConfig(reader);
RCHECK(dv_config.has_value());
video_codec = VideoCodec::kDolbyVision;
video_codec_profile = dv_config->codec_profile;
video_codec_level = dv_config->dv_level;
break;
}
#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
case FOURCC_VP09: {
DVLOG(2) << __func__ << " parsing VPCodecConfigurationRecord (vpcC)";
std::unique_ptr<VPCodecConfigurationRecord> vp_config(
new VPCodecConfigurationRecord());
RCHECK(reader->ReadChild(vp_config.get()));
frame_bitstream_converter = nullptr;
video_codec = VideoCodec::kVP9;
video_codec_profile = vp_config->profile;
video_color_space = vp_config->color_space;
video_codec_level = vp_config->level;
SMPTE2086MasteringDisplayMetadataBox color_volume;
if (reader->HasChild(&color_volume)) {
RCHECK(reader->ReadChild(&color_volume));
mastering_display_color_volume = color_volume;
}
ContentLightLevel level_information;
if (reader->HasChild(&level_information)) {
RCHECK(reader->ReadChild(&level_information));
content_light_level_information = level_information;
}
break;
}
#if BUILDFLAG(ENABLE_AV1_DECODER)
case FOURCC_AV01: {
DVLOG(2) << __func__ << " reading AV1 configuration.";
AV1CodecConfigurationRecord av1_config;
RCHECK(reader->ReadChild(&av1_config));
frame_bitstream_converter = nullptr;
video_codec = VideoCodec::kAV1;
video_codec_profile = av1_config.profile;
break;
}
#endif
default:
// Unknown/unsupported format
MEDIA_LOG(ERROR, reader->media_log())
<< "Unsupported VisualSampleEntry type "
<< FourCCToString(actual_format);
return false;
}
ColorParameterInformation color_parameter_information;
if (reader->HasChild(&color_parameter_information)) {
RCHECK(reader->ReadChild(&color_parameter_information));
if (color_parameter_information.fully_parsed) {
video_color_space = ConvertColorParameterInformationToColorSpace(
color_parameter_information);
}
}
MasteringDisplayColorVolume color_volume;
if (reader->HasChild(&color_volume)) {
RCHECK(reader->ReadChild(&color_volume));
mastering_display_color_volume = color_volume;
}
ContentLightLevelInformation level_information;
if (reader->HasChild(&level_information)) {
RCHECK(reader->ReadChild(&level_information));
content_light_level_information = level_information;
}
if (video_codec_profile == VIDEO_CODEC_PROFILE_UNKNOWN) {
MEDIA_LOG(ERROR, reader->media_log()) << "Unrecognized video codec profile";
return false;
}
return true;
}
bool VideoSampleEntry::IsFormatValid() const {
const FourCC actual_format =
format == FOURCC_ENCV ? sinf.format.format : format;
switch (actual_format) {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case FOURCC_AVC1:
case FOURCC_AVC3:
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case FOURCC_HEV1:
case FOURCC_HVC1:
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
case FOURCC_DVH1:
case FOURCC_DVHE:
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
#endif // BUILDFLAG(ENABLE_PLATFORM_HEVC)
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
case FOURCC_DVA1:
case FOURCC_DVAV:
#endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
case FOURCC_VP09:
return true;
#if BUILDFLAG(ENABLE_AV1_DECODER)
case FOURCC_AV01:
return true;
#endif
default:
return false;
}
}
ElementaryStreamDescriptor::ElementaryStreamDescriptor()
: object_type(kForbidden) {}
ElementaryStreamDescriptor::ElementaryStreamDescriptor(
const ElementaryStreamDescriptor& other) = default;
ElementaryStreamDescriptor::~ElementaryStreamDescriptor() = default;
FourCC ElementaryStreamDescriptor::BoxType() const {
return FOURCC_ESDS;
}
bool ElementaryStreamDescriptor::Parse(BoxReader* reader) {
std::vector<uint8_t> data;
ESDescriptor es_desc;
RCHECK(reader->ReadFullBoxHeader());
RCHECK(reader->ReadVec(&data, reader->box_size() - reader->pos()));
RCHECK(es_desc.Parse(data));
object_type = es_desc.object_type();
if (es_desc.IsAAC(object_type)) {
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
RCHECK(aac.Parse(es_desc.decoder_specific_info(), reader->media_log()));
#else
return false;
#endif
}
return true;
}
FlacSpecificBox::FlacSpecificBox()
: sample_rate(0), channel_count(0), bits_per_sample(0) {}
FlacSpecificBox::FlacSpecificBox(const FlacSpecificBox& other) = default;
FlacSpecificBox::~FlacSpecificBox() = default;
FourCC FlacSpecificBox::BoxType() const {
return FOURCC_DFLA;
}
bool FlacSpecificBox::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
RCHECK_MEDIA_LOGGED(reader->version() == 0, reader->media_log(),
"Only version 0 FLACSpecificBox (dfLa) is supported.");
RCHECK_MEDIA_LOGGED(reader->flags() == 0, reader->media_log(),
"Only 0 flags in FLACSpecificBox (dfLa) is supported.");
// From https://github.com/xiph/flac/blob/master/doc/isoflac.txt, a
// FLACMetadataBlock is formatted as:
// unsigned int(1) LastMetadataBlockFlag;
// unsigned int(7) BlockType;
// unsigned int(24) Length;
// unsigned int(8) BlockData[Length];
// We only care about the first block, which must exist, and must be
// STREAMINFO.
uint32_t metadata_framing;
RCHECK_MEDIA_LOGGED(reader->Read4(&metadata_framing), reader->media_log(),
"Missing STREAMINFO block in FLACSpecificBox (dfLa).");
uint8_t block_type = (metadata_framing >> 24) & 0x7f;
RCHECK_MEDIA_LOGGED(block_type == 0, reader->media_log(),
"FLACSpecificBox metadata must begin with STREAMINFO.");
uint32_t block_length = metadata_framing & 0x00ffffff;
RCHECK_MEDIA_LOGGED(
block_length == kFlacMetadataBlockStreaminfoSize, reader->media_log(),
"STREAMINFO block in FLACSpecificBox (dfLa) has incorrect size.");
// See https://xiph.org/flac/format.html#metadata_block_streaminfo for
// STREAMINFO structure format and semantics. We only care about
// |sample_rate|, |channel_count|, and |bits_per_sample|,
// though we also copy the STREAMINFO block for use later in audio decoder
// configuration. See also the FLAC AudioSampleEntry logic: the |sample_rate|
// here is used instead of that in the AudioSampleEntry per
// https://github.com/xiph/flac/blob/master/doc/isoflac.txt.
RCHECK(reader->ReadVec(&stream_info, kFlacMetadataBlockStreaminfoSize));
// Bytes 0-9 (min/max block and frame sizes) are ignored here.
sample_rate = stream_info[10] << 12;
sample_rate += stream_info[11] << 4;
sample_rate += (stream_info[12] >> 4) & 0xf;
RCHECK_MEDIA_LOGGED(sample_rate > 0, reader->media_log(),
"STREAMINFO block in FLACSpecificBox (dfLa) must have "
"nonzero sample rate.");
channel_count = (stream_info[12] >> 1) & 0x7;
channel_count++;
bits_per_sample = (stream_info[12] & 1) << 4;
bits_per_sample += (stream_info[13] >> 4) & 0xf;
bits_per_sample++;
// The lower 4 bits of byte 13 and all of bytes 14-17 (number of samples in
// stream) are ignored here.
// Bytes 18-33 (hash of the unencoded audio data) are ignored here.
return true;
}
OpusSpecificBox::OpusSpecificBox()
: seek_preroll(base::Milliseconds(80)), codec_delay_in_frames(0) {}
OpusSpecificBox::OpusSpecificBox(const OpusSpecificBox& other) = default;
OpusSpecificBox::~OpusSpecificBox() = default;
FourCC OpusSpecificBox::BoxType() const {
return FOURCC_DOPS;
}
bool OpusSpecificBox::Parse(BoxReader* reader) {
// Extradata must start with "OpusHead" magic.
extradata.insert(extradata.end(),
{0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64});
// The opus specific box must be present and at least OPUS_EXTRADATA_SIZE - 8
// bytes in length. The -8 is for the missing "OpusHead" magic signature that
// is required at the start of the extradata we give to the codec.
const size_t headerless_extradata_size = reader->box_size() - reader->pos();
RCHECK(headerless_extradata_size >= OPUS_EXTRADATA_SIZE - extradata.size());
extradata.resize(extradata.size() + headerless_extradata_size);
int16_t gain_db;
RCHECK(reader->Read1(&extradata[OPUS_EXTRADATA_VERSION_OFFSET]));
RCHECK(reader->Read1(&extradata[OPUS_EXTRADATA_CHANNELS_OFFSET]));
RCHECK(reader->Read2(&codec_delay_in_frames /* PreSkip */));
RCHECK(reader->Read4(&sample_rate));
RCHECK(reader->Read2s(&gain_db));
#if !defined(ARCH_CPU_LITTLE_ENDIAN)
#error The code below assumes little-endianness.
#endif
memcpy(&extradata[OPUS_EXTRADATA_SKIP_SAMPLES_OFFSET], &codec_delay_in_frames,
sizeof(codec_delay_in_frames));
memcpy(&extradata[OPUS_EXTRADATA_SAMPLE_RATE_OFFSET], &sample_rate,
sizeof(sample_rate));
memcpy(&extradata[OPUS_EXTRADATA_GAIN_OFFSET], &gain_db, sizeof(gain_db));
channel_count = extradata[OPUS_EXTRADATA_CHANNELS_OFFSET];
// Any remaining data is 1-byte data, so copy it over as is, there should
// only be a handful of these entries, so reading byte by byte is okay.
for (size_t i = OPUS_EXTRADATA_CHANNEL_MAPPING_OFFSET; i < extradata.size();
++i) {
RCHECK(reader->Read1(&extradata[i]));
}
return true;
}
AudioSampleEntry::AudioSampleEntry()
: format(FOURCC_NULL),
data_reference_index(0),
channelcount(0),
samplesize(0),
samplerate(0) {}
AudioSampleEntry::AudioSampleEntry(const AudioSampleEntry& other) = default;
AudioSampleEntry::~AudioSampleEntry() = default;
FourCC AudioSampleEntry::BoxType() const {
DCHECK(false) << "AudioSampleEntry should be parsed according to the "
<< "handler type recovered in its Media ancestor.";
return FOURCC_NULL;
}
bool AudioSampleEntry::Parse(BoxReader* reader) {
format = reader->type();
RCHECK(reader->SkipBytes(6) &&
reader->Read2(&data_reference_index) &&
reader->SkipBytes(8) &&
reader->Read2(&channelcount) &&
reader->Read2(&samplesize) &&
reader->SkipBytes(4) &&
reader->Read4(&samplerate));
// Convert from 16.16 fixed point to integer
samplerate >>= 16;
RCHECK(reader->ScanChildren());
if (format == FOURCC_ENCA) {
// Continue scanning until a recognized protection scheme is found, or until
// we run out of protection schemes.
while (!sinf.HasSupportedScheme()) {
if (!reader->ReadChild(&sinf))
return false;
}
}
if (format == FOURCC_OPUS ||
(format == FOURCC_ENCA && sinf.format.format == FOURCC_OPUS)) {
RCHECK_MEDIA_LOGGED(reader->ReadChild(&dops), reader->media_log(),
"Failure parsing OpusSpecificBox (dOps)");
RCHECK_MEDIA_LOGGED(channelcount == dops.channel_count, reader->media_log(),
"Opus AudioSampleEntry channel count mismatches "
"OpusSpecificBox STREAMINFO channel count");
RCHECK_MEDIA_LOGGED(samplerate == dops.sample_rate, reader->media_log(),
"Opus AudioSampleEntry sample rate mismatches "
"OpusSpecificBox STREAMINFO channel count");
}
// Read the FLACSpecificBox, even if CENC is signalled.
if (format == FOURCC_FLAC ||
(format == FOURCC_ENCA && sinf.format.format == FOURCC_FLAC)) {
RCHECK_MEDIA_LOGGED(reader->ReadChild(&dfla), reader->media_log(),
"Failure parsing FLACSpecificBox (dfLa)");
// AudioSampleEntry is constrained to max 65535Hz. Instead, use the sample
// rate from the FlacSpecificBox per
// https://github.com/xiph/flac/blob/master/doc/isoflac.txt
if (samplerate != dfla.sample_rate) {
MEDIA_LOG(INFO, reader->media_log())
<< "FLAC AudioSampleEntry sample rate " << samplerate
<< " overridden by rate " << dfla.sample_rate
<< " from FLACSpecificBox's STREAMINFO metadata";
samplerate = dfla.sample_rate;
}
RCHECK_MEDIA_LOGGED(channelcount == dfla.channel_count, reader->media_log(),
"FLAC AudioSampleEntry channel count mismatches "
"FLACSpecificBox STREAMINFO channel count");
RCHECK_MEDIA_LOGGED(samplesize == dfla.bits_per_sample, reader->media_log(),
"FLAC AudioSampleEntry sample size mismatches "
"FLACSpecificBox STREAMINFO sample size");
} else {
RCHECK_MEDIA_LOGGED(!reader->HasChild(&dfla), reader->media_log(),
"FLACSpecificBox (dfLa) must only be used with FLAC "
"AudioSampleEntry or CENC AudioSampleEntry wrapping "
"FLAC");
}
// ESDS is not valid in case of EAC3.
RCHECK(reader->MaybeReadChild(&esds));
return true;
}
MediaHeader::MediaHeader()
: creation_time(0),
modification_time(0),
timescale(0),
duration(0),
language_code(0) {}
MediaHeader::MediaHeader(const MediaHeader& other) = default;
MediaHeader::~MediaHeader() = default;
FourCC MediaHeader::BoxType() const { return FOURCC_MDHD; }
bool MediaHeader::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->version() == 1) {
RCHECK(reader->Read8(&creation_time) && reader->Read8(&modification_time) &&
reader->Read4(&timescale) && reader->Read8(&duration) &&
reader->Read2(&language_code));
} else {
RCHECK(reader->Read4Into8(&creation_time) &&
reader->Read4Into8(&modification_time) &&
reader->Read4(&timescale) && reader->Read4Into8(&duration) &&
reader->Read2(&language_code));
}
RCHECK_MEDIA_LOGGED(timescale > 0, reader->media_log(),
"Track media header's timescale must not be 0");
// ISO 639-2/T language code only uses 15 lower bits, so reset the 16th bit.
language_code &= 0x7fff;
// Skip playback quality information
return reader->SkipBytes(2);
}
std::string MediaHeader::language() const {
if (language_code == 0x7fff || language_code < 0x400) {
return "und";
}
char lang_chars[4];
lang_chars[3] = 0;
lang_chars[2] = 0x60 + (language_code & 0x1f);
lang_chars[1] = 0x60 + ((language_code >> 5) & 0x1f);
lang_chars[0] = 0x60 + ((language_code >> 10) & 0x1f);
if (lang_chars[0] < 'a' || lang_chars[0] > 'z' || lang_chars[1] < 'a' ||
lang_chars[1] > 'z' || lang_chars[2] < 'a' || lang_chars[2] > 'z') {
// Got unexpected characteds in ISO 639-2/T language code. Something must be
// wrong with the input file, report 'und' language to be safe.
DVLOG(2) << "Ignoring MDHD language_code (non ISO 639-2 compliant): "
<< lang_chars;
lang_chars[0] = 'u';
lang_chars[1] = 'n';
lang_chars[2] = 'd';
}
return lang_chars;
}
MediaInformation::MediaInformation() = default;
MediaInformation::MediaInformation(const MediaInformation& other) = default;
MediaInformation::~MediaInformation() = default;
FourCC MediaInformation::BoxType() const { return FOURCC_MINF; }
bool MediaInformation::Parse(BoxReader* reader) {
return reader->ScanChildren() &&
reader->ReadChild(&sample_table);
}
Media::Media() = default;
Media::Media(const Media& other) = default;
Media::~Media() = default;
FourCC Media::BoxType() const { return FOURCC_MDIA; }
bool Media::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() &&
reader->ReadChild(&header) &&
reader->ReadChild(&handler));
// Maddeningly, the HandlerReference box specifies how to parse the
// SampleDescription box, making the latter the only box (of those that we
// support) which cannot be parsed correctly on its own (or even with
// information from its strict ancestor tree). We thus copy the handler type
// to the sample description box *before* parsing it to provide this
// information while parsing.
information.sample_table.description.type = handler.type;
RCHECK(reader->ReadChild(&information));
return true;
}
Track::Track() = default;
Track::Track(const Track& other) = default;
Track::~Track() = default;
FourCC Track::BoxType() const { return FOURCC_TRAK; }
bool Track::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() &&
reader->ReadChild(&header) &&
reader->ReadChild(&media) &&
reader->MaybeReadChild(&edit));
return true;
}
MovieExtendsHeader::MovieExtendsHeader() : fragment_duration(0) {}
MovieExtendsHeader::MovieExtendsHeader(const MovieExtendsHeader& other) =
default;
MovieExtendsHeader::~MovieExtendsHeader() = default;
FourCC MovieExtendsHeader::BoxType() const { return FOURCC_MEHD; }
bool MovieExtendsHeader::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->version() == 1) {
RCHECK(reader->Read8(&fragment_duration));
} else {
RCHECK(reader->Read4Into8(&fragment_duration));
}
return true;
}
TrackExtends::TrackExtends()
: track_id(0),
default_sample_description_index(0),
default_sample_duration(0),
default_sample_size(0),
default_sample_flags(0) {}
TrackExtends::TrackExtends(const TrackExtends& other) = default;
TrackExtends::~TrackExtends() = default;
FourCC TrackExtends::BoxType() const { return FOURCC_TREX; }
bool TrackExtends::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader() &&
reader->Read4(&track_id) &&
reader->Read4(&default_sample_description_index) &&
reader->Read4(&default_sample_duration) &&
reader->Read4(&default_sample_size) &&
reader->Read4(&default_sample_flags));
return true;
}
MovieExtends::MovieExtends() = default;
MovieExtends::MovieExtends(const MovieExtends& other) = default;
MovieExtends::~MovieExtends() = default;
FourCC MovieExtends::BoxType() const { return FOURCC_MVEX; }
bool MovieExtends::Parse(BoxReader* reader) {
header.fragment_duration = 0;
return reader->ScanChildren() &&
reader->MaybeReadChild(&header) &&
reader->ReadChildren(&tracks);
}
Movie::Movie() : fragmented(false) {}
Movie::Movie(const Movie& other) = default;
Movie::~Movie() = default;
FourCC Movie::BoxType() const { return FOURCC_MOOV; }
bool Movie::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() && reader->ReadChild(&header) &&
reader->ReadChildren(&tracks));
RCHECK_MEDIA_LOGGED(reader->ReadChild(&extends), reader->media_log(),
"Detected unfragmented MP4. Media Source Extensions "
"require ISO BMFF moov to contain mvex to indicate that "
"Movie Fragments are to be expected.");
MetadataBox meta;
RCHECK(reader->MaybeReadChild(&meta));
base::UmaHistogramBoolean("Media.MSE.DetectedShakaPackagerInMp4",
meta.used_shaka_packager);
return reader->MaybeReadChildren(&pssh);
}
TrackFragmentDecodeTime::TrackFragmentDecodeTime() : decode_time(0) {}
TrackFragmentDecodeTime::TrackFragmentDecodeTime(
const TrackFragmentDecodeTime& other) = default;
TrackFragmentDecodeTime::~TrackFragmentDecodeTime() = default;
FourCC TrackFragmentDecodeTime::BoxType() const { return FOURCC_TFDT; }
bool TrackFragmentDecodeTime::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
if (reader->version() == 1)
return reader->Read8(&decode_time);
else
return reader->Read4Into8(&decode_time);
}
MovieFragmentHeader::MovieFragmentHeader() : sequence_number(0) {}
MovieFragmentHeader::MovieFragmentHeader(const MovieFragmentHeader& other) =
default;
MovieFragmentHeader::~MovieFragmentHeader() = default;
FourCC MovieFragmentHeader::BoxType() const { return FOURCC_MFHD; }
bool MovieFragmentHeader::Parse(BoxReader* reader) {
return reader->SkipBytes(4) && reader->Read4(&sequence_number);
}
TrackFragmentHeader::TrackFragmentHeader()
: track_id(0),
sample_description_index(0),
default_sample_duration(0),
default_sample_size(0),
default_sample_flags(0),
has_default_sample_flags(false) {}
TrackFragmentHeader::TrackFragmentHeader(const TrackFragmentHeader& other) =
default;
TrackFragmentHeader::~TrackFragmentHeader() = default;
FourCC TrackFragmentHeader::BoxType() const { return FOURCC_TFHD; }
bool TrackFragmentHeader::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader() && reader->Read4(&track_id));
// Media Source specific: reject tracks that set 'base-data-offset-present'.
// Although the Media Source requires that 'default-base-is-moof' (14496-12
// Amendment 2) be set, we omit this check as many otherwise-valid files in
// the wild don't set it.
//
// RCHECK((flags & 0x020000) && !(flags & 0x1));
RCHECK_MEDIA_LOGGED(!(reader->flags() & 0x1), reader->media_log(),
"TFHD base-data-offset not allowed by MSE. See "
"https://www.w3.org/TR/mse-byte-stream-format-isobmff/"
"#movie-fragment-relative-addressing");
if (reader->flags() & 0x2) {
RCHECK(reader->Read4(&sample_description_index));
} else {
sample_description_index = 0;
}
if (reader->flags() & 0x8) {
RCHECK(reader->Read4(&default_sample_duration));
} else {
default_sample_duration = 0;
}
if (reader->flags() & 0x10) {
RCHECK(reader->Read4(&default_sample_size));
} else {
default_sample_size = 0;
}
if (reader->flags() & 0x20) {
RCHECK(reader->Read4(&default_sample_flags));
has_default_sample_flags = true;
} else {
has_default_sample_flags = false;
}
return true;
}
TrackFragmentRun::TrackFragmentRun()
: sample_count(0), data_offset(0) {}
TrackFragmentRun::TrackFragmentRun(const TrackFragmentRun& other) = default;
TrackFragmentRun::~TrackFragmentRun() = default;
FourCC TrackFragmentRun::BoxType() const { return FOURCC_TRUN; }
bool TrackFragmentRun::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader() &&
reader->Read4(&sample_count));
const uint32_t flags = reader->flags();
bool data_offset_present = (flags & 0x1) != 0;
bool first_sample_flags_present = (flags & 0x4) != 0;
bool sample_duration_present = (flags & 0x100) != 0;
bool sample_size_present = (flags & 0x200) != 0;
bool sample_flags_present = (flags & 0x400) != 0;
bool sample_composition_time_offsets_present = (flags & 0x800) != 0;
if (data_offset_present) {
RCHECK(reader->Read4(&data_offset));
} else {
data_offset = 0;
}
uint32_t first_sample_flags = 0;
if (first_sample_flags_present)
RCHECK(reader->Read4(&first_sample_flags));
int fields = sample_duration_present + sample_size_present +
sample_flags_present + sample_composition_time_offsets_present;
const size_t bytes_per_field = 4;
// Cast |sample_count| to size_t before multiplying to support maximum
// platform size.
base::CheckedNumeric<size_t> bytes_needed = base::CheckMul(
fields, bytes_per_field, static_cast<size_t>(sample_count));
RCHECK_MEDIA_LOGGED(
bytes_needed.IsValid(), reader->media_log(),
"Extreme TRUN sample count exceeds implementation limit.");
RCHECK(reader->HasBytes(bytes_needed.ValueOrDie()));
if (sample_duration_present) {
RCHECK(sample_count <= sample_durations.max_size());
sample_durations.resize(sample_count);
}
if (sample_size_present) {
RCHECK(sample_count <= sample_sizes.max_size());
sample_sizes.resize(sample_count);
}
if (sample_flags_present) {
RCHECK(sample_count <= sample_flags.max_size());
sample_flags.resize(sample_count);
}
if (sample_composition_time_offsets_present) {
RCHECK(sample_count <= sample_composition_time_offsets.max_size());
sample_composition_time_offsets.resize(sample_count);
}
if (sample_duration_present || sample_size_present || sample_flags_present ||
sample_composition_time_offsets_present) {
for (uint32_t i = 0; i < sample_count; ++i) {
if (sample_duration_present)
RCHECK(reader->Read4(&sample_durations[i]));
if (sample_size_present)
RCHECK(reader->Read4(&sample_sizes[i]));
if (sample_flags_present)
RCHECK(reader->Read4(&sample_flags[i]));
if (sample_composition_time_offsets_present)
RCHECK(reader->Read4s(&sample_composition_time_offsets[i]));
}
}
if (first_sample_flags_present) {
if (sample_flags.size() == 0) {
sample_flags.push_back(first_sample_flags);
} else {
sample_flags[0] = first_sample_flags;
}
}
return true;
}
SampleToGroup::SampleToGroup() : grouping_type(0), grouping_type_parameter(0) {}
SampleToGroup::SampleToGroup(const SampleToGroup& other) = default;
SampleToGroup::~SampleToGroup() = default;
FourCC SampleToGroup::BoxType() const { return FOURCC_SBGP; }
bool SampleToGroup::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader() &&
reader->Read4(&grouping_type));
if (reader->version() == 1)
RCHECK(reader->Read4(&grouping_type_parameter));
if (grouping_type != FOURCC_SEIG) {
DLOG(WARNING) << "SampleToGroup box with grouping_type '" << grouping_type
<< "' is not supported.";
return true;
}
uint32_t count;
RCHECK(reader->Read4(&count));
const size_t bytes_per_entry = 8;
// Cast |count| to size_t before multiplying to support maximum platform size.
base::CheckedNumeric<size_t> bytes_needed =
base::CheckMul(bytes_per_entry, static_cast<size_t>(count));
RCHECK_MEDIA_LOGGED(bytes_needed.IsValid(), reader->media_log(),
"Extreme SBGP count exceeds implementation limit.");
RCHECK(reader->HasBytes(bytes_needed.ValueOrDie()));
RCHECK(count <= entries.max_size());
entries.resize(count);
for (uint32_t i = 0; i < count; ++i) {
RCHECK(reader->Read4(&entries[i].sample_count) &&
reader->Read4(&entries[i].group_description_index));
}
return true;
}
CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry()
: is_encrypted(false),
iv_size(0),
crypt_byte_block(0),
skip_byte_block(0),
constant_iv_size(0) {}
CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry(
const CencSampleEncryptionInfoEntry& other) = default;
CencSampleEncryptionInfoEntry::~CencSampleEncryptionInfoEntry() = default;
bool CencSampleEncryptionInfoEntry::Parse(BoxReader* reader) {
uint8_t flag;
uint8_t possible_pattern_info;
RCHECK(reader->SkipBytes(1) && // reserved.
reader->Read1(&possible_pattern_info) && reader->Read1(&flag) &&
reader->Read1(&iv_size) && reader->ReadVec(&key_id, kKeyIdSize));
is_encrypted = (flag != 0);
if (is_encrypted) {
crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
skip_byte_block = possible_pattern_info & 0x0f;
if (iv_size == 0) {
RCHECK(reader->Read1(&constant_iv_size));
RCHECK(constant_iv_size == 8 || constant_iv_size == 16);
memset(constant_iv, 0, sizeof(constant_iv));
for (uint8_t i = 0; i < constant_iv_size; i++)
RCHECK(reader->Read1(constant_iv + i));
} else {
RCHECK(iv_size == 8 || iv_size == 16);
}
} else {
RCHECK(iv_size == 0);
}
return true;
}
SampleGroupDescription::SampleGroupDescription() : grouping_type(0) {}
SampleGroupDescription::SampleGroupDescription(
const SampleGroupDescription& other) = default;
SampleGroupDescription::~SampleGroupDescription() = default;
FourCC SampleGroupDescription::BoxType() const { return FOURCC_SGPD; }
bool SampleGroupDescription::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader() &&
reader->Read4(&grouping_type));
if (grouping_type != FOURCC_SEIG) {
DLOG(WARNING) << "SampleGroupDescription box with grouping_type '"
<< grouping_type << "' is not supported.";
return true;
}
const uint8_t version = reader->version();
const size_t kEntrySize = sizeof(uint32_t) + kKeyIdSize;
uint32_t default_length = 0;
if (version == 1) {
RCHECK(reader->Read4(&default_length));
RCHECK(default_length == 0 || default_length >= kEntrySize);
}
uint32_t count;
RCHECK(reader->Read4(&count));
// Check that we have at least two bytes for each entry before allocating a
// potentially huge entries vector. In reality each entry will require a
// variable number of bytes in excess of 2.
const int bytes_per_entry = 2;
// Cast |count| to size_t before multiplying to support maximum platform size.
base::CheckedNumeric<size_t> bytes_needed =
base::CheckMul(bytes_per_entry, static_cast<size_t>(count));
RCHECK_MEDIA_LOGGED(bytes_needed.IsValid(), reader->media_log(),
"Extreme SGPD count exceeds implementation limit.");
RCHECK(reader->HasBytes(bytes_needed.ValueOrDie()));
RCHECK(count <= entries.max_size());
entries.resize(count);
for (uint32_t i = 0; i < count; ++i) {
if (version == 1) {
if (default_length == 0) {
uint32_t description_length = 0;
RCHECK(reader->Read4(&description_length));
RCHECK(description_length >= kEntrySize);
}
}
RCHECK(entries[i].Parse(reader));
}
return true;
}
TrackFragment::TrackFragment() = default;
TrackFragment::TrackFragment(const TrackFragment& other) = default;
TrackFragment::~TrackFragment() = default;
FourCC TrackFragment::BoxType() const { return FOURCC_TRAF; }
bool TrackFragment::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() && reader->ReadChild(&header) &&
// Media Source specific: 'tfdt' required
reader->ReadChild(&decode_time) && reader->MaybeReadChildren(&runs) &&
reader->MaybeReadChild(&auxiliary_offset) &&
reader->MaybeReadChild(&auxiliary_size) &&
reader->MaybeReadChild(&sdtp) &&
reader->MaybeReadChild(&sample_encryption));
// 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 (reader->HasChild(&sample_group_description)) {
RCHECK(reader->ReadChild(&sample_group_description));
if (sample_group_description.grouping_type == FOURCC_SEIG)
break;
sample_group_description.entries.clear();
}
while (reader->HasChild(&sample_to_group)) {
RCHECK(reader->ReadChild(&sample_to_group));
if (sample_to_group.grouping_type == FOURCC_SEIG)
break;
sample_to_group.entries.clear();
}
return true;
}
MovieFragment::MovieFragment() = default;
MovieFragment::MovieFragment(const MovieFragment& other) = default;
MovieFragment::~MovieFragment() = default;
FourCC MovieFragment::BoxType() const { return FOURCC_MOOF; }
bool MovieFragment::Parse(BoxReader* reader) {
RCHECK(reader->ScanChildren() &&
reader->ReadChild(&header) &&
reader->ReadChildren(&tracks) &&
reader->MaybeReadChildren(&pssh));
return true;
}
IndependentAndDisposableSamples::IndependentAndDisposableSamples() = default;
IndependentAndDisposableSamples::IndependentAndDisposableSamples(
const IndependentAndDisposableSamples& other) = default;
IndependentAndDisposableSamples::~IndependentAndDisposableSamples() = default;
FourCC IndependentAndDisposableSamples::BoxType() const { return FOURCC_SDTP; }
bool IndependentAndDisposableSamples::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
RCHECK(reader->version() == 0);
RCHECK(reader->flags() == 0);
size_t sample_count = reader->box_size() - reader->pos();
RCHECK(sample_count <= sample_depends_on_.max_size());
sample_depends_on_.resize(sample_count);
for (size_t i = 0; i < sample_count; ++i) {
uint8_t sample_info;
RCHECK(reader->Read1(&sample_info));
sample_depends_on_[i] =
static_cast<SampleDependsOn>((sample_info >> 4) & 0x3);
RCHECK(sample_depends_on_[i] != kSampleDependsOnReserved);
}
return true;
}
SampleDependsOn IndependentAndDisposableSamples::sample_depends_on(
size_t i) const {
if (i >= sample_depends_on_.size())
return kSampleDependsOnUnknown;
return sample_depends_on_[i];
}
ID3v2Box::ID3v2Box() = default;
ID3v2Box::ID3v2Box(const ID3v2Box& other) = default;
ID3v2Box::~ID3v2Box() = default;
FourCC ID3v2Box::BoxType() const {
return FOURCC_ID32;
}
bool ID3v2Box::Parse(BoxReader* reader) {
// This is reading the ID32 box without regard for what's in it -- there will
// likely be binary data in this vector. We don't care though since we're just
// going to scan the memory without caring about sentinel values like \0.
RCHECK(reader->ReadVec(&id3v2_data,
std::min(static_cast<size_t>(128),
reader->buffer_size() - reader->pos())));
return true;
}
MetadataBox::MetadataBox() : used_shaka_packager(false) {}
MetadataBox::MetadataBox(const MetadataBox& other) = default;
MetadataBox::~MetadataBox() = default;
FourCC MetadataBox::BoxType() const {
return FOURCC_META;
}
bool MetadataBox::Parse(BoxReader* reader) {
RCHECK(reader->ReadFullBoxHeader());
// This is an optional box, so generate no errors.
if (!reader->ScanChildren())
return true;
ID3v2Box id3v2;
if (!reader->ReadChild(&id3v2))
return true;
constexpr char kShakaPackager[] = "shaka-packager";
used_shaka_packager =
base::StringPiece(reinterpret_cast<char*>(id3v2.id3v2_data.data()),
id3v2.id3v2_data.size())
.find(kShakaPackager) != base::StringPiece::npos;
return true;
}
} // namespace mp4
} // namespace media