Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blobdd9ba733212972b5e80d5b92cbc0f22a5d289f7c
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/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"
39 #endif
41 using ::testing::_;
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;
51 namespace content {
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.
58 // #define DUMP_VIDEO
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.
65 class DumpVideo {
66 public:
67 DumpVideo() {}
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"),
72 coded_size.width(),
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()));
85 private:
86 base::ScopedFILE file_;
87 gfx::Size coded_size_;
90 class MockMediaStreamRequester : public MediaStreamRequester {
91 public:
92 MockMediaStreamRequester() {}
93 virtual ~MockMediaStreamRequester() {}
95 // MediaStreamRequester implementation.
96 MOCK_METHOD5(StreamGenerated,
97 void(int render_frame_id,
98 int page_request_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,
104 int page_request_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,
110 int page_request_id,
111 const std::string& label,
112 const StreamDeviceInfoArray& devices));
113 MOCK_METHOD4(DeviceOpened, void(int render_frame_id,
114 int page_request_id,
115 const std::string& label,
116 const StreamDeviceInfo& device_info));
118 private:
119 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
122 class MockVideoCaptureHost : public VideoCaptureHost {
123 public:
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,
131 void(int device_id,
132 base::SharedMemoryHandle handle,
133 int length,
134 int buffer_id));
135 MOCK_METHOD2(OnBufferFreed,
136 void(int device_id, int buffer_id));
137 MOCK_METHOD6(OnBufferFilled,
138 void(int device_id,
139 int buffer_id,
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,
145 void(int device_id,
146 int buffer_id,
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();
165 while (handle) {
166 this->OnReceiveEmptyBuffer(device_id, handle, 0);
167 handle = GetReceivedDib();
171 int GetReceivedDib() {
172 if (filled_dib_.empty())
173 return 0;
174 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
175 int h = it->first;
176 delete it->second;
177 filled_dib_.erase(it);
179 return h;
182 private:
183 virtual ~MockVideoCaptureHost() {
184 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
185 filled_dib_.end());
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 {
192 CHECK(message);
194 // In this method we dispatch the messages to the according handlers as if
195 // we are the renderer.
196 bool handled = true;
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);
208 delete message;
209 return true;
212 // These handler methods do minimal things and delegate to the mock methods.
213 void OnNewBufferCreatedDispatch(int device_id,
214 base::SharedMemoryHandle handle,
215 uint32 length,
216 int buffer_id) {
217 OnNewBufferCreated(device_id, handle, length, buffer_id);
218 base::SharedMemory* dib = new base::SharedMemory(handle, false);
219 dib->Map(length);
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());
229 delete it->second;
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);
237 if (dump_video_) {
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_;
270 bool dump_video_;
271 media::VideoCaptureFormat format_;
272 DumpVideo dumper_;
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 {
283 public:
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();
294 #endif
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);
301 #endif
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());
309 OpenSession();
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());
318 CloseSession();
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.
325 host_ = NULL;
327 #if defined(OS_CHROMEOS)
328 chromeos::CrasAudioHandler::Shutdown();
329 #endif
332 void OpenSession() {
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(
345 &stream_requester_,
346 render_process_id,
347 render_frame_id,
348 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
349 page_request_id,
350 MEDIA_DEVICE_VIDEO_CAPTURE,
351 security_origin);
352 EXPECT_CALL(stream_requester_, DevicesEnumerated(render_frame_id,
353 page_request_id,
354 label,
356 .Times(1).WillOnce(
357 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
358 SaveArg<3>(&devices)));
359 run_loop.Run();
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(
371 &stream_requester_,
372 render_process_id,
373 render_frame_id,
374 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
375 page_request_id,
376 devices[0].device.id,
377 MEDIA_DEVICE_VIDEO_CAPTURE,
378 security_origin);
379 EXPECT_CALL(stream_requester_, DeviceOpened(render_frame_id,
380 page_request_id,
383 .Times(1).WillOnce(
384 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
385 SaveArg<2>(&opened_device_label_),
386 SaveArg<3>(&opened_device)));
387 run_loop.Run();
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())
396 return;
397 media_stream_manager_->CancelRequest(opened_device_label_);
398 opened_device_label_.clear();
399 opened_session_id_ = kInvalidMediaCaptureSessionId;
402 protected:
403 void StartCapture() {
404 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
405 .Times(AnyNumber())
406 .WillRepeatedly(Return());
408 base::RunLoop run_loop;
409 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _, _, _, _))
410 .Times(AnyNumber())
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);
417 run_loop.Run();
420 void StartStopCapture() {
421 // Quickly start and then stop capture, without giving much chance for
422 // asynchronous start operations to complete.
423 InSequence s;
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();
436 #ifdef DUMP_VIDEO
437 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
438 InSequence s;
439 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
440 .Times(AnyNumber()).WillRepeatedly(Return());
442 base::RunLoop run_loop;
443 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _, _, _))
444 .Times(AnyNumber())
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);
452 run_loop.Run();
454 #endif
456 void StopCapture() {
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);
466 run_loop.Run();
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, _, _, _, _, _))
476 .Times(AnyNumber())
477 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()))
478 .RetiresOnSaturation();
479 run_loop.Run();
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);
491 host_->OnError(id);
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()
499 ->PostTaskAndReply(
500 FROM_HERE,
501 base::Bind(&base::DoNothing),
502 run_loop.QuitClosure());
503 run_loop.Run();
506 scoped_refptr<MockVideoCaptureHost> host_;
508 private:
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) {
523 StartCapture();
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);
529 CloseSession();
530 base::RunLoop().RunUntilIdle();
533 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
534 StartStopCapture();
537 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
538 StartCapture();
539 NotifyPacketReady();
540 NotifyPacketReady();
541 ReturnReceivedPackets();
542 StopCapture();
545 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
546 StartCapture();
547 SimulateError();
548 StopCapture();
551 TEST_F(VideoCaptureHostTest, StartCaptureError) {
552 EXPECT_CALL(*host_.get(),
553 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
554 StartCapture();
555 NotifyPacketReady();
556 SimulateError();
557 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
560 #ifdef DUMP_VIDEO
561 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
562 CaptureAndDumpVideo(640, 480, 30);
564 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
565 CaptureAndDumpVideo(1280, 720, 30);
567 #endif
569 } // namespace content