Upstream version 9.38.198.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/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"
36
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;
46
47 namespace content {
48
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;
52
53 // Define to enable test where video is dumped to file.
54 // #define DUMP_VIDEO
55
56 // Define to use a real video capture device.
57 // #define TEST_REAL_CAPTURE_DEVICE
58
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));
70   }
71   void NewVideoFrame(const void* buffer) {
72     if (file_.get() != NULL) {
73       ASSERT_EQ(1U, fwrite(buffer, expected_size_, 1, file_.get()));
74     }
75   }
76
77  private:
78   base::ScopedFILE file_;
79   int expected_size_;
80 };
81
82 class MockMediaStreamRequester : public MediaStreamRequester {
83  public:
84   MockMediaStreamRequester() {}
85   virtual ~MockMediaStreamRequester() {}
86
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));
109
110  private:
111   DISALLOW_COPY_AND_ASSIGN(MockMediaStreamRequester);
112 };
113
114 class MockVideoCaptureHost : public VideoCaptureHost {
115  public:
116   MockVideoCaptureHost(MediaStreamManager* manager)
117       : VideoCaptureHost(manager),
118         return_buffers_(false),
119         dump_video_(false) {}
120
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_METHOD4(OnBufferFilled,
130                void(int device_id,
131                     int buffer_id,
132                     const media::VideoCaptureFormat& format,
133                     base::TimeTicks timestamp));
134   MOCK_METHOD5(OnMailboxBufferFilled,
135                void(int device_id,
136                     int buffer_id,
137                     const gpu::MailboxHolder& mailbox_holder,
138                     const media::VideoCaptureFormat& format,
139                     base::TimeTicks timestamp));
140   MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
141
142   // Use class DumpVideo to write I420 video to file.
143   void SetDumpVideo(bool enable) {
144     dump_video_ = enable;
145   }
146
147   void SetReturnReceivedDibs(bool enable) {
148     return_buffers_ = enable;
149   }
150
151   // Return Dibs we currently have received.
152   void ReturnReceivedDibs(int device_id)  {
153     int handle = GetReceivedDib();
154     while (handle) {
155       this->OnReceiveEmptyBuffer(device_id, handle, 0);
156       handle = GetReceivedDib();
157     }
158   }
159
160   int GetReceivedDib() {
161     if (filled_dib_.empty())
162       return 0;
163     std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
164     int h = it->first;
165     delete it->second;
166     filled_dib_.erase(it);
167
168     return h;
169   }
170
171  private:
172   virtual ~MockVideoCaptureHost() {
173     STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
174                                          filled_dib_.end());
175   }
176
177   // This method is used to dispatch IPC messages to the renderer. We intercept
178   // these messages here and dispatch to our mock methods to verify the
179   // conversation between this object and the renderer.
180   virtual bool Send(IPC::Message* message) OVERRIDE {
181     CHECK(message);
182
183     // In this method we dispatch the messages to the according handlers as if
184     // we are the renderer.
185     bool handled = true;
186     IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
187       IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
188       IPC_MESSAGE_HANDLER(VideoCaptureMsg_FreeBuffer, OnBufferFreedDispatch)
189       IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
190       IPC_MESSAGE_HANDLER(VideoCaptureMsg_MailboxBufferReady,
191                           OnMailboxBufferFilledDispatch)
192       IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
193       IPC_MESSAGE_UNHANDLED(handled = false)
194     IPC_END_MESSAGE_MAP()
195     EXPECT_TRUE(handled);
196
197     delete message;
198     return true;
199   }
200
201   // These handler methods do minimal things and delegate to the mock methods.
202   void OnNewBufferCreatedDispatch(int device_id,
203                                   base::SharedMemoryHandle handle,
204                                   uint32 length,
205                                   int buffer_id) {
206     OnNewBufferCreated(device_id, handle, length, buffer_id);
207     base::SharedMemory* dib = new base::SharedMemory(handle, false);
208     dib->Map(length);
209     filled_dib_[buffer_id] = dib;
210   }
211
212   void OnBufferFreedDispatch(int device_id, int buffer_id) {
213     OnBufferFreed(device_id, buffer_id);
214
215     std::map<int, base::SharedMemory*>::iterator it =
216         filled_dib_.find(buffer_id);
217     ASSERT_TRUE(it != filled_dib_.end());
218     delete it->second;
219     filled_dib_.erase(it);
220   }
221
222   void OnBufferFilledDispatch(int device_id,
223                               int buffer_id,
224                               const media::VideoCaptureFormat& frame_format,
225                               base::TimeTicks timestamp) {
226     base::SharedMemory* dib = filled_dib_[buffer_id];
227     ASSERT_TRUE(dib != NULL);
228     if (dump_video_) {
229       if (!format_.IsValid()) {
230         dumper_.StartDump(frame_format.frame_size.width(),
231                           frame_format.frame_size.height());
232         format_ = frame_format;
233       }
234       ASSERT_EQ(format_.frame_size.width(), frame_format.frame_size.width())
235           << "Dump format does not handle variable resolution.";
236       ASSERT_EQ(format_.frame_size.height(), frame_format.frame_size.height())
237           << "Dump format does not handle variable resolution.";
238       dumper_.NewVideoFrame(dib->memory());
239     }
240
241     OnBufferFilled(device_id, buffer_id, frame_format, timestamp);
242     if (return_buffers_) {
243       VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id, 0);
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(device_id, buffer_id, 0);
256     }
257   }
258
259   void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
260     OnStateChanged(device_id, state);
261   }
262
263   std::map<int, base::SharedMemory*> filled_dib_;
264   bool return_buffers_;
265   bool dump_video_;
266   media::VideoCaptureFormat format_;
267   DumpVideo dumper_;
268 };
269
270 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
271   message_loop->PostTask(FROM_HERE, quit_closure);
272 }
273
274 // This is an integration test of VideoCaptureHost in conjunction with
275 // MediaStreamManager, VideoCaptureManager, VideoCaptureController, and
276 // VideoCaptureDevice.
277 class VideoCaptureHostTest : public testing::Test {
278  public:
279   VideoCaptureHostTest()
280       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
281         message_loop_(base::MessageLoopProxy::current()),
282         opened_session_id_(kInvalidMediaCaptureSessionId) {}
283
284   virtual void SetUp() OVERRIDE {
285     SetBrowserClientForTesting(&browser_client_);
286     // Create our own MediaStreamManager.
287     audio_manager_.reset(media::AudioManager::CreateForTesting());
288 #ifndef TEST_REAL_CAPTURE_DEVICE
289     base::CommandLine::ForCurrentProcess()->AppendSwitch(
290         switches::kUseFakeDeviceForMediaStream);
291 #endif
292     media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
293     media_stream_manager_->UseFakeUI(scoped_ptr<FakeMediaStreamUIProxy>());
294
295     // Create a Host and connect it to a simulated IPC channel.
296     host_ = new MockVideoCaptureHost(media_stream_manager_.get());
297     host_->OnChannelConnected(base::GetCurrentProcId());
298
299     OpenSession();
300   }
301
302   virtual void TearDown() OVERRIDE {
303     // Verifies and removes the expectations on host_ and
304     // returns true iff successful.
305     Mock::VerifyAndClearExpectations(host_.get());
306     EXPECT_EQ(0u, host_->entries_.size());
307
308     CloseSession();
309
310     // Simulate closing the IPC sender.
311     host_->OnChannelClosing();
312
313     // Release the reference to the mock object. The object will be destructed
314     // on the current message loop.
315     host_ = NULL;
316   }
317
318   void OpenSession() {
319     const int render_process_id = 1;
320     const int render_frame_id = 1;
321     const int page_request_id = 1;
322     const GURL security_origin("http://test.com");
323
324     ASSERT_TRUE(opened_device_label_.empty());
325
326     // Enumerate video devices.
327     StreamDeviceInfoArray devices;
328     {
329       base::RunLoop run_loop;
330       std::string label = media_stream_manager_->EnumerateDevices(
331           &stream_requester_,
332           render_process_id,
333           render_frame_id,
334           browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
335           page_request_id,
336           MEDIA_DEVICE_VIDEO_CAPTURE,
337           security_origin,
338           true);
339       EXPECT_CALL(stream_requester_, DevicesEnumerated(render_frame_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_frame_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_frame_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