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 "base/message_loop/message_loop.h"
13 #include "cc/test/test_context_provider.h"
14 #include "cc/test/test_web_graphics_context_3d.h"
15 #include "content/browser/compositor/buffer_queue.h"
16 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
17 #include "content/browser/renderer_host/media/video_capture_controller.h"
18 #include "media/base/video_frame.h"
19 #include "media/base/video_util.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
25 struct PixelFormatAndStorage
{
26 media::VideoCapturePixelFormat pixel_format
;
27 media::VideoPixelStorage pixel_storage
;
30 static const PixelFormatAndStorage kCapturePixelFormatAndStorages
[] = {
31 {media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
, media::PIXEL_STORAGE_CPU
},
32 {media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
, media::PIXEL_STORAGE_CPU
},
33 {media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
, media::PIXEL_STORAGE_TEXTURE
},
34 #if !defined(OS_ANDROID)
35 {media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
,
36 media::PIXEL_STORAGE_GPUMEMORYBUFFER
},
37 {media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
,
38 media::PIXEL_STORAGE_GPUMEMORYBUFFER
},
42 static const int kTestBufferPoolSize
= 3;
44 class VideoCaptureBufferPoolTest
45 : public testing::TestWithParam
<PixelFormatAndStorage
> {
47 // A GpuMemoryBuffer Mock to provide a trivial RGBA buffer as Map() backing.
48 // We need to allocate on ctor and deallocate on dtor so that consecutive
49 // Map()-Unmap() cycles yield the same underlying data pointer.
50 class MockGpuMemoryBuffer
: public gfx::GpuMemoryBuffer
{
52 explicit MockGpuMemoryBuffer(const gfx::Size
& size
)
53 : size_(size
), data_(new uint8
[size_
.GetArea() * 4]), mapped_(false) {}
54 ~MockGpuMemoryBuffer() override
{ delete[] data_
; }
56 bool Map(void** data
) override
{
57 EXPECT_EQ(mapped_
, false);
59 data
[0] = static_cast<void*>(data_
);
62 void Unmap() override
{
63 EXPECT_EQ(mapped_
, true);
66 bool IsMapped() const override
{ return mapped_
; }
67 Format
GetFormat() const override
{ return BGRA_8888
; }
68 void GetStride(int* stride
) const override
{
69 *stride
= size_
.width() * 4;
72 gfx::GpuMemoryBufferId
GetId() const override
{ return 0; }
73 gfx::GpuMemoryBufferHandle
GetHandle() const override
{
74 return gfx::GpuMemoryBufferHandle();
76 ClientBuffer
AsClientBuffer() override
{ return nullptr; }
79 const gfx::Size size_
;
84 #if !defined(OS_ANDROID)
85 // The next two classes are needed to replicate the GpuMemoryBuffer allocation
87 class StubBrowserGpuMemoryBufferManager
88 : public BrowserGpuMemoryBufferManager
{
90 StubBrowserGpuMemoryBufferManager() : BrowserGpuMemoryBufferManager(1) {}
92 scoped_ptr
<gfx::GpuMemoryBuffer
> AllocateGpuMemoryBuffer(
93 const gfx::Size
& size
,
94 gfx::GpuMemoryBuffer::Format format
,
95 gfx::GpuMemoryBuffer::Usage usage
) override
{
96 return make_scoped_ptr(new MockGpuMemoryBuffer(size
));
99 class MockBufferQueue
: public BufferQueue
{
101 MockBufferQueue(scoped_refptr
<cc::ContextProvider
> context_provider
,
102 BrowserGpuMemoryBufferManager
* gpu_memory_buffer_manager
,
104 unsigned int internalformat
)
105 : BufferQueue(context_provider
,
109 gpu_memory_buffer_manager
,
111 MOCK_METHOD4(CopyBufferDamage
,
112 void(int, int, const gfx::Rect
&, const gfx::Rect
&));
116 // This is a generic Buffer tracker
119 Buffer(const scoped_refptr
<VideoCaptureBufferPool
> pool
,
120 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
> buffer_handle
,
122 : id_(id
), pool_(pool
), buffer_handle_(buffer_handle
.Pass()) {}
123 ~Buffer() { pool_
->RelinquishProducerReservation(id()); }
124 int id() const { return id_
; }
125 size_t size() { return buffer_handle_
->size(); }
126 void* data() { return buffer_handle_
->data(); }
130 const scoped_refptr
<VideoCaptureBufferPool
> pool_
;
131 const scoped_ptr
<VideoCaptureBufferPool::BufferHandle
> buffer_handle_
;
134 VideoCaptureBufferPoolTest()
135 : expected_dropped_id_(0),
136 pool_(new VideoCaptureBufferPool(kTestBufferPoolSize
)) {}
138 #if !defined(OS_ANDROID)
139 void SetUp() override
{
140 scoped_refptr
<cc::TestContextProvider
> context_provider
=
141 cc::TestContextProvider::Create(cc::TestWebGraphicsContext3D::Create());
142 context_provider
->BindToCurrentThread();
143 gpu_memory_buffer_manager_
.reset(new StubBrowserGpuMemoryBufferManager
);
144 output_surface_
.reset(new MockBufferQueue(context_provider
,
145 gpu_memory_buffer_manager_
.get(),
146 GL_TEXTURE_2D
, GL_RGBA
));
147 output_surface_
->Initialize();
151 void ExpectDroppedId(int expected_dropped_id
) {
152 expected_dropped_id_
= expected_dropped_id
;
155 scoped_ptr
<Buffer
> ReserveBuffer(const gfx::Size
& dimensions
,
156 PixelFormatAndStorage format_and_storage
) {
157 // To verify that ReserveBuffer always sets |buffer_id_to_drop|,
158 // initialize it to something different than the expected value.
159 int buffer_id_to_drop
= ~expected_dropped_id_
;
160 DVLOG(1) << media::VideoCaptureFormat::PixelStorageToString(
161 format_and_storage
.pixel_storage
) << " "
162 << media::VideoCaptureFormat::PixelFormatToString(
163 format_and_storage
.pixel_format
) << " "
164 << dimensions
.ToString();
165 const int buffer_id
= pool_
->ReserveForProducer(
166 format_and_storage
.pixel_format
, format_and_storage
.pixel_storage
,
167 dimensions
, &buffer_id_to_drop
);
168 if (buffer_id
== VideoCaptureBufferPool::kInvalidId
)
169 return scoped_ptr
<Buffer
>();
170 EXPECT_EQ(expected_dropped_id_
, buffer_id_to_drop
);
172 scoped_ptr
<VideoCaptureBufferPool::BufferHandle
> buffer_handle
=
173 pool_
->GetBufferHandle(buffer_id
);
174 return scoped_ptr
<Buffer
>(
175 new Buffer(pool_
, buffer_handle
.Pass(), buffer_id
));
178 base::MessageLoop loop_
;
179 int expected_dropped_id_
;
180 scoped_refptr
<VideoCaptureBufferPool
> pool_
;
183 #if !defined(OS_ANDROID)
184 scoped_ptr
<StubBrowserGpuMemoryBufferManager
> gpu_memory_buffer_manager_
;
185 scoped_ptr
<MockBufferQueue
> output_surface_
;
188 DISALLOW_COPY_AND_ASSIGN(VideoCaptureBufferPoolTest
);
191 TEST_P(VideoCaptureBufferPoolTest
, BufferPool
) {
192 const gfx::Size size_lo
= gfx::Size(10, 10);
193 const gfx::Size size_hi
= gfx::Size(21, 33);
194 const media::VideoCaptureFormat
format_lo(
195 size_lo
, 0.0, GetParam().pixel_format
, GetParam().pixel_storage
);
196 const media::VideoCaptureFormat
format_hi(
197 size_hi
, 0.0, GetParam().pixel_format
, GetParam().pixel_storage
);
199 // Reallocation won't happen for the first part of the test.
200 ExpectDroppedId(VideoCaptureBufferPool::kInvalidId
);
202 // The buffer pool should have zero utilization before any buffers have been
204 ASSERT_EQ(0.0, pool_
->GetBufferPoolUtilization());
206 scoped_ptr
<Buffer
> buffer1
= ReserveBuffer(size_lo
, GetParam());
207 ASSERT_NE(nullptr, buffer1
.get());
208 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer1
->size());
209 ASSERT_EQ(1.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
210 scoped_ptr
<Buffer
> buffer2
= ReserveBuffer(size_lo
, GetParam());
211 ASSERT_NE(nullptr, buffer2
.get());
212 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer2
->size());
213 ASSERT_EQ(2.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
214 scoped_ptr
<Buffer
> buffer3
= ReserveBuffer(size_lo
, GetParam());
215 ASSERT_NE(nullptr, buffer3
.get());
216 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer3
->size());
217 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
219 // Texture backed Frames cannot be manipulated via mapping.
220 if (GetParam().pixel_storage
!= media::PIXEL_STORAGE_TEXTURE
) {
221 ASSERT_NE(nullptr, buffer1
->data());
222 ASSERT_NE(nullptr, buffer2
->data());
223 ASSERT_NE(nullptr, buffer3
->data());
227 if (buffer1
->data() != nullptr)
228 memset(buffer1
->data(), 0x11, buffer1
->size());
229 if (buffer2
->data() != nullptr)
230 memset(buffer2
->data(), 0x44, buffer2
->size());
231 if (buffer3
->data() != nullptr)
232 memset(buffer3
->data(), 0x77, buffer3
->size());
234 // Fourth buffer should fail. Buffer pool utilization should be at 100%.
235 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
236 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
238 // Release 1st buffer and retry; this should succeed.
240 ASSERT_EQ(2.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
241 scoped_ptr
<Buffer
> buffer4
= ReserveBuffer(size_lo
, GetParam());
242 ASSERT_NE(nullptr, buffer4
.get());
243 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
245 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
246 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
247 ASSERT_FALSE(ReserveBuffer(size_hi
, GetParam())) << "Pool should be empty";
248 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
251 int buffer_id2
= buffer2
->id();
252 ASSERT_EQ(1, buffer_id2
);
253 const int buffer_id3
= buffer3
->id();
254 ASSERT_EQ(2, buffer_id3
);
255 const int buffer_id4
= buffer4
->id();
256 ASSERT_EQ(0, buffer_id4
);
257 void* const memory_pointer3
= buffer3
->data();
260 pool_
->HoldForConsumers(buffer_id3
, 2);
262 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
263 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
265 buffer3
.reset(); // Old producer releases buffer. Should be a noop.
266 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
267 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
268 ASSERT_FALSE(ReserveBuffer(size_hi
, GetParam())) << "Pool should be empty";
269 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
271 buffer2
.reset(); // Active producer releases buffer. Should free a buffer.
273 buffer1
= ReserveBuffer(size_lo
, GetParam());
274 ASSERT_NE(nullptr, buffer1
.get());
275 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
276 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
277 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
279 // First consumer finishes.
280 pool_
->RelinquishConsumerHold(buffer_id3
, 1);
281 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
282 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
284 // Second consumer finishes. This should free that buffer.
285 pool_
->RelinquishConsumerHold(buffer_id3
, 1);
286 buffer3
= ReserveBuffer(size_lo
, GetParam());
287 ASSERT_NE(nullptr, buffer3
.get());
288 ASSERT_EQ(buffer_id3
, buffer3
->id()) << "Buffer ID should be reused.";
289 ASSERT_EQ(memory_pointer3
, buffer3
->data());
290 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
291 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
292 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
294 // Now deliver & consume buffer1, but don't release the buffer.
295 int buffer_id1
= buffer1
->id();
296 ASSERT_EQ(1, buffer_id1
);
297 pool_
->HoldForConsumers(buffer_id1
, 5);
298 pool_
->RelinquishConsumerHold(buffer_id1
, 5);
300 // Even though the consumer is done with the buffer at |buffer_id1|, it cannot
301 // be re-allocated to the producer, because |buffer1| still references it. But
302 // when |buffer1| goes away, we should be able to re-reserve the buffer (and
303 // the ID ought to be the same).
304 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
305 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
306 buffer1
.reset(); // Should free the buffer.
307 ASSERT_EQ(2.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
308 buffer2
= ReserveBuffer(size_lo
, GetParam());
309 ASSERT_NE(nullptr, buffer2
.get());
310 ASSERT_EQ(buffer_id1
, buffer2
->id());
311 buffer_id2
= buffer_id1
;
312 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
313 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
314 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
316 // Now try reallocation with different resolutions. We expect reallocation
317 // to occur only when the old buffer is too small.
319 ExpectDroppedId(buffer_id2
);
320 ASSERT_EQ(2.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
321 buffer2
= ReserveBuffer(size_hi
, GetParam());
322 ASSERT_NE(nullptr, buffer2
.get());
323 ASSERT_LE(format_hi
.ImageAllocationSize(), buffer2
->size());
324 ASSERT_EQ(3, buffer2
->id());
325 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
326 void* const memory_pointer_hi
= buffer2
->data();
327 buffer2
.reset(); // Frees it.
328 ExpectDroppedId(VideoCaptureBufferPool::kInvalidId
);
329 ASSERT_EQ(2.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
330 buffer2
= ReserveBuffer(size_lo
, GetParam());
331 void* const memory_pointer_lo
= buffer2
->data();
332 ASSERT_EQ(memory_pointer_hi
, memory_pointer_lo
)
333 << "Decrease in resolution should not reallocate buffer";
334 ASSERT_NE(nullptr, buffer2
.get());
335 ASSERT_EQ(3, buffer2
->id());
336 ASSERT_LE(format_lo
.ImageAllocationSize(), buffer2
->size());
337 ASSERT_EQ(3.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
338 ASSERT_FALSE(ReserveBuffer(size_lo
, GetParam())) << "Pool should be empty";
339 ASSERT_EQ(1.0, pool_
->GetBufferPoolUtilization());
341 // Tear down the pool_, writing into the buffers. The buffer should preserve
342 // the lifetime of the underlying memory.
344 ASSERT_EQ(2.0 / kTestBufferPoolSize
, pool_
->GetBufferPoolUtilization());
348 if (buffer2
->data() != nullptr)
349 memset(buffer2
->data(), 0x22, buffer2
->size());
350 if (buffer4
->data() != nullptr)
351 memset(buffer4
->data(), 0x55, buffer4
->size());
354 if (buffer4
->data() != nullptr)
355 memset(buffer4
->data(), 0x77, buffer4
->size());
359 INSTANTIATE_TEST_CASE_P(,
360 VideoCaptureBufferPoolTest
,
361 testing::ValuesIn(kCapturePixelFormatAndStorages
));
363 } // namespace content