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