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/environment.h"
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/sync_socket.h"
     10 #include "content/browser/browser_thread_impl.h"
     11 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
     12 #include "content/browser/renderer_host/media/audio_mirroring_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/browser/renderer_host/media/mock_media_observer.h"
     16 #include "content/common/media/audio_messages.h"
     17 #include "content/common/media/media_stream_options.h"
     18 #include "ipc/ipc_message_utils.h"
     19 #include "media/audio/audio_manager.h"
     20 #include "media/audio/audio_manager_base.h"
     21 #include "media/audio/fake_audio_output_stream.h"
     22 #include "net/url_request/url_request_context.h"
     23 #include "testing/gmock/include/gmock/gmock.h"
     24 #include "testing/gtest/include/gtest/gtest.h"
     25 
     26 using ::testing::_;
     27 using ::testing::Assign;
     28 using ::testing::DoAll;
     29 using ::testing::NotNull;
     30 
     31 namespace content {
     32 
     33 static const int kRenderProcessId = 1;
     34 static const int kRenderViewId = 4;
     35 static const int kStreamId = 50;
     36 
     37 class MockAudioMirroringManager : public AudioMirroringManager {
     38  public:
     39   MockAudioMirroringManager() {}
     40   virtual ~MockAudioMirroringManager() {}
     41 
     42   MOCK_METHOD3(AddDiverter,
     43                void(int render_process_id, int render_view_id,
     44                     Diverter* diverter));
     45   MOCK_METHOD3(RemoveDiverter,
     46                void(int render_process_id, int render_view_id,
     47                     Diverter* diverter));
     48 
     49  private:
     50   DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
     51 };
     52 
     53 class MockAudioRendererHost : public AudioRendererHost {
     54  public:
     55   explicit MockAudioRendererHost(
     56       media::AudioManager* audio_manager,
     57       AudioMirroringManager* mirroring_manager,
     58       MediaInternals* media_internals,
     59       MediaStreamManager* media_stream_manager)
     60       : AudioRendererHost(kRenderProcessId,
     61                           audio_manager,
     62                           mirroring_manager,
     63                           media_internals,
     64                           media_stream_manager),
     65         shared_memory_length_(0) {
     66   }
     67 
     68   // A list of mock methods.
     69   MOCK_METHOD2(OnStreamCreated,
     70                void(int stream_id, int length));
     71   MOCK_METHOD1(OnStreamPlaying, void(int stream_id));
     72   MOCK_METHOD1(OnStreamPaused, void(int stream_id));
     73   MOCK_METHOD1(OnStreamError, void(int stream_id));
     74 
     75  private:
     76   virtual ~MockAudioRendererHost() {
     77     // Make sure all audio streams have been deleted.
     78     EXPECT_TRUE(audio_entries_.empty());
     79   }
     80 
     81   // This method is used to dispatch IPC messages to the renderer. We intercept
     82   // these messages here and dispatch to our mock methods to verify the
     83   // conversation between this object and the renderer.
     84   virtual bool Send(IPC::Message* message) {
     85     CHECK(message);
     86 
     87     // In this method we dispatch the messages to the according handlers as if
     88     // we are the renderer.
     89     bool handled = true;
     90     IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
     91       IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated,
     92                           OnStreamCreated)
     93       IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged,
     94                           OnStreamStateChanged)
     95       IPC_MESSAGE_UNHANDLED(handled = false)
     96     IPC_END_MESSAGE_MAP()
     97     EXPECT_TRUE(handled);
     98 
     99     delete message;
    100     return true;
    101   }
    102 
    103   void OnStreamCreated(const IPC::Message& msg, int stream_id,
    104                        base::SharedMemoryHandle handle,
    105 #if defined(OS_WIN)
    106                        base::SyncSocket::Handle socket_handle,
    107 #else
    108                        base::FileDescriptor socket_descriptor,
    109 #endif
    110                        uint32 length) {
    111     // Maps the shared memory.
    112     shared_memory_.reset(new base::SharedMemory(handle, false));
    113     CHECK(shared_memory_->Map(length));
    114     CHECK(shared_memory_->memory());
    115     shared_memory_length_ = length;
    116 
    117     // Create the SyncSocket using the handle.
    118     base::SyncSocket::Handle sync_socket_handle;
    119 #if defined(OS_WIN)
    120     sync_socket_handle = socket_handle;
    121 #else
    122     sync_socket_handle = socket_descriptor.fd;
    123 #endif
    124     sync_socket_.reset(new base::SyncSocket(sync_socket_handle));
    125 
    126     // And then delegate the call to the mock method.
    127     OnStreamCreated(stream_id, length);
    128   }
    129 
    130   void OnStreamStateChanged(const IPC::Message& msg, int stream_id,
    131                             media::AudioOutputIPCDelegate::State state) {
    132     switch (state) {
    133       case media::AudioOutputIPCDelegate::kPlaying:
    134         OnStreamPlaying(stream_id);
    135         break;
    136       case media::AudioOutputIPCDelegate::kPaused:
    137         OnStreamPaused(stream_id);
    138         break;
    139       case media::AudioOutputIPCDelegate::kError:
    140         OnStreamError(stream_id);
    141         break;
    142       default:
    143         FAIL() << "Unknown stream state";
    144         break;
    145     }
    146   }
    147 
    148   scoped_ptr<base::SharedMemory> shared_memory_;
    149   scoped_ptr<base::SyncSocket> sync_socket_;
    150   uint32 shared_memory_length_;
    151 
    152   DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
    153 };
    154 
    155 ACTION_P(QuitMessageLoop, message_loop) {
    156   message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
    157 }
    158 
    159 class AudioRendererHostTest : public testing::Test {
    160  public:
    161   AudioRendererHostTest() : is_stream_active_(false) {}
    162 
    163  protected:
    164   virtual void SetUp() {
    165     // Create a message loop so AudioRendererHost can use it.
    166     message_loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_IO));
    167 
    168     // Claim to be on both the UI and IO threads to pass all the DCHECKS.
    169     io_thread_.reset(new BrowserThreadImpl(BrowserThread::IO,
    170                                            message_loop_.get()));
    171     ui_thread_.reset(new BrowserThreadImpl(BrowserThread::UI,
    172                                            message_loop_.get()));
    173     audio_manager_.reset(media::AudioManager::Create());
    174     media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get()));
    175     media_stream_manager_->UseFakeDevice();
    176     observer_.reset(new MockMediaInternals());
    177     host_ = new MockAudioRendererHost(
    178         audio_manager_.get(), &mirroring_manager_, observer_.get(),
    179         media_stream_manager_.get());
    180 
    181     // Simulate IPC channel connected.
    182     host_->OnChannelConnected(base::GetCurrentProcId());
    183   }
    184 
    185   virtual void TearDown() {
    186     // Simulate closing the IPC channel.
    187     host_->OnChannelClosing();
    188 
    189     // Release the reference to the mock object.  The object will be destructed
    190     // on message_loop_.
    191     host_ = NULL;
    192 
    193     // We need to continue running message_loop_ to complete all destructions.
    194     SyncWithAudioThread();
    195     audio_manager_.reset();
    196 
    197     // Make sure the stream has been deleted before continuing.
    198     while (is_stream_active_)
    199       message_loop_->Run();
    200 
    201     io_thread_.reset();
    202     ui_thread_.reset();
    203 
    204     // Delete the IO message loop.  This will cause the MediaStreamManager to be
    205     // notified so it will stop its device thread and device managers.
    206     message_loop_.reset();
    207   }
    208 
    209   void Create(bool unified_stream) {
    210     EXPECT_CALL(*observer_,
    211                 OnSetAudioStreamStatus(_, kStreamId, "created"));
    212     EXPECT_CALL(*host_.get(), OnStreamCreated(kStreamId, _))
    213         .WillOnce(DoAll(Assign(&is_stream_active_, true),
    214                         QuitMessageLoop(message_loop_.get())));
    215     EXPECT_CALL(mirroring_manager_,
    216                 AddDiverter(kRenderProcessId, kRenderViewId, NotNull()))
    217         .RetiresOnSaturation();
    218 
    219     // Send a create stream message to the audio output stream and wait until
    220     // we receive the created message.
    221     int session_id;
    222     media::AudioParameters params;
    223     if (unified_stream) {
    224       // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to
    225       // pass the permission check.
    226       session_id = AudioInputDeviceManager::kFakeOpenSessionId;
    227       params = media::AudioParameters(
    228           media::AudioParameters::AUDIO_FAKE,
    229           media::CHANNEL_LAYOUT_STEREO,
    230           2,
    231           media::AudioParameters::kAudioCDSampleRate, 16,
    232           media::AudioParameters::kAudioCDSampleRate / 10);
    233     } else {
    234       session_id = 0;
    235       params = media::AudioParameters(
    236           media::AudioParameters::AUDIO_FAKE,
    237           media::CHANNEL_LAYOUT_STEREO,
    238           media::AudioParameters::kAudioCDSampleRate, 16,
    239           media::AudioParameters::kAudioCDSampleRate / 10);
    240     }
    241     host_->OnCreateStream(kStreamId, kRenderViewId, session_id, params);
    242     message_loop_->Run();
    243 
    244     // At some point in the future, a corresponding RemoveDiverter() call must
    245     // be made.
    246     EXPECT_CALL(mirroring_manager_,
    247                 RemoveDiverter(kRenderProcessId, kRenderViewId, NotNull()))
    248         .RetiresOnSaturation();
    249 
    250     // All created streams should ultimately be closed.
    251     EXPECT_CALL(*observer_,
    252                 OnSetAudioStreamStatus(_, kStreamId, "closed"));
    253 
    254     // Expect the audio stream will be deleted at some later point.
    255     EXPECT_CALL(*observer_, OnDeleteAudioStream(_, kStreamId))
    256         .WillOnce(DoAll(Assign(&is_stream_active_, false),
    257                         QuitMessageLoop(message_loop_.get())));
    258   }
    259 
    260   void Close() {
    261     // Send a message to AudioRendererHost to tell it we want to close the
    262     // stream.
    263     host_->OnCloseStream(kStreamId);
    264     if (is_stream_active_)
    265       message_loop_->Run();
    266     else
    267       message_loop_->RunUntilIdle();
    268   }
    269 
    270   void Play() {
    271     EXPECT_CALL(*observer_,
    272                 OnSetAudioStreamPlaying(_, kStreamId, true));
    273     EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId))
    274         .WillOnce(QuitMessageLoop(message_loop_.get()));
    275 
    276     host_->OnPlayStream(kStreamId);
    277     message_loop_->Run();
    278   }
    279 
    280   void Pause() {
    281     EXPECT_CALL(*observer_,
    282                 OnSetAudioStreamPlaying(_, kStreamId, false));
    283     EXPECT_CALL(*host_.get(), OnStreamPaused(kStreamId))
    284         .WillOnce(QuitMessageLoop(message_loop_.get()));
    285 
    286     host_->OnPauseStream(kStreamId);
    287     message_loop_->Run();
    288   }
    289 
    290   void SetVolume(double volume) {
    291     EXPECT_CALL(*observer_,
    292                 OnSetAudioStreamVolume(_, kStreamId, volume));
    293 
    294     host_->OnSetVolume(kStreamId, volume);
    295     message_loop_->RunUntilIdle();
    296   }
    297 
    298   void SimulateError() {
    299     EXPECT_CALL(*observer_,
    300                 OnSetAudioStreamStatus(_, kStreamId, "error"));
    301     EXPECT_EQ(1u, host_->audio_entries_.size())
    302         << "Calls Create() before calling this method";
    303 
    304     // Expect an error signal sent through IPC.
    305     EXPECT_CALL(*host_.get(), OnStreamError(kStreamId));
    306 
    307     // Simulate an error sent from the audio device.
    308     host_->ReportErrorAndClose(kStreamId);
    309     SyncWithAudioThread();
    310 
    311     // Expect the audio stream record is removed.
    312     EXPECT_EQ(0u, host_->audio_entries_.size());
    313   }
    314 
    315   // Called on the audio thread.
    316   static void PostQuitMessageLoop(base::MessageLoop* message_loop) {
    317     message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
    318   }
    319 
    320   // Called on the main thread.
    321   static void PostQuitOnAudioThread(media::AudioManager* audio_manager,
    322                                     base::MessageLoop* message_loop) {
    323     audio_manager->GetMessageLoop()->PostTask(FROM_HERE,
    324         base::Bind(&PostQuitMessageLoop, message_loop));
    325   }
    326 
    327   // SyncWithAudioThread() waits until all pending tasks on the audio thread
    328   // are executed while also processing pending task in message_loop_ on the
    329   // current thread. It is used to synchronize with the audio thread when we are
    330   // closing an audio stream.
    331   void SyncWithAudioThread() {
    332     // Don't use scoped_refptr to addref the media::AudioManager when posting
    333     // to the thread that itself owns.
    334     message_loop_->PostTask(
    335         FROM_HERE, base::Bind(&PostQuitOnAudioThread,
    336             base::Unretained(audio_manager_.get()),
    337             message_loop_.get()));
    338     message_loop_->Run();
    339   }
    340 
    341  private:
    342   scoped_ptr<MockMediaInternals> observer_;
    343   MockAudioMirroringManager mirroring_manager_;
    344   scoped_refptr<MockAudioRendererHost> host_;
    345   scoped_ptr<base::MessageLoop> message_loop_;
    346   scoped_ptr<BrowserThreadImpl> io_thread_;
    347   scoped_ptr<BrowserThreadImpl> ui_thread_;
    348   scoped_ptr<media::AudioManager> audio_manager_;
    349   scoped_ptr<MediaStreamManager> media_stream_manager_;
    350 
    351   bool is_stream_active_;
    352 
    353   DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
    354 };
    355 
    356 TEST_F(AudioRendererHostTest, CreateAndClose) {
    357   Create(false);
    358   Close();
    359 }
    360 
    361 // Simulate the case where a stream is not properly closed.
    362 TEST_F(AudioRendererHostTest, CreateAndShutdown) {
    363   Create(false);
    364 }
    365 
    366 TEST_F(AudioRendererHostTest, CreatePlayAndClose) {
    367   Create(false);
    368   Play();
    369   Close();
    370 }
    371 
    372 TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) {
    373   Create(false);
    374   Play();
    375   Pause();
    376   Close();
    377 }
    378 
    379 TEST_F(AudioRendererHostTest, SetVolume) {
    380   Create(false);
    381   SetVolume(0.5);
    382   Play();
    383   Pause();
    384   Close();
    385 }
    386 
    387 // Simulate the case where a stream is not properly closed.
    388 TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) {
    389   Create(false);
    390   Play();
    391 }
    392 
    393 // Simulate the case where a stream is not properly closed.
    394 TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) {
    395   Create(false);
    396   Play();
    397   Pause();
    398 }
    399 
    400 TEST_F(AudioRendererHostTest, SimulateError) {
    401   Create(false);
    402   Play();
    403   SimulateError();
    404 }
    405 
    406 // Simulate the case when an error is generated on the browser process,
    407 // the audio device is closed but the render process try to close the
    408 // audio stream again.
    409 TEST_F(AudioRendererHostTest, SimulateErrorAndClose) {
    410   Create(false);
    411   Play();
    412   SimulateError();
    413   Close();
    414 }
    415 
    416 TEST_F(AudioRendererHostTest, CreateUnifiedStreamAndClose) {
    417   Create(true);
    418   Close();
    419 }
    420 
    421 // TODO(hclam): Add tests for data conversation in low latency mode.
    422 
    423 }  // namespace content
    424