[blink-in-js] Migrate resources required for blink-in-js to grd - part 2
[chromium-blink-merge.git] / content / browser / renderer_host / media / video_capture_host_unittest.cc
blob3b94007eab41e7f601442a3caf54e43ceeb95a13
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_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 using ::testing::_;
38 using ::testing::AtLeast;
39 using ::testing::AnyNumber;
40 using ::testing::DoAll;
41 using ::testing::InSequence;
42 using ::testing::Mock;
43 using ::testing::Return;
44 using ::testing::SaveArg;
45 using ::testing::StrictMock;
47 namespace content {
49 // Id used to identify the capture session between renderer and
50 // video_capture_host. This is an arbitrary value.
51 static const int kDeviceId = 555;
53 // Define to enable test where video is dumped to file.
54 // #define DUMP_VIDEO
56 // Define to use a real video capture device.
57 // #define TEST_REAL_CAPTURE_DEVICE
59 // Simple class used for dumping video to a file. This can be used for
60 // verifying the output.
61 class DumpVideo {
62 public:
63 DumpVideo() : expected_size_(0) {}
64 void StartDump(int width, int height) {
65 base::FilePath file_name = base::FilePath(base::StringPrintf(
66 FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
67 file_.reset(base::OpenFile(file_name, "wb"));
68 expected_size_ = media::VideoFrame::AllocationSize(
69 media::VideoFrame::I420, gfx::Size(width, height));
71 void NewVideoFrame(const void* buffer) {
72 if (file_.get() != NULL) {
73 ASSERT_EQ(1U, fwrite(buffer, expected_size_, 1, file_.get()));
77 private:
78 base::ScopedFILE file_;
79 int expected_size_;
82 class MockMediaStreamRequester : public MediaStreamRequester {
83 public:
84 MockMediaStreamRequester() {}
85 virtual ~MockMediaStreamRequester() {}
87 // MediaStreamRequester implementation.
88 MOCK_METHOD5(StreamGenerated,
89 void(int render_frame_id,
90 int page_request_id,
91 const std::string& label,
92 const StreamDeviceInfoArray& audio_devices,
93 const StreamDeviceInfoArray& video_devices));
94 MOCK_METHOD3(StreamGenerationFailed,
95 void(int render_frame_id,
96 int page_request_id,
97 content::MediaStreamRequestResult result));
98 MOCK_METHOD3(DeviceStopped, void(int render_frame_id,
99 const std::string& label,
100 const StreamDeviceInfo& device));
101 MOCK_METHOD4(DevicesEnumerated, void(int render_frame_id,
102 int page_request_id,
103 const std::string& label,
104 const StreamDeviceInfoArray& devices));
105 MOCK_METHOD4(DeviceOpened, void(int render_frame_id,
106 int page_request_id,
107 const std::string& label,
108 const StreamDeviceInfo& device_info));
110 private:
111 DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
114 class MockVideoCaptureHost : public VideoCaptureHost {
115 public:
116 MockVideoCaptureHost(MediaStreamManager* manager)
117 : VideoCaptureHost(manager),
118 return_buffers_(false),
119 dump_video_(false) {}
121 // A list of mock methods.
122 MOCK_METHOD4(OnNewBufferCreated,
123 void(int device_id,
124 base::SharedMemoryHandle handle,
125 int length,
126 int buffer_id));
127 MOCK_METHOD2(OnBufferFreed,
128 void(int device_id, int buffer_id));
129 MOCK_METHOD5(OnBufferFilled,
130 void(int device_id,
131 int buffer_id,
132 const media::VideoCaptureFormat& format,
133 const gfx::Rect& visible_rect,
134 base::TimeTicks timestamp));
135 MOCK_METHOD5(OnMailboxBufferFilled,
136 void(int device_id,
137 int buffer_id,
138 const gpu::MailboxHolder& mailbox_holder,
139 const media::VideoCaptureFormat& format,
140 base::TimeTicks timestamp));
141 MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
143 // Use class DumpVideo to write I420 video to file.
144 void SetDumpVideo(bool enable) {
145 dump_video_ = enable;
148 void SetReturnReceivedDibs(bool enable) {
149 return_buffers_ = enable;
152 // Return Dibs we currently have received.
153 void ReturnReceivedDibs(int device_id) {
154 int handle = GetReceivedDib();
155 while (handle) {
156 this->OnReceiveEmptyBuffer(device_id, handle, 0);
157 handle = GetReceivedDib();
161 int GetReceivedDib() {
162 if (filled_dib_.empty())
163 return 0;
164 std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
165 int h = it->first;
166 delete it->second;
167 filled_dib_.erase(it);
169 return h;
172 private:
173 virtual ~MockVideoCaptureHost() {
174 STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
175 filled_dib_.end());
178 // This method is used to dispatch IPC messages to the renderer. We intercept
179 // these messages here and dispatch to our mock methods to verify the
180 // conversation between this object and the renderer.
181 virtual bool Send(IPC::Message* message) OVERRIDE {
182 CHECK(message);
184 // In this method we dispatch the messages to the according handlers as if
185 // we are the renderer.
186 bool handled = true;
187 IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
188 IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
189 IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
190 IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
191 IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady,
192 OnMailboxBufferFilledDispatch)
193 IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
194 IPC_MESSAGE_UNHANDLED(handled = false)
195 IPC_END_MESSAGE_MAP()
196 EXPECT_TRUE(handled);
198 delete message;
199 return true;
202 // These handler methods do minimal things and delegate to the mock methods.
203 void OnNewBufferCreatedDispatch(int device_id,
204 base::SharedMemoryHandle handle,
205 uint32 length,
206 int buffer_id) {
207 OnNewBufferCreated(device_id, handle, length, buffer_id);
208 base::SharedMemory* dib = new base::SharedMemory(handle, false);
209 dib->Map(length);
210 filled_dib_[buffer_id] = dib;
213 void OnBufferFreedDispatch(int device_id, int buffer_id) {
214 OnBufferFreed(device_id, buffer_id);
216 std::map<int, base::SharedMemory*>::iterator it =
217 filled_dib_.find(buffer_id);
218 ASSERT_TRUE(it != filled_dib_.end());
219 delete it->second;
220 filled_dib_.erase(it);
223 void OnBufferFilledDispatch(int device_id,
224 int buffer_id,
225 const media::VideoCaptureFormat& frame_format,
226 const gfx::Rect& visible_rect,
227 base::TimeTicks timestamp) {
228 base::SharedMemory* dib = filled_dib_[buffer_id];
229 ASSERT_TRUE(dib != NULL);
230 if (dump_video_) {
231 if (!format_.IsValid()) {
232 dumper_.StartDump(frame_format.frame_size.width(),
233 frame_format.frame_size.height());
234 format_ = frame_format;
236 ASSERT_EQ(format_.frame_size.width(), frame_format.frame_size.width())
237 << "Dump format does not handle variable resolution.";
238 ASSERT_EQ(format_.frame_size.height(), frame_format.frame_size.height())
239 << "Dump format does not handle variable resolution.";
240 dumper_.NewVideoFrame(dib->memory());
243 OnBufferFilled(device_id, buffer_id, frame_format, visible_rect, timestamp);
244 if (return_buffers_) {
245 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id, 0);
249 void OnMailboxBufferFilledDispatch(int device_id,
250 int buffer_id,
251 const gpu::MailboxHolder& mailbox_holder,
252 const media::VideoCaptureFormat& format,
253 base::TimeTicks timestamp) {
254 OnMailboxBufferFilled(
255 device_id, buffer_id, mailbox_holder, format, timestamp);
256 if (return_buffers_) {
257 VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id, 0);
261 void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
262 OnStateChanged(device_id, state);
265 std::map<int, base::SharedMemory*> filled_dib_;
266 bool return_buffers_;
267 bool dump_video_;
268 media::VideoCaptureFormat format_;
269 DumpVideo dumper_;
272 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
273 message_loop->PostTask(FROM_HERE, quit_closure);
276 // This is an integration test of VideoCaptureHost in conjunction with
277 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
278 // VideoCaptureDevice.
279 class VideoCaptureHostTest : public testing::Test {
280 public:
281 VideoCaptureHostTest()
282 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
283 message_loop_(base::MessageLoopProxy::current()),
284 opened_session_id_(kInvalidMediaCaptureSessionId) {}
286 virtual void SetUp() OVERRIDE {
287 SetBrowserClientForTesting(&browser_client_);
288 // Create our own MediaStreamManager.
289 audio_manager_.reset(media::AudioManager::CreateForTesting());
290 #ifndef TEST_REAL_CAPTURE_DEVICE
291 base::CommandLine::ForCurrentProcess()->AppendSwitch(
292 switches::kUseFakeDeviceForMediaStream);
293 #endif
294 media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
295 media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
297 // Create a Host and connect it to a simulated IPC channel.
298 host_ = new MockVideoCaptureHost(media_stream_manager_.get());
299 host_->OnChannelConnected(base::GetCurrentProcId());
301 OpenSession();
304 virtual void TearDown() OVERRIDE {
305 // Verifies and removes the expectations on host_ and
306 // returns true iff successful.
307 Mock::VerifyAndClearExpectations(host_.get());
308 EXPECT_EQ(0u, host_->entries_.size());
310 CloseSession();
312 // Simulate closing the IPC sender.
313 host_->OnChannelClosing();
315 // Release the reference to the mock object. The object will be destructed
316 // on the current message loop.
317 host_ = NULL;
320 void OpenSession() {
321 const int render_process_id = 1;
322 const int render_frame_id = 1;
323 const int page_request_id = 1;
324 const GURL security_origin("http://test.com");
326 ASSERT_TRUE(opened_device_label_.empty());
328 // Enumerate video devices.
329 StreamDeviceInfoArray devices;
331 base::RunLoop run_loop;
332 std::string label = media_stream_manager_->EnumerateDevices(
333 &stream_requester_,
334 render_process_id,
335 render_frame_id,
336 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
337 page_request_id,
338 MEDIA_DEVICE_VIDEO_CAPTURE,
339 security_origin);
340 EXPECT_CALL(stream_requester_, DevicesEnumerated(render_frame_id,
341 page_request_id,
342 label,
344 .Times(1).WillOnce(
345 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
346 SaveArg<3>(&devices)));
347 run_loop.Run();
348 Mock::VerifyAndClearExpectations(&stream_requester_);
349 media_stream_manager_->CancelRequest(label);
351 ASSERT_FALSE(devices.empty());
352 ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
354 // Open the first device.
356 base::RunLoop run_loop;
357 StreamDeviceInfo opened_device;
358 media_stream_manager_->OpenDevice(
359 &stream_requester_,
360 render_process_id,
361 render_frame_id,
362 browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
363 page_request_id,
364 devices[0].device.id,
365 MEDIA_DEVICE_VIDEO_CAPTURE,
366 security_origin);
367 EXPECT_CALL(stream_requester_, DeviceOpened(render_frame_id,
368 page_request_id,
371 .Times(1).WillOnce(
372 DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
373 SaveArg<2>(&opened_device_label_),
374 SaveArg<3>(&opened_device)));
375 run_loop.Run();
376 Mock::VerifyAndClearExpectations(&stream_requester_);
377 ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
378 opened_session_id_ = opened_device.session_id;
382 void CloseSession() {
383 if (opened_device_label_.empty())
384 return;
385 media_stream_manager_->CancelRequest(opened_device_label_);
386 opened_device_label_.clear();
387 opened_session_id_ = kInvalidMediaCaptureSessionId;
390 protected:
391 void StartCapture() {
392 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
393 .Times(AnyNumber())
394 .WillRepeatedly(Return());
396 base::RunLoop run_loop;
397 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _, _, _))
398 .Times(AnyNumber())
399 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
401 media::VideoCaptureParams params;
402 params.requested_format = media::VideoCaptureFormat(
403 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
404 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
405 run_loop.Run();
408 void StartStopCapture() {
409 // Quickly start and then stop capture, without giving much chance for
410 // asynchronous start operations to complete.
411 InSequence s;
412 base::RunLoop run_loop;
413 EXPECT_CALL(*host_.get(),
414 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
415 media::VideoCaptureParams params;
416 params.requested_format = media::VideoCaptureFormat(
417 gfx::Size(352, 288), 30, media::PIXEL_FORMAT_I420);
418 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
419 host_->OnStopCapture(kDeviceId);
420 run_loop.RunUntilIdle();
423 #ifdef DUMP_VIDEO
424 void CaptureAndDumpVideo(int width, int height, int frame_rate) {
425 InSequence s;
426 EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
427 .Times(AnyNumber()).WillRepeatedly(Return());
429 base::RunLoop run_loop;
430 EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _, _))
431 .Times(AnyNumber())
432 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
434 media::VideoCaptureParams params;
435 params.requested_format =
436 media::VideoCaptureFormat(gfx::Size(width, height), frame_rate);
437 host_->SetDumpVideo(true);
438 host_->OnStartCapture(kDeviceId, opened_session_id_, params);
439 run_loop.Run();
441 #endif
443 void StopCapture() {
444 base::RunLoop run_loop;
445 EXPECT_CALL(*host_.get(),
446 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
447 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
449 host_->OnStopCapture(kDeviceId);
450 host_->SetReturnReceivedDibs(true);
451 host_->ReturnReceivedDibs(kDeviceId);
453 run_loop.Run();
455 host_->SetReturnReceivedDibs(false);
456 // Expect the VideoCaptureDevice has been stopped
457 EXPECT_EQ(0u, host_->entries_.size());
460 void NotifyPacketReady() {
461 base::RunLoop run_loop;
462 EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _, _, _))
463 .Times(AnyNumber())
464 .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()))
465 .RetiresOnSaturation();
466 run_loop.Run();
469 void ReturnReceivedPackets() {
470 host_->ReturnReceivedDibs(kDeviceId);
473 void SimulateError() {
474 // Expect a change state to error state sent through IPC.
475 EXPECT_CALL(*host_.get(),
476 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR)).Times(1);
477 VideoCaptureControllerID id(kDeviceId);
478 host_->OnError(id);
479 // Wait for the error callback.
480 base::RunLoop().RunUntilIdle();
483 scoped_refptr<MockVideoCaptureHost> host_;
485 private:
486 StrictMock<MockMediaStreamRequester> stream_requester_;
487 scoped_ptr<media::AudioManager> audio_manager_;
488 scoped_ptr<MediaStreamManager> media_stream_manager_;
489 content::TestBrowserThreadBundle thread_bundle_;
490 content::TestBrowserContext browser_context_;
491 content::TestContentBrowserClient browser_client_;
492 scoped_refptr<base::MessageLoopProxy> message_loop_;
493 int opened_session_id_;
494 std::string opened_device_label_;
496 DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
499 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
500 StartCapture();
502 // When the session is closed via the stream without stopping capture, the
503 // ENDED event is sent.
504 EXPECT_CALL(*host_.get(),
505 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED)).Times(1);
506 CloseSession();
507 base::RunLoop().RunUntilIdle();
510 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
511 StartStopCapture();
514 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
515 StartCapture();
516 NotifyPacketReady();
517 NotifyPacketReady();
518 ReturnReceivedPackets();
519 StopCapture();
522 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
523 StartCapture();
524 SimulateError();
525 StopCapture();
528 TEST_F(VideoCaptureHostTest, StartCaptureError) {
529 EXPECT_CALL(*host_.get(),
530 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
531 StartCapture();
532 NotifyPacketReady();
533 SimulateError();
534 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
537 #ifdef DUMP_VIDEO
538 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
539 CaptureAndDumpVideo(640, 480, 30);
541 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
542 CaptureAndDumpVideo(1280, 720, 30);
544 #endif
546 } // namespace content