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/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/video_capture_host.h"
     18 #include "content/browser/renderer_host/media/video_capture_manager.h"
     19 #include "content/common/media/video_capture_messages.h"
     20 #include "content/public/test/mock_resource_context.h"
     21 #include "content/public/test/test_browser_thread_bundle.h"
     22 #include "media/audio/audio_manager.h"
     23 #include "media/video/capture/video_capture_types.h"
     24 #include "net/url_request/url_request_context.h"
     25 #include "testing/gmock/include/gmock/gmock.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 using ::testing::_;
     29 using ::testing::AtLeast;
     30 using ::testing::AnyNumber;
     31 using ::testing::DoAll;
     32 using ::testing::InSequence;
     33 using ::testing::Mock;
     34 using ::testing::Return;
     35 
     36 namespace content {
     37 
     38 // Id used to identify the capture session between renderer and
     39 // video_capture_host.
     40 static const int kDeviceId = 1;
     41 // Id of a video capture device
     42 static const media::VideoCaptureSessionId kTestFakeDeviceId =
     43     VideoCaptureManager::kStartOpenSessionId;
     44 
     45 // Define to enable test where video is dumped to file.
     46 // #define DUMP_VIDEO
     47 
     48 // Define to use a real video capture device.
     49 // #define TEST_REAL_CAPTURE_DEVICE
     50 
     51 // Simple class used for dumping video to a file. This can be used for
     52 // verifying the output.
     53 class DumpVideo {
     54  public:
     55   DumpVideo() : expected_size_(0) {}
     56   void StartDump(int width, int height) {
     57     base::FilePath file_name = base::FilePath(base::StringPrintf(
     58         FILE_PATH_LITERAL("dump_w%d_h%d.yuv"), width, height));
     59     file_.reset(file_util::OpenFile(file_name, "wb"));
     60     expected_size_ = width * height * 3 / 2;
     61   }
     62   void NewVideoFrame(const void* buffer) {
     63     if (file_.get() != NULL) {
     64       fwrite(buffer, expected_size_, 1, file_.get());
     65     }
     66   }
     67 
     68  private:
     69   file_util::ScopedFILE file_;
     70   int expected_size_;
     71 };
     72 
     73 class MockVideoCaptureHost : public VideoCaptureHost {
     74  public:
     75   MockVideoCaptureHost(MediaStreamManager* manager)
     76       : VideoCaptureHost(manager),
     77         return_buffers_(false),
     78         dump_video_(false) {}
     79 
     80   // A list of mock methods.
     81   MOCK_METHOD4(OnNewBufferCreated,
     82                void(int device_id, base::SharedMemoryHandle handle,
     83                     int length, int buffer_id));
     84   MOCK_METHOD3(OnBufferFilled,
     85                void(int device_id, int buffer_id, base::Time timestamp));
     86   MOCK_METHOD2(OnStateChanged, void(int device_id, VideoCaptureState state));
     87   MOCK_METHOD1(OnDeviceInfo, void(int device_id));
     88 
     89   // Use class DumpVideo to write I420 video to file.
     90   void SetDumpVideo(bool enable) {
     91     dump_video_ = enable;
     92   }
     93 
     94   void SetReturnReceviedDibs(bool enable) {
     95     return_buffers_ = enable;
     96   }
     97 
     98   // Return Dibs we currently have received.
     99   void ReturnReceivedDibs(int device_id)  {
    100     int handle = GetReceivedDib();
    101     while (handle) {
    102       this->OnReceiveEmptyBuffer(device_id, handle);
    103       handle = GetReceivedDib();
    104     }
    105   }
    106 
    107   int GetReceivedDib() {
    108     if (filled_dib_.empty())
    109       return 0;
    110     std::map<int, base::SharedMemory*>::iterator it = filled_dib_.begin();
    111     int h = it->first;
    112     delete it->second;
    113     filled_dib_.erase(it);
    114 
    115     return h;
    116   }
    117 
    118  private:
    119   virtual ~MockVideoCaptureHost() {
    120     STLDeleteContainerPairSecondPointers(filled_dib_.begin(),
    121                                          filled_dib_.end());
    122   }
    123 
    124   // This method is used to dispatch IPC messages to the renderer. We intercept
    125   // these messages here and dispatch to our mock methods to verify the
    126   // conversation between this object and the renderer.
    127   virtual bool Send(IPC::Message* message) OVERRIDE {
    128     CHECK(message);
    129 
    130     // In this method we dispatch the messages to the according handlers as if
    131     // we are the renderer.
    132     bool handled = true;
    133     IPC_BEGIN_MESSAGE_MAP(MockVideoCaptureHost, *message)
    134       IPC_MESSAGE_HANDLER(VideoCaptureMsg_NewBuffer, OnNewBufferCreatedDispatch)
    135       IPC_MESSAGE_HANDLER(VideoCaptureMsg_BufferReady, OnBufferFilledDispatch)
    136       IPC_MESSAGE_HANDLER(VideoCaptureMsg_StateChanged, OnStateChangedDispatch)
    137       IPC_MESSAGE_HANDLER(VideoCaptureMsg_DeviceInfo, OnDeviceInfoDispatch)
    138       IPC_MESSAGE_UNHANDLED(handled = false)
    139     IPC_END_MESSAGE_MAP()
    140     EXPECT_TRUE(handled);
    141 
    142     delete message;
    143     return true;
    144   }
    145 
    146   // These handler methods do minimal things and delegate to the mock methods.
    147   void OnNewBufferCreatedDispatch(int device_id,
    148                                   base::SharedMemoryHandle handle,
    149                                   int length, int buffer_id) {
    150     OnNewBufferCreated(device_id, handle, length, buffer_id);
    151     base::SharedMemory* dib = new base::SharedMemory(handle, false);
    152     dib->Map(length);
    153     filled_dib_[buffer_id] = dib;
    154   }
    155 
    156   void OnBufferFilledDispatch(int device_id, int buffer_id,
    157                               base::Time timestamp) {
    158     if (dump_video_) {
    159       base::SharedMemory* dib = filled_dib_[buffer_id];
    160       ASSERT_TRUE(dib != NULL);
    161       dumper_.NewVideoFrame(dib->memory());
    162     }
    163 
    164     OnBufferFilled(device_id, buffer_id, timestamp);
    165     if (return_buffers_) {
    166       VideoCaptureHost::OnReceiveEmptyBuffer(device_id, buffer_id);
    167     }
    168   }
    169 
    170   void OnStateChangedDispatch(int device_id, VideoCaptureState state) {
    171     OnStateChanged(device_id, state);
    172   }
    173 
    174   void OnDeviceInfoDispatch(int device_id,
    175                             media::VideoCaptureParams params) {
    176     if (dump_video_) {
    177       dumper_.StartDump(params.width, params.height);
    178     }
    179     OnDeviceInfo(device_id);
    180   }
    181 
    182   std::map<int, base::SharedMemory*> filled_dib_;
    183   bool return_buffers_;
    184   bool dump_video_;
    185   DumpVideo dumper_;
    186 };
    187 
    188 ACTION_P2(ExitMessageLoop, message_loop, quit_closure) {
    189   message_loop->PostTask(FROM_HERE, quit_closure);
    190 }
    191 
    192 class VideoCaptureHostTest : public testing::Test {
    193  public:
    194   VideoCaptureHostTest()
    195       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
    196         message_loop_(base::MessageLoopProxy::current()) {
    197     // Create our own MediaStreamManager.
    198     audio_manager_.reset(media::AudioManager::Create());
    199     media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
    200 #ifndef TEST_REAL_CAPTURE_DEVICE
    201     media_stream_manager_->UseFakeDevice();
    202 #endif
    203 
    204     host_ = new MockVideoCaptureHost(media_stream_manager_.get());
    205 
    206     // Simulate IPC channel connected.
    207     host_->OnChannelConnected(base::GetCurrentProcId());
    208   }
    209 
    210   virtual ~VideoCaptureHostTest() {
    211     // Verifies and removes the expectations on host_ and
    212     // returns true iff successful.
    213     Mock::VerifyAndClearExpectations(host_.get());
    214 
    215     EXPECT_CALL(*host_.get(),
    216                 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
    217         .Times(AnyNumber());
    218 
    219     // Simulate closing the IPC channel.
    220     host_->OnChannelClosing();
    221 
    222     // Release the reference to the mock object. The object will be destructed
    223     // on the current message loop.
    224     host_ = NULL;
    225 
    226     media_stream_manager_->WillDestroyCurrentMessageLoop();
    227   }
    228 
    229  protected:
    230   void StartCapture() {
    231     InSequence s;
    232     // 1. First - get info about the new resolution
    233     EXPECT_CALL(*host_.get(), OnDeviceInfo(kDeviceId));
    234 
    235     // 2. Change state to started
    236     EXPECT_CALL(*host_.get(),
    237                 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STARTED));
    238 
    239     // 3. Newly created buffers will arrive.
    240     EXPECT_CALL(*host_.get(), OnNewBufferCreated(kDeviceId, _, _, _))
    241         .Times(AnyNumber()).WillRepeatedly(Return());
    242 
    243     // 4. First filled buffer will arrive.
    244     base::RunLoop run_loop;
    245     EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _))
    246         .Times(AnyNumber()).WillOnce(ExitMessageLoop(
    247             message_loop_, run_loop.QuitClosure()));
    248 
    249     media::VideoCaptureParams params;
    250     params.width = 352;
    251     params.height = 288;
    252     params.frame_per_second = 30;
    253     params.session_id = kTestFakeDeviceId;
    254     host_->OnStartCapture(kDeviceId, params);
    255     run_loop.Run();
    256   }
    257 
    258 #ifdef DUMP_VIDEO
    259   void CaptureAndDumpVideo(int width, int heigt, int frame_rate) {
    260     InSequence s;
    261     // 1. First - get info about the new resolution
    262     EXPECT_CALL(*host_, OnDeviceInfo(kDeviceId));
    263 
    264     // 2. Change state to started
    265     EXPECT_CALL(*host_, OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STARTED));
    266 
    267     // 3. First filled buffer will arrive.
    268     base::RunLoop run_loop;
    269     EXPECT_CALL(*host_, OnBufferFilled(kDeviceId, _, _))
    270         .Times(AnyNumber())
    271         .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
    272 
    273     media::VideoCaptureParams params;
    274     params.width = width;
    275     params.height = heigt;
    276     params.frame_per_second = frame_rate;
    277     params.session_id = kTestFakeDeviceId;
    278     host_->SetDumpVideo(true);
    279     host_->OnStartCapture(kDeviceId, params);
    280     run_loop.Run();
    281   }
    282 #endif
    283 
    284   void StopCapture() {
    285     base::RunLoop run_loop;
    286     EXPECT_CALL(*host_.get(),
    287                 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED))
    288         .WillOnce(ExitMessageLoop(message_loop_, run_loop.QuitClosure()));
    289 
    290     host_->OnStopCapture(kDeviceId);
    291     host_->SetReturnReceviedDibs(true);
    292     host_->ReturnReceivedDibs(kDeviceId);
    293 
    294     run_loop.Run();
    295 
    296     host_->SetReturnReceviedDibs(false);
    297     // Expect the VideoCaptureDevice has been stopped
    298     EXPECT_EQ(0u, host_->entries_.size());
    299   }
    300 
    301   void NotifyPacketReady() {
    302     base::RunLoop run_loop;
    303     EXPECT_CALL(*host_.get(), OnBufferFilled(kDeviceId, _, _))
    304         .Times(AnyNumber()).WillOnce(ExitMessageLoop(
    305             message_loop_, run_loop.QuitClosure()))
    306         .RetiresOnSaturation();
    307     run_loop.Run();
    308   }
    309 
    310   void ReturnReceivedPackets() {
    311     host_->ReturnReceivedDibs(kDeviceId);
    312   }
    313 
    314   void SimulateError() {
    315     // Expect a change state to error state  sent through IPC.
    316     EXPECT_CALL(*host_.get(),
    317                 OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_ERROR)).Times(1);
    318     VideoCaptureControllerID id(kDeviceId);
    319     host_->OnError(id);
    320     // Wait for the error callback.
    321     base::RunLoop().RunUntilIdle();
    322   }
    323 
    324   scoped_refptr<MockVideoCaptureHost> host_;
    325 
    326  private:
    327   scoped_ptr<media::AudioManager> audio_manager_;
    328   scoped_ptr<MediaStreamManager> media_stream_manager_;
    329   content::TestBrowserThreadBundle thread_bundle_;
    330   scoped_refptr<base::MessageLoopProxy> message_loop_;
    331 
    332   DISALLOW_COPY_AND_ASSIGN(VideoCaptureHostTest);
    333 };
    334 
    335 TEST_F(VideoCaptureHostTest, StartCapture) {
    336   StartCapture();
    337 }
    338 
    339 TEST_F(VideoCaptureHostTest, StartCapturePlayStop) {
    340   StartCapture();
    341   NotifyPacketReady();
    342   NotifyPacketReady();
    343   ReturnReceivedPackets();
    344   StopCapture();
    345 }
    346 
    347 TEST_F(VideoCaptureHostTest, StartCaptureErrorStop) {
    348   StartCapture();
    349   SimulateError();
    350   StopCapture();
    351 }
    352 
    353 TEST_F(VideoCaptureHostTest, StartCaptureError) {
    354   EXPECT_CALL(*host_.get(),
    355               OnStateChanged(kDeviceId, VIDEO_CAPTURE_STATE_STOPPED)).Times(0);
    356   StartCapture();
    357   NotifyPacketReady();
    358   SimulateError();
    359   base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
    360 }
    361 
    362 #ifdef DUMP_VIDEO
    363 TEST_F(VideoCaptureHostTest, CaptureAndDumpVideoVga) {
    364   CaptureAndDumpVideo(640, 480, 30);
    365 }
    366 TEST_F(VideoCaptureHostTest, CaptureAndDump720P) {
    367   CaptureAndDumpVideo(1280, 720, 30);
    368 }
    369 #endif
    370 
    371 }  // namespace content
    372