blob: ac7bb4ae5b11977c718caff0357a74270b74ba29 [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/bind_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/gpu/chromeos/fourcc.h"
#include "media/video/fake_gpu_memory_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
scoped_refptr<VideoFrame> CreateGpuMemoryBufferVideoFrame(
gpu::GpuMemoryBufferFactory* factory,
VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) {
base::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),
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));
pool_->set_parent_task_runner(base::ThreadTaskRunnerHandle::Get());
}
bool Initialize(const Fourcc& fourcc) {
constexpr gfx::Size kCodedSize(320, 240);
constexpr size_t kNumFrames = 10;
visible_rect_.set_size(kCodedSize);
natural_size_ = kCodedSize;
layout_ = pool_->Initialize(fourcc, kCodedSize, visible_rect_,
natural_size_, kNumFrames);
return !!layout_;
}
scoped_refptr<VideoFrame> GetFrame(int timestamp_ms) {
scoped_refptr<VideoFrame> frame = pool_->GetFrame();
frame->set_timestamp(base::TimeDelta::FromMilliseconds(timestamp_ms));
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_;
base::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, 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, InitializeWithSameFourcc) {
const auto fourcc = Fourcc::FromVideoPixelFormat(GetParam());
ASSERT_TRUE(fourcc.has_value());
ASSERT_TRUE(Initialize(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. The pool should not request new frames.
ASSERT_TRUE(Initialize(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, base::TimeDelta timestamp) {
auto frame = scoped_refptr<VideoFrame>(nullptr);
return frame;
}));
EXPECT_FALSE(Initialize(fourcc.value()));
}
// TODO(akahuang): Add a testcase to verify calling Initialize() only with
// different |max_num_frames|.
} // namespace media