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/location.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "content/browser/browser_thread_impl.h"
20 #include "content/browser/renderer_host/media/media_stream_manager.h"
21 #include "content/browser/renderer_host/media/media_stream_requester.h"
22 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
23 #include "content/browser/renderer_host/media/video_capture_host.h"
24 #include "content/browser/renderer_host/media/video_capture_manager.h"
25 #include "content/common/media/video_capture_messages.h"
26 #include "content/public/common/content_switches.h"
27 #include "content/public/test/mock_resource_context.h"
28 #include "content/public/test/test_browser_context.h"
29 #include "content/public/test/test_browser_thread_bundle.h"
30 #include "content/test/test_content_browser_client.h"
31 #include "media/audio/audio_manager.h"
32 #include "media/base/media_switches.h"
33 #include "media/base/video_capture_types.h"
34 #include "media/base/video_frame.h"
35 #include "net/url_request/url_request_context.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "testing/gtest/include/gtest/gtest.h"
39 #if defined(OS_CHROMEOS)
40 #include "chromeos/audio/cras_audio_handler.h"
44 using ::testing::AtLeast
;
45 using ::testing::AnyNumber
;
46 using ::testing::DoAll
;
47 using ::testing::InSequence
;
48 using ::testing::Mock
;
49 using ::testing::Return
;
50 using ::testing::SaveArg
;
51 using ::testing::StrictMock
;
55 // Id used to identify the capture session between renderer and
56 // video_capture_host. This is an arbitrary value.
57 static const int kDeviceId
= 555;
59 // Define to enable test where video is dumped to file.
62 // Define to use a real video capture device.
63 // #define TEST_REAL_CAPTURE_DEVICE
65 // Simple class used for dumping video to a file. This can be used for
66 // verifying the output.
70 const gfx::Size
& coded_size() const { return coded_size_
; }
71 void StartDump(const gfx::Size
& coded_size
) {
72 base::FilePath file_name
= base::FilePath(base::StringPrintf(
73 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"),
75 coded_size
.height()));
76 file_
.reset(base::OpenFile(file_name
, "wb"));
77 coded_size_
= coded_size
;
79 void NewVideoFrame(const void* buffer
) {
80 if (file_
.get() != NULL
) {
81 const int size
= media::VideoFrame::AllocationSize(
82 media::PIXEL_FORMAT_I420
, coded_size_
);
83 ASSERT_EQ(1U, fwrite(buffer
, size
, 1, file_
.get()));
88 base::ScopedFILE file_
;
89 gfx::Size coded_size_
;
92 class MockMediaStreamRequester
: public MediaStreamRequester
{
94 MockMediaStreamRequester() {}
95 virtual ~MockMediaStreamRequester() {}
97 // MediaStreamRequester implementation.
98 MOCK_METHOD5(StreamGenerated
,
99 void(int render_frame_id
,
101 const std::string
& label
,
102 const StreamDeviceInfoArray
& audio_devices
,
103 const StreamDeviceInfoArray
& video_devices
));
104 MOCK_METHOD3(StreamGenerationFailed
,
105 void(int render_frame_id
,
107 content::MediaStreamRequestResult result
));
108 MOCK_METHOD3(DeviceStopped
, void(int render_frame_id
,
109 const std::string
& label
,
110 const StreamDeviceInfo
& device
));
111 MOCK_METHOD4(DevicesEnumerated
, void(int render_frame_id
,
113 const std::string
& label
,
114 const StreamDeviceInfoArray
& devices
));
115 MOCK_METHOD4(DeviceOpened
, void(int render_frame_id
,
117 const std::string
& label
,
118 const StreamDeviceInfo
& device_info
));
121 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester
);
124 class MockVideoCaptureHost
: public VideoCaptureHost
{
126 MockVideoCaptureHost(MediaStreamManager
* manager
)
127 : VideoCaptureHost(manager
),
128 return_buffers_(false),
129 dump_video_(false) {}
131 // A list of mock methods.
132 MOCK_METHOD4(OnNewBufferCreated
,
134 base::SharedMemoryHandle handle
,
137 MOCK_METHOD2(OnBufferFreed
, void(int device_id
, int buffer_id
));
138 MOCK_METHOD1(OnBufferFilled
, void(int device_id
));
139 MOCK_METHOD2(OnStateChanged
, void(int device_id
, VideoCaptureState state
));
141 // Use class DumpVideo to write I420 video to file.
142 void SetDumpVideo(bool enable
) {
143 dump_video_
= enable
;
146 void SetReturnReceivedDibs(bool enable
) {
147 return_buffers_
= enable
;
150 // Return Dibs we currently have received.
151 void ReturnReceivedDibs(int device_id
) {
152 int handle
= GetReceivedDib();
154 this->OnRendererFinishedWithBuffer(device_id
, handle
, 0, -1.0);
155 handle
= GetReceivedDib();
159 int GetReceivedDib() {
160 if (filled_dib_
.empty())
162 std::map
<int, base::SharedMemory
*>::iterator it
= filled_dib_
.begin();
165 filled_dib_
.erase(it
);
171 ~MockVideoCaptureHost() override
{
172 STLDeleteContainerPairSecondPointers(filled_dib_
.begin(),
176 // This method is used to dispatch IPC messages to the renderer. We intercept
177 // these messages here and dispatch to our mock methods to verify the
178 // conversation between this object and the renderer.
179 bool Send(IPC::Message
* message
) override
{
182 // In this method we dispatch the messages to the according handlers as if
183 // we are the renderer.
185 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost
, *message
)
186 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer
, OnNewBufferCreatedDispatch
)
187 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer
, OnBufferFreedDispatch
)
188 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady
, OnBufferFilledDispatch
)
189 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged
, OnStateChangedDispatch
)
190 IPC_MESSAGE_UNHANDLED(handled
= false)
191 IPC_END_MESSAGE_MAP()
192 EXPECT_TRUE(handled
);
198 // These handler methods do minimal things and delegate to the mock methods.
199 void OnNewBufferCreatedDispatch(int device_id
,
200 base::SharedMemoryHandle handle
,
203 OnNewBufferCreated(device_id
, handle
, length
, buffer_id
);
204 base::SharedMemory
* dib
= new base::SharedMemory(handle
, false);
206 filled_dib_
[buffer_id
] = dib
;
209 void OnBufferFreedDispatch(int device_id
, int buffer_id
) {
210 OnBufferFreed(device_id
, buffer_id
);
212 std::map
<int, base::SharedMemory
*>::iterator it
=
213 filled_dib_
.find(buffer_id
);
214 ASSERT_TRUE(it
!= filled_dib_
.end());
216 filled_dib_
.erase(it
);
219 void OnBufferFilledDispatch(
220 const VideoCaptureMsg_BufferReady_Params
& params
) {
221 base::SharedMemory
* dib
= filled_dib_
[params
.buffer_id
];
222 ASSERT_TRUE(dib
!= NULL
);
224 if (dumper_
.coded_size().IsEmpty())
225 dumper_
.StartDump(params
.coded_size
);
226 ASSERT_TRUE(dumper_
.coded_size() == params
.coded_size
)
227 << "Dump format does not handle variable resolution.";
228 dumper_
.NewVideoFrame(dib
->memory());
231 OnBufferFilled(params
.device_id
);
232 if (return_buffers_
) {
233 VideoCaptureHost::OnRendererFinishedWithBuffer(
234 params
.device_id
, params
.buffer_id
, 0, -1.0);
238 void OnStateChangedDispatch(int device_id
, VideoCaptureState state
) {
239 OnStateChanged(device_id
, state
);
242 std::map
<int, base::SharedMemory
*> filled_dib_
;
243 bool return_buffers_
;
245 media::VideoCaptureFormat format_
;
249 ACTION_P2(ExitMessageLoop
, task_runner
, quit_closure
) {
250 task_runner
->PostTask(FROM_HERE
, quit_closure
);
253 // This is an integration test of VideoCaptureHost in conjunction with
254 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
255 // VideoCaptureDevice.
256 class VideoCaptureHostTest
: public testing::Test
{
258 VideoCaptureHostTest()
259 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
260 task_runner_(base::ThreadTaskRunnerHandle::Get()),
261 opened_session_id_(kInvalidMediaCaptureSessionId
) {}
263 void SetUp() override
{
264 SetBrowserClientForTesting(&browser_client_
);
266 #if defined(OS_CHROMEOS)
267 chromeos::CrasAudioHandler::InitializeForTesting();
270 // Create our own MediaStreamManager.
271 audio_manager_
.reset(media::AudioManager::CreateForTesting());
272 #ifndef TEST_REAL_CAPTURE_DEVICE
273 base::CommandLine::ForCurrentProcess()->AppendSwitch(
274 switches::kUseFakeDeviceForMediaStream
);
276 media_stream_manager_
.reset(new MediaStreamManager(audio_manager_
.get()));
277 media_stream_manager_
->UseFakeUIForTests(
278 scoped_ptr
<FakeMediaStreamUIProxy
>());
280 // Create a Host and connect it to a simulated IPC channel.
281 host_
= new MockVideoCaptureHost(media_stream_manager_
.get());
282 host_
->OnChannelConnected(base::GetCurrentProcId());
287 void TearDown() override
{
288 // Verifies and removes the expectations on host_ and
289 // returns true iff successful.
290 Mock::VerifyAndClearExpectations(host_
.get());
291 EXPECT_EQ(0u, host_
->entries_
.size());
295 // Simulate closing the IPC sender.
296 host_
->OnChannelClosing();
298 // Release the reference to the mock object. The object will be destructed
299 // on the current message loop.
302 #if defined(OS_CHROMEOS)
303 chromeos::CrasAudioHandler::Shutdown();
308 const int render_process_id
= 1;
309 const int render_frame_id
= 1;
310 const int page_request_id
= 1;
311 const GURL
security_origin("http://test.com");
313 ASSERT_TRUE(opened_device_label_
.empty());
315 // Enumerate video devices.
316 StreamDeviceInfoArray devices
;
318 base::RunLoop run_loop
;
319 std::string label
= media_stream_manager_
->EnumerateDevices(
323 browser_context_
.GetResourceContext()->GetMediaDeviceIDSalt(),
325 MEDIA_DEVICE_VIDEO_CAPTURE
,
327 EXPECT_CALL(stream_requester_
,
328 DevicesEnumerated(render_frame_id
, page_request_id
, label
, _
))
330 .WillOnce(DoAll(ExitMessageLoop(task_runner_
, run_loop
.QuitClosure()),
331 SaveArg
<3>(&devices
)));
333 Mock::VerifyAndClearExpectations(&stream_requester_
);
334 media_stream_manager_
->CancelRequest(label
);
336 ASSERT_FALSE(devices
.empty());
337 ASSERT_EQ(StreamDeviceInfo::kNoId
, devices
[0].session_id
);
339 // Open the first device.
341 base::RunLoop run_loop
;
342 StreamDeviceInfo opened_device
;
343 media_stream_manager_
->OpenDevice(
347 browser_context_
.GetResourceContext()->GetMediaDeviceIDSalt(),
349 devices
[0].device
.id
,
350 MEDIA_DEVICE_VIDEO_CAPTURE
,
352 EXPECT_CALL(stream_requester_
,
353 DeviceOpened(render_frame_id
, page_request_id
, _
, _
))
355 .WillOnce(DoAll(ExitMessageLoop(task_runner_
, run_loop
.QuitClosure()),
356 SaveArg
<2>(&opened_device_label_
),
357 SaveArg
<3>(&opened_device
)));
359 Mock::VerifyAndClearExpectations(&stream_requester_
);
360 ASSERT_NE(StreamDeviceInfo::kNoId
, opened_device
.session_id
);
361 opened_session_id_
= opened_device
.session_id
;
365 void CloseSession() {
366 if (opened_device_label_
.empty())
368 media_stream_manager_
->CancelRequest(opened_device_label_
);
369 opened_device_label_
.clear();
370 opened_session_id_
= kInvalidMediaCaptureSessionId
;
374 void StartCapture() {
375 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
377 .WillRepeatedly(Return());
379 base::RunLoop run_loop
;
380 EXPECT_CALL(*host_
.get(), OnBufferFilled(kDeviceId
))
382 .WillOnce(ExitMessageLoop(task_runner_
, run_loop
.QuitClosure()));
384 media::VideoCaptureParams params
;
385 params
.requested_format
= media::VideoCaptureFormat(
386 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
387 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
391 void StartStopCapture() {
392 // Quickly start and then stop capture, without giving much chance for
393 // asynchronous start operations to complete.
395 base::RunLoop run_loop
;
396 EXPECT_CALL(*host_
.get(),
397 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
));
398 media::VideoCaptureParams params
;
399 params
.requested_format
= media::VideoCaptureFormat(
400 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
401 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
402 host_
->OnStopCapture(kDeviceId
);
403 run_loop
.RunUntilIdle();
404 WaitForVideoDeviceThread();
408 void CaptureAndDumpVideo(int width
, int height
, int frame_rate
) {
410 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
411 .Times(AnyNumber()).WillRepeatedly(Return());
413 base::RunLoop run_loop
;
414 EXPECT_CALL(*host_
, OnBufferFilled(kDeviceId
, _
, _
, _
, _
, _
, _
, _
, _
, _
))
416 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
418 media::VideoCaptureParams params
;
419 params
.requested_format
=
420 media::VideoCaptureFormat(gfx::Size(width
, height
), frame_rate
);
421 host_
->SetDumpVideo(true);
422 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
428 base::RunLoop run_loop
;
429 EXPECT_CALL(*host_
.get(),
430 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
))
431 .WillOnce(ExitMessageLoop(task_runner_
, run_loop
.QuitClosure()));
433 host_
->OnStopCapture(kDeviceId
);
434 host_
->SetReturnReceivedDibs(true);
435 host_
->ReturnReceivedDibs(kDeviceId
);
439 host_
->SetReturnReceivedDibs(false);
440 // Expect the VideoCaptureDevice has been stopped
441 EXPECT_EQ(0u, host_
->entries_
.size());
444 void NotifyPacketReady() {
445 base::RunLoop run_loop
;
446 EXPECT_CALL(*host_
.get(), OnBufferFilled(kDeviceId
))
448 .WillOnce(ExitMessageLoop(task_runner_
, run_loop
.QuitClosure()))
449 .RetiresOnSaturation();
453 void ReturnReceivedPackets() {
454 host_
->ReturnReceivedDibs(kDeviceId
);
457 void SimulateError() {
458 // Expect a change state to error state sent through IPC.
459 EXPECT_CALL(*host_
.get(),
460 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ERROR
)).Times(1);
461 VideoCaptureControllerID
id(kDeviceId
);
463 // Wait for the error callback.
464 base::RunLoop().RunUntilIdle();
467 void WaitForVideoDeviceThread() {
468 base::RunLoop run_loop
;
469 media_stream_manager_
->video_capture_manager()->device_task_runner()
472 base::Bind(&base::DoNothing
),
473 run_loop
.QuitClosure());
477 scoped_refptr
<MockVideoCaptureHost
> host_
;
480 StrictMock
<MockMediaStreamRequester
> stream_requester_
;
481 scoped_ptr
<media::AudioManager
> audio_manager_
;
482 scoped_ptr
<MediaStreamManager
> media_stream_manager_
;
483 content::TestBrowserThreadBundle thread_bundle_
;
484 content::TestBrowserContext browser_context_
;
485 content::TestContentBrowserClient browser_client_
;
486 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
487 int opened_session_id_
;
488 std::string opened_device_label_
;
490 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest
);
493 TEST_F(VideoCaptureHostTest
, CloseSessionWithoutStopping
) {
496 // When the session is closed via the stream without stopping capture, the
497 // ENDED event is sent.
498 EXPECT_CALL(*host_
.get(),
499 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ENDED
)).Times(1);
501 base::RunLoop().RunUntilIdle();
504 TEST_F(VideoCaptureHostTest
, StopWhileStartPending
) {
508 TEST_F(VideoCaptureHostTest
, StartCapturePlayStop
) {
512 ReturnReceivedPackets();
516 TEST_F(VideoCaptureHostTest
, StartCaptureErrorStop
) {
522 TEST_F(VideoCaptureHostTest
, StartCaptureError
) {
523 EXPECT_CALL(*host_
.get(),
524 OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
)).Times(0);
528 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
532 TEST_F(VideoCaptureHostTest
, CaptureAndDumpVideoVga
) {
533 CaptureAndDumpVideo(640, 480, 30);
535 TEST_F(VideoCaptureHostTest
, CaptureAndDump720P
) {
536 CaptureAndDumpVideo(1280, 720, 30);
540 } // namespace content