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 "gpu/command_buffer/common/mailbox_holder.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(DoMailboxBufferReady
, void(const VideoCaptureControllerID
&));
46 MOCK_METHOD1(DoEnded
, void(const VideoCaptureControllerID
&));
47 MOCK_METHOD1(DoError
, void(const VideoCaptureControllerID
&));
49 virtual void OnError(const VideoCaptureControllerID
& id
) OVERRIDE
{
52 virtual void OnBufferCreated(const VideoCaptureControllerID
& id
,
53 base::SharedMemoryHandle handle
,
54 int length
, int buffer_id
) OVERRIDE
{
57 virtual void OnBufferDestroyed(const VideoCaptureControllerID
& id
,
58 int buffer_id
) OVERRIDE
{
59 DoBufferDestroyed(id
);
61 virtual void OnBufferReady(const VideoCaptureControllerID
& id
,
63 const media::VideoCaptureFormat
& format
,
64 base::TimeTicks timestamp
) OVERRIDE
{
66 base::MessageLoop::current()->PostTask(
68 base::Bind(&VideoCaptureController::ReturnBuffer
,
69 base::Unretained(controller_
),
73 std::vector
<uint32
>()));
75 virtual void OnMailboxBufferReady(const VideoCaptureControllerID
& id
,
77 const gpu::MailboxHolder
& mailbox_holder
,
78 const media::VideoCaptureFormat
& format
,
79 base::TimeTicks timestamp
) OVERRIDE
{
80 DoMailboxBufferReady(id
);
81 // Use a very different syncpoint value when returning a new syncpoint.
82 std::vector
<uint32
> release_sync_points
;
83 release_sync_points
.push_back(~mailbox_holder
.sync_point
);
84 base::MessageLoop::current()->PostTask(
86 base::Bind(&VideoCaptureController::ReturnBuffer
,
87 base::Unretained(controller_
),
91 release_sync_points
));
93 virtual void OnEnded(const VideoCaptureControllerID
& id
) OVERRIDE
{
95 // OnEnded() must respond by (eventually) unregistering the client.
96 base::MessageLoop::current()->PostTask(FROM_HERE
,
97 base::Bind(base::IgnoreResult(&VideoCaptureController::RemoveClient
),
98 base::Unretained(controller_
), id
, this));
101 VideoCaptureController
* controller_
;
105 class VideoCaptureControllerTest
: public testing::Test
{
107 VideoCaptureControllerTest() {}
108 virtual ~VideoCaptureControllerTest() {}
111 static const int kPoolSize
= 3;
113 virtual void SetUp() OVERRIDE
{
114 controller_
.reset(new VideoCaptureController());
115 device_
= controller_
->NewDeviceClient().Pass();
116 client_a_
.reset(new MockVideoCaptureControllerEventHandler(
118 client_b_
.reset(new MockVideoCaptureControllerEventHandler(
122 virtual void TearDown() OVERRIDE
{
123 base::RunLoop().RunUntilIdle();
126 scoped_refptr
<media::VideoFrame
> WrapI420Buffer(
127 const scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>& buffer
,
128 gfx::Size dimensions
) {
129 return media::VideoFrame::WrapExternalPackedMemory(
130 media::VideoFrame::I420
,
132 gfx::Rect(dimensions
),
134 reinterpret_cast<uint8
*>(buffer
->data()),
135 media::VideoFrame::AllocationSize(media::VideoFrame::I420
, dimensions
),
136 base::SharedMemory::NULLHandle(),
141 scoped_refptr
<media::VideoFrame
> WrapMailboxBuffer(
142 const scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>& buffer
,
143 scoped_ptr
<gpu::MailboxHolder
> holder
,
144 const media::VideoFrame::ReleaseMailboxCB
& release_cb
,
145 gfx::Size dimensions
) {
146 return media::VideoFrame::WrapNativeTexture(
150 gfx::Rect(dimensions
),
153 media::VideoFrame::ReadPixelsCB());
156 TestBrowserThreadBundle bundle_
;
157 scoped_ptr
<MockVideoCaptureControllerEventHandler
> client_a_
;
158 scoped_ptr
<MockVideoCaptureControllerEventHandler
> client_b_
;
159 scoped_ptr
<VideoCaptureController
> controller_
;
160 scoped_ptr
<media::VideoCaptureDevice::Client
> device_
;
163 DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest
);
166 // A simple test of VideoCaptureController's ability to add, remove, and keep
168 TEST_F(VideoCaptureControllerTest
, AddAndRemoveClients
) {
169 media::VideoCaptureParams session_100
;
170 session_100
.requested_format
= media::VideoCaptureFormat(
171 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
172 media::VideoCaptureParams session_200
= session_100
;
174 media::VideoCaptureParams session_300
= session_100
;
176 media::VideoCaptureParams session_400
= session_100
;
178 // Intentionally use the same route ID for two of the clients: the device_ids
179 // are a per-VideoCaptureHost namespace, and can overlap across hosts.
180 const VideoCaptureControllerID
client_a_route_1(44);
181 const VideoCaptureControllerID
client_a_route_2(30);
182 const VideoCaptureControllerID
client_b_route_1(30);
183 const VideoCaptureControllerID
client_b_route_2(1);
185 // Clients in controller: []
186 ASSERT_EQ(0, controller_
->GetClientCount())
187 << "Client count should initially be zero.";
188 controller_
->AddClient(client_a_route_1
,
190 base::kNullProcessHandle
,
193 // Clients in controller: [A/1]
194 ASSERT_EQ(1, controller_
->GetClientCount())
195 << "Adding client A/1 should bump client count.";
196 controller_
->AddClient(client_a_route_2
,
198 base::kNullProcessHandle
,
201 // Clients in controller: [A/1, A/2]
202 ASSERT_EQ(2, controller_
->GetClientCount())
203 << "Adding client A/2 should bump client count.";
204 controller_
->AddClient(client_b_route_1
,
206 base::kNullProcessHandle
,
209 // Clients in controller: [A/1, A/2, B/1]
210 ASSERT_EQ(3, controller_
->GetClientCount())
211 << "Adding client B/1 should bump client count.";
213 controller_
->RemoveClient(client_a_route_2
, client_a_
.get()))
214 << "Removing client A/1 should return its session_id.";
215 // Clients in controller: [A/1, B/1]
216 ASSERT_EQ(2, controller_
->GetClientCount());
217 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId
),
218 controller_
->RemoveClient(client_a_route_2
, client_a_
.get()))
219 << "Removing a nonexistant client should fail.";
220 // Clients in controller: [A/1, B/1]
221 ASSERT_EQ(2, controller_
->GetClientCount());
223 controller_
->RemoveClient(client_b_route_1
, client_b_
.get()))
224 << "Removing client B/1 should return its session_id.";
225 // Clients in controller: [A/1]
226 ASSERT_EQ(1, controller_
->GetClientCount());
227 controller_
->AddClient(client_b_route_2
,
229 base::kNullProcessHandle
,
232 // Clients in controller: [A/1, B/2]
234 EXPECT_CALL(*client_a_
, DoEnded(client_a_route_1
)).Times(1);
235 controller_
->StopSession(100); // Session 100 == client A/1
236 Mock::VerifyAndClearExpectations(client_a_
.get());
237 ASSERT_EQ(2, controller_
->GetClientCount())
238 << "Client should be closed but still exist after StopSession.";
239 // Clients in controller: [A/1 (closed, removal pending), B/2]
240 base::RunLoop().RunUntilIdle();
241 // Clients in controller: [B/2]
242 ASSERT_EQ(1, controller_
->GetClientCount())
243 << "Client A/1 should be deleted by now.";
244 controller_
->StopSession(200); // Session 200 does not exist anymore
245 // Clients in controller: [B/2]
246 ASSERT_EQ(1, controller_
->GetClientCount())
247 << "Stopping non-existant session 200 should be a no-op.";
248 controller_
->StopSession(256); // Session 256 never existed.
249 // Clients in controller: [B/2]
250 ASSERT_EQ(1, controller_
->GetClientCount())
251 << "Stopping non-existant session 256 should be a no-op.";
252 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId
),
253 controller_
->RemoveClient(client_a_route_1
, client_a_
.get()))
254 << "Removing already-removed client A/1 should fail.";
255 // Clients in controller: [B/2]
256 ASSERT_EQ(1, controller_
->GetClientCount())
257 << "Removing non-existant session 200 should be a no-op.";
259 controller_
->RemoveClient(client_b_route_2
, client_b_
.get()))
260 << "Removing client B/2 should return its session_id.";
261 // Clients in controller: []
262 ASSERT_EQ(0, controller_
->GetClientCount())
263 << "Client count should return to zero after all clients are gone.";
266 static void CacheSyncPoint(std::vector
<uint32
>* called_release_sync_points
,
267 const std::vector
<uint32
>& release_sync_points
) {
268 DCHECK(called_release_sync_points
->empty());
269 called_release_sync_points
->assign(release_sync_points
.begin(),
270 release_sync_points
.end());
273 // This test will connect and disconnect several clients while simulating an
274 // active capture device being started and generating frames. It runs on one
275 // thread and is intended to behave deterministically.
276 TEST_F(VideoCaptureControllerTest
, NormalCaptureMultipleClients
) {
277 media::VideoCaptureParams session_100
;
278 session_100
.requested_format
= media::VideoCaptureFormat(
279 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
281 media::VideoCaptureParams session_200
= session_100
;
283 media::VideoCaptureParams session_300
= session_100
;
285 media::VideoCaptureParams session_1
= session_100
;
287 gfx::Size
capture_resolution(444, 200);
289 // The device format needn't match the VideoCaptureParams (the camera can do
290 // what it wants). Pick something random.
291 media::VideoCaptureFormat
device_format(
292 gfx::Size(10, 10), 25, media::PIXEL_FORMAT_RGB24
);
294 const VideoCaptureControllerID
client_a_route_1(0xa1a1a1a1);
295 const VideoCaptureControllerID
client_a_route_2(0xa2a2a2a2);
296 const VideoCaptureControllerID
client_b_route_1(0xb1b1b1b1);
297 const VideoCaptureControllerID
client_b_route_2(0xb2b2b2b2);
299 // Start with two clients.
300 controller_
->AddClient(client_a_route_1
,
302 base::kNullProcessHandle
,
305 controller_
->AddClient(client_b_route_1
,
307 base::kNullProcessHandle
,
310 controller_
->AddClient(client_a_route_2
,
312 base::kNullProcessHandle
,
315 ASSERT_EQ(3, controller_
->GetClientCount());
317 // Now, simulate an incoming captured buffer from the capture device. As a
318 // side effect this will cause the first buffer to be shared with clients.
320 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
;
322 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
324 memset(buffer
->data(), buffer_no
++, buffer
->size());
327 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_1
)).Times(1);
328 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_1
)).Times(1);
332 EXPECT_CALL(*client_b_
, DoBufferCreated(client_b_route_1
)).Times(1);
333 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_1
)).Times(1);
337 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_2
)).Times(1);
338 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_2
)).Times(1);
340 device_
->OnIncomingCapturedVideoFrame(
342 media::VideoCaptureFormat(capture_resolution
,
343 device_format
.frame_rate
,
344 media::PIXEL_FORMAT_I420
),
345 WrapI420Buffer(buffer
, capture_resolution
),
349 base::RunLoop().RunUntilIdle();
350 Mock::VerifyAndClearExpectations(client_a_
.get());
351 Mock::VerifyAndClearExpectations(client_b_
.get());
353 // Second buffer which ought to use the same shared memory buffer. In this
354 // case pretend that the Buffer pointer is held by the device for a long
355 // delay. This shouldn't affect anything.
357 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
359 memset(buffer
->data(), buffer_no
++, buffer
->size());
360 device_
->OnIncomingCapturedVideoFrame(
362 media::VideoCaptureFormat(capture_resolution
,
363 device_format
.frame_rate
,
364 media::PIXEL_FORMAT_I420
),
365 WrapI420Buffer(buffer
, capture_resolution
),
369 // The buffer should be delivered to the clients in any order.
370 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_1
)).Times(1);
371 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_1
)).Times(1);
372 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_2
)).Times(1);
373 base::RunLoop().RunUntilIdle();
374 Mock::VerifyAndClearExpectations(client_a_
.get());
375 Mock::VerifyAndClearExpectations(client_b_
.get());
377 // Add a fourth client now that some buffers have come through.
378 controller_
->AddClient(client_b_route_2
,
380 base::kNullProcessHandle
,
383 Mock::VerifyAndClearExpectations(client_b_
.get());
385 // Third, fourth, and fifth buffers. Pretend they all arrive at the same time.
386 for (int i
= 0; i
< kPoolSize
; i
++) {
387 buffer
= device_
->ReserveOutputBuffer(media::VideoFrame::I420
,
390 memset(buffer
->data(), buffer_no
++, buffer
->size());
391 device_
->OnIncomingCapturedVideoFrame(
393 media::VideoCaptureFormat(capture_resolution
,
394 device_format
.frame_rate
,
395 media::PIXEL_FORMAT_I420
),
396 WrapI420Buffer(buffer
, capture_resolution
),
400 // ReserveOutputBuffer ought to fail now, because the pool is depleted.
401 ASSERT_FALSE(device_
->ReserveOutputBuffer(media::VideoFrame::I420
,
402 capture_resolution
));
404 // The new client needs to be told of 3 buffers; the old clients only 2.
405 EXPECT_CALL(*client_b_
, DoBufferCreated(client_b_route_2
)).Times(kPoolSize
);
406 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_2
)).Times(kPoolSize
);
407 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_1
))
408 .Times(kPoolSize
- 1);
409 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_1
)).Times(kPoolSize
);
410 EXPECT_CALL(*client_a_
, DoBufferCreated(client_a_route_2
))
411 .Times(kPoolSize
- 1);
412 EXPECT_CALL(*client_a_
, DoBufferReady(client_a_route_2
)).Times(kPoolSize
);
413 EXPECT_CALL(*client_b_
, DoBufferCreated(client_b_route_1
))
414 .Times(kPoolSize
- 1);
415 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_1
)).Times(kPoolSize
);
416 base::RunLoop().RunUntilIdle();
417 Mock::VerifyAndClearExpectations(client_a_
.get());
418 Mock::VerifyAndClearExpectations(client_b_
.get());
420 // Now test the interaction of client shutdown and buffer delivery.
421 // Kill A1 via renderer disconnect (synchronous).
422 controller_
->RemoveClient(client_a_route_1
, client_a_
.get());
423 // Kill B1 via session close (posts a task to disconnect).
424 EXPECT_CALL(*client_b_
, DoEnded(client_b_route_1
)).Times(1);
425 controller_
->StopSession(300);
426 // Queue up another buffer.
428 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
430 memset(buffer
->data(), buffer_no
++, buffer
->size());
431 device_
->OnIncomingCapturedVideoFrame(
433 media::VideoCaptureFormat(capture_resolution
,
434 device_format
.frame_rate
,
435 media::PIXEL_FORMAT_I420
),
436 WrapI420Buffer(buffer
, capture_resolution
),
440 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
442 // Kill A2 via session close (posts a task to disconnect, but A2 must not
443 // be sent either of these two buffers).
444 EXPECT_CALL(*client_a_
, DoEnded(client_a_route_2
)).Times(1);
445 controller_
->StopSession(200);
448 memset(buffer
->data(), buffer_no
++, buffer
->size());
449 device_
->OnIncomingCapturedVideoFrame(
451 media::VideoCaptureFormat(capture_resolution
,
452 device_format
.frame_rate
,
453 media::PIXEL_FORMAT_I420
),
454 WrapI420Buffer(buffer
, capture_resolution
),
457 // B2 is the only client left, and is the only one that should
459 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_2
)).Times(2);
460 base::RunLoop().RunUntilIdle();
461 Mock::VerifyAndClearExpectations(client_a_
.get());
462 Mock::VerifyAndClearExpectations(client_b_
.get());
464 // Allocate all buffers from the buffer pool, half as SHM buffer and half as
465 // mailbox buffers. Make sure of different counts though.
466 int shm_buffers
= kPoolSize
/ 2;
467 int mailbox_buffers
= kPoolSize
- shm_buffers
;
468 if (shm_buffers
== mailbox_buffers
) {
473 for (int i
= 0; i
< shm_buffers
; ++i
) {
474 buffer
= device_
->ReserveOutputBuffer(media::VideoFrame::I420
,
477 device_
->OnIncomingCapturedVideoFrame(
479 media::VideoCaptureFormat(capture_resolution
,
480 device_format
.frame_rate
,
481 media::PIXEL_FORMAT_I420
),
482 WrapI420Buffer(buffer
, capture_resolution
),
486 std::vector
<uint32
> mailbox_syncpoints(mailbox_buffers
);
487 std::vector
<std::vector
<uint32
> > release_syncpoint_vectors(mailbox_buffers
);
488 for (int i
= 0; i
< mailbox_buffers
; ++i
) {
489 buffer
= device_
->ReserveOutputBuffer(media::VideoFrame::NATIVE_TEXTURE
,
492 mailbox_syncpoints
[i
] = i
;
493 device_
->OnIncomingCapturedVideoFrame(
495 media::VideoCaptureFormat(capture_resolution
,
496 device_format
.frame_rate
,
497 media::PIXEL_FORMAT_TEXTURE
),
500 make_scoped_ptr(new gpu::MailboxHolder(
501 gpu::Mailbox(), 0, mailbox_syncpoints
[i
])),
502 base::Bind(&CacheSyncPoint
, &release_syncpoint_vectors
[i
]),
507 // ReserveOutputBuffers ought to fail now regardless of buffer format, because
508 // the pool is depleted.
509 ASSERT_FALSE(device_
->ReserveOutputBuffer(media::VideoFrame::I420
,
510 capture_resolution
));
511 ASSERT_FALSE(device_
->ReserveOutputBuffer(media::VideoFrame::NATIVE_TEXTURE
,
513 EXPECT_CALL(*client_b_
, DoBufferReady(client_b_route_2
)).Times(shm_buffers
);
514 EXPECT_CALL(*client_b_
, DoMailboxBufferReady(client_b_route_2
))
515 .Times(mailbox_buffers
);
516 base::RunLoop().RunUntilIdle();
517 for (size_t i
= 0; i
< mailbox_syncpoints
.size(); ++i
) {
518 // See: MockVideoCaptureControllerEventHandler::OnMailboxBufferReady()
519 ASSERT_EQ(1u, release_syncpoint_vectors
[i
].size());
520 ASSERT_EQ(mailbox_syncpoints
[i
], ~release_syncpoint_vectors
[i
][0]);
522 Mock::VerifyAndClearExpectations(client_b_
.get());
525 // Exercises the OnError() codepath of VideoCaptureController, and tests the
526 // behavior of various operations after the error state has been signalled.
527 TEST_F(VideoCaptureControllerTest
, ErrorBeforeDeviceCreation
) {
528 media::VideoCaptureParams session_100
;
529 session_100
.requested_format
= media::VideoCaptureFormat(
530 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
532 media::VideoCaptureParams session_200
= session_100
;
534 const gfx::Size
capture_resolution(320, 240);
536 const VideoCaptureControllerID
route_id(0x99);
538 // Start with one client.
539 controller_
->AddClient(
540 route_id
, client_a_
.get(), base::kNullProcessHandle
, 100, session_100
);
541 device_
->OnError("Test Error");
542 EXPECT_CALL(*client_a_
, DoError(route_id
)).Times(1);
543 base::RunLoop().RunUntilIdle();
544 Mock::VerifyAndClearExpectations(client_a_
.get());
546 // Second client connects after the error state. It also should get told of
548 EXPECT_CALL(*client_b_
, DoError(route_id
)).Times(1);
549 controller_
->AddClient(
550 route_id
, client_b_
.get(), base::kNullProcessHandle
, 200, session_200
);
551 base::RunLoop().RunUntilIdle();
552 Mock::VerifyAndClearExpectations(client_b_
.get());
554 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
=
555 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, capture_resolution
);
558 device_
->OnIncomingCapturedVideoFrame(
560 media::VideoCaptureFormat(
561 capture_resolution
, 30, media::PIXEL_FORMAT_I420
),
562 WrapI420Buffer(buffer
, capture_resolution
),
566 base::RunLoop().RunUntilIdle();
569 // Exercises the OnError() codepath of VideoCaptureController, and tests the
570 // behavior of various operations after the error state has been signalled.
571 TEST_F(VideoCaptureControllerTest
, ErrorAfterDeviceCreation
) {
572 media::VideoCaptureParams session_100
;
573 session_100
.requested_format
= media::VideoCaptureFormat(
574 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420
);
576 media::VideoCaptureParams session_200
= session_100
;
578 const VideoCaptureControllerID
route_id(0x99);
580 // Start with one client.
581 controller_
->AddClient(
582 route_id
, client_a_
.get(), base::kNullProcessHandle
, 100, session_100
);
583 media::VideoCaptureFormat
device_format(
584 gfx::Size(10, 10), 25, media::PIXEL_FORMAT_ARGB
);
586 // Start the device. Then, before the first buffer, signal an error and
587 // deliver the buffer. The error should be propagated to clients; the buffer
589 base::RunLoop().RunUntilIdle();
590 Mock::VerifyAndClearExpectations(client_a_
.get());
592 const gfx::Size
dims(320, 240);
593 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
=
594 device_
->ReserveOutputBuffer(media::VideoFrame::I420
, dims
);
597 device_
->OnError("Test error");
598 device_
->OnIncomingCapturedVideoFrame(
600 media::VideoCaptureFormat(
601 dims
, device_format
.frame_rate
, media::PIXEL_FORMAT_I420
),
602 WrapI420Buffer(buffer
, dims
),
606 EXPECT_CALL(*client_a_
, DoError(route_id
)).Times(1);
607 base::RunLoop().RunUntilIdle();
608 Mock::VerifyAndClearExpectations(client_a_
.get());
610 // Second client connects after the error state. It also should get told of
612 EXPECT_CALL(*client_b_
, DoError(route_id
)).Times(1);
613 controller_
->AddClient(
614 route_id
, client_b_
.get(), base::kNullProcessHandle
, 200, session_200
);
615 Mock::VerifyAndClearExpectations(client_b_
.get());
618 } // namespace content