1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Unit test for VideoCaptureBufferPool.
7 #include "content/browser/renderer_host/media/video_capture_buffer_pool.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "cc/test/test_context_provider.h"
13 #include "cc/test/test_web_graphics_context_3d.h"
14 #include "content/browser/compositor/buffer_queue.h"
15 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
16 #include "content/browser/renderer_host/media/video_capture_controller.h"
17 #include "media/base/video_frame.h"
18 #include "media/base/video_util.h"
19 #include "testing/gmock/include/gmock/gmock.h"
20 #include "testing/gtest/include/gtest/gtest.h"
24 static const media::VideoPixelFormat kCaptureFormats
[] = {
25 media::PIXEL_FORMAT_I420
,
26 media::PIXEL_FORMAT_TEXTURE
,
27 #if !defined(OS_ANDROID)
28 media::PIXEL_FORMAT_GPUMEMORYBUFFER
32 class VideoCaptureBufferPoolTest
33 : public testing::TestWithParam
<media::VideoPixelFormat
> {
35 // A GpuMemoryBuffer Mock to provide a trivial RGBA buffer as Map() backing.
36 // We need to allocate on ctor and deallocate on dtor so that consecutive
37 // Map()-Unmap() cycles yield the same underlying data pointer.
38 class MockGpuMemoryBuffer
: public gfx::GpuMemoryBuffer
{
40 explicit MockGpuMemoryBuffer(const gfx::Size
& size
)
41 : size_(size
), data_(new uint8
[size_
.GetArea() * 4]), mapped_(false) {}
42 ~MockGpuMemoryBuffer() override
{ delete[] data_
; }
44 bool Map(void** data
) override
{
45 EXPECT_EQ(mapped_
, false);
47 data
[0] = static_cast<void*>(data_
);
50 void Unmap() override
{
51 EXPECT_EQ(mapped_
, true);
54 bool IsMapped() const override
{ return mapped_
; }
55 Format
GetFormat() const override
{ return BGRA_8888
; }
56 void GetStride(int* stride
) const override
{
57 *stride
= size_
.width() * 4;
60 gfx::GpuMemoryBufferHandle
GetHandle() const override
{
61 return gfx::GpuMemoryBufferHandle();
63 ClientBuffer
AsClientBuffer() override
{ return nullptr; }
66 const gfx::Size size_
;
71 #if !defined(OS_ANDROID)
72 // The next two classes are needed to replicate the GpuMemoryBuffer allocation
74 class StubBrowserGpuMemoryBufferManager
75 : public BrowserGpuMemoryBufferManager
{
77 StubBrowserGpuMemoryBufferManager()
78 : BrowserGpuMemoryBufferManager(nullptr, 1) {}
80 scoped_ptr
<gfx::GpuMemoryBuffer
> AllocateGpuMemoryBuffer(
81 const gfx::Size
& size
,
82 gfx::GpuMemoryBuffer::Format format
,
83 gfx::GpuMemoryBuffer::Usage usage
) override
{
84 return make_scoped_ptr(new MockGpuMemoryBuffer(size
));
87 class MockBufferQueue
: public BufferQueue
{
89 MockBufferQueue(scoped_refptr
<cc::ContextProvider
> context_provider
,
90 BrowserGpuMemoryBufferManager
* gpu_memory_buffer_manager
,
91 unsigned int internalformat
)
92 : BufferQueue(context_provider
,
95 gpu_memory_buffer_manager
,
97 MOCK_METHOD4(CopyBufferDamage
,
98 void(int, int, const gfx::Rect
&, const gfx::Rect
&));
102 // This is a generic Buffer tracker
105 Buffer(const scoped_refptr
<VideoCaptureBufferPool
> pool
,
106 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
> buffer_handle
,
108 : id_(id
), pool_(pool
), buffer_handle_(buffer_handle
.Pass()) {}
109 ~Buffer() { pool_
->RelinquishProducerReservation(id()); }
110 int id() const { return id_
; }
111 size_t size() { return buffer_handle_
->size(); }
112 void* data() { return buffer_handle_
->data(); }
116 const scoped_refptr
<VideoCaptureBufferPool
> pool_
;
117 const scoped_ptr
<VideoCaptureBufferPool::BufferHandle
> buffer_handle_
;
120 VideoCaptureBufferPoolTest()
121 : expected_dropped_id_(0),
122 pool_(new VideoCaptureBufferPool(3)) {}
124 #if !defined(OS_ANDROID)
125 void SetUp() override
{
126 scoped_refptr
<cc::TestContextProvider
> context_provider
=
127 cc::TestContextProvider::Create(cc::TestWebGraphicsContext3D::Create());
128 context_provider
->BindToCurrentThread();
129 gpu_memory_buffer_manager_
.reset(new StubBrowserGpuMemoryBufferManager
);
130 output_surface_
.reset(new MockBufferQueue(
131 context_provider
, gpu_memory_buffer_manager_
.get(), GL_RGBA
));
132 output_surface_
->Initialize();
136 void ExpectDroppedId(int expected_dropped_id
) {
137 expected_dropped_id_
= expected_dropped_id
;
140 scoped_ptr
<Buffer
> ReserveBuffer(const gfx::Size
& dimensions
,
141 media::VideoPixelFormat pixel_format
) {
142 // To verify that ReserveBuffer always sets |buffer_id_to_drop|,
143 // initialize it to something different than the expected value.
144 int buffer_id_to_drop
= ~expected_dropped_id_
;
145 DVLOG(1) << media::VideoCaptureFormat::PixelFormatToString(pixel_format
)
146 << " " << dimensions
.ToString();
148 pool_
->ReserveForProducer(pixel_format
, dimensions
, &buffer_id_to_drop
);
149 if (buffer_id
== VideoCaptureBufferPool::kInvalidId
)
150 return scoped_ptr
<Buffer
>();
151 EXPECT_EQ(expected_dropped_id_
, buffer_id_to_drop
);
153 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
> buffer_handle
=
154 pool_
->GetBufferHandle(buffer_id
);
155 return scoped_ptr
<Buffer
>(
156 new Buffer(pool_
, buffer_handle
.Pass(), buffer_id
));
159 int expected_dropped_id_
;
160 scoped_refptr
<VideoCaptureBufferPool
> pool_
;
163 #if !defined(OS_ANDROID)
164 scoped_ptr
<StubBrowserGpuMemoryBufferManager
> gpu_memory_buffer_manager_
;
165 scoped_ptr
<MockBufferQueue
> output_surface_
;
168 DISALLOW_COPY_AND_ASSIGN(VideoCaptureBufferPoolTest
);
171 TEST_P(VideoCaptureBufferPoolTest
, BufferPool
) {
172 const gfx::Size size_lo
= gfx::Size(10, 10);
173 const gfx::Size size_hi
= gfx::Size(21, 33);
174 const media::VideoCaptureFormat
format_lo(size_lo
, 0.0, GetParam());
175 const media::VideoCaptureFormat
format_hi(size_hi
, 0.0, GetParam());
177 // Reallocation won't happen for the first part of the test.
178 ExpectDroppedId(VideoCaptureBufferPool::kInvalidId
);
180 scoped_ptr
<Buffer
> buffer1
= ReserveBuffer(size_lo
, GetParam());
181 ASSERT_NE(nullptr, buffer1
.get());
182 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer1
->size());
183 scoped_ptr
<Buffer
> buffer2
= ReserveBuffer(size_lo
, GetParam());
184 ASSERT_NE(nullptr, buffer2
.get());
185 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer2
->size());
186 scoped_ptr
<Buffer
> buffer3
= ReserveBuffer(size_lo
, GetParam());
187 ASSERT_NE(nullptr, buffer3
.get());
188 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer3
->size());
190 // Texture backed Frames cannot be manipulated via mapping.
191 if (GetParam() != media::PIXEL_FORMAT_TEXTURE
) {
192 ASSERT_NE(nullptr, buffer1
->data());
193 ASSERT_NE(nullptr, buffer2
->data());
194 ASSERT_NE(nullptr, buffer3
->data());
198 if (buffer1
->data() != nullptr)
199 memset(buffer1
->data(), 0x11, buffer1
->size());
200 if (buffer2
->data() != nullptr)
201 memset(buffer2
->data(), 0x44, buffer2
->size());
202 if (buffer3
->data() != nullptr)
203 memset(buffer3
->data(), 0x77, buffer3
->size());
205 // Fourth buffer should fail.
206 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
208 // Release 1st buffer and retry; this should succeed.
210 scoped_ptr
<Buffer
> buffer4
= ReserveBuffer(size_lo
, GetParam());
211 ASSERT_NE(nullptr, buffer4
.get());
213 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
214 ASSERT_FALSE(ReserveBuffer(size_hi
, GetParam())) << "Pool should be empty";
217 int buffer_id2
= buffer2
->id();
218 ASSERT_EQ(1, buffer_id2
);
219 const int buffer_id3
= buffer3
->id();
220 ASSERT_EQ(2, buffer_id3
);
221 const int buffer_id4
= buffer4
->id();
222 ASSERT_EQ(0, buffer_id4
);
223 void* const memory_pointer3
= buffer3
->data();
226 pool_
->HoldForConsumers(buffer_id3
, 2);
228 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
230 buffer3
.reset(); // Old producer releases buffer. Should be a noop.
231 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
232 ASSERT_FALSE(ReserveBuffer(size_hi
, GetParam())) << "Pool should be empty";
234 buffer2
.reset(); // Active producer releases buffer. Should free a buffer.
236 buffer1
= ReserveBuffer(size_lo
, GetParam());
237 ASSERT_NE(nullptr, buffer1
.get());
238 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
240 // First consumer finishes.
241 pool_
->RelinquishConsumerHold(buffer_id3
, 1);
242 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
244 // Second consumer finishes. This should free that buffer.
245 pool_
->RelinquishConsumerHold(buffer_id3
, 1);
246 buffer3
= ReserveBuffer(size_lo
, GetParam());
247 ASSERT_NE(nullptr, buffer3
.get());
248 ASSERT_EQ(buffer_id3
, buffer3
->id()) << "Buffer ID should be reused.";
249 ASSERT_EQ(memory_pointer3
, buffer3
->data());
250 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
252 // Now deliver & consume buffer1, but don't release the buffer.
253 int buffer_id1
= buffer1
->id();
254 ASSERT_EQ(1, buffer_id1
);
255 pool_
->HoldForConsumers(buffer_id1
, 5);
256 pool_
->RelinquishConsumerHold(buffer_id1
, 5);
258 // Even though the consumer is done with the buffer at |buffer_id1|, it cannot
259 // be re-allocated to the producer, because |buffer1| still references it. But
260 // when |buffer1| goes away, we should be able to re-reserve the buffer (and
261 // the ID ought to be the same).
262 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
263 buffer1
.reset(); // Should free the buffer.
264 buffer2
= ReserveBuffer(size_lo
, GetParam());
265 ASSERT_NE(nullptr, buffer2
.get());
266 ASSERT_EQ(buffer_id1
, buffer2
->id());
267 buffer_id2
= buffer_id1
;
268 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
270 // Now try reallocation with different resolutions. We expect reallocation
271 // to occur only when the old buffer is too small.
273 ExpectDroppedId(buffer_id2
);
274 buffer2
= ReserveBuffer(size_hi
, GetParam());
275 ASSERT_NE(nullptr, buffer2
.get());
276 ASSERT_LE(format_hi
.ImageAllocationSize(), buffer2
->size());
277 ASSERT_EQ(3, buffer2
->id());
278 void* const memory_pointer_hi
= buffer2
->data();
279 buffer2
.reset(); // Frees it.
280 ExpectDroppedId(VideoCaptureBufferPool::kInvalidId
);
281 buffer2
= ReserveBuffer(size_lo
, GetParam());
282 void* const memory_pointer_lo
= buffer2
->data();
283 ASSERT_EQ(memory_pointer_hi
, memory_pointer_lo
)
284 << "Decrease in resolution should not reallocate buffer";
285 ASSERT_NE(nullptr, buffer2
.get());
286 ASSERT_EQ(3, buffer2
->id());
287 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer2
->size());
288 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
290 // Tear down the pool_, writing into the buffers. The buffer should preserve
291 // the lifetime of the underlying memory.
296 if (buffer2
->data() != nullptr)
297 memset(buffer2
->data(), 0x22, buffer2
->size());
298 if (buffer4
->data() != nullptr)
299 memset(buffer4
->data(), 0x55, buffer4
->size());
302 if (buffer4
->data() != nullptr)
303 memset(buffer4
->data(), 0x77, buffer4
->size());
307 INSTANTIATE_TEST_CASE_P(,
308 VideoCaptureBufferPoolTest
,
309 testing::ValuesIn(kCaptureFormats
));
311 } // namespace content