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.
9 #include "base/command_line.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_file.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/run_loop.h"
15 #include "base/stl_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "content/browser/browser_thread_impl.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h"
19 #include "content/browser/renderer_host/media/media_stream_requester.h"
20 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
21 #include "content/browser/renderer_host/media/video_capture_host.h"
22 #include "content/browser/renderer_host/media/video_capture_manager.h"
23 #include "content/common/media/video_capture_messages.h"
24 #include "content/public/common/content_switches.h"
25 #include "content/public/test/mock_resource_context.h"
26 #include "content/public/test/test_browser_context.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "content/test/test_content_browser_client.h"
29 #include "media/audio/audio_manager.h"
30 #include "media/base/media_switches.h"
31 #include "media/base/video_frame.h"
32 #include "media/video/capture/video_capture_types.h"
33 #include "net/url_request/url_request_context.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
38 using ::testing::AtLeast
;
39 using ::testing::AnyNumber
;
40 using ::testing::DoAll
;
41 using ::testing::InSequence
;
42 using ::testing::Mock
;
43 using ::testing::Return
;
44 using ::testing::SaveArg
;
45 using ::testing::StrictMock
;
49 // Id used to identify the capture session between renderer and
50 // video_capture_host. This is an arbitrary value.
51 static const int kDeviceId
= 555;
53 // Define to enable test where video is dumped to file.
56 // Define to use a real video capture device.
57 // #define TEST_REAL_CAPTURE_DEVICE
59 // Simple class used for dumping video to a file. This can be used for
60 // verifying the output.
63 DumpVideo() : expected_size_(0) {}
64 void StartDump(int width
, int height
) {
65 base::FilePath file_name
= base::FilePath(base::StringPrintf(
66 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width
, height
));
67 file_
.reset(base::OpenFile(file_name
, "wb"));
68 expected_size_
= media::VideoFrame::AllocationSize(
69 media::VideoFrame::I420
, gfx::Size(width
, height
));
71 void NewVideoFrame(const void* buffer
) {
72 if (file_
.get() != NULL
) {
73 ASSERT_EQ(1U, fwrite(buffer
, expected_size_
, 1, file_
.get()));
78 base::ScopedFILE file_
;
82 class MockMediaStreamRequester
: public MediaStreamRequester
{
84 MockMediaStreamRequester() {}
85 virtual ~MockMediaStreamRequester() {}
87 // MediaStreamRequester implementation.
88 MOCK_METHOD5(StreamGenerated
,
89 void(int render_frame_id
,
91 const std::string
& label
,
92 const StreamDeviceInfoArray
& audio_devices
,
93 const StreamDeviceInfoArray
& video_devices
));
94 MOCK_METHOD3(StreamGenerationFailed
,
95 void(int render_frame_id
,
97 content::MediaStreamRequestResult result
));
98 MOCK_METHOD3(DeviceStopped
, void(int render_frame_id
,
99 const std::string
& label
,
100 const StreamDeviceInfo
& device
));
101 MOCK_METHOD4(DevicesEnumerated
, void(int render_frame_id
,
103 const std::string
& label
,
104 const StreamDeviceInfoArray
& devices
));
105 MOCK_METHOD4(DeviceOpened
, void(int render_frame_id
,
107 const std::string
& label
,
108 const StreamDeviceInfo
& device_info
));
111 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester
);
114 class MockVideoCaptureHost
: public VideoCaptureHost
{
116 MockVideoCaptureHost(MediaStreamManager
* manager
)
117 : VideoCaptureHost(manager
),
118 return_buffers_(false),
119 dump_video_(false) {}
121 // A list of mock methods.
122 MOCK_METHOD4(OnNewBufferCreated
,
124 base::SharedMemoryHandle handle
,
127 MOCK_METHOD2(OnBufferFreed
,
128 void(int device_id
, int buffer_id
));
129 MOCK_METHOD5(OnBufferFilled
,
132 const media::VideoCaptureFormat
& format
,
133 const gfx::Rect
& visible_rect
,
134 base::TimeTicks timestamp
));
135 MOCK_METHOD5(OnMailboxBufferFilled
,
138 const gpu::MailboxHolder
& mailbox_holder
,
139 const media::VideoCaptureFormat
& format
,
140 base::TimeTicks timestamp
));
141 MOCK_METHOD2(OnStateChanged
, void(int device_id
, VideoCaptureState state
));
143 // Use class DumpVideo to write I420 video to file.
144 void SetDumpVideo(bool enable
) {
145 dump_video_
= enable
;
148 void SetReturnReceivedDibs(bool enable
) {
149 return_buffers_
= enable
;
152 // Return Dibs we currently have received.
153 void ReturnReceivedDibs(int device_id
) {
154 int handle
= GetReceivedDib();
156 this->OnReceiveEmptyBuffer(device_id
, handle
, 0);
157 handle
= GetReceivedDib();
161 int GetReceivedDib() {
162 if (filled_dib_
.empty())
164 std::map
<int, base::SharedMemory
*>::iterator it
= filled_dib_
.begin();
167 filled_dib_
.erase(it
);
173 virtual ~MockVideoCaptureHost() {
174 STLDeleteContainerPairSecondPointers(filled_dib_
.begin(),
178 // This method is used to dispatch IPC messages to the renderer. We intercept
179 // these messages here and dispatch to our mock methods to verify the
180 // conversation between this object and the renderer.
181 virtual bool Send(IPC::Message
* message
) OVERRIDE
{
184 // In this method we dispatch the messages to the according handlers as if
185 // we are the renderer.
187 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost
, *message
)
188 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer
, OnNewBufferCreatedDispatch
)
189 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer
, OnBufferFreedDispatch
)
190 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady
, OnBufferFilledDispatch
)
191 IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady
,
192 OnMailboxBufferFilledDispatch
)
193 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged
, OnStateChangedDispatch
)
194 IPC_MESSAGE_UNHANDLED(handled
= false)
195 IPC_END_MESSAGE_MAP()
196 EXPECT_TRUE(handled
);
202 // These handler methods do minimal things and delegate to the mock methods.
203 void OnNewBufferCreatedDispatch(int device_id
,
204 base::SharedMemoryHandle handle
,
207 OnNewBufferCreated(device_id
, handle
, length
, buffer_id
);
208 base::SharedMemory
* dib
= new base::SharedMemory(handle
, false);
210 filled_dib_
[buffer_id
] = dib
;
213 void OnBufferFreedDispatch(int device_id
, int buffer_id
) {
214 OnBufferFreed(device_id
, buffer_id
);
216 std::map
<int, base::SharedMemory
*>::iterator it
=
217 filled_dib_
.find(buffer_id
);
218 ASSERT_TRUE(it
!= filled_dib_
.end());
220 filled_dib_
.erase(it
);
223 void OnBufferFilledDispatch(int device_id
,
225 const media::VideoCaptureFormat
& frame_format
,
226 const gfx::Rect
& visible_rect
,
227 base::TimeTicks timestamp
) {
228 base::SharedMemory
* dib
= filled_dib_
[buffer_id
];
229 ASSERT_TRUE(dib
!= NULL
);
231 if (!format_
.IsValid()) {
232 dumper_
.StartDump(frame_format
.frame_size
.width(),
233 frame_format
.frame_size
.height());
234 format_
= frame_format
;
236 ASSERT_EQ(format_
.frame_size
.width(), frame_format
.frame_size
.width())
237 << "Dump format does not handle variable resolution.";
238 ASSERT_EQ(format_
.frame_size
.height(), frame_format
.frame_size
.height())
239 << "Dump format does not handle variable resolution.";
240 dumper_
.NewVideoFrame(dib
->memory());
243 OnBufferFilled(device_id
, buffer_id
, frame_format
, visible_rect
, timestamp
);
244 if (return_buffers_
) {
245 VideoCaptureHost::OnReceiveEmptyBuffer(device_id
, buffer_id
, 0);
249 void OnMailboxBufferFilledDispatch(int device_id
,
251 const gpu::MailboxHolder
& mailbox_holder
,
252 const media::VideoCaptureFormat
& format
,
253 base::TimeTicks timestamp
) {
254 OnMailboxBufferFilled(
255 device_id
, buffer_id
, mailbox_holder
, format
, timestamp
);
256 if (return_buffers_
) {
257 VideoCaptureHost::OnReceiveEmptyBuffer(device_id
, buffer_id
, 0);
261 void OnStateChangedDispatch(int device_id
, VideoCaptureState state
) {
262 OnStateChanged(device_id
, state
);
265 std::map
<int, base::SharedMemory
*> filled_dib_
;
266 bool return_buffers_
;
268 media::VideoCaptureFormat format_
;
272 ACTION_P2(ExitMessageLoop
, message_loop
, quit_closure
) {
273 message_loop
->PostTask(FROM_HERE
, quit_closure
);
276 // This is an integration test of VideoCaptureHost in conjunction with
277 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
278 // VideoCaptureDevice.
279 class VideoCaptureHostTest
: public testing::Test
{
281 VideoCaptureHostTest()
282 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
283 message_loop_(base::MessageLoopProxy::current()),
284 opened_session_id_(kInvalidMediaCaptureSessionId
) {}
286 virtual void SetUp() OVERRIDE
{
287 SetBrowserClientForTesting(&browser_client_
);
288 // Create our own MediaStreamManager.
289 audio_manager_
.reset(media::AudioManager::CreateForTesting());
290 #ifndef TEST_REAL_CAPTURE_DEVICE
291 base::CommandLine::ForCurrentProcess()->AppendSwitch(
292 switches::kUseFakeDeviceForMediaStream
);
294 media_stream_manager_
.reset(new MediaStreamManager(audio_manager_
.get()));
295 media_stream_manager_
->UseFakeUI(scoped_ptr
<FakeMediaStreamUIProxy
>());
297 // Create a Host and connect it to a simulated IPC channel.
298 host_
= new MockVideoCaptureHost(media_stream_manager_
.get());
299 host_
->OnChannelConnected(base::GetCurrentProcId());
304 virtual void TearDown() OVERRIDE
{
305 // Verifies and removes the expectations on host_ and
306 // returns true iff successful.
307 Mock::VerifyAndClearExpectations(host_
.get());
308 EXPECT_EQ(0u, host_
->entries_
.size());
312 // Simulate closing the IPC sender.
313 host_
->OnChannelClosing();
315 // Release the reference to the mock object. The object will be destructed
316 // on the current message loop.
321 const int render_process_id
= 1;
322 const int render_frame_id
= 1;
323 const int page_request_id
= 1;
324 const GURL
security_origin("http://test.com");
326 ASSERT_TRUE(opened_device_label_
.empty());
328 // Enumerate video devices.
329 StreamDeviceInfoArray devices
;
331 base::RunLoop run_loop
;
332 std::string label
= media_stream_manager_
->EnumerateDevices(
336 browser_context_
.GetResourceContext()->GetMediaDeviceIDSalt(),
338 MEDIA_DEVICE_VIDEO_CAPTURE
,
340 EXPECT_CALL(stream_requester_
, DevicesEnumerated(render_frame_id
,
345 DoAll(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()),
346 SaveArg
<3>(&devices
)));
348 Mock::VerifyAndClearExpectations(&stream_requester_
);
349 media_stream_manager_
->CancelRequest(label
);
351 ASSERT_FALSE(devices
.empty());
352 ASSERT_EQ(StreamDeviceInfo::kNoId
, devices
[0].session_id
);
354 // Open the first device.
356 base::RunLoop run_loop
;
357 StreamDeviceInfo opened_device
;
358 media_stream_manager_
->OpenDevice(
362 browser_context_
.GetResourceContext()->GetMediaDeviceIDSalt(),
364 devices
[0].device
.id
,
365 MEDIA_DEVICE_VIDEO_CAPTURE
,
367 EXPECT_CALL(stream_requester_
, DeviceOpened(render_frame_id
,
372 DoAll(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()),
373 SaveArg
<2>(&opened_device_label_
),
374 SaveArg
<3>(&opened_device
)));
376 Mock::VerifyAndClearExpectations(&stream_requester_
);
377 ASSERT_NE(StreamDeviceInfo::kNoId
, opened_device
.session_id
);
378 opened_session_id_
= opened_device
.session_id
;
382 void CloseSession() {
383 if (opened_device_label_
.empty())
385 media_stream_manager_
->CancelRequest(opened_device_label_
);
386 opened_device_label_
.clear();
387 opened_session_id_
= kInvalidMediaCaptureSessionId
;
391 void StartCapture() {
392 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
394 .WillRepeatedly(Return());
396 base::RunLoop run_loop
;
397 EXPECT_CALL(*host_
.get(), OnBufferFilled(kDeviceId
, _
, _
, _
, _
))
399 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
401 media::VideoCaptureParams params
;
402 params
.requested_format
= media::VideoCaptureFormat(
403 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
404 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
408 void StartStopCapture() {
409 // Quickly start and then stop capture, without giving much chance for
410 // asynchronous start operations to complete.
412 base::RunLoop run_loop
;
413 EXPECT_CALL(*host_
.get(),
414 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
));
415 media::VideoCaptureParams params
;
416 params
.requested_format
= media::VideoCaptureFormat(
417 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
418 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
419 host_
->OnStopCapture(kDeviceId
);
420 run_loop
.RunUntilIdle();
424 void CaptureAndDumpVideo(int width
, int height
, int frame_rate
) {
426 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
427 .Times(AnyNumber()).WillRepeatedly(Return());
429 base::RunLoop run_loop
;
430 EXPECT_CALL(*host_
, OnBufferFilled(kDeviceId
, _
, _
, _
, _
))
432 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
434 media::VideoCaptureParams params
;
435 params
.requested_format
=
436 media::VideoCaptureFormat(gfx::Size(width
, height
), frame_rate
);
437 host_
->SetDumpVideo(true);
438 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
444 base::RunLoop run_loop
;
445 EXPECT_CALL(*host_
.get(),
446 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
))
447 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
449 host_
->OnStopCapture(kDeviceId
);
450 host_
->SetReturnReceivedDibs(true);
451 host_
->ReturnReceivedDibs(kDeviceId
);
455 host_
->SetReturnReceivedDibs(false);
456 // Expect the VideoCaptureDevice has been stopped
457 EXPECT_EQ(0u, host_
->entries_
.size());
460 void NotifyPacketReady() {
461 base::RunLoop run_loop
;
462 EXPECT_CALL(*host_
.get(), OnBufferFilled(kDeviceId
, _
, _
, _
, _
))
464 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()))
465 .RetiresOnSaturation();
469 void ReturnReceivedPackets() {
470 host_
->ReturnReceivedDibs(kDeviceId
);
473 void SimulateError() {
474 // Expect a change state to error state sent through IPC.
475 EXPECT_CALL(*host_
.get(),
476 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ERROR
)).Times(1);
477 VideoCaptureControllerID
id(kDeviceId
);
479 // Wait for the error callback.
480 base::RunLoop().RunUntilIdle();
483 scoped_refptr
<MockVideoCaptureHost
> host_
;
486 StrictMock
<MockMediaStreamRequester
> stream_requester_
;
487 scoped_ptr
<media::AudioManager
> audio_manager_
;
488 scoped_ptr
<MediaStreamManager
> media_stream_manager_
;
489 content::TestBrowserThreadBundle thread_bundle_
;
490 content::TestBrowserContext browser_context_
;
491 content::TestContentBrowserClient browser_client_
;
492 scoped_refptr
<base::MessageLoopProxy
> message_loop_
;
493 int opened_session_id_
;
494 std::string opened_device_label_
;
496 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest
);
499 TEST_F(VideoCaptureHostTest
, CloseSessionWithoutStopping
) {
502 // When the session is closed via the stream without stopping capture, the
503 // ENDED event is sent.
504 EXPECT_CALL(*host_
.get(),
505 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ENDED
)).Times(1);
507 base::RunLoop().RunUntilIdle();
510 TEST_F(VideoCaptureHostTest
, StopWhileStartPending
) {
514 TEST_F(VideoCaptureHostTest
, StartCapturePlayStop
) {
518 ReturnReceivedPackets();
522 TEST_F(VideoCaptureHostTest
, StartCaptureErrorStop
) {
528 TEST_F(VideoCaptureHostTest
, StartCaptureError
) {
529 EXPECT_CALL(*host_
.get(),
530 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
)).Times(0);
534 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
538 TEST_F(VideoCaptureHostTest
, CaptureAndDumpVideoVga
) {
539 CaptureAndDumpVideo(640, 480, 30);
541 TEST_F(VideoCaptureHostTest
, CaptureAndDump720P
) {
542 CaptureAndDumpVideo(1280, 720, 30);
546 } // namespace content