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