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