#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include "base/big_endian.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "media/base/subsample_entry.h"
#include "media/base/video_codecs.h"
#include "media/base/video_color_space.h"
#include "media/formats/mp2t/es_parser_adts.h"
#include "media/formats/mp4/bitstream_converter.h"
#include "media/formats/mp4/box_definitions.h"
#include "media/formats/mp4/box_reader.h"
#include "media/formats/mp4/es_descriptor.h"
#include "media/formats/mp4/writable_box_definitions.h"
#include "media/formats/mpeg/adts_stream_parser.h"
#include "media/muxers/box_byte_stream.h"
#include "media/muxers/mp4_box_writer.h"
#include "media/muxers/mp4_fragment_box_writer.h"
#include "media/muxers/mp4_movie_box_writer.h"
#include "media/muxers/mp4_muxer_context.h"
#include "media/muxers/mp4_type_conversion.h"
#include "media/muxers/output_position_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
constexpr uint32_t kDuration1 = …;
constexpr uint32_t kDuration2 = …;
constexpr uint32_t kDefaultSampleSize = …;
constexpr uint32_t kWidth = …;
constexpr uint32_t kHeight = …;
constexpr uint32_t kVideoTimescale = …;
constexpr uint32_t kAudioTimescale = …;
constexpr uint32_t kVideoSampleFlags = …;
constexpr uint32_t kAudioSampleFlags = …;
constexpr uint16_t kAudioVolume = …;
constexpr char kVideoHandlerName[] = …;
constexpr char kAudioHandlerName[] = …;
constexpr uint32_t kTotalSizeLength = …;
constexpr uint32_t kFlagsAndVersionLength = …;
constexpr uint32_t kEntryCountLength = …;
constexpr uint32_t kSampleSizeAndCount = …;
constexpr size_t kVideoIndex = …;
constexpr size_t kAudioIndex = …;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
constexpr uint8_t kProfileIndicationNoChroma = 77;
constexpr uint8_t kProfileIndication = 122;
constexpr uint8_t kProfileCompatibility = 100;
constexpr uint8_t kLevelIndication = 64;
constexpr uint8_t kNALUUnitLength = 4;
constexpr uint8_t kSPS[] = {0x67, 0x64, 0x00, 0x0C, 0xAC, 0xD9, 0x41,
0x41, 0xFB, 0x01, 0x10, 0x00, 0x00, 0x03,
0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x01,
0xE0, 0xF1, 0x42, 0x99, 0x60};
constexpr uint8_t kPPS[] = {0x68, 0xEE, 0xE3, 0xCB, 0x22, 0xC0};
constexpr uint8_t kChromaFormat = 0x3;
constexpr uint8_t kLumaMinus8 = 0x4;
constexpr uint8_t kChromaMinus8 = 0x4;
#endif
uint64_t ConvertTo1904TimeInSeconds(base::Time time) { … }
}
class Mp4MuxerBoxWriterTest : public testing::Test { … };
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieAndHeader) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieExtends) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieTrackAndMediaHeader) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieMediaDataInformation) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieMediaMultipleSampleBoxes) { … }
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieVisualSampleEntry) {
std::vector<uint8_t> written_data;
CreateContext(written_data);
mp4::writable_boxes::SampleDescription sample_description;
mp4::writable_boxes::VisualSampleEntry visual_sample_entry(VideoCodec::kH264);
visual_sample_entry.coded_size = gfx::Size(kWidth, kHeight);
visual_sample_entry.compressor_name = "Chromium AVC Coding";
mp4::writable_boxes::AVCDecoderConfiguration avc = {};
avc.avc_config_record.version = 1;
avc.avc_config_record.profile_indication = kProfileIndicationNoChroma;
avc.avc_config_record.profile_compatibility = kProfileCompatibility;
avc.avc_config_record.avc_level = kLevelIndication;
avc.avc_config_record.length_size = kNALUUnitLength;
std::vector<uint8_t> sps(std::begin(kSPS), std::end(kSPS));
avc.avc_config_record.sps_list.emplace_back(sps);
std::vector<uint8_t> pps(std::begin(kPPS), std::end(kPPS));
avc.avc_config_record.pps_list.emplace_back(pps);
visual_sample_entry.avc_decoder_configuration = std::move(avc);
sample_description.video_sample_entry = std::move(visual_sample_entry);
Mp4MovieSampleDescriptionBoxWriter box_writer(*context(), sample_description);
FlushAndWait(&box_writer);
std::unique_ptr<mp4::BoxReader> box_reader(
mp4::BoxReader::ReadConcatentatedBoxes(written_data.data(),
written_data.size(), nullptr));
EXPECT_TRUE(box_reader->ScanChildren());
mp4::SampleDescription reader_sample_description;
reader_sample_description.type = mp4::kVideo;
EXPECT_TRUE(box_reader->ReadChild(&reader_sample_description));
EXPECT_EQ(1u, reader_sample_description.video_entries.size());
const auto& video_sample_entry = reader_sample_description.video_entries[0];
EXPECT_TRUE(video_sample_entry.IsFormatValid());
EXPECT_EQ(1, video_sample_entry.data_reference_index);
EXPECT_EQ(static_cast<uint16_t>(kWidth), video_sample_entry.width);
EXPECT_EQ(static_cast<uint16_t>(kHeight), video_sample_entry.height);
EXPECT_EQ(VideoCodecProfile::H264PROFILE_MAIN,
video_sample_entry.video_info.profile);
}
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieAVCDecoderConfigurationRecord) {
std::vector<uint8_t> written_data;
CreateContext(written_data);
mp4::writable_boxes::AVCDecoderConfiguration avc = {};
avc.avc_config_record.version = 1;
avc.avc_config_record.profile_indication = kProfileIndication;
avc.avc_config_record.profile_compatibility = kProfileCompatibility;
avc.avc_config_record.avc_level = kLevelIndication;
avc.avc_config_record.length_size = kNALUUnitLength;
std::vector<uint8_t> sps(std::begin(kSPS), std::end(kSPS));
avc.avc_config_record.sps_list.emplace_back(sps);
std::vector<uint8_t> pps(std::begin(kPPS), std::end(kPPS));
avc.avc_config_record.pps_list.emplace_back(pps);
avc.avc_config_record.chroma_format = kChromaFormat;
avc.avc_config_record.bit_depth_luma_minus8 = kLumaMinus8;
avc.avc_config_record.bit_depth_chroma_minus8 = kChromaMinus8;
Mp4MovieAVCDecoderConfigurationBoxWriter box_writer(*context(), avc);
FlushAndWait(&box_writer);
std::unique_ptr<mp4::BoxReader> box_reader(
mp4::BoxReader::ReadConcatentatedBoxes(written_data.data(),
written_data.size(), nullptr));
EXPECT_TRUE(box_reader->ScanChildren());
mp4::AVCDecoderConfigurationRecord avc_config_reader;
EXPECT_TRUE(box_reader->ReadChild(&avc_config_reader));
EXPECT_EQ(kProfileIndication, avc_config_reader.profile_indication);
EXPECT_EQ(kProfileCompatibility, avc_config_reader.profile_compatibility);
EXPECT_EQ(kLevelIndication, avc_config_reader.avc_level);
EXPECT_EQ(kNALUUnitLength, avc_config_reader.length_size);
EXPECT_EQ(1u, avc_config_reader.sps_list.size());
EXPECT_EQ(1u, avc_config_reader.pps_list.size());
std::vector<uint8_t> sps1 = avc_config_reader.sps_list[0];
std::vector<uint8_t> pps1 = avc_config_reader.pps_list[0];
EXPECT_EQ(std::vector<uint8_t>(std::begin(kSPS), std::end(kSPS)), sps1);
EXPECT_EQ(std::vector<uint8_t>(std::begin(kPPS), std::end(kPPS)), pps1);
EXPECT_EQ((kChromaFormat & 0x3), avc_config_reader.chroma_format);
EXPECT_EQ((kLumaMinus8 & 0x7), avc_config_reader.bit_depth_luma_minus8);
EXPECT_EQ((kChromaMinus8 & 0x7), avc_config_reader.bit_depth_chroma_minus8);
EXPECT_EQ(0u, avc_config_reader.sps_ext_list.size());
}
TEST_F(Mp4MuxerBoxWriterTest, Mp4AacAudioSampleEntry) {
std::vector<uint8_t> written_data;
CreateContext(written_data);
mp4::writable_boxes::SampleDescription sample_description;
constexpr uint32_t kSampleRate = 48000u;
mp4::writable_boxes::AudioSampleEntry audio_sample_entry(AudioCodec::kAAC,
kSampleRate, 2u);
mp4::writable_boxes::ElementaryStreamDescriptor esds;
constexpr uint32_t kBitRate = 341000u;
constexpr int32_t kSampleFrequency = 48000;
esds.aac_codec_description.push_back(0x11);
esds.aac_codec_description.push_back(0x90);
audio_sample_entry.elementary_stream_descriptor = std::move(esds);
mp4::writable_boxes::BitRate bit_rate;
bit_rate.max_bit_rate = kBitRate;
bit_rate.avg_bit_rate = kBitRate;
audio_sample_entry.bit_rate = std::move(bit_rate);
sample_description.audio_sample_entry = std::move(audio_sample_entry);
Mp4MovieSampleDescriptionBoxWriter box_writer(*context(), sample_description);
FlushAndWait(&box_writer);
std::unique_ptr<mp4::BoxReader> box_reader(
mp4::BoxReader::ReadConcatentatedBoxes(written_data.data(),
written_data.size(), nullptr));
EXPECT_TRUE(box_reader->ScanChildren());
mp4::SampleDescription reader_sample_description;
reader_sample_description.type = mp4::kAudio;
EXPECT_TRUE(box_reader->ReadChild(&reader_sample_description));
EXPECT_EQ(1u, reader_sample_description.audio_entries.size());
const auto& audio_sample = reader_sample_description.audio_entries[0];
EXPECT_EQ(1, audio_sample.data_reference_index);
EXPECT_EQ(2, audio_sample.channelcount);
EXPECT_EQ(16, audio_sample.samplesize);
EXPECT_EQ(kSampleRate, audio_sample.samplerate);
const mp4::ElementaryStreamDescriptor& esds_reader = audio_sample.esds;
EXPECT_EQ(mp4::kISO_14496_3, esds_reader.object_type);
const mp4::AAC& aac = esds_reader.aac;
AudioCodecProfile profile = aac.GetProfile();
EXPECT_EQ(AudioCodecProfile::kUnknown, profile);
int aac_frequency = aac.GetOutputSamplesPerSecond(false);
EXPECT_EQ(kSampleFrequency, aac_frequency);
ChannelLayout channel_layout = aac.GetChannelLayout(false);
EXPECT_EQ(media::CHANNEL_LAYOUT_STEREO, channel_layout);
int adts_header_size;
auto buffer = aac.CreateAdtsFromEsds({}, &adts_header_size);
EXPECT_FALSE(buffer.empty());
ADTSStreamParser adts_parser;
int frame_size = 0, sample_rate = 0, sample_count = 0;
ChannelLayout adts_channel_layout;
bool metadata_frame;
EXPECT_NE(adts_parser.ParseFrameHeader(
buffer.data(), adts_header_size, &frame_size, &sample_rate,
&adts_channel_layout, &sample_count, &metadata_frame, nullptr),
-1);
EXPECT_EQ(adts_header_size, frame_size);
EXPECT_EQ(kSampleFrequency, sample_rate);
EXPECT_EQ(media::CHANNEL_LAYOUT_STEREO, adts_channel_layout);
EXPECT_EQ(1024, sample_count);
EXPECT_FALSE(metadata_frame);
}
#endif
TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieVPConfigurationRecord) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4OpusAudioSampleEntry) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4Fragments) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4FtypBox) { … }
TEST_F(Mp4MuxerBoxWriterTest, Mp4MfraBox) { … }
}