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"
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.
67 DumpVideo() : expected_size_(0) {}
68 void StartDump(int width, int height) {
69 base::FilePath file_name = base::FilePath(base::StringPrintf(
70 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
71 file_.reset(base::OpenFile(file_name, "wb"));
72 expected_size_ = media::VideoFrame::AllocationSize(
73 media::VideoFrame::I420, gfx::Size(width, height));
75 void NewVideoFrame(const void* buffer) {
76 if (file_.get() != NULL) {
77 ASSERT_EQ(1U, fwrite(buffer, expected_size_, 1, file_.get()));
82 base::ScopedFILE file_;
86 class MockMediaStreamRequester : public MediaStreamRequester {
88 MockMediaStreamRequester() {}
89 virtual ~MockMediaStreamRequester() {}
91 // MediaStreamRequester implementation.
92 MOCK_METHOD5(StreamGenerated,
93 void(int render_frame_id,
95 const std::string& label,
96 const StreamDeviceInfoArray& audio_devices,
97 const StreamDeviceInfoArray& video_devices));
98 MOCK_METHOD3(StreamGenerationFailed,
99 void(int render_frame_id,
101 content::MediaStreamRequestResult result));
102 MOCK_METHOD3(DeviceStopped, void(int render_frame_id,
103 const std::string& label,
104 const StreamDeviceInfo& device));
105 MOCK_METHOD4(DevicesEnumerated, void(int render_frame_id,
107 const std::string& label,
108 const StreamDeviceInfoArray& devices));
109 MOCK_METHOD4(DeviceOpened, void(int render_frame_id,
111 const std::string& label,
112 const StreamDeviceInfo& device_info));
115 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
118 class MockVideoCaptureHost : public VideoCaptureHost {
120 MockVideoCaptureHost(MediaStreamManager* manager)
121 : VideoCaptureHost(manager),
122 return_buffers_(false),
123 dump_video_(false) {}
125 // A list of mock methods.
126 MOCK_METHOD4(OnNewBufferCreated,
128 base::SharedMemoryHandle handle,
131 MOCK_METHOD2(OnBufferFreed,
132 void(int device_id, int buffer_id));
133 MOCK_METHOD5(OnBufferFilled,
136 const media::VideoCaptureFormat& format,
137 const gfx::Rect& visible_rect,
138 base::TimeTicks timestamp));
139 MOCK_METHOD5(OnMailboxBufferFilled,
142 const gpu::MailboxHolder& mailbox_holder,
143 const media::VideoCaptureFormat& format,
144 base::TimeTicks timestamp));
145 MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
147 // Use class DumpVideo to write I420 video to file.
148 void SetDumpVideo(bool enable) {
149 dump_video_ = enable;
152 void SetReturnReceivedDibs(bool enable) {
153 return_buffers_ = enable;
156 // Return Dibs we currently have received.
157 void ReturnReceivedDibs(int device_id) {
158 int handle = GetReceivedDib();
160 this->OnReceiveEmptyBuffer(device_id, handle, 0);
161 handle = GetReceivedDib();
165 int GetReceivedDib() {
166 if (filled_dib_.empty())
168 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
171 filled_dib_.erase(it);
177 virtual ~MockVideoCaptureHost() {
178 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
182 // This method is used to dispatch IPC messages to the renderer. We intercept
183 // these messages here and dispatch to our mock methods to verify the
184 // conversation between this object and the renderer.
185 virtual bool Send(IPC::Message* message) override {
188 // In this method we dispatch the messages to the according handlers as if
189 // we are the renderer.
191 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
192 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
193 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
194 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
195 IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady,
196 OnMailboxBufferFilledDispatch)
197 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
198 IPC_MESSAGE_UNHANDLED(handled = false)
199 IPC_END_MESSAGE_MAP()
200 EXPECT_TRUE(handled);
206 // These handler methods do minimal things and delegate to the mock methods.
207 void OnNewBufferCreatedDispatch(int device_id,
208 base::SharedMemoryHandle handle,
211 OnNewBufferCreated(device_id, handle, length, buffer_id);
212 base::SharedMemory* dib = new base::SharedMemory(handle, false);
214 filled_dib_[buffer_id] = dib;
217 void OnBufferFreedDispatch(int device_id, int buffer_id) {
218 OnBufferFreed(device_id, buffer_id);
220 std::map<int, base::SharedMemory*>::iterator it =
221 filled_dib_.find(buffer_id);
222 ASSERT_TRUE(it != filled_dib_.end());
224 filled_dib_.erase(it);
227 void OnBufferFilledDispatch(int device_id,
229 const media::VideoCaptureFormat& frame_format,
230 const gfx::Rect& visible_rect,
231 base::TimeTicks timestamp) {
232 base::SharedMemory* dib = filled_dib_[buffer_id];
233 ASSERT_TRUE(dib != NULL);
235 if (!format_.IsValid()) {
236 dumper_.StartDump(frame_format.frame_size.width(),
237 frame_format.frame_size.height());
238 format_ = frame_format;
240 ASSERT_EQ(format_.frame_size.width(), frame_format.frame_size.width())
241 << "Dump format does not handle variable resolution.";
242 ASSERT_EQ(format_.frame_size.height(), frame_format.frame_size.height())
243 << "Dump format does not handle variable resolution.";
244 dumper_.NewVideoFrame(dib->memory());
247 OnBufferFilled(device_id, buffer_id, frame_format, visible_rect, timestamp);
248 if (return_buffers_) {
249 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id, 0);
253 void OnMailboxBufferFilledDispatch(int device_id,
255 const gpu::MailboxHolder& mailbox_holder,
256 const media::VideoCaptureFormat& format,
257 base::TimeTicks timestamp) {
258 OnMailboxBufferFilled(
259 device_id, buffer_id, mailbox_holder, format, timestamp);
260 if (return_buffers_) {
261 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id, 0);
265 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
266 OnStateChanged(device_id, state);
269 std::map<int, base::SharedMemory*> filled_dib_;
270 bool return_buffers_;
272 media::VideoCaptureFormat format_;
276 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
277 message_loop->PostTask(FROM_HERE, quit_closure);
280 // This is an integration test of VideoCaptureHost in conjunction with
281 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
282 // VideoCaptureDevice.
283 class VideoCaptureHostTest : public testing::Test {
285 VideoCaptureHostTest()
286 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
287 message_loop_(base::MessageLoopProxy::current()),
288 opened_session_id_(kInvalidMediaCaptureSessionId) {}
290 virtual void SetUp() override {
291 SetBrowserClientForTesting(&browser_client_);
293 #if defined(OS_CHROMEOS)
294 chromeos::CrasAudioHandler::InitializeForTesting();
297 // Create our own MediaStreamManager.
298 audio_manager_.reset(media::AudioManager::CreateForTesting());
299 #ifndef TEST_REAL_CAPTURE_DEVICE
300 base::CommandLine::ForCurrentProcess()->AppendSwitch(
301 switches::kUseFakeDeviceForMediaStream);
303 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
304 media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
306 // Create a Host and connect it to a simulated IPC channel.
307 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
308 host_->OnChannelConnected(base::GetCurrentProcId());
313 virtual void TearDown() override {
314 // Verifies and removes the expectations on host_ and
315 // returns true iff successful.
316 Mock::VerifyAndClearExpectations(host_.get());
317 EXPECT_EQ(0u, host_->entries_.size());
321 // Simulate closing the IPC sender.
322 host_->OnChannelClosing();
324 // Release the reference to the mock object. The object will be destructed
325 // on the current message loop.
328 #if defined(OS_CHROMEOS)
329 chromeos::CrasAudioHandler::Shutdown();
334 const int render_process_id = 1;
335 const int render_frame_id = 1;
336 const int page_request_id = 1;
337 const GURL security_origin("http://test.com");
339 ASSERT_TRUE(opened_device_label_.empty());
341 // Enumerate video devices.
342 StreamDeviceInfoArray devices;
344 base::RunLoop run_loop;
345 std::string label = media_stream_manager_->EnumerateDevices(
349 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
351 MEDIA_DEVICE_VIDEO_CAPTURE,
353 EXPECT_CALL(stream_requester_, DevicesEnumerated(render_frame_id,
358 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
359 SaveArg<3>(&devices)));
361 Mock::VerifyAndClearExpectations(&stream_requester_);
362 media_stream_manager_->CancelRequest(label);
364 ASSERT_FALSE(devices.empty());
365 ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
367 // Open the first device.
369 base::RunLoop run_loop;
370 StreamDeviceInfo opened_device;
371 media_stream_manager_->OpenDevice(
375 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
377 devices[0].device.id,
378 MEDIA_DEVICE_VIDEO_CAPTURE,
380 EXPECT_CALL(stream_requester_, DeviceOpened(render_frame_id,
385 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
386 SaveArg<2>(&opened_device_label_),
387 SaveArg<3>(&opened_device)));
389 Mock::VerifyAndClearExpectations(&stream_requester_);
390 ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
391 opened_session_id_ = opened_device.session_id;
395 void CloseSession() {
396 if (opened_device_label_.empty())
398 media_stream_manager_->CancelRequest(opened_device_label_);
399 opened_device_label_.clear();
400 opened_session_id_ = kInvalidMediaCaptureSessionId;
404 void StartCapture() {
405 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
407 .WillRepeatedly(Return());
409 base::RunLoop run_loop;
410 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _, _, _))
412 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
414 media::VideoCaptureParams params;
415 params.requested_format = media::VideoCaptureFormat(
416 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
417 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
421 void StartStopCapture() {
422 // Quickly start and then stop capture, without giving much chance for
423 // asynchronous start operations to complete.
425 base::RunLoop run_loop;
426 EXPECT_CALL(*host_.get(),
427 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
428 media::VideoCaptureParams params;
429 params.requested_format = media::VideoCaptureFormat(
430 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
431 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
432 host_->OnStopCapture(kDeviceId);
433 run_loop.RunUntilIdle();
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 scoped_refptr<MockVideoCaptureHost> host_;
499 StrictMock<MockMediaStreamRequester> stream_requester_;
500 scoped_ptr<media::AudioManager> audio_manager_;
501 scoped_ptr<MediaStreamManager> media_stream_manager_;
502 content::TestBrowserThreadBundle thread_bundle_;
503 content::TestBrowserContext browser_context_;
504 content::TestContentBrowserClient browser_client_;
505 scoped_refptr<base::MessageLoopProxy> message_loop_;
506 int opened_session_id_;
507 std::string opened_device_label_;
509 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
512 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
515 // When the session is closed via the stream without stopping capture, the
516 // ENDED event is sent.
517 EXPECT_CALL(*host_.get(),
518 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED)).Times(1);
520 base::RunLoop().RunUntilIdle();
523 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
527 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
531 ReturnReceivedPackets();
535 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
541 TEST_F(VideoCaptureHostTest, StartCaptureError) {
542 EXPECT_CALL(*host_.get(),
543 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
547 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
551 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
552 CaptureAndDumpVideo(640, 480, 30);
554 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
555 CaptureAndDumpVideo(1280, 720, 30);
559 } // namespace content