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