| // Copyright (c) 2013 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. |
| |
| // Unit test for VideoCaptureBufferPool. |
| |
| #include "content/browser/renderer_host/media/video_capture_buffer_pool.h" |
| |
| #include "base/bind.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "content/browser/renderer_host/media/video_capture_controller.h" |
| #include "media/base/video_frame.h" |
| #include "media/base/video_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| TEST(VideoCaptureBufferPoolTest, BufferPool) { |
| const gfx::Size size = gfx::Size(640, 480); |
| scoped_refptr<media::VideoFrame> non_pool_frame = |
| media::VideoFrame::CreateFrame(media::VideoFrame::YV12, size, |
| gfx::Rect(size), size, base::TimeDelta()); |
| scoped_refptr<VideoCaptureBufferPool> pool = new VideoCaptureBufferPool( |
| media::VideoFrame::AllocationSize(media::VideoFrame::I420, size), 3); |
| |
| ASSERT_EQ(460800u, pool->GetMemorySize()); |
| ASSERT_TRUE(pool->Allocate()); |
| |
| scoped_refptr<media::VideoFrame> frame1 = |
| pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame1.get()); |
| ASSERT_EQ(size, frame1->coded_size()); |
| scoped_refptr<media::VideoFrame> frame2 = |
| pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame2.get()); |
| ASSERT_EQ(size, frame2->coded_size()); |
| scoped_refptr<media::VideoFrame> frame3 = |
| pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame3.get()); |
| |
| // Touch the memory. |
| media::FillYUV(frame1.get(), 0x11, 0x22, 0x33); |
| media::FillYUV(frame2.get(), 0x44, 0x55, 0x66); |
| media::FillYUV(frame3.get(), 0x77, 0x88, 0x99); |
| |
| // Fourth frame should fail. |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| |
| // Release 1st frame and retry; this should succeed. |
| frame1 = NULL; |
| scoped_refptr<media::VideoFrame> frame4 = |
| pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame4.get()); |
| |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| |
| // Validate the IDs |
| int buffer_id2 = |
| pool->RecognizeReservedBuffer(frame2->shared_memory_handle()); |
| ASSERT_LE(0, buffer_id2); |
| int buffer_id3 = |
| pool->RecognizeReservedBuffer(frame3->shared_memory_handle()); |
| ASSERT_LE(0, buffer_id3); |
| int buffer_id4 = |
| pool->RecognizeReservedBuffer(frame4->shared_memory_handle()); |
| ASSERT_LE(0, buffer_id4); |
| int buffer_id_non_pool = |
| pool->RecognizeReservedBuffer(non_pool_frame->shared_memory_handle()); |
| ASSERT_GT(0, buffer_id_non_pool); |
| |
| ASSERT_FALSE(pool->IsAnyBufferHeldForConsumers()); |
| |
| // Deliver a frame. |
| pool->HoldForConsumers(buffer_id3, 2); |
| |
| ASSERT_TRUE(pool->IsAnyBufferHeldForConsumers()); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| frame3 = NULL; // Old producer releases frame. Should be a noop. |
| ASSERT_TRUE(pool->IsAnyBufferHeldForConsumers()); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| frame2 = NULL; // Active producer releases frame. Should free a frame. |
| buffer_id2 = 0; |
| |
| ASSERT_TRUE(pool->IsAnyBufferHeldForConsumers()); |
| frame1 = pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame1.get()); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| ASSERT_TRUE(pool->IsAnyBufferHeldForConsumers()); |
| |
| // First consumer finishes. |
| pool->RelinquishConsumerHold(buffer_id3, 1); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| ASSERT_TRUE(pool->IsAnyBufferHeldForConsumers()); |
| |
| // Second consumer finishes. This should free that frame. |
| pool->RelinquishConsumerHold(buffer_id3, 1); |
| ASSERT_FALSE(pool->IsAnyBufferHeldForConsumers()); |
| frame3 = pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame3.get()); |
| ASSERT_FALSE(pool->IsAnyBufferHeldForConsumers()); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| |
| // Now deliver & consume frame1, but don't release the VideoFrame. |
| int buffer_id1 = |
| pool->RecognizeReservedBuffer(frame1->shared_memory_handle()); |
| ASSERT_LE(0, buffer_id1); |
| pool->HoldForConsumers(buffer_id1, 5); |
| ASSERT_TRUE(pool->IsAnyBufferHeldForConsumers()); |
| pool->RelinquishConsumerHold(buffer_id1, 5); |
| ASSERT_FALSE(pool->IsAnyBufferHeldForConsumers()); |
| |
| // Even though the consumer is done with the buffer at |buffer_id1|, it cannot |
| // be re-allocated to the producer, because |frame1| still references it. But |
| // when |frame1| goes away, we should be able to re-reserve the buffer (and |
| // the ID ought to be the same). |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| frame1 = NULL; // Should free the frame. |
| frame2 = pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame2.get()); |
| ASSERT_EQ(buffer_id1, |
| pool->RecognizeReservedBuffer(frame2->shared_memory_handle())); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| |
| // For good measure, do one more cycle of free/realloc without delivery, now |
| // that this buffer has been through the consumer-hold cycle. |
| frame2 = NULL; |
| frame1 = pool->ReserveI420VideoFrame(size, 0); |
| ASSERT_TRUE(NULL != frame1.get()); |
| ASSERT_EQ(buffer_id1, |
| pool->RecognizeReservedBuffer(frame1->shared_memory_handle())); |
| ASSERT_EQ(NULL, pool->ReserveI420VideoFrame(size, 0).get()) |
| << "Pool should be empty"; |
| |
| // Tear down the pool, writing into the frames. The VideoFrame should |
| // preserve the lifetime of the underlying memory. |
| frame3 = NULL; |
| pool = NULL; |
| |
| // Touch the memory. |
| media::FillYUV(frame1.get(), 0x11, 0x22, 0x33); |
| media::FillYUV(frame4.get(), 0x44, 0x55, 0x66); |
| |
| frame1 = NULL; |
| |
| media::FillYUV(frame4.get(), 0x44, 0x55, 0x66); |
| frame4 = NULL; |
| } |
| |
| } // namespace content |