blob: df7d5c3cd2dc371064d55a657668e83589ceb473 [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 <algorithm>
#include <vector>
#include "base/big_endian.h"
#include "base/test/task_environment.h"
#include "media/muxers/box_byte_stream.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();
base::BigEndianReader reader(written_data.data(), written_data.size());
for (auto& data : test_data) {
uint64_t ret_value = 0;
switch (data.type) {
case DataType::kPlaceHolder:
EXPECT_TRUE(reader.ReadU32(reinterpret_cast<uint32_t*>(&ret_value)));
EXPECT_EQ(data.value, ret_value);
EXPECT_TRUE(reader.ReadU32(reinterpret_cast<uint32_t*>(&ret_value)));
EXPECT_EQ(mp4::FOURCC_MOOV, ret_value);
break;
case DataType::kEndBox:
break;
case DataType::kType_8:
EXPECT_TRUE(reader.ReadU8(reinterpret_cast<uint8_t*>(&ret_value)));
EXPECT_EQ(data.value, ret_value);
break;
case DataType::kType_16:
EXPECT_TRUE(reader.ReadU16(reinterpret_cast<uint16_t*>(&ret_value)));
EXPECT_EQ(data.value, ret_value);
break;
case DataType::kType_32:
EXPECT_TRUE(reader.ReadU32(reinterpret_cast<uint32_t*>(&ret_value)));
EXPECT_EQ(data.value, ret_value);
break;
case DataType::kType_64:
EXPECT_TRUE(reader.ReadU64(&ret_value));
EXPECT_EQ(data.value, ret_value);
break;
case DataType::kType_Bytes:
EXPECT_TRUE(reader.ReadBytes(&ret_value, 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.ReadBytes(&ret_value, 1));
EXPECT_EQ(ret_value, 0u);
} else {
std::vector<uint8_t> ret_string;
ret_string.resize(expected_string.size());
EXPECT_TRUE(
reader.ReadBytes(ret_string.data(), expected_string.size()));
EXPECT_EQ(expected_string,
std::string(ret_string.begin(), ret_string.end()));
}
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();
base::BigEndianReader reader(written_data.data(), written_data.size());
uint32_t expected_total_size =
8 + BoxByteStream::kDefaultBufferLimit + 8 + 2 + 4;
uint32_t value;
reader.ReadU32(&value);
EXPECT_EQ(expected_total_size, value);
reader.ReadU32(&value);
EXPECT_EQ(mp4::FOURCC_MOOV, value);
reader.Skip(BoxByteStream::kDefaultBufferLimit);
reader.ReadU32(&value);
EXPECT_EQ(14u, value);
reader.ReadU32(&value);
EXPECT_EQ(mp4::FOURCC_TRAK, value);
uint16_t value16;
reader.ReadU16(&value16);
EXPECT_EQ(0x1617u, value16);
reader.ReadU32(&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);
}
box_byte_stream.EndBox();
EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 0u);
// Read.
std::vector<uint8_t> written_data = box_byte_stream.Flush();
base::BigEndianReader reader(written_data.data(), written_data.size());
uint32_t parent;
uint32_t fourcc;
reader.ReadU32(&parent);
EXPECT_EQ(50u, parent);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_MOOV, fourcc);
reader.Skip(8);
uint32_t child_1;
reader.ReadU32(&child_1);
EXPECT_EQ(22u, child_1);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_TRAK, fourcc);
reader.Skip(4);
uint32_t grand_child_1;
reader.ReadU32(&grand_child_1);
EXPECT_EQ(10u, grand_child_1);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_MDIA, fourcc);
reader.Skip(2);
uint32_t child_2;
reader.ReadU32(&child_2);
EXPECT_EQ(12u, child_2);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_MVEX, fourcc);
}
}
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();
base::BigEndianReader reader(written_data.data(), written_data.size());
uint32_t fourcc;
reader.Skip(4);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_MOOF, fourcc);
reader.Skip(4);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_TRAF, fourcc);
reader.Skip(4);
reader.ReadU32(&fourcc);
EXPECT_EQ(mp4::FOURCC_TRUN, fourcc);
// placeholder.
uint32_t data_offset;
reader.ReadU32(&data_offset);
EXPECT_EQ(44u, data_offset);
reader.Skip(4);
reader.ReadU32(&data_offset);
EXPECT_EQ(44u + data.size(), data_offset);
}
} // namespace media