blob: 86baab97eb3a42b2293e19c2d46f63df3ab956ff [file] [log] [blame]
// Copyright 2021 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/gpu/v4l2/v4l2_stateful_workaround.h"
#include <vector>
#include "base/containers/span.h"
#include "base/files/memory_mapped_file.h"
#include "media/base/decoder_buffer.h"
#include "media/base/test_data_util.h"
#include "media/filters/ivf_parser.h"
#include "media/filters/vp9_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
namespace media {
namespace {
// Append |frame_sizes| to |decoder_buffer|'s side_data.
void AppendSideData(DecoderBuffer& decoder_buffer,
const std::vector<uint32_t>& frame_sizes) {
const uint8_t* side_data =
reinterpret_cast<const uint8_t*>(frame_sizes.data());
size_t side_data_size =
frame_sizes.size() * sizeof(uint32_t) / sizeof(uint8_t);
decoder_buffer.CopySideDataFrom(side_data, side_data_size);
}
} // namespace
// Checks superframe index size is expected.
TEST(V4L2StatefulWorkaroundTest, CheckSuperFrameIndexSize) {
constexpr uint32_t kFrameSizes[] = {
0x10, // 1 byte
0x1020, // 2 byte
0x010203, // 3 byte
0x01020304 // 4 byte
};
constexpr size_t kNumFrames = std::size(kFrameSizes);
for (size_t mask = 1; mask < (1 << kNumFrames) - 1; mask++) {
size_t buffer_size = 0;
size_t expected_bytes_per_framesize = 0;
std::vector<uint32_t> frame_sizes;
for (size_t i = 0; i < kNumFrames; i++) {
if (!(mask & (1 << i)))
continue;
frame_sizes.push_back(kFrameSizes[i]);
buffer_size += kFrameSizes[i];
expected_bytes_per_framesize = i + 1;
}
// Since we don't care the buffer content, the buffer is zero except VP9
// frame marker.
std::vector<uint8_t> tmp_buffer(buffer_size);
size_t offset = 0;
for (const uint32_t frame_size : frame_sizes) {
uint8_t* header = tmp_buffer.data() + offset;
*header = 0x8f;
offset += frame_size;
}
auto decoder_buffer =
DecoderBuffer::CopyFrom(tmp_buffer.data(), tmp_buffer.size());
AppendSideData(*decoder_buffer, frame_sizes);
AppendVP9SuperFrameIndex(decoder_buffer);
if (frame_sizes.size() == 1) {
EXPECT_EQ(decoder_buffer->data_size(), buffer_size);
continue;
}
EXPECT_GT(decoder_buffer->data_size(), buffer_size);
size_t superframe_index_size = decoder_buffer->data_size() - buffer_size;
EXPECT_EQ(superframe_index_size,
2 + expected_bytes_per_framesize * frame_sizes.size());
}
}
TEST(V4L2StatefulWorkaroundTest, ParseAppendedSuperFrameIndex) {
auto stream = std::make_unique<base::MemoryMappedFile>();
ASSERT_TRUE(stream->Initialize(GetTestDataFilePath("test-25fps.vp9")));
// Read three frames from test-25fps.vp9.
IvfParser ivf_parser;
IvfFileHeader ivf_file_header;
ASSERT_TRUE(ivf_parser.Initialize(stream->data(), stream->length(),
&ivf_file_header));
ASSERT_EQ(ivf_file_header.fourcc, 0x30395056u); // VP90
constexpr size_t kNumBuffers = 3;
std::vector<base::span<const uint8_t>> buffers(3);
for (size_t i = 0; i < kNumBuffers; i++) {
IvfFrameHeader ivf_frame_header;
const uint8_t* ivf_payload;
ASSERT_TRUE(ivf_parser.ParseNextFrame(&ivf_frame_header, &ivf_payload));
buffers[i] = base::make_span(ivf_payload, ivf_frame_header.frame_size);
}
std::vector<uint32_t> frame_sizes;
std::vector<uint8_t> merged_buffer;
for (size_t i = 0; i < kNumBuffers; ++i) {
frame_sizes.push_back(buffers[i].size());
// |merged_buffer| is composed of [0, i] frames.
const size_t offset = merged_buffer.size();
merged_buffer.resize(offset + buffers[i].size());
memcpy(merged_buffer.data() + offset, buffers[i].data(), buffers[i].size());
auto decoder_buffer =
DecoderBuffer::CopyFrom(merged_buffer.data(), merged_buffer.size());
AppendSideData(*decoder_buffer, frame_sizes);
AppendVP9SuperFrameIndex(decoder_buffer);
Vp9Parser vp9_parser(/*parsing_compressed_header=*/false);
vp9_parser.SetStream(decoder_buffer->data(), decoder_buffer->data_size(),
/*stream_config=*/nullptr);
// Parse the merged buffer with the created superframe index.
for (size_t j = 0; j <= i; j++) {
Vp9FrameHeader frame_header{};
gfx::Size allocate_size;
std::unique_ptr<DecryptConfig> frame_decrypt_config;
EXPECT_EQ(vp9_parser.ParseNextFrame(&frame_header, &allocate_size,
&frame_decrypt_config),
Vp9Parser::Result::kOk);
EXPECT_EQ(frame_header.frame_size, buffers[j].size());
// show_frame is 1 if and only if the frame is in the top spatial layer.
EXPECT_EQ(frame_header.show_frame, j == i);
}
}
}
} // namespace media