#include "media/muxers/mp4_movie_box_writer.h"
#include <memory>
#include <string>
#include <vector>
#include "base/big_endian.h"
#include "base/numerics/safe_conversions.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/formats/mp4/box_constants.h"
#include "media/formats/mp4/es_descriptor.h"
#include "media/muxers/box_byte_stream.h"
#include "media/muxers/mp4_muxer_context.h"
#include "media/muxers/mp4_type_conversion.h"
#include "media/muxers/output_position_tracker.h"
namespace media {
namespace {
void WriteIsoTime(BoxByteStream& writer, base::Time time) {
uint64_t iso_time =
base::saturated_cast<uint64_t>((time - kMP4Epoch).InSecondsF());
writer.WriteU64(iso_time);
}
void WriteLowHigh(BoxByteStream& writer, uint32_t value) {
writer.WriteU16(value & 0xFFFF);
writer.WriteU16(value >> 16);
}
}
Mp4FileTypeBoxWriter::Mp4FileTypeBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::FileType& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4FileTypeBoxWriter::~Mp4FileTypeBoxWriter() = default;
void Mp4FileTypeBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_FTYP);
writer.WriteU32(box_->major_brand);
writer.WriteU32(box_->minor_version);
CHECK_GE(box_->compatible_brands.size(), 1u);
CHECK(box_->compatible_brands.end() !=
std::find_if(box_->compatible_brands.begin(),
box_->compatible_brands.end(), [](const auto type) {
return type == mp4::FOURCC_AVC1 ||
type == mp4::FOURCC_AVC3 ||
type == mp4::FOURCC_AV01 ||
type == mp4::FOURCC_VP09
#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
|| type == mp4::FOURCC_HVC1 ||
type == mp4::FOURCC_HEV1
#endif
;
}));
for (const uint32_t& brand : box_->compatible_brands) {
writer.WriteU32(brand);
}
writer.EndBox();
}
Mp4MovieBoxWriter::Mp4MovieBoxWriter(const Mp4MuxerContext& context,
const mp4::writable_boxes::Movie& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(std::make_unique<Mp4MovieHeaderBoxWriter>(context, box_->header));
bool video_is_first_track = false;
auto video_track = context.GetVideoTrack();
if (video_track) {
DCHECK_LE(video_track.value().index, box_->tracks.size());
video_is_first_track = video_track.value().index == 0;
if (video_is_first_track) {
AddChildBox(std::make_unique<Mp4MovieTrackBoxWriter>(
context, box_->tracks[video_track.value().index]));
}
}
if (auto audio_track = context.GetAudioTrack()) {
DCHECK_LE(audio_track.value().index, box_->tracks.size());
AddChildBox(std::make_unique<Mp4MovieTrackBoxWriter>(
context, box_->tracks[audio_track.value().index]));
}
if (!video_is_first_track && video_track) {
AddChildBox(std::make_unique<Mp4MovieTrackBoxWriter>(
context, box_->tracks[video_track.value().index]));
}
AddChildBox(
std::make_unique<Mp4MovieExtendsBoxWriter>(context, box_->extends));
}
Mp4MovieBoxWriter::~Mp4MovieBoxWriter() = default;
void Mp4MovieBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_MOOV);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieHeaderBoxWriter::Mp4MovieHeaderBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MovieHeader& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieHeaderBoxWriter::~Mp4MovieHeaderBoxWriter() = default;
void Mp4MovieHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_MVHD, 0, 1);
WriteIsoTime(writer, box_->creation_time);
WriteIsoTime(writer, box_->modification_time);
writer.WriteU32(box_->timescale);
writer.WriteU64(box_->duration.InMilliseconds());
writer.WriteU32(0x00010000);
writer.WriteU16(0x0100);
writer.WriteU16(0);
writer.WriteU32(0);
writer.WriteU32(0);
for (auto element : kDisplayIdentityMatrix) {
writer.WriteU32(element);
}
for (uint32_t i = 0; i < 6; ++i) {
writer.WriteU32(0);
}
writer.WriteU32(box_->next_track_id);
writer.EndBox();
}
Mp4MovieExtendsBoxWriter::Mp4MovieExtendsBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MovieExtends& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (auto video_track = context.GetVideoTrack()) {
DCHECK_LE(video_track.value().index, box_->track_extends.size());
AddChildBox(std::make_unique<Mp4MovieTrackExtendsBoxWriter>(
context, box_->track_extends[video_track.value().index]));
}
if (auto audio_track = context.GetAudioTrack()) {
DCHECK_LE(audio_track.value().index, box_->track_extends.size());
AddChildBox(std::make_unique<Mp4MovieTrackExtendsBoxWriter>(
context, box_->track_extends[audio_track.value().index]));
}
}
Mp4MovieExtendsBoxWriter::~Mp4MovieExtendsBoxWriter() = default;
void Mp4MovieExtendsBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_MVEX);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieTrackExtendsBoxWriter::Mp4MovieTrackExtendsBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::TrackExtends& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieTrackExtendsBoxWriter::~Mp4MovieTrackExtendsBoxWriter() = default;
void Mp4MovieTrackExtendsBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_TREX, 0, 0);
writer.WriteU32(box_->track_id);
writer.WriteU32(box_->default_sample_description_index);
writer.WriteU32(
static_cast<uint32_t>(box_->default_sample_duration.InMilliseconds()));
writer.WriteU32(box_->default_sample_size);
writer.WriteU32(box_->default_sample_flags);
writer.EndBox();
}
Mp4MovieTrackBoxWriter::Mp4MovieTrackBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::Track& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(
std::make_unique<Mp4MovieTrackHeaderBoxWriter>(context, box_->header));
AddChildBox(std::make_unique<Mp4MovieMediaBoxWriter>(context, box_->media));
}
Mp4MovieTrackBoxWriter::~Mp4MovieTrackBoxWriter() = default;
void Mp4MovieTrackBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_TRAK);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieTrackHeaderBoxWriter::Mp4MovieTrackHeaderBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::TrackHeader& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieTrackHeaderBoxWriter::~Mp4MovieTrackHeaderBoxWriter() = default;
void Mp4MovieTrackHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_TKHD, box_->flags, 1);
WriteIsoTime(writer, box_->creation_time);
WriteIsoTime(writer, box_->modification_time);
writer.WriteU32(box_->track_id);
writer.WriteU32(0);
writer.WriteU64(box_->duration.InMilliseconds());
writer.WriteU32(0);
writer.WriteU32(0);
writer.WriteU16(0);
writer.WriteU16(0);
if (box_->is_audio) {
writer.WriteU16(0x0100);
} else {
writer.WriteU16(0);
}
writer.WriteU16(0);
for (auto element : box_->matrix) {
writer.WriteU32(element);
}
WriteLowHigh(writer, box_->natural_size.width());
WriteLowHigh(writer, box_->natural_size.height());
writer.EndBox();
}
Mp4MovieMediaBoxWriter::Mp4MovieMediaBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::Media& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(
std::make_unique<Mp4MovieMediaHeaderBoxWriter>(context, box_->header));
AddChildBox(
std::make_unique<Mp4MovieMediaHandlerBoxWriter>(context, box_->handler));
AddChildBox(std::make_unique<Mp4MovieMediaInformationBoxWriter>(
context, box_->information));
}
Mp4MovieMediaBoxWriter::~Mp4MovieMediaBoxWriter() = default;
void Mp4MovieMediaBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_MDIA);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieMediaHeaderBoxWriter::Mp4MovieMediaHeaderBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MediaHeader& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieMediaHeaderBoxWriter::~Mp4MovieMediaHeaderBoxWriter() = default;
void Mp4MovieMediaHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_MDHD, 0, 1);
WriteIsoTime(writer, box_->creation_time);
WriteIsoTime(writer, box_->modification_time);
writer.WriteU32(box_->timescale);
writer.WriteU64(box_->duration.InMilliseconds());
uint16_t language_code = ConvertIso639LanguageCodeToU16(box_->language);
writer.WriteU16(language_code);
writer.WriteU16(0);
writer.EndBox();
}
Mp4MovieMediaHandlerBoxWriter::Mp4MovieMediaHandlerBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MediaHandler& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieMediaHandlerBoxWriter::~Mp4MovieMediaHandlerBoxWriter() = default;
void Mp4MovieMediaHandlerBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_HDLR, 0);
writer.WriteU32(0);
writer.WriteU32(box_->handler_type);
writer.WriteU32(0);
writer.WriteU32(0);
writer.WriteU32(0);
writer.WriteString(box_->name);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieMediaInformationBoxWriter::Mp4MovieMediaInformationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MediaInformation& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (box_->video_header.has_value()) {
AddChildBox(std::make_unique<Mp4MovieVideoHeaderBoxWriter>(context));
} else if (box_->sound_header.has_value()) {
AddChildBox(std::make_unique<Mp4MovieSoundHeaderBoxWriter>(context));
}
AddChildBox(std::make_unique<Mp4MovieDataInformationBoxWriter>(
context, box_->data_information));
AddChildBox(std::make_unique<Mp4MovieSampleTableBoxWriter>(
context, box_->sample_table));
}
Mp4MovieMediaInformationBoxWriter::~Mp4MovieMediaInformationBoxWriter() =
default;
void Mp4MovieMediaInformationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_MINF);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieVideoHeaderBoxWriter::Mp4MovieVideoHeaderBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieVideoHeaderBoxWriter::~Mp4MovieVideoHeaderBoxWriter() = default;
void Mp4MovieVideoHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_VMHD, 1);
writer.WriteU16(0);
writer.WriteU16(0);
writer.WriteU16(0);
writer.WriteU16(0);
writer.EndBox();
}
Mp4MovieSoundHeaderBoxWriter::Mp4MovieSoundHeaderBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieSoundHeaderBoxWriter::~Mp4MovieSoundHeaderBoxWriter() = default;
void Mp4MovieSoundHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_SMHD, 0);
writer.WriteU16(0);
writer.WriteU16(0);
writer.EndBox();
}
Mp4MovieDataInformationBoxWriter::Mp4MovieDataInformationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::DataInformation& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(std::make_unique<Mp4MovieDataReferenceBoxWriter>(
context, box_->data_reference));
}
Mp4MovieDataInformationBoxWriter::~Mp4MovieDataInformationBoxWriter() = default;
void Mp4MovieDataInformationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_DINF);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieDataReferenceBoxWriter::Mp4MovieDataReferenceBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::DataReference& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (size_t i = 0; i < box_->entries.size(); ++i) {
AddChildBox(std::make_unique<Mp4MovieDataUrlEntryBoxWriter>(context));
}
}
Mp4MovieDataReferenceBoxWriter::~Mp4MovieDataReferenceBoxWriter() = default;
void Mp4MovieDataReferenceBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_DREF, 0);
writer.WriteU32(box_->entries.size());
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieDataUrlEntryBoxWriter::Mp4MovieDataUrlEntryBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieDataUrlEntryBoxWriter::~Mp4MovieDataUrlEntryBoxWriter() = default;
void Mp4MovieDataUrlEntryBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_URL, 1);
writer.WriteString("");
writer.EndBox();
}
Mp4MovieSampleTableBoxWriter::Mp4MovieSampleTableBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::SampleTable& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(std::make_unique<Mp4MovieSampleToChunkBoxWriter>(context));
AddChildBox(std::make_unique<Mp4MovieDecodingTimeToSampleBoxWriter>(context));
AddChildBox(std::make_unique<Mp4MovieSampleSizeBoxWriter>(context));
AddChildBox(std::make_unique<Mp4MovieSampleChunkOffsetBoxWriter>(context));
AddChildBox(std::make_unique<Mp4MovieSampleDescriptionBoxWriter>(
context, box_->sample_description));
}
Mp4MovieSampleTableBoxWriter::~Mp4MovieSampleTableBoxWriter() = default;
void Mp4MovieSampleTableBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_STBL);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieSampleToChunkBoxWriter::Mp4MovieSampleToChunkBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieSampleToChunkBoxWriter::~Mp4MovieSampleToChunkBoxWriter() = default;
void Mp4MovieSampleToChunkBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_STSC, 0);
writer.WriteU32(0);
writer.EndBox();
}
Mp4MovieDecodingTimeToSampleBoxWriter::Mp4MovieDecodingTimeToSampleBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieDecodingTimeToSampleBoxWriter::
~Mp4MovieDecodingTimeToSampleBoxWriter() = default;
void Mp4MovieDecodingTimeToSampleBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_STTS, 0);
writer.WriteU32(0);
writer.EndBox();
}
Mp4MovieSampleSizeBoxWriter::Mp4MovieSampleSizeBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieSampleSizeBoxWriter::~Mp4MovieSampleSizeBoxWriter() = default;
void Mp4MovieSampleSizeBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_STSZ);
writer.WriteU32(0);
writer.WriteU32(0);
writer.EndBox();
}
Mp4MovieSampleChunkOffsetBoxWriter::Mp4MovieSampleChunkOffsetBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieSampleChunkOffsetBoxWriter::~Mp4MovieSampleChunkOffsetBoxWriter() =
default;
void Mp4MovieSampleChunkOffsetBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_STCO, 0);
writer.WriteU32(0);
writer.EndBox();
}
Mp4MovieSampleDescriptionBoxWriter::Mp4MovieSampleDescriptionBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::SampleDescription& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (box_->video_sample_entry.has_value()) {
CHECK(!box_->audio_sample_entry.has_value());
AddChildBox(std::make_unique<Mp4MovieVisualSampleEntryBoxWriter>(
context, box_->video_sample_entry.value()));
return;
}
CHECK(box_->audio_sample_entry.has_value());
AddChildBox(std::make_unique<Mp4MovieAudioSampleEntryBoxWriter>(
context, box_->audio_sample_entry.value()));
}
Mp4MovieSampleDescriptionBoxWriter::~Mp4MovieSampleDescriptionBoxWriter() =
default;
void Mp4MovieSampleDescriptionBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_STSD, 0);
writer.WriteU32(box_->entry_count);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieColorInformationBoxWriter::Mp4MovieColorInformationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::ColorInformation& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieColorInformationBoxWriter::~Mp4MovieColorInformationBoxWriter() =
default;
void Mp4MovieColorInformationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_COLR);
writer.WriteU32(mp4::FOURCC_NCLX);
writer.WriteU16(static_cast<uint16_t>(box_->video_color_space.primaries));
writer.WriteU16(static_cast<uint16_t>(box_->video_color_space.transfer));
writer.WriteU16(static_cast<uint16_t>(box_->video_color_space.matrix));
gfx::ColorSpace::RangeID range = box_->video_color_space.range;
writer.WriteU8(range == gfx::ColorSpace::RangeID::FULL ? 0x80 : 0x00);
writer.EndBox();
}
Mp4MovieVisualSampleEntryBoxWriter::Mp4MovieVisualSampleEntryBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::VisualSampleEntry& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(std::make_unique<Mp4MoviePixelAspectRatioBoxBoxWriter>(context));
AddChildBox(
std::make_unique<Mp4MovieBitRateBoxWriter>(context, box_->bit_rate));
switch (box_->codec) {
case VideoCodec::kVP9:
CHECK(box_->vp_decoder_configuration.has_value());
AddChildBox(std::make_unique<Mp4MovieVPCodecConfigurationBoxWriter>(
context, box_->vp_decoder_configuration.value()));
break;
case VideoCodec::kAV1:
CHECK(box_->av1_decoder_configuration.has_value());
AddChildBox(std::make_unique<Mp4MovieAV1CodecConfigurationBoxWriter>(
context, box_->av1_decoder_configuration.value()));
break;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case VideoCodec::kH264:
CHECK(box_->avc_decoder_configuration.has_value());
AddChildBox(std::make_unique<Mp4MovieAVCDecoderConfigurationBoxWriter>(
context, box_->avc_decoder_configuration.value()));
break;
#endif
#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
case VideoCodec::kHEVC:
CHECK(box_->hevc_decoder_configuration.has_value());
AddChildBox(std::make_unique<Mp4MovieHEVCDecoderConfigurationBoxWriter>(
context, box_->hevc_decoder_configuration.value()));
break;
#endif
default:
NOTREACHED();
}
if (box_->color_information.has_value()) {
AddChildBox(std::make_unique<Mp4MovieColorInformationBoxWriter>(
context, box_->color_information.value()));
}
}
Mp4MovieVisualSampleEntryBoxWriter::~Mp4MovieVisualSampleEntryBoxWriter() =
default;
void Mp4MovieVisualSampleEntryBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (box_->codec) {
case VideoCodec::kVP9:
writer.StartBox(mp4::FOURCC_VP09);
break;
case VideoCodec::kAV1:
writer.StartBox(mp4::FOURCC_AV01);
break;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case VideoCodec::kH264:
writer.StartBox(
box_->avc_decoder_configuration->add_parameter_sets_in_bitstream
? mp4::FOURCC_AVC3
: mp4::FOURCC_AVC1);
break;
#endif
#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
case VideoCodec::kHEVC:
writer.StartBox(
box_->hevc_decoder_configuration->add_parameter_sets_in_bitstream
? mp4::FOURCC_HEV1
: mp4::FOURCC_HVC1);
break;
#endif
default:
NOTREACHED();
}
writer.WriteU32(0);
writer.WriteU16(0);
writer.WriteU16(1);
writer.WriteU16(0);
writer.WriteU16(1);
writer.WriteU32(0);
writer.WriteU32(0);
writer.WriteU32(0);
writer.WriteU16(box_->coded_size.width());
writer.WriteU16(box_->coded_size.height());
writer.WriteU32(0x00480000);
writer.WriteU32(0x00480000);
writer.WriteU32(0);
writer.WriteU16(1);
constexpr size_t kMaxCompressorNameSize = 30;
std::string compressor_name = box_->compressor_name;
uint8_t compressor_name_size =
std::min(compressor_name.size(), kMaxCompressorNameSize);
writer.WriteU8(compressor_name_size);
compressor_name.resize(kMaxCompressorNameSize);
compressor_name.push_back(0);
writer.WriteString(compressor_name);
writer.WriteU16(0x0018);
writer.WriteU16(0xFFFF);
WriteChildren(writer);
writer.EndBox();
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
Mp4MovieAVCDecoderConfigurationBoxWriter::
Mp4MovieAVCDecoderConfigurationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::AVCDecoderConfiguration& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieAVCDecoderConfigurationBoxWriter::
~Mp4MovieAVCDecoderConfigurationBoxWriter() = default;
void Mp4MovieAVCDecoderConfigurationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_AVCC);
std::vector<uint8_t> write_data;
CHECK(box_->avc_config_record.Serialize(write_data));
writer.WriteBytes(write_data.data(), write_data.size());
writer.EndBox();
}
Mp4MovieElementaryStreamDescriptorBoxWriter::
Mp4MovieElementaryStreamDescriptorBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::ElementaryStreamDescriptor& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieElementaryStreamDescriptorBoxWriter::
~Mp4MovieElementaryStreamDescriptorBoxWriter() = default;
void Mp4MovieElementaryStreamDescriptorBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_ESDS);
std::vector<uint8_t> esds =
mp4::ESDescriptor::CreateEsds(box_->aac_codec_description);
writer.WriteBytes(esds.data(), esds.size());
writer.EndBox();
}
#endif
#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER)
Mp4MovieHEVCDecoderConfigurationBoxWriter::
Mp4MovieHEVCDecoderConfigurationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::HEVCDecoderConfiguration& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieHEVCDecoderConfigurationBoxWriter::
~Mp4MovieHEVCDecoderConfigurationBoxWriter() = default;
void Mp4MovieHEVCDecoderConfigurationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_HVCC);
std::vector<uint8_t> write_data;
CHECK(box_->hevc_config_record.Serialize(write_data));
writer.WriteBytes(write_data.data(), write_data.size());
writer.EndBox();
}
#endif
Mp4MovieAudioSampleEntryBoxWriter::Mp4MovieAudioSampleEntryBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::AudioSampleEntry& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(
std::make_unique<Mp4MovieBitRateBoxWriter>(context, box_->bit_rate));
switch (box_->codec) {
case AudioCodec::kOpus:
AddChildBox(std::make_unique<Mp4MovieOpusSpecificBoxWriter>(
context, box_->opus_specific_box.value()));
break;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case AudioCodec::kAAC:
AddChildBox(std::make_unique<Mp4MovieElementaryStreamDescriptorBoxWriter>(
context, box_->elementary_stream_descriptor.value()));
break;
#endif
default:
NOTREACHED();
}
}
Mp4MovieAudioSampleEntryBoxWriter::~Mp4MovieAudioSampleEntryBoxWriter() =
default;
void Mp4MovieAudioSampleEntryBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (box_->codec) {
case AudioCodec::kOpus:
writer.StartBox(mp4::FOURCC_OPUS);
break;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case AudioCodec::kAAC:
writer.StartBox(mp4::FOURCC_MP4A);
break;
#endif
default:
NOTREACHED();
}
constexpr size_t kAudioSampleEntryReservedSize = 6u;
for (size_t i = 0; i < kAudioSampleEntryReservedSize; ++i) {
writer.WriteU8(0);
}
writer.WriteU16(1);
writer.WriteU32(0);
writer.WriteU32(0);
writer.WriteU16(box_->channel_count);
writer.WriteU16(16);
writer.WriteU16(0);
writer.WriteU16(0);
WriteLowHigh(writer, box_->sample_rate);
WriteChildren(writer);
writer.EndBox();
}
Mp4MovieOpusSpecificBoxWriter::Mp4MovieOpusSpecificBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::OpusSpecificBox& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieOpusSpecificBoxWriter::~Mp4MovieOpusSpecificBoxWriter() = default;
void Mp4MovieOpusSpecificBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_DOPS);
constexpr base::TimeDelta kOpusSkipDuration = base::Milliseconds(80);
writer.WriteU8(0u);
writer.WriteU8(box_->channel_count);
writer.WriteU16(static_cast<uint16_t>(AudioTimestampHelper::TimeToFrames(
kOpusSkipDuration, box_->sample_rate)));
writer.WriteU32(box_->sample_rate);
writer.WriteU16(0u);
writer.WriteU8(0u);
writer.EndBox();
}
Mp4MovieVPCodecConfigurationBoxWriter::Mp4MovieVPCodecConfigurationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::VPCodecConfiguration& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieVPCodecConfigurationBoxWriter::
~Mp4MovieVPCodecConfigurationBoxWriter() = default;
void Mp4MovieVPCodecConfigurationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_VPCC, 0, 1);
switch (box_->profile) {
case VP9PROFILE_PROFILE0:
writer.WriteU8(0);
break;
case VP9PROFILE_PROFILE1:
writer.WriteU8(1);
break;
case VP9PROFILE_PROFILE2:
writer.WriteU8(2);
break;
case VP9PROFILE_PROFILE3:
writer.WriteU8(3);
break;
default:
NOTREACHED();
}
writer.WriteU8(box_->level);
auto video_color_space =
box_->color_space.IsValid()
? VideoColorSpace::FromGfxColorSpace(box_->color_space)
: VideoColorSpace(VideoColorSpace::PrimaryID::UNSPECIFIED,
VideoColorSpace::TransferID::UNSPECIFIED,
VideoColorSpace::MatrixID::UNSPECIFIED,
gfx::ColorSpace::RangeID::DERIVED);
constexpr uint8_t bit_depth = 8u;
constexpr uint8_t chroma_sub_sampling = 0u;
uint8_t video_full_range_flag =
video_color_space.range == gfx::ColorSpace::RangeID::FULL ? 1u : 0u;
writer.WriteU8(bit_depth << 4 | chroma_sub_sampling << 1 |
video_full_range_flag);
writer.WriteU8(static_cast<uint8_t>(video_color_space.primaries));
writer.WriteU8(static_cast<uint8_t>(video_color_space.transfer));
writer.WriteU8(static_cast<uint8_t>(video_color_space.matrix));
writer.WriteU16( 0);
writer.EndBox();
}
Mp4MovieAV1CodecConfigurationBoxWriter::Mp4MovieAV1CodecConfigurationBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::AV1CodecConfiguration& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieAV1CodecConfigurationBoxWriter::
~Mp4MovieAV1CodecConfigurationBoxWriter() = default;
void Mp4MovieAV1CodecConfigurationBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_AV1C);
writer.WriteBytes(box_->av1_decoder_configuration_data.data(),
box_->av1_decoder_configuration_data.size());
writer.EndBox();
}
Mp4MoviePixelAspectRatioBoxBoxWriter::Mp4MoviePixelAspectRatioBoxBoxWriter(
const Mp4MuxerContext& context)
: Mp4BoxWriter(context) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MoviePixelAspectRatioBoxBoxWriter::~Mp4MoviePixelAspectRatioBoxBoxWriter() =
default;
void Mp4MoviePixelAspectRatioBoxBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_PASP);
writer.WriteU32(1);
writer.WriteU32(1);
writer.EndBox();
}
Mp4MovieBitRateBoxWriter::Mp4MovieBitRateBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::BitRate& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieBitRateBoxWriter::~Mp4MovieBitRateBoxWriter() = default;
void Mp4MovieBitRateBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_BTRT);
writer.WriteU32(0);
writer.WriteU32(box_->max_bit_rate);
writer.WriteU32(box_->avg_bit_rate);
writer.EndBox();
}
}