| 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
|
|
|