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/file_util.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "content/browser/browser_thread_impl.h"
16 #include "content/browser/renderer_host/media/media_stream_manager.h"
17 #include "content/browser/renderer_host/media/media_stream_requester.h"
18 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
19 #include "content/browser/renderer_host/media/video_capture_host.h"
20 #include "content/browser/renderer_host/media/video_capture_manager.h"
21 #include "content/common/media/video_capture_messages.h"
22 #include "content/public/test/mock_resource_context.h"
23 #include "content/public/test/test_browser_context.h"
24 #include "content/public/test/test_browser_thread_bundle.h"
25 #include "content/test/test_content_browser_client.h"
26 #include "media/audio/audio_manager.h"
27 #include "media/base/video_frame.h"
28 #include "media/video/capture/video_capture_types.h"
29 #include "net/url_request/url_request_context.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
34 using ::testing::AtLeast
;
35 using ::testing::AnyNumber
;
36 using ::testing::DoAll
;
37 using ::testing::InSequence
;
38 using ::testing::Mock
;
39 using ::testing::Return
;
40 using ::testing::SaveArg
;
41 using ::testing::StrictMock
;
45 // Id used to identify the capture session between renderer and
46 // video_capture_host. This is an arbitrary value.
47 static const int kDeviceId
= 555;
49 // Define to enable test where video is dumped to file.
52 // Define to use a real video capture device.
53 // #define TEST_REAL_CAPTURE_DEVICE
55 // Simple class used for dumping video to a file. This can be used for
56 // verifying the output.
59 DumpVideo() : expected_size_(0) {}
60 void StartDump(int width
, int height
) {
61 base::FilePath file_name
= base::FilePath(base::StringPrintf(
62 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width
, height
));
63 file_
.reset(base::OpenFile(file_name
, "wb"));
64 expected_size_
= media::VideoFrame::AllocationSize(
65 media::VideoFrame::I420
, gfx::Size(width
, height
));
67 void NewVideoFrame(const void* buffer
) {
68 if (file_
.get() != NULL
) {
69 ASSERT_EQ(1U, fwrite(buffer
, expected_size_
, 1, file_
.get()));
74 file_util::ScopedFILE file_
;
78 class MockMediaStreamRequester
: public MediaStreamRequester
{
80 MockMediaStreamRequester() {}
81 virtual ~MockMediaStreamRequester() {}
83 // MediaStreamRequester implementation.
84 MOCK_METHOD3(StreamGenerated
,
85 void(const std::string
& label
,
86 const StreamDeviceInfoArray
& audio_devices
,
87 const StreamDeviceInfoArray
& video_devices
));
88 MOCK_METHOD1(StreamGenerationFailed
, void(const std::string
& label
));
89 MOCK_METHOD3(DeviceStopped
, void(int render_view_id
,
90 const std::string
& label
,
91 const StreamDeviceInfo
& device
));
92 MOCK_METHOD2(DevicesEnumerated
, void(const std::string
& label
,
93 const StreamDeviceInfoArray
& devices
));
94 MOCK_METHOD2(DeviceOpened
, void(const std::string
& label
,
95 const StreamDeviceInfo
& device_info
));
98 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester
);
101 class MockVideoCaptureHost
: public VideoCaptureHost
{
103 MockVideoCaptureHost(MediaStreamManager
* manager
)
104 : VideoCaptureHost(manager
),
105 return_buffers_(false),
106 dump_video_(false) {}
108 // A list of mock methods.
109 MOCK_METHOD4(OnNewBufferCreated
,
110 void(int device_id
, base::SharedMemoryHandle handle
,
111 int length
, int buffer_id
));
112 MOCK_METHOD2(OnBufferFreed
,
113 void(int device_id
, int buffer_id
));
114 MOCK_METHOD4(OnBufferFilled
,
115 void(int device_id
, int buffer_id
, base::Time timestamp
,
116 const media::VideoCaptureFormat
& format
));
117 MOCK_METHOD2(OnStateChanged
, void(int device_id
, VideoCaptureState state
));
119 // Use class DumpVideo to write I420 video to file.
120 void SetDumpVideo(bool enable
) {
121 dump_video_
= enable
;
124 void SetReturnReceivedDibs(bool enable
) {
125 return_buffers_
= enable
;
128 // Return Dibs we currently have received.
129 void ReturnReceivedDibs(int device_id
) {
130 int handle
= GetReceivedDib();
132 this->OnReceiveEmptyBuffer(device_id
, handle
);
133 handle
= GetReceivedDib();
137 int GetReceivedDib() {
138 if (filled_dib_
.empty())
140 std::map
<int, base::SharedMemory
*>::iterator it
= filled_dib_
.begin();
143 filled_dib_
.erase(it
);
149 virtual ~MockVideoCaptureHost() {
150 STLDeleteContainerPairSecondPointers(filled_dib_
.begin(),
154 // This method is used to dispatch IPC messages to the renderer. We intercept
155 // these messages here and dispatch to our mock methods to verify the
156 // conversation between this object and the renderer.
157 virtual bool Send(IPC::Message
* message
) OVERRIDE
{
160 // In this method we dispatch the messages to the according handlers as if
161 // we are the renderer.
163 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost
, *message
)
164 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer
, OnNewBufferCreatedDispatch
)
165 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer
, OnBufferFreedDispatch
)
166 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady
, OnBufferFilledDispatch
)
167 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged
, OnStateChangedDispatch
)
168 IPC_MESSAGE_UNHANDLED(handled
= false)
169 IPC_END_MESSAGE_MAP()
170 EXPECT_TRUE(handled
);
176 // These handler methods do minimal things and delegate to the mock methods.
177 void OnNewBufferCreatedDispatch(int device_id
,
178 base::SharedMemoryHandle handle
,
181 OnNewBufferCreated(device_id
, handle
, length
, buffer_id
);
182 base::SharedMemory
* dib
= new base::SharedMemory(handle
, false);
184 filled_dib_
[buffer_id
] = dib
;
187 void OnBufferFreedDispatch(int device_id
, int buffer_id
) {
188 OnBufferFreed(device_id
, buffer_id
);
190 std::map
<int, base::SharedMemory
*>::iterator it
=
191 filled_dib_
.find(buffer_id
);
192 ASSERT_TRUE(it
!= filled_dib_
.end());
194 filled_dib_
.erase(it
);
197 void OnBufferFilledDispatch(int device_id
,
199 base::Time timestamp
,
200 const media::VideoCaptureFormat
& frame_format
) {
201 base::SharedMemory
* dib
= filled_dib_
[buffer_id
];
202 ASSERT_TRUE(dib
!= NULL
);
204 if (!format_
.IsValid()) {
205 dumper_
.StartDump(frame_format
.frame_size
.width(),
206 frame_format
.frame_size
.height());
207 format_
= frame_format
;
209 ASSERT_EQ(format_
.frame_size
.width(), frame_format
.frame_size
.width())
210 << "Dump format does not handle variable resolution.";
211 ASSERT_EQ(format_
.frame_size
.height(), frame_format
.frame_size
.height())
212 << "Dump format does not handle variable resolution.";
213 dumper_
.NewVideoFrame(dib
->memory());
216 OnBufferFilled(device_id
, buffer_id
, timestamp
, frame_format
);
217 if (return_buffers_
) {
218 VideoCaptureHost::OnReceiveEmptyBuffer(device_id
, buffer_id
);
222 void OnStateChangedDispatch(int device_id
, VideoCaptureState state
) {
223 OnStateChanged(device_id
, state
);
226 std::map
<int, base::SharedMemory
*> filled_dib_
;
227 bool return_buffers_
;
229 media::VideoCaptureFormat format_
;
233 ACTION_P2(ExitMessageLoop
, message_loop
, quit_closure
) {
234 message_loop
->PostTask(FROM_HERE
, quit_closure
);
237 // This is an integration test of VideoCaptureHost in conjunction with
238 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
239 // VideoCaptureDevice.
240 class VideoCaptureHostTest
: public testing::Test
{
242 VideoCaptureHostTest()
243 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP
),
244 message_loop_(base::MessageLoopProxy::current()),
245 opened_session_id_(kInvalidMediaCaptureSessionId
) {}
247 virtual void SetUp() OVERRIDE
{
248 SetBrowserClientForTesting(&browser_client_
);
249 // Create our own MediaStreamManager.
250 audio_manager_
.reset(media::AudioManager::CreateForTesting());
251 media_stream_manager_
.reset(new MediaStreamManager(audio_manager_
.get()));
252 #ifndef TEST_REAL_CAPTURE_DEVICE
253 media_stream_manager_
->UseFakeDevice();
255 media_stream_manager_
->UseFakeUI(scoped_ptr
<FakeMediaStreamUIProxy
>());
257 // Create a Host and connect it to a simulated IPC channel.
258 host_
= new MockVideoCaptureHost(media_stream_manager_
.get());
259 host_
->OnChannelConnected(base::GetCurrentProcId());
264 virtual void TearDown() OVERRIDE
{
265 // Verifies and removes the expectations on host_ and
266 // returns true iff successful.
267 Mock::VerifyAndClearExpectations(host_
.get());
268 EXPECT_EQ(0u, host_
->entries_
.size());
272 // Simulate closing the IPC channel.
273 host_
->OnChannelClosing();
275 // Release the reference to the mock object. The object will be destructed
276 // on the current message loop.
281 const int render_process_id
= 1;
282 const int render_view_id
= 1;
283 const int page_request_id
= 1;
284 const GURL
security_origin("http://test.com");
286 ASSERT_TRUE(opened_device_label_
.empty());
288 // Enumerate video devices.
289 StreamDeviceInfoArray devices
;
291 base::RunLoop run_loop
;
292 std::string label
= media_stream_manager_
->EnumerateDevices(
296 browser_context_
.GetResourceContext(),
298 MEDIA_DEVICE_VIDEO_CAPTURE
,
300 EXPECT_CALL(stream_requester_
, DevicesEnumerated(label
, _
))
302 DoAll(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()),
303 SaveArg
<1>(&devices
)));
305 Mock::VerifyAndClearExpectations(&stream_requester_
);
306 media_stream_manager_
->CancelRequest(label
);
308 ASSERT_FALSE(devices
.empty());
309 ASSERT_EQ(StreamDeviceInfo::kNoId
, devices
[0].session_id
);
311 // Open the first device.
313 base::RunLoop run_loop
;
314 StreamDeviceInfo opened_device
;
315 opened_device_label_
= media_stream_manager_
->OpenDevice(
319 browser_context_
.GetResourceContext(),
321 devices
[0].device
.id
,
322 MEDIA_DEVICE_VIDEO_CAPTURE
,
324 EXPECT_CALL(stream_requester_
, DeviceOpened(opened_device_label_
, _
))
326 DoAll(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()),
327 SaveArg
<1>(&opened_device
)));
329 Mock::VerifyAndClearExpectations(&stream_requester_
);
330 ASSERT_NE(StreamDeviceInfo::kNoId
, opened_device
.session_id
);
331 opened_session_id_
= opened_device
.session_id
;
335 void CloseSession() {
336 if (opened_device_label_
.empty())
338 media_stream_manager_
->CancelRequest(opened_device_label_
);
339 opened_device_label_
.clear();
340 opened_session_id_
= kInvalidMediaCaptureSessionId
;
344 void StartCapture() {
345 EXPECT_CALL(*host_
, OnNewBufferCreated(kDeviceId
, _
, _
, _
))
346 .Times(AnyNumber()).WillRepeatedly(Return());
348 base::RunLoop run_loop
;
349 EXPECT_CALL(*host_
, OnBufferFilled(kDeviceId
, _
, _
, _
))
350 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
351 message_loop_
, run_loop
.QuitClosure()));
353 media::VideoCaptureParams params
;
354 params
.requested_format
= media::VideoCaptureFormat(
355 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
356 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
360 void StartStopCapture() {
361 // Quickly start and then stop capture, without giving much chance for
362 // asynchronous start operations to complete.
364 base::RunLoop run_loop
;
365 EXPECT_CALL(*host_
, OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
));
366 media::VideoCaptureParams params
;
367 params
.requested_format
= media::VideoCaptureFormat(
368 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420
);
369 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
370 host_
->OnStopCapture(kDeviceId
);
371 run_loop
.RunUntilIdle();
375 void CaptureAndDumpVideo(int width
, int height
, int frame_rate
) {
377 EXPECT_CALL(*host_
.get(), OnNewBufferCreated(kDeviceId
, _
, _
, _
))
378 .Times(AnyNumber()).WillRepeatedly(Return());
380 base::RunLoop run_loop
;
381 EXPECT_CALL(*host_
, OnBufferFilled(kDeviceId
, _
, _
, _
))
383 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
385 media::VideoCaptureParams params
;
386 params
.requested_format
=
387 media::VideoCaptureFormat(gfx::Size(width
, height
), frame_rate
);
388 host_
->SetDumpVideo(true);
389 host_
->OnStartCapture(kDeviceId
, opened_session_id_
, params
);
395 base::RunLoop run_loop
;
396 EXPECT_CALL(*host_
, OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
))
397 .WillOnce(ExitMessageLoop(message_loop_
, run_loop
.QuitClosure()));
399 host_
->OnStopCapture(kDeviceId
);
400 host_
->SetReturnReceivedDibs(true);
401 host_
->ReturnReceivedDibs(kDeviceId
);
405 host_
->SetReturnReceivedDibs(false);
406 // Expect the VideoCaptureDevice has been stopped
407 EXPECT_EQ(0u, host_
->entries_
.size());
410 void NotifyPacketReady() {
411 base::RunLoop run_loop
;
412 EXPECT_CALL(*host_
, OnBufferFilled(kDeviceId
, _
, _
, _
))
413 .Times(AnyNumber()).WillOnce(ExitMessageLoop(
414 message_loop_
, run_loop
.QuitClosure()))
415 .RetiresOnSaturation();
419 void ReturnReceivedPackets() {
420 host_
->ReturnReceivedDibs(kDeviceId
);
423 void SimulateError() {
424 // Expect a change state to error state sent through IPC.
425 EXPECT_CALL(*host_
, OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ERROR
))
427 VideoCaptureControllerID
id(kDeviceId
);
429 // Wait for the error callback.
430 base::RunLoop().RunUntilIdle();
433 scoped_refptr
<MockVideoCaptureHost
> host_
;
436 StrictMock
<MockMediaStreamRequester
> stream_requester_
;
437 scoped_ptr
<media::AudioManager
> audio_manager_
;
438 scoped_ptr
<MediaStreamManager
> media_stream_manager_
;
439 content::TestBrowserThreadBundle thread_bundle_
;
440 content::TestBrowserContext browser_context_
;
441 content::TestContentBrowserClient browser_client_
;
442 scoped_refptr
<base::MessageLoopProxy
> message_loop_
;
443 int opened_session_id_
;
444 std::string opened_device_label_
;
446 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest
);
449 TEST_F(VideoCaptureHostTest
, CloseSessionWithoutStopping
) {
452 // When the session is closed via the stream without stopping capture, the
453 // ENDED event is sent.
454 EXPECT_CALL(*host_
, OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_ENDED
))
457 base::RunLoop().RunUntilIdle();
460 TEST_F(VideoCaptureHostTest
, StopWhileStartPending
) {
464 TEST_F(VideoCaptureHostTest
, StartCapturePlayStop
) {
468 ReturnReceivedPackets();
472 TEST_F(VideoCaptureHostTest
, StartCaptureErrorStop
) {
478 TEST_F(VideoCaptureHostTest
, StartCaptureError
) {
479 EXPECT_CALL(*host_
, OnStateChanged(kDeviceId
, VIDEO_CAPTURE_STATE_STOPPED
))
484 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
488 TEST_F(VideoCaptureHostTest
, CaptureAndDumpVideoVga
) {
489 CaptureAndDumpVideo(640, 480, 30);
491 TEST_F(VideoCaptureHostTest
, CaptureAndDump720P
) {
492 CaptureAndDumpVideo(1280, 720, 30);
496 } // namespace content