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_capture_types.h"
32 #include "media/base/video_frame.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"
37 #if defined(OS_CHROMEOS)
38 #include "chromeos/audio/cras_audio_handler.h"
42 using ::testing::AtLeast
;
43 using ::testing::AnyNumber
;
44 using ::testing::DoAll
;
45 using ::testing::InSequence
;
46 using ::testing::Mock
;
47 using ::testing::Return
;
48 using ::testing::SaveArg
;
49 using ::testing::StrictMock
;
53 // Id used to identify the capture session between renderer and
54 // video_capture_host. This is an arbitrary value.
55 static const int kDeviceId
= 555;
57 // Define to enable test where video is dumped to file.
60 // Define to use a real video capture device.
61 // #define TEST_REAL_CAPTURE_DEVICE
63 // Simple class used for dumping video to a file. This can be used for
64 // verifying the output.
68 const gfx::Size
& coded_size() const { return coded_size_
; }
69 void StartDump(const gfx::Size
& coded_size
) {
70 base::FilePath file_name
= base::FilePath(base::StringPrintf(
71 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"),
73 coded_size
.height()));
74 file_
.reset(base::OpenFile(file_name
, "wb"));
75 coded_size_
= coded_size
;
77 void NewVideoFrame(const void* buffer
) {
78 if (file_
.get() != NULL
) {
79 const int size
= media::VideoFrame::AllocationSize(
80 media::VideoFrame::I420
, coded_size_
);
81 ASSERT_EQ(1U, fwrite(buffer
, size
, 1, file_
.get()));
86 base::ScopedFILE file_
;
87 gfx::Size coded_size_
;
90 class MockMediaStreamRequester
: public MediaStreamRequester
{
92 MockMediaStreamRequester() {}
93 virtual ~MockMediaStreamRequester() {}
95 // MediaStreamRequester implementation.
96 MOCK_METHOD5(StreamGenerated
,
97 void(int render_frame_id
,
99 const std::string
& label
,
100 const StreamDeviceInfoArray
& audio_devices
,
101 const StreamDeviceInfoArray
& video_devices
));
102 MOCK_METHOD3(StreamGenerationFailed
,
103 void(int render_frame_id
,
105 content::MediaStreamRequestResult result
));
106 MOCK_METHOD3(DeviceStopped
, void(int render_frame_id
,
107 const std::string
& label
,
108 const StreamDeviceInfo
& device
));
109 MOCK_METHOD4(DevicesEnumerated
, void(int render_frame_id
,
111 const std::string
& label
,
112 const StreamDeviceInfoArray
& devices
));
113 MOCK_METHOD4(DeviceOpened
, void(int render_frame_id
,
115 const std::string
& label
,
116 const StreamDeviceInfo
& device_info
));
119 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester
);
122 class MockVideoCaptureHost
: public VideoCaptureHost
{
124 MockVideoCaptureHost(MediaStreamManager
* manager
)
125 : VideoCaptureHost(manager
),
126 return_buffers_(false),
127 dump_video_(false) {}
129 // A list of mock methods.
130 MOCK_METHOD4(OnNewBufferCreated
,
132 base::SharedMemoryHandle handle
,
135 MOCK_METHOD2(OnBufferFreed
,
136 void(int device_id
, int buffer_id
));
137 MOCK_METHOD6(OnBufferFilled
,
140 const gfx::Size
& coded_size
,
141 const gfx::Rect
& visible_rect
,
142 const base::TimeTicks
& timestamp
,
143 const base::DictionaryValue
& metadata
));
144 MOCK_METHOD6(OnMailboxBufferFilled
,
147 const gpu::MailboxHolder
& mailbox_holder
,
148 const gfx::Size
& packed_frame_size
,
149 const base::TimeTicks
& timestamp
,
150 const base::DictionaryValue
& metadata
));
151 MOCK_METHOD2(OnStateChanged
, void(int device_id
, VideoCaptureState state
));
153 // Use class DumpVideo to write I420 video to file.
154 void SetDumpVideo(bool enable
) {
155 dump_video_
= enable
;
158 void SetReturnReceivedDibs(bool enable
) {
159 return_buffers_
= enable
;
162 // Return Dibs we currently have received.
163 void ReturnReceivedDibs(int device_id
) {
164 int handle
= GetReceivedDib();
166 this->OnReceiveEmptyBuffer(device_id
, handle
, 0);
167 handle
= GetReceivedDib();
171 int GetReceivedDib() {
172 if (filled_dib_
.empty())
174 std::map
<int, base::SharedMemory
*>::iterator it
= filled_dib_
.begin();
177 filled_dib_
.erase(it
);
183 virtual ~MockVideoCaptureHost() {
184 STLDeleteContainerPairSecondPointers(filled_dib_
.begin(),
188 // This method is used to dispatch IPC messages to the renderer. We intercept
189 // these messages here and dispatch to our mock methods to verify the
190 // conversation between this object and the renderer.
191 virtual bool Send(IPC::Message
* message
) override
{
194 // In this method we dispatch the messages to the according handlers as if
195 // we are the renderer.
197 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost
, *message
)
198 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer
, OnNewBufferCreatedDispatch
)
199 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer
, OnBufferFreedDispatch
)
200 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady
, OnBufferFilledDispatch
)
201 IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady
,
202 OnMailboxBufferFilledDispatch
)
203 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged
, OnStateChangedDispatch
)
204 IPC_MESSAGE_UNHANDLED(handled
= false)
205 IPC_END_MESSAGE_MAP()
206 EXPECT_TRUE(handled
);
212 // These handler methods do minimal things and delegate to the mock methods.
213 void OnNewBufferCreatedDispatch(int device_id
,
214 base::SharedMemoryHandle handle
,
217 OnNewBufferCreated(device_id
, handle
, length
, buffer_id
);
218 base::SharedMemory
* dib
= new base::SharedMemory(handle
, false);
220 filled_dib_
[buffer_id
] = dib
;
223 void OnBufferFreedDispatch(int device_id
, int buffer_id
) {
224 OnBufferFreed(device_id
, buffer_id
);
226 std::map
<int, base::SharedMemory
*>::iterator it
=
227 filled_dib_
.find(buffer_id
);
228 ASSERT_TRUE(it
!= filled_dib_
.end());
230 filled_dib_
.erase(it
);
233 void OnBufferFilledDispatch(
234 const VideoCaptureMsg_BufferReady_Params
& params
) {
235 base::SharedMemory
* dib
= filled_dib_
[params
.buffer_id
];
236 ASSERT_TRUE(dib
!= NULL
);
238 if (dumper_
.coded_size().IsEmpty())
239 dumper_
.StartDump(params
.coded_size
);
240 ASSERT_TRUE(dumper_
.coded_size() == params
.coded_size
)
241 << "Dump format does not handle variable resolution.";
242 dumper_
.NewVideoFrame(dib
->memory());
245 OnBufferFilled(params
.device_id
, params
.buffer_id
, params
.coded_size
,
246 params
.visible_rect
, params
.timestamp
, params
.metadata
);
247 if (return_buffers_
) {
248 VideoCaptureHost::OnReceiveEmptyBuffer(
249 params
.device_id
, params
.buffer_id
, 0);
253 void OnMailboxBufferFilledDispatch(
254 const VideoCaptureMsg_MailboxBufferReady_Params
& params
) {
255 OnMailboxBufferFilled(params
.device_id
, params
.buffer_id
,
256 params
.mailbox_holder
, params
.packed_frame_size
,
257 params
.timestamp
, params
.metadata
);
258 if (return_buffers_
) {
259 VideoCaptureHost::OnReceiveEmptyBuffer(
260 params
.device_id
, params
.buffer_id
, 0);
264 void OnStateChangedDispatch(int device_id
, VideoCaptureState state
) {
265 OnStateChanged(device_id
, state
);
268 std::map
<int, base::SharedMemory
*> filled_dib_
;
269 bool return_buffers_
;
271 media::VideoCaptureFormat format_
;
275 ACTION_P2(ExitMessageLoop
, message_loop
, quit_closure
) {
276 message_loop
->PostTask(FROM_HERE
, quit_closure
);
279 // This is an integration test of VideoCaptureHost in conjunction with
280 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
281 // VideoCaptureDevice.
282 class VideoCaptureHostTest
: public testing::Test
{
284 VideoCaptureHostTest()
285 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
286 message_loop_(base::MessageLoopProxy::current()),
287 opened_session_id_(kInvalidMediaCaptureSessionId
) {}
289 virtual void SetUp() override
{
290 SetBrowserClientForTesting(&browser_client_
);
292 #if defined(OS_CHROMEOS)
293 chromeos::CrasAudioHandler::InitializeForTesting();
296 // Create our own MediaStreamManager.
297 audio_manager_
.reset(media::AudioManager::CreateForTesting());
298 #ifndef TEST_REAL_CAPTURE_DEVICE
299 base::CommandLine::ForCurrentProcess()->AppendSwitch(
300 switches::kUseFakeDeviceForMediaStream
);
302 media_stream_manager_
.reset(new MediaStreamManager(audio_manager_
.get()));
303 media_stream_manager_
->UseFakeUI(scoped_ptr
<FakeMediaStreamUIProxy
>());
305 // Create a Host and connect it to a simulated IPC channel.
306 host_
= new MockVideoCaptureHost(media_stream_manager_
.get());
307 host_
->OnChannelConnected(base::GetCurrentProcId());
312 virtual void TearDown() override
{
313 // Verifies and removes the expectations on host_ and
314 // returns true iff successful.
315 Mock::VerifyAndClearExpectations(host_
.get());
316 EXPECT_EQ(0u, host_
->entries_
.size());
320 // Simulate closing the IPC sender.
321 host_
->OnChannelClosing();
323 // Release the reference to the mock object. The object will be destructed
324 // on the current message loop.
327 #if defined(OS_CHROMEOS)
328 chromeos::CrasAudioHandler::Shutdown();
333 const int render_process_id
= 1;
334 const int render_frame_id
= 1;
335 const int page_request_id
= 1;
336 const GURL
security_origin("http://test.com");
338 ASSERT_TRUE(opened_device_label_
.empty());
340 // Enumerate video devices.
341 StreamDeviceInfoArray devices
;
343 base::RunLoop run_loop
;
344 std::string label
= media_stream_manager_
->EnumerateDevices(
348 browser_context_
.GetResourceContext()->GetMediaDeviceIDSalt(),
350 MEDIA_DEVICE_VIDEO_CAPTURE
,
352 EXPECT_CALL(stream_requester_
, DevicesEnumerated(render_frame_id
,
357 DoAll(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()),
358 SaveArg
<3>(&devices
)));
360 Mock::VerifyAndClearExpectations(&stream_requester_
);
361 media_stream_manager_
->CancelRequest(label
);
363 ASSERT_FALSE(devices
.empty());
364 ASSERT_EQ(StreamDeviceInfo::kNoId
, devices
[0].session_id
);
366 // Open the first device.
368 base::RunLoop run_loop
;
369 StreamDeviceInfo opened_device
;
370 media_stream_manager_
->OpenDevice(
374 browser_context_
.GetResourceContext()->GetMediaDeviceIDSalt(),
376 devices
[0].device
.id
,
377 MEDIA_DEVICE_VIDEO_CAPTURE
,
379 EXPECT_CALL(stream_requester_
, DeviceOpened(render_frame_id
,
384 DoAll(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()),
385 SaveArg
<2>(&opened_device_label_
),
386 SaveArg
<3>(&opened_device
)));
388 Mock::VerifyAndClearExpectations(&stream_requester_
);
389 ASSERT_NE(StreamDeviceInfo::kNoId
, opened_device
.session_id
);
390 opened_session_id_
= opened_device
.session_id
;
394 void CloseSession() {
395 if (opened_device_label_
.empty())
397 media_stream_manager_
->CancelRequest(opened_device_label_
);
398 opened_device_label_
.clear();
399 opened_session_id_
= kInvalidMediaCaptureSessionId
;
403 void StartCapture() {
404 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
406 .WillRepeatedly(Return());
408 base::RunLoop run_loop
;
409 EXPECT_CALL(*host_
.get(), OnBufferFilled(kDeviceId
, _
, _
, _
, _
, _
))
411 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
413 media::VideoCaptureParams params
;
414 params
.requested_format
= media::VideoCaptureFormat(
415 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
416 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
420 void StartStopCapture() {
421 // Quickly start and then stop capture, without giving much chance for
422 // asynchronous start operations to complete.
424 base::RunLoop run_loop
;
425 EXPECT_CALL(*host_
.get(),
426 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
));
427 media::VideoCaptureParams params
;
428 params
.requested_format
= media::VideoCaptureFormat(
429 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
430 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
431 host_
->OnStopCapture(kDeviceId
);
432 run_loop
.RunUntilIdle();
433 WaitForVideoDeviceThread();
437 void CaptureAndDumpVideo(int width
, int height
, int frame_rate
) {
439 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
440 .Times(AnyNumber()).WillRepeatedly(Return());
442 base::RunLoop run_loop
;
443 EXPECT_CALL(*host_
, OnBufferFilled(kDeviceId
, _
, _
, _
, _
, _
))
445 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
447 media::VideoCaptureParams params
;
448 params
.requested_format
=
449 media::VideoCaptureFormat(gfx::Size(width
, height
), frame_rate
);
450 host_
->SetDumpVideo(true);
451 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
457 base::RunLoop run_loop
;
458 EXPECT_CALL(*host_
.get(),
459 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
))
460 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
462 host_
->OnStopCapture(kDeviceId
);
463 host_
->SetReturnReceivedDibs(true);
464 host_
->ReturnReceivedDibs(kDeviceId
);
468 host_
->SetReturnReceivedDibs(false);
469 // Expect the VideoCaptureDevice has been stopped
470 EXPECT_EQ(0u, host_
->entries_
.size());
473 void NotifyPacketReady() {
474 base::RunLoop run_loop
;
475 EXPECT_CALL(*host_
.get(), OnBufferFilled(kDeviceId
, _
, _
, _
, _
, _
))
477 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()))
478 .RetiresOnSaturation();
482 void ReturnReceivedPackets() {
483 host_
->ReturnReceivedDibs(kDeviceId
);
486 void SimulateError() {
487 // Expect a change state to error state sent through IPC.
488 EXPECT_CALL(*host_
.get(),
489 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ERROR
)).Times(1);
490 VideoCaptureControllerID
id(kDeviceId
);
492 // Wait for the error callback.
493 base::RunLoop().RunUntilIdle();
496 void WaitForVideoDeviceThread() {
497 base::RunLoop run_loop
;
498 media_stream_manager_
->video_capture_manager()->device_task_runner()
501 base::Bind(&base::DoNothing
),
502 run_loop
.QuitClosure());
506 scoped_refptr
<MockVideoCaptureHost
> host_
;
509 StrictMock
<MockMediaStreamRequester
> stream_requester_
;
510 scoped_ptr
<media::AudioManager
> audio_manager_
;
511 scoped_ptr
<MediaStreamManager
> media_stream_manager_
;
512 content::TestBrowserThreadBundle thread_bundle_
;
513 content::TestBrowserContext browser_context_
;
514 content::TestContentBrowserClient browser_client_
;
515 scoped_refptr
<base::MessageLoopProxy
> message_loop_
;
516 int opened_session_id_
;
517 std::string opened_device_label_
;
519 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest
);
522 TEST_F(VideoCaptureHostTest
, CloseSessionWithoutStopping
) {
525 // When the session is closed via the stream without stopping capture, the
526 // ENDED event is sent.
527 EXPECT_CALL(*host_
.get(),
528 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ENDED
)).Times(1);
530 base::RunLoop().RunUntilIdle();
533 TEST_F(VideoCaptureHostTest
, StopWhileStartPending
) {
537 TEST_F(VideoCaptureHostTest
, StartCapturePlayStop
) {
541 ReturnReceivedPackets();
545 TEST_F(VideoCaptureHostTest
, StartCaptureErrorStop
) {
551 TEST_F(VideoCaptureHostTest
, StartCaptureError
) {
552 EXPECT_CALL(*host_
.get(),
553 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
)).Times(0);
557 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
561 TEST_F(VideoCaptureHostTest
, CaptureAndDumpVideoVga
) {
562 CaptureAndDumpVideo(640, 480, 30);
564 TEST_F(VideoCaptureHostTest
, CaptureAndDump720P
) {
565 CaptureAndDumpVideo(1280, 720, 30);
569 } // namespace content