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