1 // Copyright (c) 2012 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 VideoCaptureController.
10 #include "base/bind_helpers.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "content/browser/renderer_host/media/media_stream_provider.h"
16 #include "content/browser/renderer_host/media/video_capture_controller.h"
17 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
18 #include "content/browser/renderer_host/media/video_capture_manager.h"
19 #include "content/common/media/media_stream_options.h"
20 #include "content/public/test/test_browser_thread_bundle.h"
21 #include "media/base/video_frame.h"
22 #include "media/base/video_util.h"
23 #include "media/video/capture/video_capture_types.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 using ::testing::InSequence
;
28 using ::testing::Mock
;
32 class MockVideoCaptureControllerEventHandler
33 : public VideoCaptureControllerEventHandler
{
35 explicit MockVideoCaptureControllerEventHandler(
36 VideoCaptureController
* controller
)
37 : controller_(controller
) {}
38 virtual ~MockVideoCaptureControllerEventHandler() {}
40 // These mock methods are delegated to by our fake implementation of
41 // VideoCaptureControllerEventHandler, to be used in EXPECT_CALL().
42 MOCK_METHOD1(DoBufferCreated
, void(const VideoCaptureControllerID
&));
43 MOCK_METHOD1(DoBufferDestroyed
, void(const VideoCaptureControllerID
&));
44 MOCK_METHOD1(DoBufferReady
, void(const VideoCaptureControllerID
&));
45 MOCK_METHOD1(DoEnded
, void(const VideoCaptureControllerID
&));
46 MOCK_METHOD1(DoError
, void(const VideoCaptureControllerID
&));
48 virtual void OnError(const VideoCaptureControllerID
& id
) OVERRIDE
{
51 virtual void OnBufferCreated(const VideoCaptureControllerID
& id
,
52 base::SharedMemoryHandle handle
,
53 int length
, int buffer_id
) OVERRIDE
{
56 virtual void OnBufferDestroyed(const VideoCaptureControllerID
& id
,
57 int buffer_id
) OVERRIDE
{
58 DoBufferDestroyed(id
);
60 virtual void OnBufferReady(const VideoCaptureControllerID
& id
,
63 const media::VideoCaptureFormat
& format
) OVERRIDE
{
65 base::MessageLoop::current()->PostTask(FROM_HERE
,
66 base::Bind(&VideoCaptureController::ReturnBuffer
,
67 base::Unretained(controller_
), id
, this, buffer_id
));
69 virtual void OnEnded(const VideoCaptureControllerID
& id
) OVERRIDE
{
71 // OnEnded() must respond by (eventually) unregistering the client.
72 base::MessageLoop::current()->PostTask(FROM_HERE
,
73 base::Bind(base::IgnoreResult(&VideoCaptureController::RemoveClient
),
74 base::Unretained(controller_
), id
, this));
77 VideoCaptureController
* controller_
;
81 class VideoCaptureControllerTest
: public testing::Test
{
83 VideoCaptureControllerTest() {}
84 virtual ~VideoCaptureControllerTest() {}
87 static const int kPoolSize
= 3;
89 virtual void SetUp() OVERRIDE
{
90 controller_
.reset(new VideoCaptureController());
91 device_
= controller_
->NewDeviceClient().Pass();
92 client_a_
.reset(new MockVideoCaptureControllerEventHandler(
94 client_b_
.reset(new MockVideoCaptureControllerEventHandler(
98 virtual void TearDown() OVERRIDE
{
99 base::RunLoop().RunUntilIdle();
102 TestBrowserThreadBundle bindle_
;
103 scoped_ptr
<MockVideoCaptureControllerEventHandler
> client_a_
;
104 scoped_ptr
<MockVideoCaptureControllerEventHandler
> client_b_
;
105 scoped_ptr
<VideoCaptureController
> controller_
;
106 scoped_ptr
<media::VideoCaptureDevice::Client
> device_
;
109 DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest
);
112 // A simple test of VideoCaptureController's ability to add, remove, and keep
114 TEST_F(VideoCaptureControllerTest
, AddAndRemoveClients
) {
115 media::VideoCaptureParams session_100
;
116 session_100
.requested_format
= media::VideoCaptureFormat(
117 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
118 media::VideoCaptureParams session_200
= session_100
;
120 media::VideoCaptureParams session_300
= session_100
;
122 media::VideoCaptureParams session_400
= session_100
;
124 // Intentionally use the same route ID for two of the clients: the device_ids
125 // are a per-VideoCaptureHost namespace, and can overlap across hosts.
126 const VideoCaptureControllerID
client_a_route_1(44);
127 const VideoCaptureControllerID
client_a_route_2(30);
128 const VideoCaptureControllerID
client_b_route_1(30);
129 const VideoCaptureControllerID
client_b_route_2(1);
131 // Clients in controller: []
132 ASSERT_EQ(0, controller_
->GetClientCount())
133 << "Client count should initially be zero.";
134 controller_
->AddClient(client_a_route_1
,
136 base::kNullProcessHandle
,
139 // Clients in controller: [A/1]
140 ASSERT_EQ(1, controller_
->GetClientCount())
141 << "Adding client A/1 should bump client count.";;
142 controller_
->AddClient(client_a_route_2
,
144 base::kNullProcessHandle
,
147 // Clients in controller: [A/1, A/2]
148 ASSERT_EQ(2, controller_
->GetClientCount())
149 << "Adding client A/2 should bump client count.";
150 controller_
->AddClient(client_b_route_1
,
152 base::kNullProcessHandle
,
155 // Clients in controller: [A/1, A/2, B/1]
156 ASSERT_EQ(3, controller_
->GetClientCount())
157 << "Adding client B/1 should bump client count.";
159 controller_
->RemoveClient(client_a_route_2
, client_a_
.get()))
160 << "Removing client A/1 should return its session_id.";
161 // Clients in controller: [A/1, B/1]
162 ASSERT_EQ(2, controller_
->GetClientCount());
163 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId
),
164 controller_
->RemoveClient(client_a_route_2
, client_a_
.get()))
165 << "Removing a nonexistant client should fail.";
166 // Clients in controller: [A/1, B/1]
167 ASSERT_EQ(2, controller_
->GetClientCount());
169 controller_
->RemoveClient(client_b_route_1
, client_b_
.get()))
170 << "Removing client B/1 should return its session_id.";
171 // Clients in controller: [A/1]
172 ASSERT_EQ(1, controller_
->GetClientCount());
173 controller_
->AddClient(client_b_route_2
,
175 base::kNullProcessHandle
,
178 // Clients in controller: [A/1, B/2]
180 EXPECT_CALL(*client_a_
, DoEnded(client_a_route_1
)).Times(1);
181 controller_
->StopSession(100); // Session 100 == client A/1
182 Mock::VerifyAndClearExpectations(client_a_
.get());
183 ASSERT_EQ(2, controller_
->GetClientCount())
184 << "Client should be closed but still exist after StopSession.";
185 // Clients in controller: [A/1 (closed, removal pending), B/2]
186 base::RunLoop().RunUntilIdle();
187 // Clients in controller: [B/2]
188 ASSERT_EQ(1, controller_
->GetClientCount())
189 << "Client A/1 should be deleted by now.";
190 controller_
->StopSession(200); // Session 200 does not exist anymore
191 // Clients in controller: [B/2]
192 ASSERT_EQ(1, controller_
->GetClientCount())
193 << "Stopping non-existant session 200 should be a no-op.";
194 controller_
->StopSession(256); // Session 256 never existed.
195 // Clients in controller: [B/2]
196 ASSERT_EQ(1, controller_
->GetClientCount())
197 << "Stopping non-existant session 256 should be a no-op.";
198 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId
),
199 controller_
->RemoveClient(client_a_route_1
, client_a_
.get()))
200 << "Removing already-removed client A/1 should fail.";
201 // Clients in controller: [B/2]
202 ASSERT_EQ(1, controller_
->GetClientCount())
203 << "Removing non-existant session 200 should be a no-op.";
205 controller_
->RemoveClient(client_b_route_2
, client_b_
.get()))
206 << "Removing client B/2 should return its session_id.";
207 // Clients in controller: []
208 ASSERT_EQ(0, controller_
->GetClientCount())
209 << "Client count should return to zero after all clients are gone.";
212 // This test will connect and disconnect several clients while simulating an
213 // active capture device being started and generating frames. It runs on one
214 // thread and is intended to behave deterministically.
215 TEST_F(VideoCaptureControllerTest
, NormalCaptureMultipleClients
) {
216 media::VideoCaptureParams session_100
;
217 session_100
.requested_format
= media::VideoCaptureFormat(
218 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
220 media::VideoCaptureParams session_200
= session_100
;
222 media::VideoCaptureParams session_300
= session_100
;
224 media::VideoCaptureParams session_1
= session_100
;
226 gfx::Size
capture_resolution(444, 200);
228 // The device format needn't match the VideoCaptureParams (the camera can do
229 // what it wants). Pick something random.
230 media::VideoCaptureFormat
device_format(
231 gfx::Size(10, 10), 25, media::PIXEL_FORMAT_RGB24
);
233 const VideoCaptureControllerID
client_a_route_1(0xa1a1a1a1);
234 const VideoCaptureControllerID
client_a_route_2(0xa2a2a2a2);
235 const VideoCaptureControllerID
client_b_route_1(0xb1b1b1b1);
236 const VideoCaptureControllerID
client_b_route_2(0xb2b2b2b2);
238 // Start with two clients.
239 controller_
->AddClient(client_a_route_1
,
241 base::kNullProcessHandle
,
244 controller_
->AddClient(client_b_route_1
,
246 base::kNullProcessHandle
,
249 controller_
->AddClient(client_a_route_2
,
251 base::kNullProcessHandle
,
254 ASSERT_EQ(3, controller_
->GetClientCount());
256 // Now, simulate an incoming captured buffer from the capture device. As a
257 // side effect this will cause the first buffer to be shared with clients.
259 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
;
261 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
263 memset(buffer
->data(), buffer_no
++, buffer
->size());
266 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_1
)).Times(1);
267 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_1
)).Times(1);
271 EXPECT_CALL(*client_b_
, DoBufferCreated(client_b_route_1
)).Times(1);
272 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_1
)).Times(1);
276 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_2
)).Times(1);
277 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_2
)).Times(1);
279 device_
->OnIncomingCapturedBuffer(buffer
,
280 media::VideoFrame::I420
,
283 device_format
.frame_rate
);
286 base::RunLoop().RunUntilIdle();
287 Mock::VerifyAndClearExpectations(client_a_
.get());
288 Mock::VerifyAndClearExpectations(client_b_
.get());
290 // Second buffer which ought to use the same shared memory buffer. In this
291 // case pretend that the Buffer pointer is held by the device for a long
292 // delay. This shouldn't affect anything.
294 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
296 memset(buffer
->data(), buffer_no
++, buffer
->size());
297 device_
->OnIncomingCapturedBuffer(buffer
,
298 media::VideoFrame::I420
,
301 device_format
.frame_rate
);
304 // The buffer should be delivered to the clients in any order.
305 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_1
)).Times(1);
306 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_1
)).Times(1);
307 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_2
)).Times(1);
308 base::RunLoop().RunUntilIdle();
309 Mock::VerifyAndClearExpectations(client_a_
.get());
310 Mock::VerifyAndClearExpectations(client_b_
.get());
312 // Add a fourth client now that some buffers have come through.
313 controller_
->AddClient(client_b_route_2
,
315 base::kNullProcessHandle
,
318 Mock::VerifyAndClearExpectations(client_b_
.get());
320 // Third, fourth, and fifth buffers. Pretend they all arrive at the same time.
321 for (int i
= 0; i
< kPoolSize
; i
++) {
322 buffer
= device_
->ReserveOutputBuffer(media::VideoFrame::I420
,
325 memset(buffer
->data(), buffer_no
++, buffer
->size());
326 device_
->OnIncomingCapturedBuffer(buffer
,
327 media::VideoFrame::I420
,
330 device_format
.frame_rate
);
333 // ReserveOutputBuffer ought to fail now, because the pool is depleted.
334 ASSERT_FALSE(device_
->ReserveOutputBuffer(media::VideoFrame::I420
,
335 capture_resolution
));
337 // The new client needs to be told of 3 buffers; the old clients only 2.
338 EXPECT_CALL(*client_b_
, DoBufferCreated(client_b_route_2
)).Times(kPoolSize
);
339 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_2
)).Times(kPoolSize
);
340 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_1
))
341 .Times(kPoolSize
- 1);
342 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_1
)).Times(kPoolSize
);
343 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_2
))
344 .Times(kPoolSize
- 1);
345 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_2
)).Times(kPoolSize
);
346 EXPECT_CALL(*client_b_
, DoBufferCreated(client_b_route_1
))
347 .Times(kPoolSize
- 1);
348 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_1
)).Times(kPoolSize
);
349 base::RunLoop().RunUntilIdle();
350 Mock::VerifyAndClearExpectations(client_a_
.get());
351 Mock::VerifyAndClearExpectations(client_b_
.get());
353 // Now test the interaction of client shutdown and buffer delivery.
354 // Kill A1 via renderer disconnect (synchronous).
355 controller_
->RemoveClient(client_a_route_1
, client_a_
.get());
356 // Kill B1 via session close (posts a task to disconnect).
357 EXPECT_CALL(*client_b_
, DoEnded(client_b_route_1
)).Times(1);
358 controller_
->StopSession(300);
359 // Queue up another buffer.
361 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
363 memset(buffer
->data(), buffer_no
++, buffer
->size());
364 device_
->OnIncomingCapturedBuffer(buffer
,
365 media::VideoFrame::I420
,
368 device_format
.frame_rate
);
371 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
373 // Kill A2 via session close (posts a task to disconnect, but A2 must not
374 // be sent either of these two buffers).
375 EXPECT_CALL(*client_a_
, DoEnded(client_a_route_2
)).Times(1);
376 controller_
->StopSession(200);
379 memset(buffer
->data(), buffer_no
++, buffer
->size());
380 device_
->OnIncomingCapturedBuffer(buffer
,
381 media::VideoFrame::I420
,
384 device_format
.frame_rate
);
386 // B2 is the only client left, and is the only one that should
388 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_2
)).Times(2);
389 base::RunLoop().RunUntilIdle();
390 Mock::VerifyAndClearExpectations(client_a_
.get());
391 Mock::VerifyAndClearExpectations(client_b_
.get());
394 // Exercises the OnError() codepath of VideoCaptureController, and tests the
395 // behavior of various operations after the error state has been signalled.
396 TEST_F(VideoCaptureControllerTest
, ErrorBeforeDeviceCreation
) {
397 media::VideoCaptureParams session_100
;
398 session_100
.requested_format
= media::VideoCaptureFormat(
399 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
401 media::VideoCaptureParams session_200
= session_100
;
403 const gfx::Size
capture_resolution(320, 240);
405 const VideoCaptureControllerID
route_id(0x99);
407 // Start with one client.
408 controller_
->AddClient(
409 route_id
, client_a_
.get(), base::kNullProcessHandle
, 100, session_100
);
411 EXPECT_CALL(*client_a_
, DoError(route_id
)).Times(1);
412 base::RunLoop().RunUntilIdle();
413 Mock::VerifyAndClearExpectations(client_a_
.get());
415 // Second client connects after the error state. It also should get told of
417 EXPECT_CALL(*client_b_
, DoError(route_id
)).Times(1);
418 controller_
->AddClient(
419 route_id
, client_b_
.get(), base::kNullProcessHandle
, 200, session_200
);
420 base::RunLoop().RunUntilIdle();
421 Mock::VerifyAndClearExpectations(client_b_
.get());
423 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
=
424 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
427 device_
->OnIncomingCapturedBuffer(
428 buffer
, media::VideoFrame::I420
, capture_resolution
, base::Time(), 30);
431 base::RunLoop().RunUntilIdle();
434 // Exercises the OnError() codepath of VideoCaptureController, and tests the
435 // behavior of various operations after the error state has been signalled.
436 TEST_F(VideoCaptureControllerTest
, ErrorAfterDeviceCreation
) {
437 media::VideoCaptureParams session_100
;
438 session_100
.requested_format
= media::VideoCaptureFormat(
439 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
441 media::VideoCaptureParams session_200
= session_100
;
443 const VideoCaptureControllerID
route_id(0x99);
445 // Start with one client.
446 controller_
->AddClient(
447 route_id
, client_a_
.get(), base::kNullProcessHandle
, 100, session_100
);
448 media::VideoCaptureFormat
device_format(
449 gfx::Size(10, 10), 25, media::PIXEL_FORMAT_ARGB
);
451 // Start the device. Then, before the first buffer, signal an error and
452 // deliver the buffer. The error should be propagated to clients; the buffer
454 base::RunLoop().RunUntilIdle();
455 Mock::VerifyAndClearExpectations(client_a_
.get());
457 const gfx::Size
dims(320, 240);
458 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
=
459 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, dims
);
463 device_
->OnIncomingCapturedBuffer(buffer
,
464 media::VideoFrame::I420
,
467 device_format
.frame_rate
);
470 EXPECT_CALL(*client_a_
, DoError(route_id
)).Times(1);
471 base::RunLoop().RunUntilIdle();
472 Mock::VerifyAndClearExpectations(client_a_
.get());
474 // Second client connects after the error state. It also should get told of
476 EXPECT_CALL(*client_b_
, DoError(route_id
)).Times(1);
477 controller_
->AddClient(
478 route_id
, client_b_
.get(), base::kNullProcessHandle
, 200, session_200
);
479 Mock::VerifyAndClearExpectations(client_b_
.get());
482 } // namespace content