910e62b5创建于 1月15日历史提交
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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);
}

}  // namespace

// Mp4FileTypeBoxWriter class.
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);    // normal rate.
  writer.WriteU32(box_->minor_version);  // normal rate.

  // It should include at least one of `avc1`, `avc3`, `av01`, `vp09`, `hvc1`,
  // or `hev1`.
  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 class.
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);

  // Write the children.
  WriteChildren(writer);

  writer.EndBox();
}

// Mp4MovieHeaderBoxWriter class.
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, /*flags=*/0, /*version=*/1);

  WriteIsoTime(writer, box_->creation_time);
  WriteIsoTime(writer, box_->modification_time);
  writer.WriteU32(box_->timescale);

  // TODO(crbug.com://1465031): The conversion to timescale will be made in
  // the box writer with its duration calculation.
  writer.WriteU64(box_->duration.InMilliseconds());

  writer.WriteU32(0x00010000);  // normal rate.
  writer.WriteU16(0x0100);      // full volume.
  writer.WriteU16(0);           // reserved.
  writer.WriteU32(0);           // reserved.
  writer.WriteU32(0);           // reserved.

  for (auto element : kDisplayIdentityMatrix) {
    writer.WriteU32(element);
  }

  // uint32_t type of predefined[6].
  for (uint32_t i = 0; i < 6; ++i) {
    writer.WriteU32(0);
  }

  writer.WriteU32(box_->next_track_id);

  writer.EndBox();
}

// Mp4MovieExtendsBoxWriter (`mvex`) class.
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);

  // Write the children.
  WriteChildren(writer);

  writer.EndBox();
}

// Mp4MovieTrackExtendsBoxWriter (`trex`) class.
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, /*flags=*/0, /*version=*/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 (`trak`) class.
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 (`tkhd`) class.
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, /*version=*/1);

  WriteIsoTime(writer, box_->creation_time);
  WriteIsoTime(writer, box_->modification_time);

  writer.WriteU32(box_->track_id);
  writer.WriteU32(0);  // reserved
  writer.WriteU64(box_->duration.InMilliseconds());
  writer.WriteU32(0);  // reserved;
  writer.WriteU32(0);  // reserved;
  writer.WriteU16(0);  // layer, 0 is the normal value.
  writer.WriteU16(0);  // alternate_group,
  if (box_->is_audio) {
    // 1.0 (0x0100) is a full volume for the audio.
    writer.WriteU16(0x0100);
  } else {
    writer.WriteU16(0);
  }
  writer.WriteU16(0);  // reserved.

  for (auto element : box_->matrix) {
    writer.WriteU32(element);
  }

  WriteLowHigh(writer, box_->natural_size.width());
  WriteLowHigh(writer, box_->natural_size.height());

  writer.EndBox();
}

// Mp4MovieMediaBoxWriter (`mdia`) class.
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 (`mdhd`) class.
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, /*flags=*/0, /*version=*/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);  // pre_defined = 0;

  writer.EndBox();
}

// Mp4MovieMediaHandlerBoxWriter (`hdlr`) class.
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, /*flags=*/0);

  writer.WriteU32(0);  // predefined = 0;
  writer.WriteU32(box_->handler_type);

  writer.WriteU32(0);  // reserved;
  writer.WriteU32(0);  // reserved;
  writer.WriteU32(0);  // reserved;

  writer.WriteString(box_->name);

  WriteChildren(writer);

  writer.EndBox();
}

// Mp4MovieMediaInformationBoxWriter (`minf`) class.
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 (`vmhd`) class.
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, /*flags=*/1);

  writer.WriteU16(0);  // graphics_mode.
  writer.WriteU16(0);  // op_color[0].
  writer.WriteU16(0);  // op_color[1]..
  writer.WriteU16(0);  // op_color[2]..

  writer.EndBox();
}

// Mp4MovieSoundHeaderBoxWriter (`smhd`) class.
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, /*flags=*/0);

  writer.WriteU16(0);  // balance.
  writer.WriteU16(0);  // reserved.

  writer.EndBox();
}

// Mp4MovieDataInformationBoxWriter (`dinf`) class.
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 (`dref`) class.
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, /*flags=*/0);

  writer.WriteU32(box_->entries.size());

  WriteChildren(writer);

  writer.EndBox();
}

// Mp4MovieDataUrlEntryBoxWriter (`url`) class.
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, /*flags=*/1);

  // We use empty Url location to prevent accidental PII leak.
  writer.WriteString("");

  writer.EndBox();
}

// Mp4MovieSampleTableBoxWriter (`stbl`) class.
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 (`stsc`) class.
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, /*flags=*/0);

  writer.WriteU32(0);  // entry_count.

  writer.EndBox();
}

// Mp4MovieDecodingTimeToSampleBoxWriter (`stts`) class.
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, /*flags=*/0);

  writer.WriteU32(0);  // entry_count.

  writer.EndBox();
}

// Mp4MovieSampleSizeBoxWriter (`stsz`) class.
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);  // sample_size.
  writer.WriteU32(0);  // sample_count.

  writer.EndBox();
}

// Mp4MovieSampleChunkOffsetBoxWriter (`stco`) class.
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, /*flags=*/0);

  writer.WriteU32(0);  // entry_count.

  writer.EndBox();
}

// Mp4MovieSampleDescriptionBoxWriter (`stsd`) class.
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, /*flags=*/0);

  writer.WriteU32(box_->entry_count);

  WriteChildren(writer);

  writer.EndBox();
}

// Mp4MovieColorInformationBoxWriter (`colr`) class
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 (`vp09`, `av01`, `avc1`, `avc3`, `hvc1`,
// `hev1`) class.
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);  // reserved.
  writer.WriteU16(0);  // reserved.
  writer.WriteU16(1);  // data_reference_index in `dref` box, 1 is start index.
  writer.WriteU16(0);  // predefined1.
  writer.WriteU16(1);  // reserved2.
  writer.WriteU32(0);  // pre_defined2[0].
  writer.WriteU32(0);  // pre_defined2[1].
  writer.WriteU32(0);  // pre_defined2[2].
  writer.WriteU16(box_->coded_size.width());
  writer.WriteU16(box_->coded_size.height());
  writer.WriteU32(0x00480000);  // horizontal resolution, 72 dpi.
  writer.WriteU32(0x00480000);  // vertical resolution, 72 dpi.
  writer.WriteU32(0);           // reserved3.
  writer.WriteU16(1);           // frame_count.

  // compressor_name.
  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);  // It will write 31 chars.

  writer.WriteU16(0x0018);  // depth.
  writer.WriteU16(0xFFFF);  // pre_defined, -1.

  WriteChildren(writer);

  writer.EndBox();
}

#if BUILDFLAG(USE_PROPRIETARY_CODECS)
// Mp4MovieAVCDecoderConfigurationBoxWriter (`avcC`) class.
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 (`esds`) class.
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 (`hvcC`) class.
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 (`mp4a` or 'Opus') class.
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);  // data_reference_index in `dref` box, 1 is start index.
  writer.WriteU32(0);  // reserved1[0]
  writer.WriteU32(0);  // reserved1[1]
  writer.WriteU16(box_->channel_count);
  writer.WriteU16(16);  // sample size.
  writer.WriteU16(0);   // predefined.
  writer.WriteU16(0);   // reserved.

  WriteLowHigh(writer, box_->sample_rate);

  WriteChildren(writer);

  writer.EndBox();
}

// Mp4MovieOpusSpecificBoxWriter (`dOps`) class.
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);  // Version.
  writer.WriteU8(box_->channel_count);
  writer.WriteU16(static_cast<uint16_t>(AudioTimestampHelper::TimeToFrames(
      kOpusSkipDuration, box_->sample_rate)));  // Preskip.
  writer.WriteU32(box_->sample_rate);
  writer.WriteU16(0u);  // OutputGain.
  writer.WriteU8(0u);   // ChannelMappingFamily

  // TODO(crbug.com/330815378): Write channel mapping table.
  writer.EndBox();
}

// Mp4MovieVPCodecConfigurationBoxWriter (`vpcC`) class.
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, /*flags=*/0, /*version=*/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(/*codecInitializationData Size*/ 0);

  writer.EndBox();
}

// Mp4MovieAV1CodecConfigurationBoxWriter (`vpcC`) class.
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 (`pasp`) class.
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);  // Horizontal spacing.
  writer.WriteU32(1);  // Vertical spacing.

  writer.EndBox();
}

// Mp4MovieBitRateBoxWriter (`btrt`) class.
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);

  // bufferSizeDB, 0 for unknown so that decoder will decide with its own
  // algorithm.
  writer.WriteU32(0);

  writer.WriteU32(box_->max_bit_rate);
  writer.WriteU32(box_->avg_bit_rate);

  writer.EndBox();
}

}  // namespace media