blob: e5cf8efbce18e8ab4e59a38fbb61519ea713c7eb [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/chromeos/platform_video_frame_pool.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/callback_helpers.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/format_utils.h"
#include "media/base/video_util.h"
#include "media/gpu/chromeos/fourcc.h"
#include "media/video/fake_gpu_memory_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
template <uint64_t modifier>
scoped_refptr<VideoFrame> CreateGpuMemoryBufferVideoFrame(
gpu::GpuMemoryBufferFactory* factory,
VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
bool use_protected,
base::TimeDelta timestamp) {
absl::optional<gfx::BufferFormat> gfx_format =
VideoPixelFormatToGfxBufferFormat(format);
DCHECK(gfx_format);
const gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes] = {};
return VideoFrame::WrapExternalGpuMemoryBuffer(
visible_rect, natural_size,
std::make_unique<FakeGpuMemoryBuffer>(coded_size, *gfx_format, modifier),
mailbox_holders, base::NullCallback(), timestamp);
}
} // namespace
class PlatformVideoFramePoolTest
: public ::testing::TestWithParam<VideoPixelFormat> {
public:
PlatformVideoFramePoolTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
pool_(new PlatformVideoFramePool(nullptr)) {
SetCreateFrameCB(
base::BindRepeating(&CreateGpuMemoryBufferVideoFrame<
gfx::NativePixmapHandle::kNoModifier>));
pool_->set_parent_task_runner(base::ThreadTaskRunnerHandle::Get());
}
bool Initialize(const Fourcc& fourcc) {
constexpr gfx::Size kCodedSize(320, 240);
return Initialize(kCodedSize, /*visible_rect=*/gfx::Rect(kCodedSize),
fourcc);
}
bool Initialize(const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const Fourcc& fourcc) {
constexpr size_t kNumFrames = 10;
visible_rect_ = visible_rect;
natural_size_ = visible_rect.size();
auto status_or_layout = pool_->Initialize(fourcc, coded_size, visible_rect_,
natural_size_, kNumFrames,
/*use_protected=*/false);
if (status_or_layout.has_error()) {
return false;
}
layout_ = std::move(status_or_layout).value();
return true;
}
scoped_refptr<VideoFrame> GetFrame(int timestamp_ms) {
scoped_refptr<VideoFrame> frame = pool_->GetFrame();
frame->set_timestamp(base::Milliseconds(timestamp_ms));
EXPECT_EQ(layout_->modifier(), frame->layout().modifier());
EXPECT_EQ(layout_->fourcc(),
*Fourcc::FromVideoPixelFormat(frame->format()));
EXPECT_EQ(layout_->size(), frame->coded_size());
EXPECT_EQ(visible_rect_, frame->visible_rect());
EXPECT_EQ(natural_size_, frame->natural_size());
return frame;
}
void SetCreateFrameCB(PlatformVideoFramePool::CreateFrameCB cb) {
pool_->create_frame_cb_ = cb;
}
protected:
base::test::TaskEnvironment task_environment_;
std::unique_ptr<PlatformVideoFramePool> pool_;
absl::optional<GpuBufferLayout> layout_;
gfx::Rect visible_rect_;
gfx::Size natural_size_;
};
INSTANTIATE_TEST_SUITE_P(All,
PlatformVideoFramePoolTest,
testing::Values(PIXEL_FORMAT_YV12,
PIXEL_FORMAT_NV12,
PIXEL_FORMAT_ARGB,
PIXEL_FORMAT_P016LE));
TEST_P(PlatformVideoFramePoolTest, SingleFrameReuse) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
ASSERT_TRUE(Initialize(fourcc.value()));
scoped_refptr<VideoFrame> frame = GetFrame(10);
gfx::GpuMemoryBufferId id =
PlatformVideoFramePool::GetGpuMemoryBufferId(*frame);
// Clear frame reference to return the frame to the pool.
frame = nullptr;
task_environment_.RunUntilIdle();
// Verify that the next frame from the pool uses the same memory.
scoped_refptr<VideoFrame> new_frame = GetFrame(20);
EXPECT_EQ(id, PlatformVideoFramePool::GetGpuMemoryBufferId(*new_frame));
}
TEST_P(PlatformVideoFramePoolTest, MultipleFrameReuse) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
ASSERT_TRUE(Initialize(fourcc.value()));
scoped_refptr<VideoFrame> frame1 = GetFrame(10);
scoped_refptr<VideoFrame> frame2 = GetFrame(20);
gfx::GpuMemoryBufferId id1 =
PlatformVideoFramePool::GetGpuMemoryBufferId(*frame1);
gfx::GpuMemoryBufferId id2 =
PlatformVideoFramePool::GetGpuMemoryBufferId(*frame2);
frame1 = nullptr;
task_environment_.RunUntilIdle();
frame1 = GetFrame(30);
EXPECT_EQ(id1, PlatformVideoFramePool::GetGpuMemoryBufferId(*frame1));
frame2 = nullptr;
task_environment_.RunUntilIdle();
frame2 = GetFrame(40);
EXPECT_EQ(id2, PlatformVideoFramePool::GetGpuMemoryBufferId(*frame2));
frame1 = nullptr;
frame2 = nullptr;
task_environment_.RunUntilIdle();
EXPECT_EQ(2u, pool_->GetPoolSizeForTesting());
}
TEST_P(PlatformVideoFramePoolTest, InitializeWithDifferentFourcc) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
ASSERT_TRUE(Initialize(fourcc.value()));
scoped_refptr<VideoFrame> frame_a = GetFrame(10);
scoped_refptr<VideoFrame> frame_b = GetFrame(10);
// Clear frame references to return the frames to the pool.
frame_a = nullptr;
frame_b = nullptr;
task_environment_.RunUntilIdle();
// Verify that both frames are in the pool.
EXPECT_EQ(2u, pool_->GetPoolSizeForTesting());
// Verify that requesting a frame with a different format causes the pool
// to get drained.
const Fourcc different_fourcc(Fourcc::XR24);
ASSERT_NE(fourcc, different_fourcc);
ASSERT_TRUE(Initialize(different_fourcc));
scoped_refptr<VideoFrame> new_frame = GetFrame(10);
EXPECT_EQ(0u, pool_->GetPoolSizeForTesting());
}
TEST_P(PlatformVideoFramePoolTest, InitializeWithDifferentUsableArea) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
constexpr gfx::Size kCodedSize(640, 368);
constexpr gfx::Rect kInitialVisibleRect(10, 20, 300, 200);
ASSERT_TRUE(Initialize(kCodedSize, kInitialVisibleRect, fourcc.value()));
scoped_refptr<VideoFrame> frame_a = GetFrame(10);
scoped_refptr<VideoFrame> frame_b = GetFrame(10);
// Clear frame references to return the frames to the pool.
frame_a = nullptr;
frame_b = nullptr;
task_environment_.RunUntilIdle();
// Verify that both frames are in the pool.
EXPECT_EQ(2u, pool_->GetPoolSizeForTesting());
// Verify that requesting a frame with a different "usable area" causes the
// pool to get drained. The usable area is the area of the buffer starting at
// (0, 0) and extending to the bottom-right corner of the visible rectangle.
// It corresponds to the area used to import the video frame into a graphics
// API or to create the DRM framebuffer for hardware overlay purposes.
constexpr gfx::Rect kDifferentVisibleRect(5, 15, 300, 200);
ASSERT_EQ(kDifferentVisibleRect.size(), natural_size_);
ASSERT_NE(GetRectSizeFromOrigin(kInitialVisibleRect),
GetRectSizeFromOrigin(kDifferentVisibleRect));
ASSERT_TRUE(Initialize(kCodedSize, kDifferentVisibleRect, fourcc.value()));
scoped_refptr<VideoFrame> new_frame = GetFrame(10);
EXPECT_EQ(0u, pool_->GetPoolSizeForTesting());
}
TEST_P(PlatformVideoFramePoolTest, InitializeWithDifferentCodedSize) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
constexpr gfx::Size kInitialCodedSize(640, 368);
constexpr gfx::Rect kVisibleRect(10, 20, 300, 200);
ASSERT_TRUE(Initialize(kInitialCodedSize, kVisibleRect, fourcc.value()));
scoped_refptr<VideoFrame> frame_a = GetFrame(10);
scoped_refptr<VideoFrame> frame_b = GetFrame(10);
// Clear frame references to return the frames to the pool.
frame_a = nullptr;
frame_b = nullptr;
task_environment_.RunUntilIdle();
// Verify that both frames are in the pool.
EXPECT_EQ(2u, pool_->GetPoolSizeForTesting());
// Verify that requesting a frame with a different coded size causes the pool
// to get drained.
constexpr gfx::Size kDifferentCodedSize(624, 368);
ASSERT_TRUE(Initialize(kDifferentCodedSize, kVisibleRect, fourcc.value()));
scoped_refptr<VideoFrame> new_frame = GetFrame(10);
EXPECT_EQ(0u, pool_->GetPoolSizeForTesting());
}
TEST_P(PlatformVideoFramePoolTest, UnwrapVideoFrame) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
ASSERT_TRUE(Initialize(fourcc.value()));
scoped_refptr<VideoFrame> frame_1 = GetFrame(10);
scoped_refptr<VideoFrame> frame_2 = VideoFrame::WrapVideoFrame(
frame_1, frame_1->format(), frame_1->visible_rect(),
frame_1->natural_size());
EXPECT_EQ(pool_->UnwrapFrame(*frame_1), pool_->UnwrapFrame(*frame_2));
EXPECT_EQ(frame_1->GetGpuMemoryBuffer(), frame_2->GetGpuMemoryBuffer());
scoped_refptr<VideoFrame> frame_3 = GetFrame(20);
EXPECT_NE(pool_->UnwrapFrame(*frame_1), pool_->UnwrapFrame(*frame_3));
EXPECT_NE(frame_1->GetGpuMemoryBuffer(), frame_3->GetGpuMemoryBuffer());
}
TEST_P(PlatformVideoFramePoolTest,
InitializeWithSameFourccUsableAreaAndCodedSize) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
constexpr gfx::Size kCodedSize(640, 368);
constexpr gfx::Rect kInitialVisibleRect(kCodedSize);
ASSERT_TRUE(Initialize(kCodedSize, kInitialVisibleRect, fourcc.value()));
scoped_refptr<VideoFrame> frame1 = GetFrame(10);
gfx::GpuMemoryBufferId id1 =
PlatformVideoFramePool::GetGpuMemoryBufferId(*frame1);
// Clear frame references to return the frames to the pool.
frame1 = nullptr;
task_environment_.RunUntilIdle();
// Request frame with the same format, coded size, and "usable area." To make
// things interesting, we change the visible rect while keeping the
// "usable area" constant. The usable area is the area of the buffer starting
// at (0, 0) and extending to the bottom-right corner of the visible
// rectangle. It corresponds to the area used to import the video frame into a
// graphics API or to create the DRM framebuffer for hardware overlay
// purposes. The pool should not request new frames.
constexpr gfx::Rect kDifferentVisibleRect(10, 20, 630, 348);
ASSERT_EQ(GetRectSizeFromOrigin(kInitialVisibleRect),
GetRectSizeFromOrigin(kDifferentVisibleRect));
ASSERT_TRUE(Initialize(kCodedSize, kDifferentVisibleRect, fourcc.value()));
scoped_refptr<VideoFrame> frame2 = GetFrame(20);
gfx::GpuMemoryBufferId id2 =
PlatformVideoFramePool::GetGpuMemoryBufferId(*frame2);
EXPECT_EQ(id1, id2);
}
TEST_P(PlatformVideoFramePoolTest, InitializeFail) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
SetCreateFrameCB(base::BindRepeating(
[](gpu::GpuMemoryBufferFactory* factory, VideoPixelFormat format,
const gfx::Size& coded_size, const gfx::Rect& visible_rect,
const gfx::Size& natural_size, bool use_protected,
base::TimeDelta timestamp) {
auto frame = scoped_refptr<VideoFrame>(nullptr);
return frame;
}));
EXPECT_FALSE(Initialize(fourcc.value()));
}
TEST_P(PlatformVideoFramePoolTest, ModifierIsPassed) {
const uint64_t kSampleModifier = 0x001234567890abcdULL;
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
SetCreateFrameCB(
base::BindRepeating(&CreateGpuMemoryBufferVideoFrame<kSampleModifier>));
ASSERT_TRUE(Initialize(fourcc.value()));
EXPECT_EQ(layout_->modifier(), kSampleModifier);
EXPECT_TRUE(GetFrame(10));
}
// TODO(akahuang): Add a testcase to verify calling Initialize() only with
// different |max_num_frames|.
} // namespace media