Home | History | Annotate | Download | only in audio
      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 <vector>
      6 
      7 #include "base/at_exit.h"
      8 #include "base/memory/shared_memory.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/process/process_handle.h"
     11 #include "base/sync_socket.h"
     12 #include "base/test/test_timeouts.h"
     13 #include "media/audio/audio_output_device.h"
     14 #include "media/audio/sample_rates.h"
     15 #include "testing/gmock/include/gmock/gmock.h"
     16 #include "testing/gmock_mutant.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 using base::CancelableSyncSocket;
     20 using base::SharedMemory;
     21 using base::SyncSocket;
     22 using testing::_;
     23 using testing::DoAll;
     24 using testing::Invoke;
     25 using testing::Return;
     26 using testing::WithArgs;
     27 using testing::StrictMock;
     28 using testing::Values;
     29 
     30 namespace media {
     31 
     32 namespace {
     33 
     34 class MockRenderCallback : public AudioRendererSink::RenderCallback {
     35  public:
     36   MockRenderCallback() {}
     37   virtual ~MockRenderCallback() {}
     38 
     39   MOCK_METHOD2(Render, int(AudioBus* dest, int audio_delay_milliseconds));
     40   MOCK_METHOD0(OnRenderError, void());
     41 };
     42 
     43 class MockAudioOutputIPC : public AudioOutputIPC {
     44  public:
     45   MockAudioOutputIPC() {}
     46   virtual ~MockAudioOutputIPC() {}
     47 
     48   MOCK_METHOD3(CreateStream, void(AudioOutputIPCDelegate* delegate,
     49                                   const AudioParameters& params,
     50                                   int session_id));
     51   MOCK_METHOD0(PlayStream, void());
     52   MOCK_METHOD0(PauseStream, void());
     53   MOCK_METHOD0(CloseStream, void());
     54   MOCK_METHOD1(SetVolume, void(double volume));
     55 };
     56 
     57 // Creates a copy of a SyncSocket handle that we can give to AudioOutputDevice.
     58 // On Windows this means duplicating the pipe handle so that AudioOutputDevice
     59 // can call CloseHandle() (since ownership has been transferred), but on other
     60 // platforms, we just copy the same socket handle since AudioOutputDevice on
     61 // those platforms won't actually own the socket (FileDescriptor.auto_close is
     62 // false).
     63 bool DuplicateSocketHandle(SyncSocket::Handle socket_handle,
     64                            SyncSocket::Handle* copy) {
     65 #if defined(OS_WIN)
     66   HANDLE process = GetCurrentProcess();
     67   ::DuplicateHandle(process, socket_handle, process, copy,
     68                     0, FALSE, DUPLICATE_SAME_ACCESS);
     69   return *copy != NULL;
     70 #else
     71   *copy = socket_handle;
     72   return *copy != -1;
     73 #endif
     74 }
     75 
     76 ACTION_P2(SendPendingBytes, socket, pending_bytes) {
     77   socket->Send(&pending_bytes, sizeof(pending_bytes));
     78 }
     79 
     80 // Used to terminate a loop from a different thread than the loop belongs to.
     81 // |loop| should be a MessageLoopProxy.
     82 ACTION_P(QuitLoop, loop) {
     83   loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
     84 }
     85 
     86 }  // namespace.
     87 
     88 class AudioOutputDeviceTest
     89     : public testing::Test,
     90       public testing::WithParamInterface<bool> {
     91  public:
     92   AudioOutputDeviceTest();
     93   ~AudioOutputDeviceTest();
     94 
     95   void StartAudioDevice();
     96   void CreateStream();
     97   void ExpectRenderCallback();
     98   void WaitUntilRenderCallback();
     99   void StopAudioDevice();
    100 
    101  protected:
    102   // Used to clean up TLS pointers that the test(s) will initialize.
    103   // Must remain the first member of this class.
    104   base::ShadowingAtExitManager at_exit_manager_;
    105   base::MessageLoopForIO io_loop_;
    106   AudioParameters default_audio_parameters_;
    107   StrictMock<MockRenderCallback> callback_;
    108   MockAudioOutputIPC* audio_output_ipc_;  // owned by audio_device_
    109   scoped_refptr<AudioOutputDevice> audio_device_;
    110 
    111  private:
    112   int CalculateMemorySize();
    113 
    114   SharedMemory shared_memory_;
    115   CancelableSyncSocket browser_socket_;
    116   CancelableSyncSocket renderer_socket_;
    117 
    118   DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceTest);
    119 };
    120 
    121 int AudioOutputDeviceTest::CalculateMemorySize() {
    122   // Calculate output memory size.
    123  return AudioBus::CalculateMemorySize(default_audio_parameters_);
    124 }
    125 
    126 AudioOutputDeviceTest::AudioOutputDeviceTest() {
    127   default_audio_parameters_.Reset(
    128       AudioParameters::AUDIO_PCM_LINEAR,
    129       CHANNEL_LAYOUT_STEREO, 2, 0, 48000, 16, 1024);
    130 
    131   audio_output_ipc_ = new MockAudioOutputIPC();
    132   audio_device_ = new AudioOutputDevice(
    133       scoped_ptr<AudioOutputIPC>(audio_output_ipc_),
    134       io_loop_.message_loop_proxy());
    135 
    136   audio_device_->Initialize(default_audio_parameters_,
    137                             &callback_);
    138 
    139   io_loop_.RunUntilIdle();
    140 }
    141 
    142 AudioOutputDeviceTest::~AudioOutputDeviceTest() {
    143   audio_device_ = NULL;
    144 }
    145 
    146 void AudioOutputDeviceTest::StartAudioDevice() {
    147   audio_device_->Start();
    148 
    149   EXPECT_CALL(*audio_output_ipc_, CreateStream(audio_device_.get(), _, 0));
    150 
    151   io_loop_.RunUntilIdle();
    152 }
    153 
    154 void AudioOutputDeviceTest::CreateStream() {
    155   const int kMemorySize = CalculateMemorySize();
    156 
    157   ASSERT_TRUE(shared_memory_.CreateAndMapAnonymous(kMemorySize));
    158   memset(shared_memory_.memory(), 0xff, kMemorySize);
    159 
    160   ASSERT_TRUE(CancelableSyncSocket::CreatePair(&browser_socket_,
    161                                                &renderer_socket_));
    162 
    163   // Create duplicates of the handles we pass to AudioOutputDevice since
    164   // ownership will be transferred and AudioOutputDevice is responsible for
    165   // freeing.
    166   SyncSocket::Handle audio_device_socket = SyncSocket::kInvalidHandle;
    167   ASSERT_TRUE(DuplicateSocketHandle(renderer_socket_.handle(),
    168                                     &audio_device_socket));
    169   base::SharedMemoryHandle duplicated_memory_handle;
    170   ASSERT_TRUE(shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(),
    171                                             &duplicated_memory_handle));
    172 
    173   audio_device_->OnStreamCreated(duplicated_memory_handle, audio_device_socket,
    174                                  kMemorySize);
    175   io_loop_.RunUntilIdle();
    176 }
    177 
    178 void AudioOutputDeviceTest::ExpectRenderCallback() {
    179   // We should get a 'play' notification when we call OnStreamCreated().
    180   // Respond by asking for some audio data.  This should ask our callback
    181   // to provide some audio data that AudioOutputDevice then writes into the
    182   // shared memory section.
    183   const int kMemorySize = CalculateMemorySize();
    184 
    185   EXPECT_CALL(*audio_output_ipc_, PlayStream())
    186       .WillOnce(SendPendingBytes(&browser_socket_, kMemorySize));
    187 
    188   // We expect calls to our audio renderer callback, which returns the number
    189   // of frames written to the memory section.
    190   // Here's the second place where it gets hacky:  There's no way for us to
    191   // know (without using a sleep loop!) when the AudioOutputDevice has finished
    192   // writing the interleaved audio data into the shared memory section.
    193   // So, for the sake of this test, we consider the call to Render a sign
    194   // of success and quit the loop.
    195   const int kNumberOfFramesToProcess = 0;
    196   EXPECT_CALL(callback_, Render(_, _))
    197       .WillOnce(DoAll(
    198           QuitLoop(io_loop_.message_loop_proxy()),
    199           Return(kNumberOfFramesToProcess)));
    200 }
    201 
    202 void AudioOutputDeviceTest::WaitUntilRenderCallback() {
    203   // Don't hang the test if we never get the Render() callback.
    204   io_loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(),
    205                            TestTimeouts::action_timeout());
    206   io_loop_.Run();
    207 }
    208 
    209 void AudioOutputDeviceTest::StopAudioDevice() {
    210   audio_device_->Stop();
    211 
    212   EXPECT_CALL(*audio_output_ipc_, CloseStream());
    213 
    214   io_loop_.RunUntilIdle();
    215 }
    216 
    217 TEST_P(AudioOutputDeviceTest, Initialize) {
    218   // Tests that the object can be constructed, initialized and destructed
    219   // without having ever been started/stopped.
    220 }
    221 
    222 // Calls Start() followed by an immediate Stop() and check for the basic message
    223 // filter messages being sent in that case.
    224 TEST_P(AudioOutputDeviceTest, StartStop) {
    225   StartAudioDevice();
    226   StopAudioDevice();
    227 }
    228 
    229 // AudioOutputDevice supports multiple start/stop sequences.
    230 TEST_P(AudioOutputDeviceTest, StartStopStartStop) {
    231   StartAudioDevice();
    232   StopAudioDevice();
    233   StartAudioDevice();
    234   StopAudioDevice();
    235 }
    236 
    237 // Simulate receiving OnStreamCreated() prior to processing ShutDownOnIOThread()
    238 // on the IO loop.
    239 TEST_P(AudioOutputDeviceTest, StopBeforeRender) {
    240   StartAudioDevice();
    241 
    242   // Call Stop() but don't run the IO loop yet.
    243   audio_device_->Stop();
    244 
    245   // Expect us to shutdown IPC but not to render anything despite the stream
    246   // getting created.
    247   EXPECT_CALL(*audio_output_ipc_, CloseStream());
    248   CreateStream();
    249 }
    250 
    251 // Full test with output only.
    252 TEST_P(AudioOutputDeviceTest, CreateStream) {
    253   StartAudioDevice();
    254   ExpectRenderCallback();
    255   CreateStream();
    256   WaitUntilRenderCallback();
    257   StopAudioDevice();
    258 }
    259 
    260 INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false));
    261 
    262 }  // namespace media.
    263