video_capture_controller_unittest.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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. 4 5// Unit test for VideoCaptureController. 6 7#include <string> 8 9#include "base/bind.h" 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" 26 27using ::testing::InSequence; 28using ::testing::Mock; 29 30namespace content { 31 32class MockVideoCaptureControllerEventHandler 33 : public VideoCaptureControllerEventHandler { 34 public: 35 explicit MockVideoCaptureControllerEventHandler( 36 VideoCaptureController* controller) 37 : controller_(controller) {} 38 virtual ~MockVideoCaptureControllerEventHandler() {} 39 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&)); 47 48 virtual void OnError(const VideoCaptureControllerID& id) OVERRIDE { 49 DoError(id); 50 } 51 virtual void OnBufferCreated(const VideoCaptureControllerID& id, 52 base::SharedMemoryHandle handle, 53 int length, int buffer_id) OVERRIDE { 54 DoBufferCreated(id); 55 } 56 virtual void OnBufferDestroyed(const VideoCaptureControllerID& id, 57 int buffer_id) OVERRIDE { 58 DoBufferDestroyed(id); 59 } 60 virtual void OnBufferReady(const VideoCaptureControllerID& id, 61 int buffer_id, 62 base::Time timestamp, 63 const media::VideoCaptureFormat& format) OVERRIDE { 64 DoBufferReady(id); 65 base::MessageLoop::current()->PostTask(FROM_HERE, 66 base::Bind(&VideoCaptureController::ReturnBuffer, 67 base::Unretained(controller_), id, this, buffer_id)); 68 } 69 virtual void OnEnded(const VideoCaptureControllerID& id) OVERRIDE { 70 DoEnded(id); 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)); 75 } 76 77 VideoCaptureController* controller_; 78}; 79 80// Test class. 81class VideoCaptureControllerTest : public testing::Test { 82 public: 83 VideoCaptureControllerTest() {} 84 virtual ~VideoCaptureControllerTest() {} 85 86 protected: 87 static const int kPoolSize = 3; 88 89 virtual void SetUp() OVERRIDE { 90 controller_.reset(new VideoCaptureController()); 91 device_ = controller_->NewDeviceClient().Pass(); 92 client_a_.reset(new MockVideoCaptureControllerEventHandler( 93 controller_.get())); 94 client_b_.reset(new MockVideoCaptureControllerEventHandler( 95 controller_.get())); 96 } 97 98 virtual void TearDown() OVERRIDE { 99 base::RunLoop().RunUntilIdle(); 100 } 101 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_; 107 108 private: 109 DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest); 110}; 111 112// A simple test of VideoCaptureController's ability to add, remove, and keep 113// track of clients. 114TEST_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); 119 120 media::VideoCaptureParams session_200 = session_100; 121 session_200.session_id = 200; 122 123 media::VideoCaptureParams session_300 = session_100; 124 session_300.session_id = 300; 125 126 media::VideoCaptureParams session_400 = session_100; 127 session_400.session_id = 400; 128 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); 135 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."; 154 ASSERT_EQ(200, 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()); 164 ASSERT_EQ(300, 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] 172 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."; 197 ASSERT_EQ(400, 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."; 203} 204 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. 208TEST_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); 213 214 media::VideoCaptureParams session_200 = session_100; 215 session_200.session_id = 200; 216 217 media::VideoCaptureParams session_300 = session_100; 218 session_300.session_id = 300; 219 220 media::VideoCaptureParams session_1 = session_100; 221 session_1.session_id = 1; 222 223 gfx::Size capture_resolution(444, 200); 224 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); 230 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); 235 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()); 244 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. 247 uint8 frame_no = 1; 248 scoped_refptr<media::VideoFrame> frame; 249 frame = device_->ReserveOutputBuffer(capture_resolution); 250 ASSERT_TRUE(frame); 251 media::FillYUV(frame, frame_no++, 0x22, 0x44); 252 { 253 InSequence s; 254 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(1); 255 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1); 256 } 257 { 258 InSequence s; 259 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(1); 260 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1); 261 } 262 { 263 InSequence s; 264 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1); 265 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1); 266 } 267 device_->OnIncomingCapturedVideoFrame(frame, base::Time()); 268 frame = NULL; 269 270 base::RunLoop().RunUntilIdle(); 271 Mock::VerifyAndClearExpectations(client_a_.get()); 272 Mock::VerifyAndClearExpectations(client_b_.get()); 273 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); 278 ASSERT_TRUE(frame); 279 media::FillYUV(frame, frame_no++, 0x22, 0x44); 280 device_->OnIncomingCapturedVideoFrame(frame, base::Time()); 281 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()); 289 frame = NULL; 290 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()); 295 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); 299 ASSERT_TRUE(frame); 300 ASSERT_EQ(media::VideoFrame::I420, frame->format()); 301 media::FillYUV(frame, frame_no++, 0x22, 0x44); 302 device_->OnIncomingCapturedVideoFrame(frame, base::Time()); 303 304 } 305 // ReserveOutputBuffer ought to fail now, because the pool is depleted. 306 ASSERT_FALSE(device_->ReserveOutputBuffer(capture_resolution)); 307 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()); 323 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); 332 ASSERT_TRUE(frame); 333 media::FillYUV(frame, frame_no++, 0x22, 0x44); 334 device_->OnIncomingCapturedVideoFrame(frame, base::Time()); 335 frame = device_->ReserveOutputBuffer(capture_resolution); 336 { 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); 341 } 342 ASSERT_TRUE(frame); 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 346 // get the frame. 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()); 351} 352 353// Exercises the OnError() codepath of VideoCaptureController, and tests the 354// behavior of various operations after the error state has been signalled. 355TEST_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); 360 361 media::VideoCaptureParams session_200 = session_100; 362 session_200.session_id = 200; 363 364 const VideoCaptureControllerID route_id(0x99); 365 366 // Start with one client. 367 controller_->AddClient(route_id, client_a_.get(), 368 base::kNullProcessHandle, session_100); 369 device_->OnError(); 370 EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); 371 base::RunLoop().RunUntilIdle(); 372 Mock::VerifyAndClearExpectations(client_a_.get()); 373 374 // Second client connects after the error state. It also should get told of 375 // the error. 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()); 381 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); 386 387 device_->OnFrameInfo(device_format); 388 base::RunLoop().RunUntilIdle(); 389} 390 391// Exercises the OnError() codepath of VideoCaptureController, and tests the 392// behavior of various operations after the error state has been signalled. 393TEST_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); 398 399 media::VideoCaptureParams session_200 = session_100; 400 session_200.session_id = 200; 401 402 const VideoCaptureControllerID route_id(0x99); 403 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); 411 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 414 // be. 415 base::RunLoop().RunUntilIdle(); 416 Mock::VerifyAndClearExpectations(client_a_.get()); 417 418 scoped_refptr<media::VideoFrame> frame = 419 device_->ReserveOutputBuffer(gfx::Size(320, 240)); 420 ASSERT_TRUE(frame); 421 422 device_->OnError(); 423 device_->OnIncomingCapturedVideoFrame(frame, base::Time()); 424 frame = NULL; 425 426 EXPECT_CALL(*client_a_, DoError(route_id)).Times(1); 427 base::RunLoop().RunUntilIdle(); 428 Mock::VerifyAndClearExpectations(client_a_.get()); 429 430 // Second client connects after the error state. It also should get told of 431 // the error. 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()); 436} 437 438} // namespace content 439