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