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/gpu/client/gl_helper.h"
20 #include "content/common/media/media_stream_options.h"
21 #include "content/public/test/test_browser_thread_bundle.h"
22 #include "gpu/command_buffer/common/mailbox_holder.h"
23 #include "media/base/video_util.h"
24 #include "media/video/capture/video_capture_types.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
28 #if defined(OS_ANDROID)
29 #include "content/browser/renderer_host/test/no_transport_image_transport_factory_android.h"
31 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
34 using ::testing::InSequence;
35 using ::testing::Mock;
39 class MockVideoCaptureControllerEventHandler
40 : public VideoCaptureControllerEventHandler {
42 explicit MockVideoCaptureControllerEventHandler(
43 VideoCaptureController* controller)
44 : controller_(controller) {}
45 virtual ~MockVideoCaptureControllerEventHandler() {}
47 // These mock methods are delegated to by our fake implementation of
48 // VideoCaptureControllerEventHandler, to be used in EXPECT_CALL().
49 MOCK_METHOD1(DoBufferCreated, void(const VideoCaptureControllerID&));
50 MOCK_METHOD1(DoBufferDestroyed, void(const VideoCaptureControllerID&));
51 MOCK_METHOD1(DoBufferReady, void(const VideoCaptureControllerID&));
52 MOCK_METHOD1(DoMailboxBufferReady, void(const VideoCaptureControllerID&));
53 MOCK_METHOD1(DoEnded, void(const VideoCaptureControllerID&));
54 MOCK_METHOD1(DoError, void(const VideoCaptureControllerID&));
56 virtual void OnError(const VideoCaptureControllerID& id) override {
59 virtual void OnBufferCreated(const VideoCaptureControllerID& id,
60 base::SharedMemoryHandle handle,
61 int length, int buffer_id) override {
64 virtual void OnBufferDestroyed(const VideoCaptureControllerID& id,
65 int buffer_id) override {
66 DoBufferDestroyed(id);
68 virtual void OnBufferReady(const VideoCaptureControllerID& id,
70 const media::VideoCaptureFormat& format,
71 const gfx::Rect& visible_rect,
72 base::TimeTicks timestamp) override {
74 base::MessageLoop::current()->PostTask(
76 base::Bind(&VideoCaptureController::ReturnBuffer,
77 base::Unretained(controller_),
83 virtual void OnMailboxBufferReady(const VideoCaptureControllerID& id,
85 const gpu::MailboxHolder& mailbox_holder,
86 const media::VideoCaptureFormat& format,
87 base::TimeTicks timestamp) override {
88 DoMailboxBufferReady(id);
89 base::MessageLoop::current()->PostTask(
91 base::Bind(&VideoCaptureController::ReturnBuffer,
92 base::Unretained(controller_),
96 mailbox_holder.sync_point));
98 virtual void OnEnded(const VideoCaptureControllerID& id) override {
100 // OnEnded() must respond by (eventually) unregistering the client.
101 base::MessageLoop::current()->PostTask(FROM_HERE,
102 base::Bind(base::IgnoreResult(&VideoCaptureController::RemoveClient),
103 base::Unretained(controller_), id, this));
106 VideoCaptureController* controller_;
110 class VideoCaptureControllerTest : public testing::Test {
112 VideoCaptureControllerTest() {}
113 ~VideoCaptureControllerTest() override {}
116 static const int kPoolSize = 3;
118 void SetUp() override {
119 controller_.reset(new VideoCaptureController(kPoolSize));
120 device_ = controller_->NewDeviceClient().Pass();
121 client_a_.reset(new MockVideoCaptureControllerEventHandler(
123 client_b_.reset(new MockVideoCaptureControllerEventHandler(
127 void TearDown() override { base::RunLoop().RunUntilIdle(); }
129 scoped_refptr<media::VideoFrame> WrapI420Buffer(
130 const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
131 gfx::Size dimensions) {
132 return media::VideoFrame::WrapExternalPackedMemory(
133 media::VideoFrame::I420,
135 gfx::Rect(dimensions),
137 reinterpret_cast<uint8*>(buffer->data()),
138 media::VideoFrame::AllocationSize(media::VideoFrame::I420, dimensions),
139 base::SharedMemory::NULLHandle(),
144 scoped_refptr<media::VideoFrame> WrapMailboxBuffer(
145 const scoped_refptr<media::VideoCaptureDevice::Client::Buffer>& buffer,
146 scoped_ptr<gpu::MailboxHolder> holder,
147 const media::VideoFrame::ReleaseMailboxCB& release_cb,
148 gfx::Size dimensions) {
149 return media::VideoFrame::WrapNativeTexture(
153 gfx::Rect(dimensions),
156 media::VideoFrame::ReadPixelsCB());
159 TestBrowserThreadBundle bundle_;
160 scoped_ptr<MockVideoCaptureControllerEventHandler> client_a_;
161 scoped_ptr<MockVideoCaptureControllerEventHandler> client_b_;
162 scoped_ptr<VideoCaptureController> controller_;
163 scoped_ptr<media::VideoCaptureDevice::Client> device_;
166 DISALLOW_COPY_AND_ASSIGN(VideoCaptureControllerTest);
169 // A simple test of VideoCaptureController's ability to add, remove, and keep
171 TEST_F(VideoCaptureControllerTest, AddAndRemoveClients) {
172 media::VideoCaptureParams session_100;
173 session_100.requested_format = media::VideoCaptureFormat(
174 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420);
175 media::VideoCaptureParams session_200 = session_100;
177 media::VideoCaptureParams session_300 = session_100;
179 media::VideoCaptureParams session_400 = session_100;
181 // Intentionally use the same route ID for two of the clients: the device_ids
182 // are a per-VideoCaptureHost namespace, and can overlap across hosts.
183 const VideoCaptureControllerID client_a_route_1(44);
184 const VideoCaptureControllerID client_a_route_2(30);
185 const VideoCaptureControllerID client_b_route_1(30);
186 const VideoCaptureControllerID client_b_route_2(1);
188 // Clients in controller: []
189 ASSERT_EQ(0, controller_->GetClientCount())
190 << "Client count should initially be zero.";
191 controller_->AddClient(client_a_route_1,
193 base::kNullProcessHandle,
196 // Clients in controller: [A/1]
197 ASSERT_EQ(1, controller_->GetClientCount())
198 << "Adding client A/1 should bump client count.";
199 controller_->AddClient(client_a_route_2,
201 base::kNullProcessHandle,
204 // Clients in controller: [A/1, A/2]
205 ASSERT_EQ(2, controller_->GetClientCount())
206 << "Adding client A/2 should bump client count.";
207 controller_->AddClient(client_b_route_1,
209 base::kNullProcessHandle,
212 // Clients in controller: [A/1, A/2, B/1]
213 ASSERT_EQ(3, controller_->GetClientCount())
214 << "Adding client B/1 should bump client count.";
216 controller_->RemoveClient(client_a_route_2, client_a_.get()))
217 << "Removing client A/1 should return its session_id.";
218 // Clients in controller: [A/1, B/1]
219 ASSERT_EQ(2, controller_->GetClientCount());
220 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId),
221 controller_->RemoveClient(client_a_route_2, client_a_.get()))
222 << "Removing a nonexistant client should fail.";
223 // Clients in controller: [A/1, B/1]
224 ASSERT_EQ(2, controller_->GetClientCount());
226 controller_->RemoveClient(client_b_route_1, client_b_.get()))
227 << "Removing client B/1 should return its session_id.";
228 // Clients in controller: [A/1]
229 ASSERT_EQ(1, controller_->GetClientCount());
230 controller_->AddClient(client_b_route_2,
232 base::kNullProcessHandle,
235 // Clients in controller: [A/1, B/2]
237 EXPECT_CALL(*client_a_, DoEnded(client_a_route_1)).Times(1);
238 controller_->StopSession(100); // Session 100 == client A/1
239 Mock::VerifyAndClearExpectations(client_a_.get());
240 ASSERT_EQ(2, controller_->GetClientCount())
241 << "Client should be closed but still exist after StopSession.";
242 // Clients in controller: [A/1 (closed, removal pending), B/2]
243 base::RunLoop().RunUntilIdle();
244 // Clients in controller: [B/2]
245 ASSERT_EQ(1, controller_->GetClientCount())
246 << "Client A/1 should be deleted by now.";
247 controller_->StopSession(200); // Session 200 does not exist anymore
248 // Clients in controller: [B/2]
249 ASSERT_EQ(1, controller_->GetClientCount())
250 << "Stopping non-existant session 200 should be a no-op.";
251 controller_->StopSession(256); // Session 256 never existed.
252 // Clients in controller: [B/2]
253 ASSERT_EQ(1, controller_->GetClientCount())
254 << "Stopping non-existant session 256 should be a no-op.";
255 ASSERT_EQ(static_cast<int>(kInvalidMediaCaptureSessionId),
256 controller_->RemoveClient(client_a_route_1, client_a_.get()))
257 << "Removing already-removed client A/1 should fail.";
258 // Clients in controller: [B/2]
259 ASSERT_EQ(1, controller_->GetClientCount())
260 << "Removing non-existant session 200 should be a no-op.";
262 controller_->RemoveClient(client_b_route_2, client_b_.get()))
263 << "Removing client B/2 should return its session_id.";
264 // Clients in controller: []
265 ASSERT_EQ(0, controller_->GetClientCount())
266 << "Client count should return to zero after all clients are gone.";
269 static void CacheSyncPoint(uint32* called_release_sync_point,
270 uint32 release_sync_point) {
271 *called_release_sync_point = release_sync_point;
274 // This test will connect and disconnect several clients while simulating an
275 // active capture device being started and generating frames. It runs on one
276 // thread and is intended to behave deterministically.
277 TEST_F(VideoCaptureControllerTest, NormalCaptureMultipleClients) {
278 // VideoCaptureController::ReturnBuffer() uses ImageTransportFactory.
279 #if defined(OS_ANDROID)
280 ImageTransportFactoryAndroid::InitializeForUnitTests(
281 scoped_ptr<ImageTransportFactoryAndroid>(
282 new NoTransportImageTransportFactoryAndroid));
284 ImageTransportFactory::InitializeForUnitTests(
285 scoped_ptr<ImageTransportFactory>(new NoTransportImageTransportFactory));
288 media::VideoCaptureParams session_100;
289 session_100.requested_format = media::VideoCaptureFormat(
290 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420);
292 media::VideoCaptureParams session_200 = session_100;
294 media::VideoCaptureParams session_300 = session_100;
296 media::VideoCaptureParams session_1 = session_100;
298 gfx::Size capture_resolution(444, 200);
300 // The device format needn't match the VideoCaptureParams (the camera can do
301 // what it wants). Pick something random.
302 media::VideoCaptureFormat device_format(
303 gfx::Size(10, 10), 25, media::PIXEL_FORMAT_RGB24);
305 const VideoCaptureControllerID client_a_route_1(0xa1a1a1a1);
306 const VideoCaptureControllerID client_a_route_2(0xa2a2a2a2);
307 const VideoCaptureControllerID client_b_route_1(0xb1b1b1b1);
308 const VideoCaptureControllerID client_b_route_2(0xb2b2b2b2);
310 // Start with two clients.
311 controller_->AddClient(client_a_route_1,
313 base::kNullProcessHandle,
316 controller_->AddClient(client_b_route_1,
318 base::kNullProcessHandle,
321 controller_->AddClient(client_a_route_2,
323 base::kNullProcessHandle,
326 ASSERT_EQ(3, controller_->GetClientCount());
328 // Now, simulate an incoming captured buffer from the capture device. As a
329 // side effect this will cause the first buffer to be shared with clients.
331 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer;
333 device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
334 ASSERT_TRUE(buffer.get());
335 memset(buffer->data(), buffer_no++, buffer->size());
338 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1)).Times(1);
339 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1);
343 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1)).Times(1);
344 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1);
348 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2)).Times(1);
349 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1);
351 device_->OnIncomingCapturedVideoFrame(
353 media::VideoCaptureFormat(capture_resolution,
354 device_format.frame_rate,
355 media::PIXEL_FORMAT_I420),
356 WrapI420Buffer(buffer, capture_resolution),
360 base::RunLoop().RunUntilIdle();
361 Mock::VerifyAndClearExpectations(client_a_.get());
362 Mock::VerifyAndClearExpectations(client_b_.get());
364 // Second buffer which ought to use the same shared memory buffer. In this
365 // case pretend that the Buffer pointer is held by the device for a long
366 // delay. This shouldn't affect anything.
368 device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
369 ASSERT_TRUE(buffer.get());
370 memset(buffer->data(), buffer_no++, buffer->size());
371 device_->OnIncomingCapturedVideoFrame(
373 media::VideoCaptureFormat(capture_resolution,
374 device_format.frame_rate,
375 media::PIXEL_FORMAT_I420),
376 WrapI420Buffer(buffer, capture_resolution),
380 // The buffer should be delivered to the clients in any order.
381 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(1);
382 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(1);
383 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(1);
384 base::RunLoop().RunUntilIdle();
385 Mock::VerifyAndClearExpectations(client_a_.get());
386 Mock::VerifyAndClearExpectations(client_b_.get());
388 // Add a fourth client now that some buffers have come through.
389 controller_->AddClient(client_b_route_2,
391 base::kNullProcessHandle,
394 Mock::VerifyAndClearExpectations(client_b_.get());
396 // Third, fourth, and fifth buffers. Pretend they all arrive at the same time.
397 for (int i = 0; i < kPoolSize; i++) {
398 buffer = device_->ReserveOutputBuffer(media::VideoFrame::I420,
400 ASSERT_TRUE(buffer.get());
401 memset(buffer->data(), buffer_no++, buffer->size());
402 device_->OnIncomingCapturedVideoFrame(
404 media::VideoCaptureFormat(capture_resolution,
405 device_format.frame_rate,
406 media::PIXEL_FORMAT_I420),
407 WrapI420Buffer(buffer, capture_resolution),
411 // ReserveOutputBuffer ought to fail now, because the pool is depleted.
412 ASSERT_FALSE(device_->ReserveOutputBuffer(media::VideoFrame::I420,
413 capture_resolution).get());
415 // The new client needs to be told of 3 buffers; the old clients only 2.
416 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_2)).Times(kPoolSize);
417 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(kPoolSize);
418 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_1))
419 .Times(kPoolSize - 1);
420 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_1)).Times(kPoolSize);
421 EXPECT_CALL(*client_a_, DoBufferCreated(client_a_route_2))
422 .Times(kPoolSize - 1);
423 EXPECT_CALL(*client_a_, DoBufferReady(client_a_route_2)).Times(kPoolSize);
424 EXPECT_CALL(*client_b_, DoBufferCreated(client_b_route_1))
425 .Times(kPoolSize - 1);
426 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_1)).Times(kPoolSize);
427 base::RunLoop().RunUntilIdle();
428 Mock::VerifyAndClearExpectations(client_a_.get());
429 Mock::VerifyAndClearExpectations(client_b_.get());
431 // Now test the interaction of client shutdown and buffer delivery.
432 // Kill A1 via renderer disconnect (synchronous).
433 controller_->RemoveClient(client_a_route_1, client_a_.get());
434 // Kill B1 via session close (posts a task to disconnect).
435 EXPECT_CALL(*client_b_, DoEnded(client_b_route_1)).Times(1);
436 controller_->StopSession(300);
437 // Queue up another buffer.
439 device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
440 ASSERT_TRUE(buffer.get());
441 memset(buffer->data(), buffer_no++, buffer->size());
442 device_->OnIncomingCapturedVideoFrame(
444 media::VideoCaptureFormat(capture_resolution,
445 device_format.frame_rate,
446 media::PIXEL_FORMAT_I420),
447 WrapI420Buffer(buffer, capture_resolution),
451 device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
453 // Kill A2 via session close (posts a task to disconnect, but A2 must not
454 // be sent either of these two buffers).
455 EXPECT_CALL(*client_a_, DoEnded(client_a_route_2)).Times(1);
456 controller_->StopSession(200);
458 ASSERT_TRUE(buffer.get());
459 memset(buffer->data(), buffer_no++, buffer->size());
460 device_->OnIncomingCapturedVideoFrame(
462 media::VideoCaptureFormat(capture_resolution,
463 device_format.frame_rate,
464 media::PIXEL_FORMAT_I420),
465 WrapI420Buffer(buffer, capture_resolution),
468 // B2 is the only client left, and is the only one that should
470 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(2);
471 base::RunLoop().RunUntilIdle();
472 Mock::VerifyAndClearExpectations(client_a_.get());
473 Mock::VerifyAndClearExpectations(client_b_.get());
475 // Allocate all buffers from the buffer pool, half as SHM buffer and half as
476 // mailbox buffers. Make sure of different counts though.
477 int shm_buffers = kPoolSize / 2;
478 int mailbox_buffers = kPoolSize - shm_buffers;
479 if (shm_buffers == mailbox_buffers) {
484 for (int i = 0; i < shm_buffers; ++i) {
485 buffer = device_->ReserveOutputBuffer(media::VideoFrame::I420,
487 ASSERT_TRUE(buffer.get());
488 device_->OnIncomingCapturedVideoFrame(
490 media::VideoCaptureFormat(capture_resolution,
491 device_format.frame_rate,
492 media::PIXEL_FORMAT_I420),
493 WrapI420Buffer(buffer, capture_resolution),
497 std::vector<uint32> mailbox_syncpoints(mailbox_buffers);
498 std::vector<uint32> release_syncpoints(mailbox_buffers);
499 #if defined(OS_ANDROID)
500 GLHelper* gl_helper =
501 ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
503 GLHelper* gl_helper = ImageTransportFactory::GetInstance()->GetGLHelper();
505 for (int i = 0; i < mailbox_buffers; ++i) {
506 buffer = device_->ReserveOutputBuffer(media::VideoFrame::NATIVE_TEXTURE,
508 ASSERT_TRUE(buffer.get());
509 mailbox_syncpoints[i] = gl_helper->InsertSyncPoint();
510 device_->OnIncomingCapturedVideoFrame(
512 media::VideoCaptureFormat(capture_resolution,
513 device_format.frame_rate,
514 media::PIXEL_FORMAT_TEXTURE),
515 WrapMailboxBuffer(buffer,
516 make_scoped_ptr(new gpu::MailboxHolder(
517 gpu::Mailbox(), 0, mailbox_syncpoints[i])),
518 base::Bind(&CacheSyncPoint, &release_syncpoints[i]),
523 // ReserveOutputBuffers ought to fail now regardless of buffer format, because
524 // the pool is depleted.
525 ASSERT_FALSE(device_->ReserveOutputBuffer(media::VideoFrame::I420,
526 capture_resolution).get());
527 ASSERT_FALSE(device_->ReserveOutputBuffer(media::VideoFrame::NATIVE_TEXTURE,
528 gfx::Size(0, 0)).get());
529 EXPECT_CALL(*client_b_, DoBufferReady(client_b_route_2)).Times(shm_buffers);
530 EXPECT_CALL(*client_b_, DoMailboxBufferReady(client_b_route_2))
531 .Times(mailbox_buffers);
532 base::RunLoop().RunUntilIdle();
533 for (size_t i = 0; i < mailbox_syncpoints.size(); ++i) {
534 // A new release sync point must be inserted when the video frame is
535 // returned to the Browser process.
536 // See: MockVideoCaptureControllerEventHandler::OnMailboxBufferReady() and
537 // VideoCaptureController::ReturnBuffer()
538 ASSERT_NE(mailbox_syncpoints[i], release_syncpoints[i]);
540 Mock::VerifyAndClearExpectations(client_b_.get());
542 #if defined(OS_ANDROID)
543 ImageTransportFactoryAndroid::TerminateForUnitTests();
545 ImageTransportFactory::Terminate();
549 // Exercises the OnError() codepath of VideoCaptureController, and tests the
550 // behavior of various operations after the error state has been signalled.
551 TEST_F(VideoCaptureControllerTest, ErrorBeforeDeviceCreation) {
552 media::VideoCaptureParams session_100;
553 session_100.requested_format = media::VideoCaptureFormat(
554 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420);
556 media::VideoCaptureParams session_200 = session_100;
558 const gfx::Size capture_resolution(320, 240);
560 const VideoCaptureControllerID route_id(0x99);
562 // Start with one client.
563 controller_->AddClient(
564 route_id, client_a_.get(), base::kNullProcessHandle, 100, session_100);
565 device_->OnError("Test Error");
566 EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
567 base::RunLoop().RunUntilIdle();
568 Mock::VerifyAndClearExpectations(client_a_.get());
570 // Second client connects after the error state. It also should get told of
572 EXPECT_CALL(*client_b_, DoError(route_id)).Times(1);
573 controller_->AddClient(
574 route_id, client_b_.get(), base::kNullProcessHandle, 200, session_200);
575 base::RunLoop().RunUntilIdle();
576 Mock::VerifyAndClearExpectations(client_b_.get());
578 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer =
579 device_->ReserveOutputBuffer(media::VideoFrame::I420, capture_resolution);
580 ASSERT_TRUE(buffer.get());
582 device_->OnIncomingCapturedVideoFrame(
584 media::VideoCaptureFormat(
585 capture_resolution, 30, media::PIXEL_FORMAT_I420),
586 WrapI420Buffer(buffer, capture_resolution),
590 base::RunLoop().RunUntilIdle();
593 // Exercises the OnError() codepath of VideoCaptureController, and tests the
594 // behavior of various operations after the error state has been signalled.
595 TEST_F(VideoCaptureControllerTest, ErrorAfterDeviceCreation) {
596 media::VideoCaptureParams session_100;
597 session_100.requested_format = media::VideoCaptureFormat(
598 gfx::Size(320, 240), 30, media::PIXEL_FORMAT_I420);
600 media::VideoCaptureParams session_200 = session_100;
602 const VideoCaptureControllerID route_id(0x99);
604 // Start with one client.
605 controller_->AddClient(
606 route_id, client_a_.get(), base::kNullProcessHandle, 100, session_100);
607 media::VideoCaptureFormat device_format(
608 gfx::Size(10, 10), 25, media::PIXEL_FORMAT_ARGB);
610 // Start the device. Then, before the first buffer, signal an error and
611 // deliver the buffer. The error should be propagated to clients; the buffer
613 base::RunLoop().RunUntilIdle();
614 Mock::VerifyAndClearExpectations(client_a_.get());
616 const gfx::Size dims(320, 240);
617 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer =
618 device_->ReserveOutputBuffer(media::VideoFrame::I420, dims);
619 ASSERT_TRUE(buffer.get());
621 device_->OnError("Test error");
622 device_->OnIncomingCapturedVideoFrame(
624 media::VideoCaptureFormat(
625 dims, device_format.frame_rate, media::PIXEL_FORMAT_I420),
626 WrapI420Buffer(buffer, dims),
630 EXPECT_CALL(*client_a_, DoError(route_id)).Times(1);
631 base::RunLoop().RunUntilIdle();
632 Mock::VerifyAndClearExpectations(client_a_.get());
634 // Second client connects after the error state. It also should get told of
636 EXPECT_CALL(*client_b_, DoError(route_id)).Times(1);
637 controller_->AddClient(
638 route_id, client_b_.get(), base::kNullProcessHandle, 200, session_200);
639 Mock::VerifyAndClearExpectations(client_b_.get());
642 } // namespace content