blob: f01b48967cf6069ccf693a2b0e4693c0b5386b08 [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 <stddef.h>
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/gpu/linux/platform_video_frame_pool.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
namespace {
base::ScopedFD CreateTmpHandle() {
base::FilePath path;
DCHECK(CreateTemporaryFile(&path));
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
DCHECK(file.IsValid());
return base::ScopedFD(file.TakePlatformFile());
}
scoped_refptr<VideoFrame> CreateDmabufVideoFrame(VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) {
base::Optional<VideoFrameLayout> layout =
VideoFrameLayout::Create(format, coded_size);
DCHECK(layout);
std::vector<base::ScopedFD> dmabuf_fds;
for (size_t i = 0; i < VideoFrame::NumPlanes(format); ++i)
dmabuf_fds.push_back(CreateTmpHandle());
return VideoFrame::WrapExternalDmabufs(*layout, visible_rect, natural_size,
std::move(dmabuf_fds), timestamp);
}
} // namespace
class PlatformVideoFramePoolTest
: public ::testing::TestWithParam<VideoPixelFormat> {
public:
using DmabufId = PlatformVideoFramePool::DmabufId;
PlatformVideoFramePoolTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::TimeSource::MOCK_TIME) {
pool_.reset(new PlatformVideoFramePool(
base::BindRepeating(&CreateDmabufVideoFrame), &test_clock_));
pool_->set_parent_task_runner(base::ThreadTaskRunnerHandle::Get());
}
void SetFrameFormat(VideoPixelFormat format) {
gfx::Size coded_size(320, 240);
visible_rect_.set_size(coded_size);
natural_size_ = coded_size;
layout_ = VideoFrameLayout::Create(format, coded_size);
DCHECK(layout_);
pool_->NegotiateFrameFormat(*layout_, visible_rect_, natural_size_);
}
scoped_refptr<VideoFrame> GetFrame(int timestamp_ms) {
scoped_refptr<VideoFrame> frame = pool_->GetFrame();
frame->set_timestamp(base::TimeDelta::FromMilliseconds(timestamp_ms));
EXPECT_EQ(layout_->format(), frame->format());
EXPECT_EQ(layout_->coded_size(), frame->coded_size());
EXPECT_EQ(visible_rect_, frame->visible_rect());
EXPECT_EQ(natural_size_, frame->natural_size());
return frame;
}
void CheckPoolSize(size_t size) const {
EXPECT_EQ(size, pool_->GetPoolSizeForTesting());
}
DmabufId GetDmabufId(const VideoFrame& frame) { return &(frame.DmabufFds()); }
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::SimpleTestTickClock test_clock_;
std::unique_ptr<PlatformVideoFramePool,
std::default_delete<DmabufVideoFramePool>>
pool_;
base::Optional<VideoFrameLayout> layout_;
gfx::Rect visible_rect_;
gfx::Size natural_size_;
};
INSTANTIATE_TEST_SUITE_P(,
PlatformVideoFramePoolTest,
testing::Values(PIXEL_FORMAT_I420,
PIXEL_FORMAT_NV12,
PIXEL_FORMAT_ARGB));
TEST_F(PlatformVideoFramePoolTest, SingleFrameReuse) {
SetFrameFormat(PIXEL_FORMAT_I420);
scoped_refptr<VideoFrame> frame = GetFrame(10);
DmabufId id = GetDmabufId(*frame);
// Clear frame reference to return the frame to the pool.
frame = nullptr;
scoped_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, GetDmabufId(*new_frame));
}
TEST_F(PlatformVideoFramePoolTest, MultipleFrameReuse) {
SetFrameFormat(PIXEL_FORMAT_I420);
scoped_refptr<VideoFrame> frame1 = GetFrame(10);
scoped_refptr<VideoFrame> frame2 = GetFrame(20);
DmabufId id1 = GetDmabufId(*frame1);
DmabufId id2 = GetDmabufId(*frame2);
frame1 = nullptr;
scoped_task_environment_.RunUntilIdle();
frame1 = GetFrame(30);
EXPECT_EQ(id1, GetDmabufId(*frame1));
frame2 = nullptr;
scoped_task_environment_.RunUntilIdle();
frame2 = GetFrame(40);
EXPECT_EQ(id2, GetDmabufId(*frame2));
frame1 = nullptr;
frame2 = nullptr;
scoped_task_environment_.RunUntilIdle();
CheckPoolSize(2u);
}
TEST_F(PlatformVideoFramePoolTest, FormatChange) {
SetFrameFormat(PIXEL_FORMAT_I420);
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;
scoped_task_environment_.RunUntilIdle();
// Verify that both frames are in the pool.
CheckPoolSize(2u);
// Verify that requesting a frame with a different format causes the pool
// to get drained.
SetFrameFormat(PIXEL_FORMAT_I420A);
scoped_refptr<VideoFrame> new_frame = GetFrame(10);
CheckPoolSize(0u);
}
TEST_F(PlatformVideoFramePoolTest, StaleFramesAreExpired) {
SetFrameFormat(PIXEL_FORMAT_I420);
scoped_refptr<VideoFrame> frame_1 = GetFrame(10);
scoped_refptr<VideoFrame> frame_2 = GetFrame(10);
EXPECT_NE(frame_1.get(), frame_2.get());
CheckPoolSize(0u);
// Drop frame and verify that resources are still available for reuse.
frame_1 = nullptr;
scoped_task_environment_.RunUntilIdle();
CheckPoolSize(1u);
// Advance clock far enough to hit stale timer; ensure only frame_1 has its
// resources released.
base::TimeDelta time_forward = base::TimeDelta::FromMinutes(1);
test_clock_.Advance(time_forward);
scoped_task_environment_.FastForwardBy(time_forward);
frame_2 = nullptr;
scoped_task_environment_.RunUntilIdle();
CheckPoolSize(1u);
}
TEST_F(PlatformVideoFramePoolTest, UnwrapVideoFrame) {
SetFrameFormat(PIXEL_FORMAT_I420);
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_TRUE(frame_1->IsSameDmaBufsAs(*frame_2));
scoped_refptr<VideoFrame> frame_3 = GetFrame(20);
EXPECT_NE(pool_->UnwrapFrame(*frame_1), pool_->UnwrapFrame(*frame_3));
EXPECT_FALSE(frame_1->IsSameDmaBufsAs(*frame_3));
}
} // namespace media