| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/audio_renderer_host.h" | 5 #include "content/browser/renderer_host/media/audio_renderer_host.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
| 13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/run_loop.h" | 14 #include "base/run_loop.h" |
| 15 #include "base/sync_socket.h" | 15 #include "base/sync_socket.h" |
| 16 #include "content/browser/media/audio_output_impl.h" |
| 16 #include "content/browser/media/capture/audio_mirroring_manager.h" | 17 #include "content/browser/media/capture/audio_mirroring_manager.h" |
| 17 #include "content/browser/media/media_internals.h" | 18 #include "content/browser/media/media_internals.h" |
| 18 #include "content/browser/renderer_host/media/audio_input_device_manager.h" | 19 #include "content/browser/renderer_host/media/audio_input_device_manager.h" |
| 19 #include "content/browser/renderer_host/media/media_stream_manager.h" | 20 #include "content/browser/renderer_host/media/media_stream_manager.h" |
| 20 #include "content/common/media/audio_messages.h" | 21 #include "content/common/media/audio_messages.h" |
| 21 #include "content/public/common/content_switches.h" | 22 #include "content/public/common/content_switches.h" |
| 22 #include "content/public/test/test_browser_thread_bundle.h" | 23 #include "content/public/test/test_browser_thread_bundle.h" |
| 23 #include "ipc/ipc_message_utils.h" | 24 #include "ipc/ipc_message_utils.h" |
| 24 #include "media/audio/audio_manager.h" | 25 #include "media/audio/audio_manager.h" |
| 25 #include "media/base/bind_to_current_loop.h" | 26 #include "media/base/bind_to_current_loop.h" |
| 26 #include "media/base/media_switches.h" | 27 #include "media/base/media_switches.h" |
| 28 #include "media/mojo/common/media_type_converters.h" |
| 29 #include "mojo/edk/embedder/embedder.h" |
| 27 #include "testing/gmock/include/gmock/gmock.h" | 30 #include "testing/gmock/include/gmock/gmock.h" |
| 28 #include "testing/gtest/include/gtest/gtest.h" | 31 #include "testing/gtest/include/gtest/gtest.h" |
| 29 | 32 |
| 30 using ::testing::_; | 33 using ::testing::_; |
| 31 using ::testing::Assign; | 34 using ::testing::Assign; |
| 32 using ::testing::DoAll; | 35 using ::testing::DoAll; |
| 33 using ::testing::NotNull; | 36 using ::testing::NotNull; |
| 34 | 37 |
| 35 namespace { | 38 namespace { |
| 36 const int kRenderProcessId = 1; | 39 const int kRenderProcessId = 1; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 MediaStreamManager* media_stream_manager, | 71 MediaStreamManager* media_stream_manager, |
| 69 const ResourceContext::SaltCallback& salt_callback) | 72 const ResourceContext::SaltCallback& salt_callback) |
| 70 : AudioRendererHost(kRenderProcessId, | 73 : AudioRendererHost(kRenderProcessId, |
| 71 audio_manager, | 74 audio_manager, |
| 72 mirroring_manager, | 75 mirroring_manager, |
| 73 media_internals, | 76 media_internals, |
| 74 media_stream_manager, | 77 media_stream_manager, |
| 75 salt_callback), | 78 salt_callback), |
| 76 shared_memory_length_(0) {} | 79 shared_memory_length_(0) {} |
| 77 | 80 |
| 81 AudioOutputImpl* Init(scoped_refptr<MockAudioRendererHost> host) { |
| 82 media::interfaces::AudioOutputPtr* stream = new media::interfaces::Audio
OutputPtr(); |
| 83 audio_output_impl_ = (new AudioOutputImpl(0, |
| 84 mojo::GetProxy(stream))); |
| 85 return audio_output_impl_; |
| 86 } |
| 78 // A list of mock methods. | 87 // A list of mock methods. |
| 79 MOCK_METHOD4(OnDeviceAuthorized, | 88 MOCK_METHOD4(OnDeviceAuthorized, |
| 80 void(int stream_id, | 89 void(int stream_id, |
| 81 media::OutputDeviceStatus device_status, | 90 media::OutputDeviceStatus device_status, |
| 82 const media::AudioParameters& output_params, | 91 const media::AudioParameters& output_params, |
| 83 const std::string& matched_device_id)); | 92 const std::string& matched_device_id)); |
| 84 MOCK_METHOD2(OnStreamCreated, void(int stream_id, int length)); | 93 MOCK_METHOD2(OnStreamCreated, void(int stream_id, int length)); |
| 85 MOCK_METHOD1(OnStreamPlaying, void(int stream_id)); | 94 MOCK_METHOD1(OnStreamPlaying, void(int stream_id)); |
| 86 MOCK_METHOD1(OnStreamPaused, void(int stream_id)); | 95 MOCK_METHOD1(OnStreamPaused, void(int stream_id)); |
| 87 MOCK_METHOD1(OnStreamError, void(int stream_id)); | 96 MOCK_METHOD1(OnStreamError, void(int stream_id)); |
| 88 | 97 |
| 98 void OnNotifyStreamCreated( |
| 99 int stream_id, |
| 100 base::SharedMemoryHandle handle, |
| 101 base::SyncSocket::TransitDescriptor socket_descriptor, |
| 102 uint32_t length) { |
| 103 // Maps the shared memory. |
| 104 shared_memory_.reset(new base::SharedMemory(handle, false)); |
| 105 CHECK(shared_memory_->Map(length)); |
| 106 CHECK(shared_memory_->memory()); |
| 107 shared_memory_length_ = length; |
| 108 |
| 109 // Create the SyncSocket using the handle. |
| 110 base::SyncSocket::Handle sync_socket_handle = |
| 111 base::SyncSocket::UnwrapHandle(socket_descriptor); |
| 112 sync_socket_.reset(new base::SyncSocket(sync_socket_handle)); |
| 113 |
| 114 // And then delegate the call to the mock method. |
| 115 OnStreamCreated(stream_id, length); |
| 116 } |
| 117 |
| 89 private: | 118 private: |
| 90 virtual ~MockAudioRendererHost() { | 119 virtual ~MockAudioRendererHost() { |
| 91 // Make sure all audio streams have been deleted. | 120 // Make sure all audio streams have been deleted. |
| 92 EXPECT_TRUE(audio_entries_.empty()); | 121 EXPECT_TRUE(audio_entries_.empty()); |
| 93 } | 122 } |
| 94 | 123 |
| 95 // This method is used to dispatch IPC messages to the renderer. We intercept | 124 // This method is used to dispatch IPC messages to the renderer. We intercept |
| 96 // these messages here and dispatch to our mock methods to verify the | 125 // these messages here and dispatch to our mock methods to verify the |
| 97 // conversation between this object and the renderer. | 126 // conversation between this object and the renderer. |
| 98 virtual bool Send(IPC::Message* message) { | 127 virtual bool Send(IPC::Message* message) { |
| 99 CHECK(message); | 128 CHECK(message); |
| 100 | 129 |
| 101 // In this method we dispatch the messages to the according handlers as if | 130 // In this method we dispatch the messages to the according handlers as if |
| 102 // we are the renderer. | 131 // we are the renderer. |
| 103 bool handled = true; | 132 bool handled = true; |
| 104 IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message) | 133 IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message) |
| 105 IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceAuthorized, | 134 IPC_MESSAGE_HANDLER(AudioMsg_NotifyDeviceAuthorized, |
| 106 OnNotifyDeviceAuthorized) | 135 OnNotifyDeviceAuthorized) |
| 107 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated, | |
| 108 OnNotifyStreamCreated) | |
| 109 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged, | 136 IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged, |
| 110 OnNotifyStreamStateChanged) | 137 OnNotifyStreamStateChanged) |
| 111 IPC_MESSAGE_UNHANDLED(handled = false) | 138 IPC_MESSAGE_UNHANDLED(handled = false) |
| 112 IPC_END_MESSAGE_MAP() | 139 IPC_END_MESSAGE_MAP() |
| 113 EXPECT_TRUE(handled); | 140 EXPECT_TRUE(handled); |
| 114 | 141 |
| 115 delete message; | 142 delete message; |
| 116 return true; | 143 return true; |
| 117 } | 144 } |
| 118 | 145 |
| 119 void OnNotifyDeviceAuthorized(int stream_id, | 146 void OnNotifyDeviceAuthorized(int stream_id, |
| 120 media::OutputDeviceStatus device_status, | 147 media::OutputDeviceStatus device_status, |
| 121 const media::AudioParameters& output_params, | 148 const media::AudioParameters& output_params, |
| 122 const std::string& matched_device_id) { | 149 const std::string& matched_device_id) { |
| 123 OnDeviceAuthorized(stream_id, device_status, output_params, | 150 OnDeviceAuthorized(stream_id, device_status, output_params, |
| 124 matched_device_id); | 151 matched_device_id); |
| 125 } | 152 } |
| 126 | 153 |
| 127 void OnNotifyStreamCreated( | |
| 128 int stream_id, | |
| 129 base::SharedMemoryHandle handle, | |
| 130 base::SyncSocket::TransitDescriptor socket_descriptor, | |
| 131 uint32_t length) { | |
| 132 // Maps the shared memory. | |
| 133 shared_memory_.reset(new base::SharedMemory(handle, false)); | |
| 134 CHECK(shared_memory_->Map(length)); | |
| 135 CHECK(shared_memory_->memory()); | |
| 136 shared_memory_length_ = length; | |
| 137 | |
| 138 // Create the SyncSocket using the handle. | |
| 139 base::SyncSocket::Handle sync_socket_handle = | |
| 140 base::SyncSocket::UnwrapHandle(socket_descriptor); | |
| 141 sync_socket_.reset(new base::SyncSocket(sync_socket_handle)); | |
| 142 | |
| 143 // And then delegate the call to the mock method. | |
| 144 OnStreamCreated(stream_id, length); | |
| 145 } | |
| 146 | |
| 147 void OnNotifyStreamStateChanged(int stream_id, | 154 void OnNotifyStreamStateChanged(int stream_id, |
| 148 media::AudioOutputIPCDelegateState state) { | 155 media::AudioOutputIPCDelegateState state) { |
| 149 switch (state) { | 156 switch (state) { |
| 150 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING: | 157 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING: |
| 151 OnStreamPlaying(stream_id); | 158 OnStreamPlaying(stream_id); |
| 152 break; | 159 break; |
| 153 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED: | 160 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED: |
| 154 OnStreamPaused(stream_id); | 161 OnStreamPaused(stream_id); |
| 155 break; | 162 break; |
| 156 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: | 163 case media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR: |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 base::RunLoop run_loop; | 206 base::RunLoop run_loop; |
| 200 media_stream_manager_->audio_output_device_enumerator()->Enumerate( | 207 media_stream_manager_->audio_output_device_enumerator()->Enumerate( |
| 201 base::Bind(&WaitForEnumeration, &run_loop)); | 208 base::Bind(&WaitForEnumeration, &run_loop)); |
| 202 run_loop.Run(); | 209 run_loop.Run(); |
| 203 | 210 |
| 204 host_ = new MockAudioRendererHost(audio_manager_.get(), &mirroring_manager_, | 211 host_ = new MockAudioRendererHost(audio_manager_.get(), &mirroring_manager_, |
| 205 MediaInternals::GetInstance(), | 212 MediaInternals::GetInstance(), |
| 206 media_stream_manager_.get(), | 213 media_stream_manager_.get(), |
| 207 GetMockSaltCallback()); | 214 GetMockSaltCallback()); |
| 208 | 215 |
| 216 audio_output_impl_.reset(host_->Init(host_.get())); |
| 217 |
| 209 // Simulate IPC channel connected. | 218 // Simulate IPC channel connected. |
| 210 host_->set_peer_process_for_testing(base::Process::Current()); | 219 host_->set_peer_process_for_testing(base::Process::Current()); |
| 211 } | 220 } |
| 212 | 221 |
| 213 ~AudioRendererHostTest() override { | 222 ~AudioRendererHostTest() override { |
| 214 // Simulate closing the IPC channel and give the audio thread time to close | 223 // Simulate closing the IPC channel and give the audio thread time to close |
| 215 // the underlying streams. | 224 // the underlying streams. |
| 216 host_->OnChannelClosing(); | 225 host_->OnChannelClosing(); |
| 217 SyncWithAudioThread(); | 226 SyncWithAudioThread(); |
| 218 | 227 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 media::AudioParameters::kAudioCDSampleRate / 10); | 263 media::AudioParameters::kAudioCDSampleRate / 10); |
| 255 int session_id = 0; | 264 int session_id = 0; |
| 256 if (unified_stream) { | 265 if (unified_stream) { |
| 257 // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to | 266 // Use AudioInputDeviceManager::kFakeOpenSessionId as the session id to |
| 258 // pass the permission check. | 267 // pass the permission check. |
| 259 session_id = AudioInputDeviceManager::kFakeOpenSessionId; | 268 session_id = AudioInputDeviceManager::kFakeOpenSessionId; |
| 260 } | 269 } |
| 261 host_->OnRequestDeviceAuthorization(kStreamId, kRenderFrameId, session_id, | 270 host_->OnRequestDeviceAuthorization(kStreamId, kRenderFrameId, session_id, |
| 262 device_id, security_origin); | 271 device_id, security_origin); |
| 263 if (expected_device_status == media::OUTPUT_DEVICE_STATUS_OK) { | 272 if (expected_device_status == media::OUTPUT_DEVICE_STATUS_OK) { |
| 264 host_->OnCreateStream(kStreamId, kRenderFrameId, params); | 273 host_->CreateStream(kStreamId, kRenderFrameId, params, |
| 274 base::Bind(&AudioRendererHostTest::CreateCallback, |
| 275 base::Unretained(this))); |
| 265 | 276 |
| 266 // At some point in the future, a corresponding RemoveDiverter() call must | 277 // At some point in the future, a corresponding RemoveDiverter() call must |
| 267 // be made. | 278 // be made. |
| 268 EXPECT_CALL(mirroring_manager_, RemoveDiverter(NotNull())) | 279 EXPECT_CALL(mirroring_manager_, RemoveDiverter(NotNull())) |
| 269 .RetiresOnSaturation(); | 280 .RetiresOnSaturation(); |
| 270 } | 281 } |
| 271 SyncWithAudioThread(); | 282 SyncWithAudioThread(); |
| 272 } | 283 } |
| 273 | 284 |
| 285 void CreateCallback(int stream_id, |
| 286 media::interfaces::AudioOutputStreamPtr stream, |
| 287 mojo::ScopedSharedBufferHandle shared_buffer, |
| 288 mojo::ScopedHandle socket_descriptor) { |
| 289 base::SharedMemoryHandle shared_memory_handle; |
| 290 size_t length; |
| 291 MojoResult pass_shared_memory_result = mojo::edk::PassSharedMemoryHandle( |
| 292 shared_buffer.release().value(), &shared_memory_handle, &length, nullptr
); |
| 293 |
| 294 if (pass_shared_memory_result != MOJO_RESULT_OK) { |
| 295 LOG(ERROR) << "Failed to pass shared memory. Closing: " |
| 296 << pass_shared_memory_result; |
| 297 return; |
| 298 } |
| 299 mojo::edk::ScopedPlatformHandle platform_handle; |
| 300 |
| 301 MojoResult pass_platform_handle_result = mojo::edk::PassWrappedPlatformHandl
e( |
| 302 socket_descriptor.release().value(), &platform_handle); |
| 303 |
| 304 if (pass_platform_handle_result != MOJO_RESULT_OK) { |
| 305 LOG(ERROR) << "Failed to pass transit descriptor. Closing: " |
| 306 << pass_platform_handle_result; |
| 307 return; |
| 308 } |
| 309 |
| 310 base::SyncSocket::TransitDescriptor descriptor; |
| 311 #if defined(OS_WIN) |
| 312 descriptor = platform_handle.release().handle; |
| 313 #else |
| 314 descriptor.fd = platform_handle.release().handle; |
| 315 #endif |
| 316 |
| 317 host_->OnNotifyStreamCreated(stream_id, shared_memory_handle, descriptor, |
| 318 length); |
| 319 streams_[stream_id] = make_scoped_ptr( |
| 320 new media::interfaces::AudioOutputStreamPtr(std::move(stream))); |
| 321 |
| 322 } |
| 323 |
| 324 |
| 274 void Close() { | 325 void Close() { |
| 275 // Send a message to AudioRendererHost to tell it we want to close the | 326 // Send a message to AudioRendererHost to tell it we want to close the |
| 276 // stream. | 327 // stream. |
| 277 host_->OnCloseStream(kStreamId); | 328 host_->OnCloseStream(kStreamId); |
| 278 SyncWithAudioThread(); | 329 SyncWithAudioThread(); |
| 279 } | 330 } |
| 280 | 331 |
| 281 void Play() { | 332 void Play() { |
| 282 EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId)); | 333 EXPECT_CALL(*host_.get(), OnStreamPlaying(kStreamId)); |
| 283 host_->OnPlayStream(kStreamId); | 334 host_->OnPlayStream(kStreamId); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 void SyncWithAudioThread() { | 368 void SyncWithAudioThread() { |
| 318 base::RunLoop().RunUntilIdle(); | 369 base::RunLoop().RunUntilIdle(); |
| 319 | 370 |
| 320 base::RunLoop run_loop; | 371 base::RunLoop run_loop; |
| 321 audio_manager_->GetTaskRunner()->PostTask( | 372 audio_manager_->GetTaskRunner()->PostTask( |
| 322 FROM_HERE, media::BindToCurrentLoop(run_loop.QuitClosure())); | 373 FROM_HERE, media::BindToCurrentLoop(run_loop.QuitClosure())); |
| 323 run_loop.Run(); | 374 run_loop.Run(); |
| 324 } | 375 } |
| 325 | 376 |
| 326 private: | 377 private: |
| 378 typedef std::map<int, std::unique_ptr<media::interfaces::AudioOutputStreamPt
r>> |
| 379 ScopedAudioOutputStreamPtrMap; |
| 327 // MediaStreamManager uses a DestructionObserver, so it must outlive the | 380 // MediaStreamManager uses a DestructionObserver, so it must outlive the |
| 328 // TestBrowserThreadBundle. | 381 // TestBrowserThreadBundle. |
| 329 std::unique_ptr<MediaStreamManager> media_stream_manager_; | 382 std::unique_ptr<MediaStreamManager> media_stream_manager_; |
| 330 TestBrowserThreadBundle thread_bundle_; | 383 TestBrowserThreadBundle thread_bundle_; |
| 331 std::unique_ptr<media::AudioManager> audio_manager_; | 384 std::unique_ptr<media::AudioManager> audio_manager_; |
| 332 MockAudioMirroringManager mirroring_manager_; | 385 MockAudioMirroringManager mirroring_manager_; |
| 333 scoped_refptr<MockAudioRendererHost> host_; | 386 scoped_refptr<MockAudioRendererHost> host_; |
| 387 std::unique_ptr<AudioOutputImpl> audio_output_impl_; |
| 388 ScopedAudioOutputStreamPtrMap streams_; |
| 334 | 389 |
| 335 DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest); | 390 DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest); |
| 336 }; | 391 }; |
| 337 | 392 |
| 393 |
| 338 TEST_F(AudioRendererHostTest, CreateAndClose) { | 394 TEST_F(AudioRendererHostTest, CreateAndClose) { |
| 339 Create(); | 395 Create(); |
| 340 Close(); | 396 Close(); |
| 341 } | 397 } |
| 342 | 398 |
| 343 // Simulate the case where a stream is not properly closed. | 399 // Simulate the case where a stream is not properly closed. |
| 344 TEST_F(AudioRendererHostTest, CreateAndShutdown) { | 400 TEST_F(AudioRendererHostTest, CreateAndShutdown) { |
| 345 Create(); | 401 Create(); |
| 346 } | 402 } |
| 347 | 403 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 } | 462 } |
| 407 | 463 |
| 408 TEST_F(AudioRendererHostTest, CreateInvalidDevice) { | 464 TEST_F(AudioRendererHostTest, CreateInvalidDevice) { |
| 409 Create(false, kInvalidDeviceId, url::Origin(GURL(kSecurityOrigin))); | 465 Create(false, kInvalidDeviceId, url::Origin(GURL(kSecurityOrigin))); |
| 410 Close(); | 466 Close(); |
| 411 } | 467 } |
| 412 | 468 |
| 413 // TODO(hclam): Add tests for data conversation in low latency mode. | 469 // TODO(hclam): Add tests for data conversation in low latency mode. |
| 414 | 470 |
| 415 } // namespace content | 471 } // namespace content |
| OLD | NEW |