Index: media/video/gpu_memory_buffer_video_frame_pool_unittest.cc |
diff --git a/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc b/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc |
index 8374ba699fde41b04e89e0cee5514c14541f3e77..c78f10ceedf601290cd2447e68cc5e1ce47f8219 100644 |
--- a/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc |
+++ b/media/video/gpu_memory_buffer_video_frame_pool_unittest.cc |
@@ -1,4 +1,4 @@ |
-// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Copyright 2016 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. |
@@ -72,14 +72,12 @@ class GpuMemoryBufferVideoFramePoolTest : public ::testing::Test { |
void SetUp() override { |
gles2_.reset(new TestGLES2Interface); |
media_task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner); |
- copy_task_runner_ = make_scoped_refptr(new base::TestSimpleTaskRunner); |
media_task_runner_handle_.reset( |
new base::ThreadTaskRunnerHandle(media_task_runner_)); |
mock_gpu_factories_.reset( |
new MockGpuVideoAcceleratorFactories(gles2_.get())); |
gpu_memory_buffer_pool_.reset(new GpuMemoryBufferVideoFramePool( |
- media_task_runner_, copy_task_runner_.get(), |
- mock_gpu_factories_.get())); |
+ media_task_runner_, mock_gpu_factories_.get())); |
} |
void TearDown() override { |
@@ -90,223 +88,249 @@ class GpuMemoryBufferVideoFramePoolTest : public ::testing::Test { |
void RunUntilIdle() { |
media_task_runner_->RunUntilIdle(); |
- copy_task_runner_->RunUntilIdle(); |
media_task_runner_->RunUntilIdle(); |
} |
- static scoped_refptr<media::VideoFrame> CreateTestYUVVideoFrame( |
- int dimension) { |
+ std::unique_ptr<VideoFrameFuture> CreateFrame(VideoPixelFormat format) { |
const int kDimension = 10; |
- static uint8_t y_data[kDimension * kDimension] = {0}; |
- static uint8_t u_data[kDimension * kDimension / 2] = {0}; |
- static uint8_t v_data[kDimension * kDimension / 2] = {0}; |
- |
- DCHECK_LE(dimension, kDimension); |
- gfx::Size size(dimension, dimension); |
- |
- scoped_refptr<VideoFrame> video_frame = |
- media::VideoFrame::WrapExternalYuvData( |
- media::PIXEL_FORMAT_YV12, // format |
- size, // coded_size |
- gfx::Rect(size), // visible_rect |
- size, // natural_size |
- size.width(), // y_stride |
- size.width() / 2, // u_stride |
- size.width() / 2, // v_stride |
- y_data, // y_data |
- u_data, // u_data |
- v_data, // v_data |
- base::TimeDelta()); // timestamp |
- EXPECT_TRUE(video_frame); |
- return video_frame; |
+ return CreateFrame(format, kDimension); |
+ } |
+ |
+ std::unique_ptr<VideoFrameFuture> CreateFrame(VideoPixelFormat format, |
+ int width) { |
+ gfx::Size coded_size(width, width); |
+ gfx::Rect visible_rect(coded_size); |
+ gfx::Size natural_size(coded_size); |
+ |
+ return gpu_memory_buffer_pool_->CreateFrame( |
+ format, coded_size, visible_rect, natural_size, kNoTimestamp()); |
+ } |
+ |
+ void CheckPoolSize(size_t size) const { |
+ EXPECT_EQ(size, gpu_memory_buffer_pool_->GetPoolSizeForTesting()); |
} |
protected: |
scoped_ptr<MockGpuVideoAcceleratorFactories> mock_gpu_factories_; |
scoped_ptr<GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool_; |
scoped_refptr<base::TestSimpleTaskRunner> media_task_runner_; |
- scoped_refptr<base::TestSimpleTaskRunner> copy_task_runner_; |
// GpuMemoryBufferVideoFramePool uses BindToCurrentLoop(), which requires |
// ThreadTaskRunnerHandle initialization. |
scoped_ptr<base::ThreadTaskRunnerHandle> media_task_runner_handle_; |
scoped_ptr<TestGLES2Interface> gles2_; |
}; |
-void MaybeCreateHardwareFrameCallback( |
- scoped_refptr<VideoFrame>* video_frame_output, |
- const scoped_refptr<VideoFrame>& video_frame) { |
- *video_frame_output = video_frame; |
+TEST_F(GpuMemoryBufferVideoFramePoolTest, FrameInitializedAndZeroed_I420) { |
+ VideoPixelFormat format = PIXEL_FORMAT_I420; |
+ std::unique_ptr<VideoFrameFuture> frame_future = CreateFrame(format); |
+ |
+ for (size_t i = 0; i < VideoFrame::NumPlanes(format); ++i) |
+ EXPECT_EQ(0, frame_future->data(i)[0]); |
+ |
+ scoped_refptr<VideoFrame> frame = frame_future->Release(); |
+ |
+ EXPECT_EQ(format, frame->format()); |
+ EXPECT_EQ(3u, gles2_->gen_textures); |
} |
-TEST_F(GpuMemoryBufferVideoFramePoolTest, VideoFrameOutputFormatUnknown) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- mock_gpu_factories_->SetVideoFrameOutputFormat(PIXEL_FORMAT_UNKNOWN); |
- scoped_refptr<VideoFrame> frame; |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+TEST_F(GpuMemoryBufferVideoFramePoolTest, FrameInitializedAndZeroed_YV12) { |
+ VideoPixelFormat format = PIXEL_FORMAT_YV12; |
+ std::unique_ptr<VideoFrameFuture> frame_future = CreateFrame(format); |
+ |
+ for (size_t i = 0; i < VideoFrame::NumPlanes(format); ++i) |
+ EXPECT_EQ(0, frame_future->data(i)[0]); |
+ |
+ scoped_refptr<VideoFrame> frame = frame_future->Release(); |
+ |
+ // I420 VideoFrame covers both PIXEL_FORMAT_I420 and PIXEL_FORMAT_YV12; |
+ EXPECT_EQ(PIXEL_FORMAT_I420, frame->format()); |
+ EXPECT_EQ(3u, gles2_->gen_textures); |
+} |
+ |
+TEST_F(GpuMemoryBufferVideoFramePoolTest, SimpleFrameReuse) { |
+ VideoPixelFormat format = PIXEL_FORMAT_I420; |
+ std::unique_ptr<VideoFrameFuture> frame_future = CreateFrame(format); |
+ |
+ const uint8_t* old_y_data = frame_future->data(VideoFrame::kYPlane); |
+ scoped_refptr<VideoFrame> frame = frame_future->Release(); |
+ |
+ // Clear frame reference to return the frame to the pool. |
+ frame = nullptr; |
+ frame_future = nullptr; |
+ |
RunUntilIdle(); |
- EXPECT_EQ(software_frame.get(), frame.get()); |
+ // Verify that the next frame from the pool uses the same memory. |
+ std::unique_ptr<VideoFrameFuture> new_frame_future = CreateFrame(format); |
+ EXPECT_EQ(old_y_data, new_frame_future->data(VideoFrame::kYPlane)); |
} |
-TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareFrame) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- scoped_refptr<VideoFrame> frame; |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+TEST_F(GpuMemoryBufferVideoFramePoolTest, SimpleFormatChange) { |
+ std::unique_ptr<VideoFrameFuture> frame_future1 = |
+ CreateFrame(PIXEL_FORMAT_YV12); |
+ std::unique_ptr<VideoFrameFuture> frame_future2 = |
+ CreateFrame(PIXEL_FORMAT_YV12); |
+ |
+ scoped_refptr<VideoFrame> frame1 = frame_future1->Release(); |
+ scoped_refptr<VideoFrame> frame2 = frame_future2->Release(); |
+ // Clear frame references to return the frames to the pool. |
+ frame1 = nullptr; |
+ frame2 = nullptr; |
+ frame_future1 = nullptr; |
+ frame_future2 = nullptr; |
+ |
+ // Verify that both frames are in the pool. |
+ CheckPoolSize(2u); |
RunUntilIdle(); |
+ CheckPoolSize(2u); |
- EXPECT_NE(software_frame.get(), frame.get()); |
- EXPECT_EQ(3u, gles2_->gen_textures); |
+ // Verify that requesting a frame with a different format causes the pool |
+ // to get drained. |
+ std::unique_ptr<VideoFrameFuture> new_frame_future = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ CheckPoolSize(1u); |
} |
-TEST_F(GpuMemoryBufferVideoFramePoolTest, ReuseFirstResource) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- scoped_refptr<VideoFrame> frame; |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+TEST_F(GpuMemoryBufferVideoFramePoolTest, FrameValidAfterPoolDestruction) { |
+ std::unique_ptr<VideoFrameFuture> frame_future = |
+ CreateFrame(PIXEL_FORMAT_YV12); |
+ |
+ // Destroy the pool. |
+ gpu_memory_buffer_pool_.reset(); |
+ |
RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame.get()); |
- gpu::Mailbox mailbox = frame->mailbox_holder(0).mailbox; |
- const gpu::SyncToken sync_token = frame->mailbox_holder(0).sync_token; |
+ // Write to the Y plane. The memory tools should detect a |
+ // use-after-free if the storage was actually removed by pool destruction. |
+ memset(frame_future->data(VideoFrame::kYPlane), 0xff, |
+ frame_future->stride(VideoFrame::kYPlane)); |
+} |
+ |
+TEST_F(GpuMemoryBufferVideoFramePoolTest, ReuseFirstResource) { |
+ std::unique_ptr<VideoFrameFuture> frame_future1 = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> frame1 = frame_future1->Release(); |
+ gpu::Mailbox mailbox = frame1->mailbox_holder(0).mailbox; |
+ const gpu::SyncToken sync_token = frame1->mailbox_holder(0).sync_token; |
EXPECT_EQ(3u, gles2_->gen_textures); |
- scoped_refptr<VideoFrame> frame2; |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame2)); |
- RunUntilIdle(); |
+ std::unique_ptr<VideoFrameFuture> frame_future2 = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> frame2 = frame_future2->Release(); |
- EXPECT_NE(software_frame.get(), frame2.get()); |
+ EXPECT_NE(frame1.get(), frame2.get()); |
EXPECT_NE(mailbox, frame2->mailbox_holder(0).mailbox); |
EXPECT_EQ(6u, gles2_->gen_textures); |
- frame = nullptr; |
+ frame1 = nullptr; |
frame2 = nullptr; |
+ frame_future1 = nullptr; |
+ frame_future2 = nullptr; |
RunUntilIdle(); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
- RunUntilIdle(); |
+ std::unique_ptr<VideoFrameFuture> new_frame_future = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> new_frame = new_frame_future->Release(); |
- EXPECT_NE(software_frame.get(), frame.get()); |
EXPECT_EQ(6u, gles2_->gen_textures); |
- EXPECT_EQ(frame->mailbox_holder(0).mailbox, mailbox); |
- EXPECT_NE(frame->mailbox_holder(0).sync_token, sync_token); |
+ EXPECT_EQ(new_frame->mailbox_holder(0).mailbox, mailbox); |
+ EXPECT_NE(new_frame->mailbox_holder(0).sync_token, sync_token); |
} |
TEST_F(GpuMemoryBufferVideoFramePoolTest, DoNotReuseInUse) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- scoped_refptr<VideoFrame> frame; |
- scoped_refptr<VideoFrame> frame2; |
- |
- // Allocate a frame. |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
- RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame.get()); |
- gpu::Mailbox mailbox = frame->mailbox_holder(0).mailbox; |
- const gpu::SyncToken sync_token = frame->mailbox_holder(0).sync_token; |
+ std::unique_ptr<VideoFrameFuture> frame_future1 = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> frame1 = frame_future1->Release(); |
+ gpu::Mailbox mailbox = frame1->mailbox_holder(0).mailbox; |
+ const gpu::SyncToken sync_token = frame1->mailbox_holder(0).sync_token; |
EXPECT_EQ(3u, gles2_->gen_textures); |
- // Allocate a second frame. |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame2)); |
- RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame2.get()); |
+ std::unique_ptr<VideoFrameFuture> frame_future2 = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> frame2 = frame_future2->Release(); |
+ |
+ EXPECT_NE(frame1.get(), frame2.get()); |
EXPECT_NE(mailbox, frame2->mailbox_holder(0).mailbox); |
EXPECT_EQ(6u, gles2_->gen_textures); |
- // Allow the frames to be recycled. |
- frame = nullptr; |
+ frame1 = nullptr; |
frame2 = nullptr; |
+ frame_future1 = nullptr; |
+ frame_future2 = nullptr; |
RunUntilIdle(); |
// Set all buffers to be in use, so the next hardware frame will require |
// a new allocation. |
mock_gpu_factories_->SetGpuMemoryBuffersInUseByMacOSWindowServer(true); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+ std::unique_ptr<VideoFrameFuture> new_frame_future = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> new_frame = new_frame_future->Release(); |
RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame.get()); |
EXPECT_EQ(9u, gles2_->gen_textures); |
- EXPECT_NE(frame->mailbox_holder(0).mailbox, mailbox); |
- EXPECT_NE(frame->mailbox_holder(0).sync_token, sync_token); |
+ EXPECT_NE(new_frame->mailbox_holder(0).mailbox, mailbox); |
+ EXPECT_NE(new_frame->mailbox_holder(0).sync_token, sync_token); |
// Set the buffers no longer in use, so no new allocations will be made. |
mock_gpu_factories_->SetGpuMemoryBuffersInUseByMacOSWindowServer(false); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame2)); |
+ std::unique_ptr<VideoFrameFuture> new_frame_future2 = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ scoped_refptr<VideoFrame> new_frame2 = new_frame_future2->Release(); |
RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame2.get()); |
EXPECT_EQ(9u, gles2_->gen_textures); |
- EXPECT_NE(frame->mailbox_holder(0).mailbox, mailbox); |
- EXPECT_NE(frame->mailbox_holder(0).sync_token, sync_token); |
+ EXPECT_EQ(new_frame2->mailbox_holder(0).mailbox, mailbox); |
+ EXPECT_NE(new_frame2->mailbox_holder(0).sync_token, sync_token); |
} |
TEST_F(GpuMemoryBufferVideoFramePoolTest, DropResourceWhenSizeIsDifferent) { |
- scoped_refptr<VideoFrame> frame; |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- CreateTestYUVVideoFrame(10), |
- base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
- RunUntilIdle(); |
- |
+ VideoPixelFormat format = PIXEL_FORMAT_I420; |
+ std::unique_ptr<VideoFrameFuture> frame_future = CreateFrame(format); |
+ scoped_refptr<VideoFrame> frame = frame_future->Release(); |
EXPECT_EQ(3u, gles2_->gen_textures); |
+ // Clear frame reference to return the frame to the pool. |
frame = nullptr; |
+ frame_future = nullptr; |
+ |
RunUntilIdle(); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- CreateTestYUVVideoFrame(4), |
- base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
- RunUntilIdle(); |
+ |
+ std::unique_ptr<VideoFrameFuture> new_frame_future = CreateFrame(format, 32); |
+ scoped_refptr<VideoFrame> new_frame = new_frame_future->Release(); |
EXPECT_EQ(6u, gles2_->gen_textures); |
} |
TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareUYUVFrame) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- scoped_refptr<VideoFrame> frame; |
- mock_gpu_factories_->SetVideoFrameOutputFormat(PIXEL_FORMAT_UYVY); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+ std::unique_ptr<VideoFrameFuture> frame_future = |
+ CreateFrame(PIXEL_FORMAT_UYVY); |
+ scoped_refptr<VideoFrame> frame = frame_future->Release(); |
- RunUntilIdle(); |
- |
- EXPECT_NE(software_frame.get(), frame.get()); |
EXPECT_EQ(1u, gles2_->gen_textures); |
EXPECT_TRUE(frame->metadata()->IsTrue( |
media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED)); |
} |
TEST_F(GpuMemoryBufferVideoFramePoolTest, CreateOneHardwareNV12Frame) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- scoped_refptr<VideoFrame> frame; |
- mock_gpu_factories_->SetVideoFrameOutputFormat(PIXEL_FORMAT_NV12); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+ std::unique_ptr<VideoFrameFuture> frame_future = |
+ CreateFrame(PIXEL_FORMAT_NV12); |
+ scoped_refptr<VideoFrame> frame = frame_future->Release(); |
RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame.get()); |
EXPECT_EQ(1u, gles2_->gen_textures); |
EXPECT_TRUE(frame->metadata()->IsTrue( |
media::VideoFrameMetadata::READ_LOCK_FENCES_ENABLED)); |
} |
-// AllocateGpuMemoryBuffer can return null (e.g: when the GPU process is down). |
-// This test checks that in that case we don't crash and still create the |
-// textures. |
+// CreateFrame can return null (e.g: when the GPU process is down). |
TEST_F(GpuMemoryBufferVideoFramePoolTest, AllocateGpuMemoryBufferFail) { |
- scoped_refptr<VideoFrame> software_frame = CreateTestYUVVideoFrame(10); |
- scoped_refptr<VideoFrame> frame; |
mock_gpu_factories_->SetFailToAllocateGpuMemoryBufferForTesting(true); |
- gpu_memory_buffer_pool_->MaybeCreateHardwareFrame( |
- software_frame, base::Bind(MaybeCreateHardwareFrameCallback, &frame)); |
+ std::unique_ptr<VideoFrameFuture> frame_future = |
+ CreateFrame(PIXEL_FORMAT_I420); |
+ EXPECT_EQ(nullptr, frame_future.get()); |
RunUntilIdle(); |
- EXPECT_NE(software_frame.get(), frame.get()); |
- EXPECT_EQ(3u, gles2_->gen_textures); |
+ EXPECT_EQ(0u, gles2_->gen_textures); |
} |
} // namespace media |