Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blob70c6112c0978edbd6aff3bec258575ec1992a9ea
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.
5 #include <map>
6 #include <string>
8 #include "base/bind.h"
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"
41 #endif
43 using ::testing::_;
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;
53 namespace content {
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.
60 // #define DUMP_VIDEO
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.
67 class DumpVideo {
68 public:
69 DumpVideo() {}
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"),
74 coded_size.width(),
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()));
87 private:
88 base::ScopedFILE file_;
89 gfx::Size coded_size_;
92 class MockMediaStreamRequester : public MediaStreamRequester {
93 public:
94 MockMediaStreamRequester() {}
95 virtual ~MockMediaStreamRequester() {}
97 // MediaStreamRequester implementation.
98 MOCK_METHOD5(StreamGenerated,
99 void(int render_frame_id,
100 int page_request_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,
106 int page_request_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,
112 int page_request_id,
113 const std::string& label,
114 const StreamDeviceInfoArray& devices));
115 MOCK_METHOD4(DeviceOpened, void(int render_frame_id,
116 int page_request_id,
117 const std::string& label,
118 const StreamDeviceInfo& device_info));
120 private:
121 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
124 class MockVideoCaptureHost : public VideoCaptureHost {
125 public:
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,
133 void(int device_id,
134 base::SharedMemoryHandle handle,
135 int length,
136 int buffer_id));
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();
153 while (handle) {
154 this->OnRendererFinishedWithBuffer(device_id, handle, 0, -1.0);
155 handle = GetReceivedDib();
159 int GetReceivedDib() {
160 if (filled_dib_.empty())
161 return 0;
162 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
163 int h = it->first;
164 delete it->second;
165 filled_dib_.erase(it);
167 return h;
170 private:
171 ~MockVideoCaptureHost() override {
172 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
173 filled_dib_.end());
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 {
180 CHECK(message);
182 // In this method we dispatch the messages to the according handlers as if
183 // we are the renderer.
184 bool handled = true;
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);
194 delete message;
195 return true;
198 // These handler methods do minimal things and delegate to the mock methods.
199 void OnNewBufferCreatedDispatch(int device_id,
200 base::SharedMemoryHandle handle,
201 uint32 length,
202 int buffer_id) {
203 OnNewBufferCreated(device_id, handle, length, buffer_id);
204 base::SharedMemory* dib = new base::SharedMemory(handle, false);
205 dib->Map(length);
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());
215 delete it->second;
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);
223 if (dump_video_) {
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_;
244 bool dump_video_;
245 media::VideoCaptureFormat format_;
246 DumpVideo dumper_;
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 {
257 public:
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();
268 #endif
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);
275 #endif
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());
284 OpenSession();
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());
293 CloseSession();
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.
300 host_ = NULL;
302 #if defined(OS_CHROMEOS)
303 chromeos::CrasAudioHandler::Shutdown();
304 #endif
307 void OpenSession() {
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(
320 &stream_requester_,
321 render_process_id,
322 render_frame_id,
323 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
324 page_request_id,
325 MEDIA_DEVICE_VIDEO_CAPTURE,
326 security_origin);
327 EXPECT_CALL(stream_requester_,
328 DevicesEnumerated(render_frame_id, page_request_id, label, _))
329 .Times(1)
330 .WillOnce(DoAll(ExitMessageLoop(task_runner_, run_loop.QuitClosure()),
331 SaveArg<3>(&devices)));
332 run_loop.Run();
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(
344 &stream_requester_,
345 render_process_id,
346 render_frame_id,
347 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
348 page_request_id,
349 devices[0].device.id,
350 MEDIA_DEVICE_VIDEO_CAPTURE,
351 security_origin);
352 EXPECT_CALL(stream_requester_,
353 DeviceOpened(render_frame_id, page_request_id, _, _))
354 .Times(1)
355 .WillOnce(DoAll(ExitMessageLoop(task_runner_, run_loop.QuitClosure()),
356 SaveArg<2>(&opened_device_label_),
357 SaveArg<3>(&opened_device)));
358 run_loop.Run();
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())
367 return;
368 media_stream_manager_->CancelRequest(opened_device_label_);
369 opened_device_label_.clear();
370 opened_session_id_ = kInvalidMediaCaptureSessionId;
373 protected:
374 void StartCapture() {
375 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
376 .Times(AnyNumber())
377 .WillRepeatedly(Return());
379 base::RunLoop run_loop;
380 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId))
381 .Times(AnyNumber())
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);
388 run_loop.Run();
391 void StartStopCapture() {
392 // Quickly start and then stop capture, without giving much chance for
393 // asynchronous start operations to complete.
394 InSequence s;
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();
407 #ifdef DUMP_VIDEO
408 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
409 InSequence s;
410 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
411 .Times(AnyNumber()).WillRepeatedly(Return());
413 base::RunLoop run_loop;
414 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _, _, _, _, _, _, _))
415 .Times(AnyNumber())
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);
423 run_loop.Run();
425 #endif
427 void StopCapture() {
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);
437 run_loop.Run();
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))
447 .Times(AnyNumber())
448 .WillOnce(ExitMessageLoop(task_runner_, run_loop.QuitClosure()))
449 .RetiresOnSaturation();
450 run_loop.Run();
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);
462 host_->OnError(id);
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()
470 ->PostTaskAndReply(
471 FROM_HERE,
472 base::Bind(&base::DoNothing),
473 run_loop.QuitClosure());
474 run_loop.Run();
477 scoped_refptr<MockVideoCaptureHost> host_;
479 private:
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) {
494 StartCapture();
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);
500 CloseSession();
501 base::RunLoop().RunUntilIdle();
504 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
505 StartStopCapture();
508 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
509 StartCapture();
510 NotifyPacketReady();
511 NotifyPacketReady();
512 ReturnReceivedPackets();
513 StopCapture();
516 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
517 StartCapture();
518 SimulateError();
519 StopCapture();
522 TEST_F(VideoCaptureHostTest, StartCaptureError) {
523 EXPECT_CALL(*host_.get(),
524 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
525 StartCapture();
526 NotifyPacketReady();
527 SimulateError();
528 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
531 #ifdef DUMP_VIDEO
532 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
533 CaptureAndDumpVideo(640, 480, 30);
535 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
536 CaptureAndDumpVideo(1280, 720, 30);
538 #endif
540 } // namespace content