blob: 23ea9df6f07251d30a398aef5f24207d6dc0eec9 [file] [log] [blame]
// 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_fragment_box_writer.h"
#include <memory>
#include <type_traits>
#include <vector>
#include "media/formats/mp4/fourccs.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 ValidateSampleFlags(uint32_t flags) {
uint32_t allowed_sample_flags =
static_cast<uint32_t>(
mp4::writable_boxes::FragmentSampleFlags::kSampleFlagIsNonSync) |
static_cast<uint32_t>(
mp4::writable_boxes::FragmentSampleFlags::kSampleFlagDependsYes) |
static_cast<uint32_t>(
mp4::writable_boxes::FragmentSampleFlags::kSampleFlagDependsNo);
CHECK_EQ(flags & ~(allowed_sample_flags), 0u);
}
} // namespace
// Mp4MovieFragmentBoxWriter (`moof`) class.
Mp4MovieFragmentBoxWriter::Mp4MovieFragmentBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MovieFragment& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(
std::make_unique<Mp4MovieFragmentHeaderBoxWriter>(context, box_.header));
for (const auto& fragment : box_.track_fragments) {
AddChildBox(std::make_unique<Mp4TrackFragmentBoxWriter>(context, fragment));
}
}
Mp4MovieFragmentBoxWriter::~Mp4MovieFragmentBoxWriter() = default;
void Mp4MovieFragmentBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_MOOF);
WriteChildren(writer);
writer.EndBox();
}
// Mp4MovieFragmentHeaderBoxWriter (`mfhd`) class.
Mp4MovieFragmentHeaderBoxWriter::Mp4MovieFragmentHeaderBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MovieFragmentHeader& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MovieFragmentHeaderBoxWriter::~Mp4MovieFragmentHeaderBoxWriter() = default;
void Mp4MovieFragmentHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_MFHD, /*flags=*/0, /*version=*/0);
writer.WriteU32(box_.sequence_number);
writer.EndBox();
}
// Mp4TrackFragmentBoxWriter (`traf`) class.
Mp4TrackFragmentBoxWriter::Mp4TrackFragmentBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::TrackFragment& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
AddChildBox(
std::make_unique<Mp4TrackFragmentHeaderBoxWriter>(context, box_.header));
AddChildBox(std::make_unique<Mp4TrackFragmentDecodeTimeBoxWriter>(
context, box_.decode_time));
AddChildBox(
std::make_unique<Mp4TrackFragmentRunBoxWriter>(context, box_.run));
}
Mp4TrackFragmentBoxWriter::~Mp4TrackFragmentBoxWriter() = default;
void Mp4TrackFragmentBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_TRAF);
WriteChildren(writer);
writer.EndBox();
}
// Mp4TrackFragmentHeaderBoxWriter (`tfhd`) class.
Mp4TrackFragmentHeaderBoxWriter::Mp4TrackFragmentHeaderBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::TrackFragmentHeader& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4TrackFragmentHeaderBoxWriter::~Mp4TrackFragmentHeaderBoxWriter() = default;
void Mp4TrackFragmentHeaderBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_TFHD, box_.flags, /*version=*/0);
writer.WriteU32(box_.track_id);
CHECK(box_.flags &
static_cast<uint32_t>(
mp4::writable_boxes::TrackFragmentHeaderFlags::kDefaultBaseIsMoof));
if (box_.flags &
static_cast<uint32_t>(mp4::writable_boxes::TrackFragmentHeaderFlags::
kSampleDescriptionIndexPresent)) {
NOTREACHED();
}
if (box_.flags &
static_cast<uint32_t>(mp4::writable_boxes::TrackFragmentHeaderFlags::
kDefaultSampleDurationPresent)) {
writer.WriteU32(box_.default_sample_duration.InSeconds());
}
if (box_.flags &
static_cast<uint32_t>(mp4::writable_boxes::TrackFragmentHeaderFlags::
kDefaultSampleSizePresent)) {
writer.WriteU32(box_.default_sample_size);
}
if (box_.flags &
static_cast<uint32_t>(mp4::writable_boxes::TrackFragmentHeaderFlags::
kkDefaultSampleFlagsPresent)) {
ValidateSampleFlags(box_.default_sample_flags);
writer.WriteU32(box_.default_sample_flags);
}
writer.EndBox();
}
// Mp4TrackFragmentDecodeTimeBoxWriter (`tfdt`) class.
Mp4TrackFragmentDecodeTimeBoxWriter::Mp4TrackFragmentDecodeTimeBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::TrackFragmentDecodeTime& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4TrackFragmentDecodeTimeBoxWriter::~Mp4TrackFragmentDecodeTimeBoxWriter() =
default;
void Mp4TrackFragmentDecodeTimeBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_TFDT);
writer.WriteU64(box_.base_media_decode_time.InSeconds());
writer.EndBox();
}
// Mp4TrackFragmentRunBoxWriter (`trun`) class.
Mp4TrackFragmentRunBoxWriter::Mp4TrackFragmentRunBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::TrackFragmentRun& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4TrackFragmentRunBoxWriter::~Mp4TrackFragmentRunBoxWriter() = default;
void Mp4TrackFragmentRunBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartFullBox(mp4::FOURCC_TRUN, box_.flags, /*version=*/0);
writer.WriteU32(box_.sample_count);
{
// `data_offset`.
// `movie-fragment relative addressing` should exist must by
// `https://www.w3.org/TR/mse-byte-stream-format-isobmff/`.
CHECK(box_.flags &
static_cast<uint16_t>(
mp4::writable_boxes::TrackFragmentRunFlags::kDataOffsetPresent));
writer.WriteOffsetPlaceholder();
}
if (box_.flags &
static_cast<uint16_t>(mp4::writable_boxes::TrackFragmentRunFlags::
kFirstSampleFlagsPresent)) {
ValidateSampleFlags(box_.first_sample_flags);
writer.WriteU32(box_.first_sample_flags);
}
bool duration_exists =
(box_.flags &
static_cast<uint16_t>(
mp4::writable_boxes::TrackFragmentRunFlags::kSampleDurationPresent));
bool size_exists =
(box_.flags &
static_cast<uint16_t>(
mp4::writable_boxes::TrackFragmentRunFlags::kSampleSizePresent));
bool flags_exists =
(box_.flags &
static_cast<uint16_t>(
mp4::writable_boxes::TrackFragmentRunFlags::kSampleFlagsPresent));
if (duration_exists) {
CHECK_EQ(box_.sample_count, box_.sample_durations.size());
}
if (size_exists) {
CHECK_EQ(box_.sample_count, box_.sample_sizes.size());
}
if (flags_exists) {
CHECK_EQ(box_.sample_count, box_.sample_flags.size());
}
for (uint32_t i = 0; i < box_.sample_count; ++i) {
if (duration_exists) {
writer.WriteU32(box_.sample_durations[i].InSeconds());
}
if (size_exists) {
writer.WriteU32(box_.sample_sizes[i]);
}
if (flags_exists) {
writer.WriteU32(box_.sample_flags[i]);
}
}
writer.EndBox();
}
// Mp4MediaDataBoxWriter (`mdat`) class.
Mp4MediaDataBoxWriter::Mp4MediaDataBoxWriter(
const Mp4MuxerContext& context,
const mp4::writable_boxes::MediaData& box)
: Mp4BoxWriter(context), box_(box) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
Mp4MediaDataBoxWriter::~Mp4MediaDataBoxWriter() = default;
void Mp4MediaDataBoxWriter::Write(BoxByteStream& writer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
writer.StartBox(mp4::FOURCC_MDAT);
for (const auto& data : box_.data) {
writer.FlushCurrentOffset();
writer.WriteBytes(data.data(), data.size());
}
writer.EndBox();
}
} // namespace media