blob: 53b6641915bcff6fbeca2e89593d083e65727942 [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/box_byte_stream.h"
#include <algorithm>
#include <vector>
#include "base/containers/span.h"
#include "base/containers/span_reader.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
enum class DataType {
kPlaceHolder = 0,
kType_8 = 1,
kType_16 = 2,
kType_32 = 3,
kType_64 = 4,
kType_Bytes = 5,
kType_String = 6,
kEndBox = 7,
};
struct DataOrder {
DataType type;
uint64_t value;
};
TEST(BoxByteStreamTest, Default) {
// Test basic Write APIs of the BoxByteStream.
BoxByteStream box_byte_stream;
DataOrder test_data[] = {
{DataType::kType_8, 0x48},
{DataType::kPlaceHolder, /*total_size=*/44},
{DataType::kType_16, 0x1617},
{DataType::kType_32, 0x32333435},
{DataType::kType_64, 0x64646667686970},
{DataType::kType_Bytes, 0x12345678901234},
{DataType::kPlaceHolder, /*total_size=*/9},
{DataType::kType_8, 0x28},
{DataType::kEndBox, 0x0},
{DataType::kType_16, 0x0},
{DataType::kType_32, 0x0},
{DataType::kEndBox, 0x0},
{DataType::kType_String, reinterpret_cast<uint64_t>("")},
{DataType::kType_String, reinterpret_cast<uint64_t>("abcdabcd")}};
for (auto& data : test_data) {
switch (data.type) {
case DataType::kPlaceHolder:
box_byte_stream.StartBox(mp4::FOURCC_MOOV);
break;
case DataType::kEndBox:
box_byte_stream.EndBox();
break;
case DataType::kType_8:
box_byte_stream.WriteU8(static_cast<uint8_t>(data.value));
break;
case DataType::kType_16:
box_byte_stream.WriteU16(static_cast<uint16_t>(data.value));
break;
case DataType::kType_32:
box_byte_stream.WriteU32(static_cast<uint32_t>(data.value));
break;
case DataType::kType_64:
box_byte_stream.WriteU64(data.value);
break;
case DataType::kType_Bytes:
box_byte_stream.WriteBytes(&data.value, 7);
break;
case DataType::kType_String:
box_byte_stream.WriteString(reinterpret_cast<char*>(data.value));
break;
}
}
std::vector<uint8_t> written_data = box_byte_stream.Flush();
auto reader = base::SpanReader(base::span(written_data));
for (auto& data : test_data) {
uint64_t ret_value = 0;
switch (data.type) {
case DataType::kPlaceHolder: {
uint32_t val;
EXPECT_TRUE(reader.ReadU32BigEndian(val));
base::byte_span_from_ref(ret_value).first<4>().copy_from(
base::byte_span_from_ref(val));
EXPECT_EQ(data.value, ret_value);
EXPECT_TRUE(reader.ReadU32BigEndian(val));
base::byte_span_from_ref(ret_value).first<4>().copy_from(
base::byte_span_from_ref(val));
EXPECT_EQ(mp4::FOURCC_MOOV, ret_value);
break;
}
case DataType::kEndBox:
break;
case DataType::kType_8: {
uint8_t val;
EXPECT_TRUE(reader.ReadU8BigEndian(val));
base::byte_span_from_ref(ret_value).first<1>().copy_from(
base::byte_span_from_ref(val));
EXPECT_EQ(data.value, ret_value);
break;
}
case DataType::kType_16: {
uint16_t val;
EXPECT_TRUE(reader.ReadU16BigEndian(val));
base::byte_span_from_ref(ret_value).first<2>().copy_from(
base::byte_span_from_ref(val));
EXPECT_EQ(data.value, ret_value);
break;
}
case DataType::kType_32: {
uint32_t val;
EXPECT_TRUE(reader.ReadU32BigEndian(val));
base::byte_span_from_ref(ret_value).first<4>().copy_from(
base::byte_span_from_ref(val));
EXPECT_EQ(data.value, ret_value);
break;
}
case DataType::kType_64:
EXPECT_TRUE(reader.ReadU64BigEndian(ret_value));
EXPECT_EQ(data.value, ret_value);
break;
case DataType::kType_Bytes:
EXPECT_TRUE(
reader.ReadCopy(base::byte_span_from_ref(ret_value).first<7>()));
EXPECT_EQ(data.value, ret_value);
break;
case DataType::kType_String:
std::string expected_string = reinterpret_cast<char*>(data.value);
if (expected_string.empty()) {
EXPECT_TRUE(
reader.ReadCopy(base::byte_span_from_ref(ret_value).first<1>()));
EXPECT_EQ(ret_value, 0u);
} else {
base::span<uint8_t> val;
EXPECT_TRUE(reader.ReadInto(expected_string.size(), val));
EXPECT_EQ(expected_string, base::as_string_view(val));
}
break;
}
}
}
TEST(BoxByteStreamTest, GrowLimit) {
// Test GrowWriter feature.
BoxByteStream box_byte_stream;
box_byte_stream.StartBox(mp4::FOURCC_MOOV);
for (int i = 0; i < BoxByteStream::kDefaultBufferLimit; ++i) {
box_byte_stream.WriteU8(0);
}
box_byte_stream.StartBox(mp4::FOURCC_TRAK);
box_byte_stream.WriteU16(0x1617);
box_byte_stream.WriteU32(0);
box_byte_stream.EndBox();
box_byte_stream.EndBox();
std::vector<uint8_t> written_data = box_byte_stream.Flush();
auto reader = base::SpanReader(base::span(written_data));
uint32_t expected_total_size =
8u + uint32_t{BoxByteStream::kDefaultBufferLimit} + 8u + 2u + 4u;
uint32_t value;
reader.ReadU32BigEndian(value);
EXPECT_EQ(expected_total_size, value);
reader.ReadU32BigEndian(value);
EXPECT_EQ(mp4::FOURCC_MOOV, value);
reader.Skip(size_t{BoxByteStream::kDefaultBufferLimit});
reader.ReadU32BigEndian(value);
EXPECT_EQ(14u, value);
reader.ReadU32BigEndian(value);
EXPECT_EQ(mp4::FOURCC_TRAK, value);
uint16_t value16;
reader.ReadU16BigEndian(value16);
EXPECT_EQ(0x1617u, value16);
reader.ReadU32BigEndian(value);
EXPECT_EQ(0u, value);
}
TEST(BoxByteStreamTest, EndBoxAndFlushDiff) {
// Test Flush and EndBox difference.
// EndBox use.
{
BoxByteStream box_byte_stream;
// <parent>
box_byte_stream.StartBox(mp4::FOURCC_MOOV);
box_byte_stream.WriteU64(0);
{
// <child 1>
box_byte_stream.StartBox(mp4::FOURCC_TRAK);
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
box_byte_stream.WriteU32(0x1617);
{
// <grand child 1>
box_byte_stream.StartBox(mp4::FOURCC_MDIA);
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 3u);
box_byte_stream.WriteU16(0);
box_byte_stream.EndBox();
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
}
box_byte_stream.EndBox();
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 1u);
// <child 2>
box_byte_stream.StartBox(mp4::FOURCC_MVEX);
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
box_byte_stream.WriteU32(0);
box_byte_stream.EndBox();
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 1u);
{
// <grand child 2 and the last box>
box_byte_stream.StartBox(mp4::FOURCC_MFRO);
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
// It will have top box's total size.
box_byte_stream.WriteU32(box_byte_stream.size() + 4);
box_byte_stream.EndBox();
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 1u);
}
}
box_byte_stream.EndBox();
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 0u);
// Read.
std::vector<uint8_t> written_data = box_byte_stream.Flush();
auto reader = base::SpanReader(base::span(written_data));
uint32_t parent;
uint32_t fourcc;
constexpr uint32_t kMoovTopBoxSize = 62u;
reader.ReadU32BigEndian(parent);
EXPECT_EQ(kMoovTopBoxSize, parent);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_MOOV, fourcc);
reader.Skip(8u);
uint32_t child_1;
reader.ReadU32BigEndian(child_1);
EXPECT_EQ(22u, child_1);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_TRAK, fourcc);
reader.Skip(4u);
uint32_t grand_child_1;
reader.ReadU32BigEndian(grand_child_1);
EXPECT_EQ(10u, grand_child_1);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_MDIA, fourcc);
reader.Skip(2u);
uint32_t child_2;
reader.ReadU32BigEndian(child_2);
EXPECT_EQ(12u, child_2);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_MVEX, fourcc);
reader.Skip(4u);
uint32_t grand_child_2;
reader.ReadU32BigEndian(grand_child_2);
EXPECT_EQ(12u, grand_child_2);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_MFRO, fourcc);
uint32_t field_top_box_size;
reader.ReadU32BigEndian(field_top_box_size);
EXPECT_EQ(kMoovTopBoxSize, field_top_box_size);
}
}
TEST(BoxByteStreamTest, OffsetPlaceholderAndFlush) {
// Test Flush and EndBox difference.
// EndBox use.
BoxByteStream box_byte_stream;
{
// `moof`
box_byte_stream.StartBox(mp4::FOURCC_MOOF);
// `traf`
box_byte_stream.StartBox(mp4::FOURCC_TRAF);
// 'trun'
box_byte_stream.StartBox(mp4::FOURCC_TRUN);
box_byte_stream.WriteOffsetPlaceholder();
box_byte_stream.WriteU32(0);
box_byte_stream.WriteOffsetPlaceholder();
box_byte_stream.EndBox(); // for FOURCC_TRUN.
box_byte_stream.EndBox(); // for FOURCC_TRAF.
box_byte_stream.EndBox(); // for FOURCC_MOOF.
}
std::vector<uint8_t> data(4000, 0);
{
// `moof`
box_byte_stream.StartBox(mp4::FOURCC_MDAT);
box_byte_stream.FlushCurrentOffset();
box_byte_stream.WriteBytes(data.data(), data.size());
box_byte_stream.EndBox(); // for FOURCC_MDAT.
box_byte_stream.FlushCurrentOffset();
}
std::vector<uint8_t> written_data = box_byte_stream.Flush();
auto reader = base::SpanReader(base::span(written_data));
uint32_t fourcc;
reader.Skip(4u);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_MOOF, fourcc);
reader.Skip(4u);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_TRAF, fourcc);
reader.Skip(4u);
reader.ReadU32BigEndian(fourcc);
EXPECT_EQ(mp4::FOURCC_TRUN, fourcc);
// placeholder.
uint32_t data_offset;
reader.ReadU32BigEndian(data_offset);
EXPECT_EQ(44u, data_offset);
reader.Skip(4u);
reader.ReadU32BigEndian(data_offset);
EXPECT_EQ(44u + data.size(), data_offset);
}
} // namespace media