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.session_id = 100;
117 session_100.requested_format = media::VideoCaptureFormat(
118 320, 240, 30, media::ConstantResolutionVideoCaptureDevice);
120 media::VideoCaptureParams session_200 = session_100;
121 session_200.session_id = 200;
123 media::VideoCaptureParams session_300 = session_100;
124 session_300.session_id = 300;
126 media::VideoCaptureParams session_400 = session_100;
127 session_400.session_id = 400;
129 // Intentionally use the same route ID for two of the clients: the device_ids
130 // are a per-VideoCaptureHost namespace, and can overlap across hosts.
131 const VideoCaptureControllerID client_a_route_1(44);
132 const VideoCaptureControllerID client_a_route_2(30);
133 const VideoCaptureControllerID client_b_route_1(30);
134 const VideoCaptureControllerID client_b_route_2(1);
136 // Clients in controller: []
137 ASSERT_EQ(0, controller_->GetClientCount())
138 << "Client count should initially be zero.";
139 controller_->AddClient(client_a_route_1, client_a_.get(),
140 base::kNullProcessHandle, session_100);
141 // Clients in controller: [A/1]
142 ASSERT_EQ(1, controller_->GetClientCount())
143 << "Adding client A/1 should bump client count.";;
144 controller_->AddClient(client_a_route_2, client_a_.get(),
145 base::kNullProcessHandle, session_200);
146 // Clients in controller: [A/1, A/2]
147 ASSERT_EQ(2, controller_->GetClientCount())
148 << "Adding client A/2 should bump client count.";
149 controller_->AddClient(client_b_route_1, client_b_.get(),
150 base::kNullProcessHandle, session_300);
151 // Clients in controller: [A/1, A/2, B/1]
152 ASSERT_EQ(3, controller_->GetClientCount())
153 << "Adding client B/1 should bump client count.";
155 controller_->RemoveClient(client_a_route_2, client_a_.get()))
156 << "Removing client A/1 should return its session_id.";
157 // Clients in controller: [A/1, B/1]
158 ASSERT_EQ(2, controller_->GetClientCount());
159 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId),
160 controller_->RemoveClient(client_a_route_2, client_a_.get()))
161 << "Removing a nonexistant client should fail.";
162 // Clients in controller: [A/1, B/1]
163 ASSERT_EQ(2, controller_->GetClientCount());
165 controller_->RemoveClient(client_b_route_1, client_b_.get()))
166 << "Removing client B/1 should return its session_id.";
167 // Clients in controller: [A/1]
168 ASSERT_EQ(1, controller_->GetClientCount());
169 controller_->AddClient(client_b_route_2, client_b_.get(),
170 base::kNullProcessHandle, session_400);
171 // Clients in controller: [A/1, B/2]
173 EXPECT_CALL(*client_a_, DoEnded(client_a_route_1)).Times(1);
174 controller_->StopSession(100); // Session 100 == client A/1
175 Mock::VerifyAndClearExpectations(client_a_.get());
176 ASSERT_EQ(2, controller_->GetClientCount())
177 << "Client should be closed but still exist after StopSession.";
178 // Clients in controller: [A/1 (closed, removal pending), B/2]
179 base::RunLoop().RunUntilIdle();
180 // Clients in controller: [B/2]
181 ASSERT_EQ(1, controller_->GetClientCount())
182 << "Client A/1 should be deleted by now.";
183 controller_->StopSession(200); // Session 200 does not exist anymore
184 // Clients in controller: [B/2]
185 ASSERT_EQ(1, controller_->GetClientCount())
186 << "Stopping non-existant session 200 should be a no-op.";
187 controller_->StopSession(256); // Session 256 never existed.
188 // Clients in controller: [B/2]
189 ASSERT_EQ(1, controller_->GetClientCount())
190 << "Stopping non-existant session 256 should be a no-op.";
191 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId),
192 controller_->RemoveClient(client_a_route_1, client_a_.get()))
193 << "Removing already-removed client A/1 should fail.";
194 // Clients in controller: [B/2]
195 ASSERT_EQ(1, controller_->GetClientCount())
196 << "Removing non-existant session 200 should be a no-op.";
198 controller_->RemoveClient(client_b_route_2, client_b_.get()))
199 << "Removing client B/2 should return its session_id.";
200 // Clients in controller: []
201 ASSERT_EQ(0, controller_->GetClientCount())
202 << "Client count should return to zero after all clients are gone.";
205 // This test will connect and disconnect several clients while simulating an
206 // active capture device being started and generating frames. It runs on one
207 // thread and is intended to behave deterministically.
208 TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
209 media::VideoCaptureParams session_100;
210 session_100.session_id = 100;
211 session_100.requested_format = media::VideoCaptureFormat(
212 320, 240, 30, media::ConstantResolutionVideoCaptureDevice);
214 media::VideoCaptureParams session_200 = session_100;
215 session_200.session_id = 200;
217 media::VideoCaptureParams session_300 = session_100;
218 session_300.session_id = 300;
220 media::VideoCaptureParams session_1 = session_100;
221 session_1.session_id = 1;
223 gfx::Size capture_resolution(444, 200);
225 // The device format needn't match the VideoCaptureParams (the camera can do
226 // what it wants). Pick something random to use for OnFrameInfo.
227 media::VideoCaptureCapability device_format(
228 10, 10, 25, media::PIXEL_FORMAT_RGB24,
229 media::ConstantResolutionVideoCaptureDevice);
231 const VideoCaptureControllerID client_a_route_1(0xa1a1a1a1);
232 const VideoCaptureControllerID client_a_route_2(0xa2a2a2a2);
233 const VideoCaptureControllerID client_b_route_1(0xb1b1b1b1);
234 const VideoCaptureControllerID client_b_route_2(0xb2b2b2b2);
236 // Start with two clients.
237 controller_->AddClient(client_a_route_1, client_a_.get(),
238 base::kNullProcessHandle, session_100);
239 controller_->AddClient(client_b_route_1, client_b_.get(),
240 base::kNullProcessHandle, session_300);
241 controller_->AddClient(client_a_route_2, client_a_.get(),
242 base::kNullProcessHandle, session_200);
243 ASSERT_EQ(3, controller_->GetClientCount());
245 // Now, simulate an incoming captured frame from the capture device. As a side
246 // effect this will cause the first buffer to be shared with clients.
248 scoped_refptr<media::VideoFrame> frame;
249 frame = device_->ReserveOutputBuffer(capture_resolution);
251 media::FillYUV(frame, frame_no++, 0x22, 0x44);
254 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(1);
255 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1);
259 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(1);
260 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1);
264 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1);
265 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1);
267 device_->OnIncomingCapturedVideoFrame(frame, base::Time());
270 base::RunLoop().RunUntilIdle();
271 Mock::VerifyAndClearExpectations(client_a_.get());
272 Mock::VerifyAndClearExpectations(client_b_.get());
274 // Second frame which ought to use the same shared memory buffer. In this case
275 // pretend that the VideoFrame pointer is held by the device for a long delay.
276 // This shouldn't affect anything.
277 frame = device_->ReserveOutputBuffer(capture_resolution);
279 media::FillYUV(frame, frame_no++, 0x22, 0x44);
280 device_->OnIncomingCapturedVideoFrame(frame, base::Time());
282 // The buffer should be delivered to the clients in any order.
283 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1);
284 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1);
285 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1);
286 base::RunLoop().RunUntilIdle();
287 Mock::VerifyAndClearExpectations(client_a_.get());
288 Mock::VerifyAndClearExpectations(client_b_.get());
291 // Add a fourth client now that some frames have come through.
292 controller_->AddClient(client_b_route_2, client_b_.get(),
293 base::kNullProcessHandle, session_1);
294 Mock::VerifyAndClearExpectations(client_b_.get());
296 // Third, fourth, and fifth frames. Pretend they all arrive at the same time.
297 for (int i = 0; i < kPoolSize; i++) {
298 frame = device_->ReserveOutputBuffer(capture_resolution);
300 ASSERT_EQ(media::VideoFrame::I420, frame->format());
301 media::FillYUV(frame, frame_no++, 0x22, 0x44);
302 device_->OnIncomingCapturedVideoFrame(frame, base::Time());
305 // ReserveOutputBuffer ought to fail now, because the pool is depleted.
306 ASSERT_FALSE(device_->ReserveOutputBuffer(capture_resolution));
308 // The new client needs to be told of 3 buffers; the old clients only 2.
309 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_2)).Times(kPoolSize);
310 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(kPoolSize);
311 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1))
312 .Times(kPoolSize - 1);
313 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(kPoolSize);
314 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2))
315 .Times(kPoolSize - 1);
316 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(kPoolSize);
317 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1))
318 .Times(kPoolSize - 1);
319 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(kPoolSize);
320 base::RunLoop().RunUntilIdle();
321 Mock::VerifyAndClearExpectations(client_a_.get());
322 Mock::VerifyAndClearExpectations(client_b_.get());
324 // Now test the interaction of client shutdown and frame delivery.
325 // Kill A1 via renderer disconnect (synchronous).
326 controller_->RemoveClient(client_a_route_1, client_a_.get());
327 // Kill B1 via session close (posts a task to disconnect).
328 EXPECT_CALL(*client_b_, DoEnded(client_b_route_1)).Times(1);
329 controller_->StopSession(300);
330 // Queue up another frame.
331 frame = device_->ReserveOutputBuffer(capture_resolution);
333 media::FillYUV(frame, frame_no++, 0x22, 0x44);
334 device_->OnIncomingCapturedVideoFrame(frame, base::Time());
335 frame = device_->ReserveOutputBuffer(capture_resolution);
337 // Kill A2 via session close (posts a task to disconnect, but A2 must not
338 // be sent either of these two frames)..
339 EXPECT_CALL(*client_a_, DoEnded(client_a_route_2)).Times(1);
340 controller_->StopSession(200);
343 media::FillYUV(frame, frame_no++, 0x22, 0x44);
344 device_->OnIncomingCapturedVideoFrame(frame, base::Time());
345 // B2 is the only client left, and is the only one that should
347 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(2);
348 base::RunLoop().RunUntilIdle();
349 Mock::VerifyAndClearExpectations(client_a_.get());
350 Mock::VerifyAndClearExpectations(client_b_.get());
353 // Exercises the OnError() codepath of VideoCaptureController, and tests the
354 // behavior of various operations after the error state has been signalled.
355 TEST_F(VideoCaptureControllerTest, ErrorBeforeDeviceCreation) {
356 media::VideoCaptureParams session_100;
357 session_100.session_id = 100;
358 session_100.requested_format = media::VideoCaptureFormat(
359 320, 240, 30, media::ConstantResolutionVideoCaptureDevice);
361 media::VideoCaptureParams session_200 = session_100;
362 session_200.session_id = 200;
364 const VideoCaptureControllerID route_id(0x99);
366 // Start with one client.
367 controller_->AddClient(route_id, client_a_.get(),
368 base::kNullProcessHandle, session_100);
370 EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
371 base::RunLoop().RunUntilIdle();
372 Mock::VerifyAndClearExpectations(client_a_.get());
374 // Second client connects after the error state. It also should get told of
376 EXPECT_CALL(*client_b_, DoError(route_id)).Times(1);
377 controller_->AddClient(route_id, client_b_.get(),
378 base::kNullProcessHandle, session_200);
379 base::RunLoop().RunUntilIdle();
380 Mock::VerifyAndClearExpectations(client_b_.get());
382 // OnFrameInfo from the VCD should become a no-op after the error occurs.
383 media::VideoCaptureCapability device_format(
384 10, 10, 25, media::PIXEL_FORMAT_ARGB,
385 media::ConstantResolutionVideoCaptureDevice);
387 device_->OnFrameInfo(device_format);
388 base::RunLoop().RunUntilIdle();
391 // Exercises the OnError() codepath of VideoCaptureController, and tests the
392 // behavior of various operations after the error state has been signalled.
393 TEST_F(VideoCaptureControllerTest, ErrorAfterDeviceCreation) {
394 media::VideoCaptureParams session_100;
395 session_100.session_id = 100;
396 session_100.requested_format = media::VideoCaptureFormat(
397 320, 240, 30, media::ConstantResolutionVideoCaptureDevice);
399 media::VideoCaptureParams session_200 = session_100;
400 session_200.session_id = 200;
402 const VideoCaptureControllerID route_id(0x99);
404 // Start with one client.
405 controller_->AddClient(route_id, client_a_.get(),
406 base::kNullProcessHandle, session_100);
407 // OnFrameInfo from the VCD should become a no-op after the error occurs.
408 media::VideoCaptureCapability device_format(
409 10, 10, 25, media::PIXEL_FORMAT_ARGB,
410 media::ConstantResolutionVideoCaptureDevice);
412 // Start the device. Then, before the first frame, signal an error and deliver
413 // the frame. The error should be propagated to clients; the frame should not
415 base::RunLoop().RunUntilIdle();
416 Mock::VerifyAndClearExpectations(client_a_.get());
418 scoped_refptr<media::VideoFrame> frame =
419 device_->ReserveOutputBuffer(gfx::Size(320, 240));
423 device_->OnIncomingCapturedVideoFrame(frame, base::Time());
426 EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
427 base::RunLoop().RunUntilIdle();
428 Mock::VerifyAndClearExpectations(client_a_.get());
430 // Second client connects after the error state. It also should get told of
432 EXPECT_CALL(*client_b_, DoError(route_id)).Times(1);
433 controller_->AddClient(route_id, client_b_.get(),
434 base::kNullProcessHandle, session_200);
435 Mock::VerifyAndClearExpectations(client_b_.get());
438 } // namespace content