Home | History | Annotate | Download | only in media
      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_view_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_view_id,
     96            int page_request_id,
     97            content::MediaStreamRequestResult result));
     98   MOCK_METHOD3(DeviceStopped, void(int render_view_id,
     99                                    const std::string& label,
    100                                    const StreamDeviceInfo& device));
    101   MOCK_METHOD4(DevicesEnumerated, void(int render_view_id,
    102                                        int page_request_id,
    103                                        const std::string& label,
    104                                        const StreamDeviceInfoArray& devices));
    105   MOCK_METHOD4(DeviceOpened, void(int render_view_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, std::vector<uint32>());
    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(
    244           device_id, buffer_id, std::vector<uint32>());
    245     }
    246   }
    247 
    248   void OnMailboxBufferFilledDispatch(int device_id,
    249                                      int buffer_id,
    250                                      const gpu::MailboxHolder& mailbox_holder,
    251                                      const media::VideoCaptureFormat& format,
    252                                      base::TimeTicks timestamp) {
    253     OnMailboxBufferFilled(
    254         device_id, buffer_id, mailbox_holder, format, timestamp);
    255     if (return_buffers_) {
    256       VideoCaptureHost::OnReceiveEmptyBuffer(
    257           device_id, buffer_id, std::vector<uint32>());
    258     }
    259   }
    260 
    261   void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
    262     OnStateChanged(device_id, state);
    263   }
    264 
    265   std::map<int, base::SharedMemory*> filled_dib_;
    266   bool return_buffers_;
    267   bool dump_video_;
    268   media::VideoCaptureFormat format_;
    269   DumpVideo dumper_;
    270 };
    271 
    272 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
    273   message_loop->PostTask(FROM_HERE, quit_closure);
    274 }
    275 
    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) {}
    285 
    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>());
    296 
    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());
    300 
    301     OpenSession();
    302   }
    303 
    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());
    309 
    310     CloseSession();
    311 
    312     // Simulate closing the IPC sender.
    313     host_->OnChannelClosing();
    314 
    315     // Release the reference to the mock object. The object will be destructed
    316     // on the current message loop.
    317     host_ = NULL;
    318   }
    319 
    320   void OpenSession() {
    321     const int render_process_id = 1;
    322     const int render_view_id = 1;
    323     const int page_request_id = 1;
    324     const GURL security_origin("http://test.com");
    325 
    326     ASSERT_TRUE(opened_device_label_.empty());
    327 
    328     // Enumerate video devices.
    329     StreamDeviceInfoArray devices;
    330     {
    331       base::RunLoop run_loop;
    332       std::string label = media_stream_manager_->EnumerateDevices(
    333           &stream_requester_,
    334           render_process_id,
    335           render_view_id,
    336           browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
    337           page_request_id,
    338           MEDIA_DEVICE_VIDEO_CAPTURE,
    339           security_origin,
    340           true);
    341       EXPECT_CALL(stream_requester_, DevicesEnumerated(render_view_id,
    342                                                        page_request_id,
    343                                                        label,
    344                                                        _))
    345           .Times(1).WillOnce(
    346               DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
    347                     SaveArg<3>(&devices)));
    348       run_loop.Run();
    349       Mock::VerifyAndClearExpectations(&stream_requester_);
    350       media_stream_manager_->CancelRequest(label);
    351     }
    352     ASSERT_FALSE(devices.empty());
    353     ASSERT_EQ(StreamDeviceInfo::kNoId, devices[0].session_id);
    354 
    355     // Open the first device.
    356     {
    357       base::RunLoop run_loop;
    358       StreamDeviceInfo opened_device;
    359       media_stream_manager_->OpenDevice(
    360           &stream_requester_,
    361           render_process_id,
    362           render_view_id,
    363           browser_context_.GetResourceContext()->GetMediaDeviceIDSalt(),
    364           page_request_id,
    365           devices[0].device.id,
    366           MEDIA_DEVICE_VIDEO_CAPTURE,
    367           security_origin);
    368       EXPECT_CALL(stream_requester_, DeviceOpened(render_view_id,
    369                                                   page_request_id,
    370                                                   _,
    371                                                   _))
    372           .Times(1).WillOnce(
    373               DoAll(ExitMessageLoop(message_loop_, run_loop.QuitClosure()),
    374                     SaveArg<2>(&opened_device_label_),
    375                     SaveArg<3>(&opened_device)));
    376       run_loop.Run();
    377       Mock::VerifyAndClearExpectations(&stream_requester_);
    378       ASSERT_NE(StreamDeviceInfo::kNoId, opened_device.session_id);
    379       opened_session_id_ = opened_device.session_id;
    380     }
    381   }
    382 
    383   void CloseSession() {
    384     if (opened_device_label_.empty())
    385       return;
    386     media_stream_manager_->CancelRequest(opened_device_label_);
    387     opened_device_label_.clear();
    388     opened_session_id_ = kInvalidMediaCaptureSessionId;
    389   }
    390 
    391  protected:
    392   void StartCapture() {
    393     EXPECT_CALL(*host_, OnNewBufferCreated(kDeviceId, _, _, _))
    394         .Times(AnyNumber()).WillRepeatedly(Return());
    395 
    396     base::RunLoop run_loop;
    397     EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
    398         .Times(AnyNumber()).WillOnce(ExitMessageLoop(
    399             message_loop_, run_loop.QuitClosure()));
    400 
    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();
    406   }
    407 
    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_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED));
    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     host_->OnStopCapture(kDeviceId);
    419     run_loop.RunUntilIdle();
    420   }
    421 
    422 #ifdef DUMP_VIDEO
    423   void CaptureAndDumpVideo(int width, int height, int frame_rate) {
    424     InSequence s;
    425     EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
    426         .Times(AnyNumber()).WillRepeatedly(Return());
    427 
    428     base::RunLoop run_loop;
    429     EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
    430         .Times(AnyNumber())
    431         .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
    432 
    433     media::VideoCaptureParams params;
    434     params.requested_format =
    435         media::VideoCaptureFormat(gfx::Size(width, height), frame_rate);
    436     host_->SetDumpVideo(true);
    437     host_->OnStartCapture(kDeviceId, opened_session_id_, params);
    438     run_loop.Run();
    439   }
    440 #endif
    441 
    442   void StopCapture() {
    443     base::RunLoop run_loop;
    444     EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
    445         .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
    446 
    447     host_->OnStopCapture(kDeviceId);
    448     host_->SetReturnReceivedDibs(true);
    449     host_->ReturnReceivedDibs(kDeviceId);
    450 
    451     run_loop.Run();
    452 
    453     host_->SetReturnReceivedDibs(false);
    454     // Expect the VideoCaptureDevice has been stopped
    455     EXPECT_EQ(0u, host_->entries_.size());
    456   }
    457 
    458   void NotifyPacketReady() {
    459     base::RunLoop run_loop;
    460     EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _, _))
    461         .Times(AnyNumber()).WillOnce(ExitMessageLoop(
    462             message_loop_, run_loop.QuitClosure()))
    463         .RetiresOnSaturation();
    464     run_loop.Run();
    465   }
    466 
    467   void ReturnReceivedPackets() {
    468     host_->ReturnReceivedDibs(kDeviceId);
    469   }
    470 
    471   void SimulateError() {
    472     // Expect a change state to error state sent through IPC.
    473     EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR))
    474         .Times(1);
    475     VideoCaptureControllerID id(kDeviceId);
    476     host_->OnError(id);
    477     // Wait for the error callback.
    478     base::RunLoop().RunUntilIdle();
    479   }
    480 
    481   scoped_refptr<MockVideoCaptureHost> host_;
    482 
    483  private:
    484   StrictMock<MockMediaStreamRequester> stream_requester_;
    485   scoped_ptr<media::AudioManager> audio_manager_;
    486   scoped_ptr<MediaStreamManager> media_stream_manager_;
    487   content::TestBrowserThreadBundle thread_bundle_;
    488   content::TestBrowserContext browser_context_;
    489   content::TestContentBrowserClient browser_client_;
    490   scoped_refptr<base::MessageLoopProxy> message_loop_;
    491   int opened_session_id_;
    492   std::string opened_device_label_;
    493 
    494   DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
    495 };
    496 
    497 TEST_F(VideoCaptureHostTest, CloseSessionWithoutStopping) {
    498   StartCapture();
    499 
    500   // When the session is closed via the stream without stopping capture, the
    501   // ENDED event is sent.
    502   EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ENDED))
    503       .Times(1);
    504   CloseSession();
    505   base::RunLoop().RunUntilIdle();
    506 }
    507 
    508 TEST_F(VideoCaptureHostTest, StopWhileStartPending) {
    509   StartStopCapture();
    510 }
    511 
    512 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
    513   StartCapture();
    514   NotifyPacketReady();
    515   NotifyPacketReady();
    516   ReturnReceivedPackets();
    517   StopCapture();
    518 }
    519 
    520 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
    521   StartCapture();
    522   SimulateError();
    523   StopCapture();
    524 }
    525 
    526 TEST_F(VideoCaptureHostTest, StartCaptureError) {
    527   EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
    528       .Times(0);
    529   StartCapture();
    530   NotifyPacketReady();
    531   SimulateError();
    532   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
    533 }
    534 
    535 #ifdef DUMP_VIDEO
    536 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
    537   CaptureAndDumpVideo(640, 480, 30);
    538 }
    539 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
    540   CaptureAndDumpVideo(1280, 720, 30);
    541 }
    542 #endif
    543 
    544 }  // namespace content
    545