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 "base/bind.h"
      6 #include "base/command_line.h"
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/run_loop.h"
      9 #include "base/sync_socket.h"
     10 #include "content/browser/media/capture/audio_mirroring_manager.h"
     11 #include "content/browser/media/media_internals.h"
     12 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
     13 #include "content/browser/renderer_host/media/audio_renderer_host.h"
     14 #include "content/browser/renderer_host/media/media_stream_manager.h"
     15 #include "content/common/media/audio_messages.h"
     16 #include "content/public/common/content_switches.h"
     17 #include "content/public/test/test_browser_thread_bundle.h"
     18 #include "ipc/ipc_message_utils.h"
     19 #include "media/audio/audio_manager.h"
     20 #include "media/base/bind_to_current_loop.h"
     21 #include "media/base/media_switches.h"
     22 #include "testing/gmock/include/gmock/gmock.h"
     23 #include "testing/gtest/include/gtest/gtest.h"
     24 
     25 using ::testing::_;
     26 using ::testing::Assign;
     27 using ::testing::DoAll;
     28 using ::testing::NotNull;
     29 
     30 namespace {
     31 const int kRenderProcessId = 1;
     32 const int kRenderViewId = 4;
     33 const int kRenderFrameId = 5;
     34 const int kStreamId = 50;
     35 }  // namespace
     36 
     37 namespace content {
     38 
     39 class MockAudioMirroringManager : public AudioMirroringManager {
     40  public:
     41   MockAudioMirroringManager() {}
     42   virtual ~MockAudioMirroringManager() {}
     43 
     44   MOCK_METHOD3(AddDiverter,
     45                void(int render_process_id,
     46                     int render_view_id,
     47                     Diverter* diverter));
     48   MOCK_METHOD3(RemoveDiverter,
     49                void(int render_process_id,
     50                     int render_view_id,
     51                     Diverter* diverter));
     52 
     53  private:
     54   DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
     55 };
     56 
     57 class MockAudioRendererHost : public AudioRendererHost {
     58  public:
     59   MockAudioRendererHost(media::AudioManager* audio_manager,
     60                         AudioMirroringManager* mirroring_manager,
     61                         MediaInternals* media_internals,
     62                         MediaStreamManager* media_stream_manager)
     63       : AudioRendererHost(kRenderProcessId,
     64                           audio_manager,
     65                           mirroring_manager,
     66                           media_internals,
     67                           media_stream_manager),
     68         shared_memory_length_(0) {}
     69 
     70   // A list of mock methods.
     71   MOCK_METHOD2(OnStreamCreated, void(int stream_id, int length));
     72   MOCK_METHOD1(OnStreamPlaying, void(int stream_id));
     73   MOCK_METHOD1(OnStreamPaused, void(int stream_id));
     74   MOCK_METHOD1(OnStreamError, void(int stream_id));
     75 
     76  private:
     77   virtual ~MockAudioRendererHost() {
     78     // Make sure all audio streams have been deleted.
     79     EXPECT_TRUE(audio_entries_.empty());
     80   }
     81 
     82   // This method is used to dispatch IPC messages to the renderer. We intercept
     83   // these messages here and dispatch to our mock methods to verify the
     84   // conversation between this object and the renderer.
     85   virtual bool Send(IPC::Message* message) {
     86     CHECK(message);
     87 
     88     // In this method we dispatch the messages to the according handlers as if
     89     // we are the renderer.
     90     bool handled = true;
     91     IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
     92       IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated,
     93                           OnNotifyStreamCreated)
     94       IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged,
     95                           OnNotifyStreamStateChanged)
     96       IPC_MESSAGE_UNHANDLED(handled = false)
     97     IPC_END_MESSAGE_MAP()
     98     EXPECT_TRUE(handled);
     99 
    100     delete message;
    101     return true;
    102   }
    103 
    104   void OnNotifyStreamCreated(int stream_id,
    105                              base::SharedMemoryHandle handle,
    106 #if defined(OS_WIN)
    107                              base::SyncSocket::Handle socket_handle,
    108 #else
    109                              base::FileDescriptor socket_descriptor,
    110 #endif
    111                              uint32 length) {
    112     // Maps the shared memory.
    113     shared_memory_.reset(new base::SharedMemory(handle, false));
    114     CHECK(shared_memory_->Map(length));
    115     CHECK(shared_memory_->memory());
    116     shared_memory_length_ = length;
    117 
    118     // Create the SyncSocket using the handle.
    119     base::SyncSocket::Handle sync_socket_handle;
    120 #if defined(OS_WIN)
    121     sync_socket_handle = socket_handle;
    122 #else
    123     sync_socket_handle = socket_descriptor.fd;
    124 #endif
    125     sync_socket_.reset(new base::SyncSocket(sync_socket_handle));
    126 
    127     // And then delegate the call to the mock method.
    128     OnStreamCreated(stream_id, length);
    129   }
    130 
    131   void OnNotifyStreamStateChanged(int stream_id,
    132                                   media::AudioOutputIPCDelegate::State state) {
    133     switch (state) {
    134       case media::AudioOutputIPCDelegate::kPlaying:
    135         OnStreamPlaying(stream_id);
    136         break;
    137       case media::AudioOutputIPCDelegate::kPaused:
    138         OnStreamPaused(stream_id);
    139         break;
    140       case media::AudioOutputIPCDelegate::kError:
    141         OnStreamError(stream_id);
    142         break;
    143       default:
    144         FAIL() << "Unknown stream state";
    145         break;
    146     }
    147   }
    148 
    149   scoped_ptr<base::SharedMemory> shared_memory_;
    150   scoped_ptr<base::SyncSocket> sync_socket_;
    151   uint32 shared_memory_length_;
    152 
    153   DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
    154 };
    155 
    156 class AudioRendererHostTest : public testing::Test {
    157  public:
    158   AudioRendererHostTest() {
    159     audio_manager_.reset(media::AudioManager::CreateForTesting());
    160     CommandLine::ForCurrentProcess()->AppendSwitch(
    161         switches::kUseFakeDeviceForMediaStream);
    162     media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
    163     host_ = new MockAudioRendererHost(audio_manager_.get(),
    164                                       &mirroring_manager_,
    165                                       MediaInternals::GetInstance(),
    166                                       media_stream_manager_.get());
    167 
    168     // Simulate IPC channel connected.
    169     host_->set_peer_pid_for_testing(base::GetCurrentProcId());
    170   }
    171 
    172   virtual ~AudioRendererHostTest() {
    173     // Simulate closing the IPC channel and give the audio thread time to close
    174     // the underlying streams.
    175     host_->OnChannelClosing();
    176     SyncWithAudioThread();
    177 
    178     // Release the reference to the mock object.  The object will be destructed
    179     // on message_loop_.
    180     host_ = NULL;
    181   }
    182 
    183  protected:
    184   void Create(bool unified_stream) {
    185     EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _));
    186 
    187     EXPECT_CALL(mirroring_manager_,
    188                 AddDiverter(kRenderProcessId, kRenderViewId, NotNull()))
    189         .RetiresOnSaturation();
    190 
    191     // Send a create stream message to the audio output stream and wait until
    192     // we receive the created message.
    193     int session_id;
    194     media::AudioParameters params;
    195     if (unified_stream) {
    196       // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to
    197       // pass the permission check.
    198       session_id = AudioInputDeviceManager::kFakeOpenSessionId;
    199       params = media::AudioParameters(
    200           media::AudioParameters::AUDIO_FAKE,
    201           media::CHANNEL_LAYOUT_STEREO,
    202           2,
    203           media::AudioParameters::kAudioCDSampleRate, 16,
    204           media::AudioParameters::kAudioCDSampleRate / 10,
    205           media::AudioParameters::NO_EFFECTS);
    206     } else {
    207       session_id = 0;
    208       params = media::AudioParameters(
    209           media::AudioParameters::AUDIO_FAKE,
    210           media::CHANNEL_LAYOUT_STEREO,
    211           media::AudioParameters::kAudioCDSampleRate, 16,
    212           media::AudioParameters::kAudioCDSampleRate / 10);
    213     }
    214     host_->OnCreateStream(kStreamId, kRenderViewId, kRenderFrameId, session_id,
    215                           params);
    216 
    217     // At some point in the future, a corresponding RemoveDiverter() call must
    218     // be made.
    219     EXPECT_CALL(mirroring_manager_,
    220                 RemoveDiverter(kRenderProcessId, kRenderViewId, NotNull()))
    221         .RetiresOnSaturation();
    222     SyncWithAudioThread();
    223   }
    224 
    225   void Close() {
    226     // Send a message to AudioRendererHost to tell it we want to close the
    227     // stream.
    228     host_->OnCloseStream(kStreamId);
    229     SyncWithAudioThread();
    230   }
    231 
    232   void Play() {
    233     EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId));
    234     host_->OnPlayStream(kStreamId);
    235     SyncWithAudioThread();
    236   }
    237 
    238   void Pause() {
    239     EXPECT_CALL(*host_.get(), OnStreamPaused(kStreamId));
    240     host_->OnPauseStream(kStreamId);
    241     SyncWithAudioThread();
    242   }
    243 
    244   void SetVolume(double volume) {
    245     host_->OnSetVolume(kStreamId, volume);
    246     SyncWithAudioThread();
    247   }
    248 
    249   void SimulateError() {
    250     EXPECT_EQ(1u, host_->audio_entries_.size())
    251         << "Calls Create() before calling this method";
    252 
    253     // Expect an error signal sent through IPC.
    254     EXPECT_CALL(*host_.get(), OnStreamError(kStreamId));
    255 
    256     // Simulate an error sent from the audio device.
    257     host_->ReportErrorAndClose(kStreamId);
    258     SyncWithAudioThread();
    259 
    260     // Expect the audio stream record is removed.
    261     EXPECT_EQ(0u, host_->audio_entries_.size());
    262   }
    263 
    264   // SyncWithAudioThread() waits until all pending tasks on the audio thread
    265   // are executed while also processing pending task in message_loop_ on the
    266   // current thread. It is used to synchronize with the audio thread when we are
    267   // closing an audio stream.
    268   void SyncWithAudioThread() {
    269     base::RunLoop().RunUntilIdle();
    270 
    271     base::RunLoop run_loop;
    272     audio_manager_->GetTaskRunner()->PostTask(
    273         FROM_HERE, media::BindToCurrentLoop(run_loop.QuitClosure()));
    274     run_loop.Run();
    275   }
    276 
    277  private:
    278   // MediaStreamManager uses a DestructionObserver, so it must outlive the
    279   // TestBrowserThreadBundle.
    280   scoped_ptr<MediaStreamManager> media_stream_manager_;
    281   TestBrowserThreadBundle thread_bundle_;
    282   scoped_ptr<media::AudioManager> audio_manager_;
    283   MockAudioMirroringManager mirroring_manager_;
    284   scoped_refptr<MockAudioRendererHost> host_;
    285 
    286   DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
    287 };
    288 
    289 TEST_F(AudioRendererHostTest, CreateAndClose) {
    290   Create(false);
    291   Close();
    292 }
    293 
    294 // Simulate the case where a stream is not properly closed.
    295 TEST_F(AudioRendererHostTest, CreateAndShutdown) {
    296   Create(false);
    297 }
    298 
    299 TEST_F(AudioRendererHostTest, CreatePlayAndClose) {
    300   Create(false);
    301   Play();
    302   Close();
    303 }
    304 
    305 TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) {
    306   Create(false);
    307   Play();
    308   Pause();
    309   Close();
    310 }
    311 
    312 TEST_F(AudioRendererHostTest, SetVolume) {
    313   Create(false);
    314   SetVolume(0.5);
    315   Play();
    316   Pause();
    317   Close();
    318 }
    319 
    320 // Simulate the case where a stream is not properly closed.
    321 TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) {
    322   Create(false);
    323   Play();
    324 }
    325 
    326 // Simulate the case where a stream is not properly closed.
    327 TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) {
    328   Create(false);
    329   Play();
    330   Pause();
    331 }
    332 
    333 TEST_F(AudioRendererHostTest, SimulateError) {
    334   Create(false);
    335   Play();
    336   SimulateError();
    337 }
    338 
    339 // Simulate the case when an error is generated on the browser process,
    340 // the audio device is closed but the render process try to close the
    341 // audio stream again.
    342 TEST_F(AudioRendererHostTest, SimulateErrorAndClose) {
    343   Create(false);
    344   Play();
    345   SimulateError();
    346   Close();
    347 }
    348 
    349 TEST_F(AudioRendererHostTest, CreateUnifiedStreamAndClose) {
    350   Create(true);
    351   Close();
    352 }
    353 
    354 // TODO(hclam): Add tests for data conversation in low latency mode.
    355 
    356 }  // namespace content
    357