diff options
author | guidou <guidou@chromium.org> | 2015-06-15 17:53:56 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-16 00:54:36 +0000 |
commit | a0037e4cbf52a6e92e3fa01fafbd7e3667cccf37 (patch) | |
tree | ddf1d4cc6765163cce37546e1179a80d60aceef1 /media/audio | |
parent | 1d2ecc2c0406ccbec246aa6e5eb041f0d5b2bbc8 (diff) | |
download | chromium_src-a0037e4cbf52a6e92e3fa01fafbd7e3667cccf37.zip chromium_src-a0037e4cbf52a6e92e3fa01fafbd7e3667cccf37.tar.gz chromium_src-a0037e4cbf52a6e92e3fa01fafbd7e3667cccf37.tar.bz2 |
Add support for the audio-output-device switching IPC mechanism to the renderer lower layers (media::AudioOutputDevice, content::AudioMessageFilter and related classes).
The idea is to provide support for the Audio Output Devices API (http://w3c.github.io/mediacapture-output/)
BUG=438023
Committed: https://crrev.com/8c7fc2c8746c9cb294a5906572f23fbfc34726b3
Cr-Commit-Position: refs/heads/master@{#334451}
Review URL: https://codereview.chromium.org/1184473002
Cr-Commit-Position: refs/heads/master@{#334520}
Diffstat (limited to 'media/audio')
-rw-r--r-- | media/audio/BUILD.gn | 1 | ||||
-rw-r--r-- | media/audio/audio_output_device.cc | 65 | ||||
-rw-r--r-- | media/audio/audio_output_device.h | 15 | ||||
-rw-r--r-- | media/audio/audio_output_device_unittest.cc | 45 | ||||
-rw-r--r-- | media/audio/audio_output_ipc.h | 9 | ||||
-rw-r--r-- | media/audio/audio_output_stream_sink.cc | 9 | ||||
-rw-r--r-- | media/audio/audio_output_stream_sink.h | 5 | ||||
-rw-r--r-- | media/audio/clockless_audio_sink.cc | 10 | ||||
-rw-r--r-- | media/audio/clockless_audio_sink.h | 5 | ||||
-rw-r--r-- | media/audio/null_audio_sink.cc | 8 | ||||
-rw-r--r-- | media/audio/null_audio_sink.h | 3 |
11 files changed, 173 insertions, 2 deletions
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn index 11d3d76..ccc6f69 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn @@ -290,6 +290,7 @@ source_set("unittests") { "//media/base:test_support", "//testing/gmock", "//testing/gtest", + "//url", ] configs += [ diff --git a/media/audio/audio_output_device.cc b/media/audio/audio_output_device.cc index d55d100..3213400 100644 --- a/media/audio/audio_output_device.cc +++ b/media/audio/audio_output_device.cc @@ -4,6 +4,9 @@ #include "media/audio/audio_output_device.h" +#include <string> + +#include "base/callback_helpers.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "base/trace_event/trace_event.h" @@ -45,7 +48,8 @@ AudioOutputDevice::AudioOutputDevice( state_(IDLE), play_on_start_(true), session_id_(-1), - stopping_hack_(false) { + stopping_hack_(false), + current_switch_request_id_(0) { CHECK(ipc_); // The correctness of the code depends on the relative values assigned in the @@ -75,6 +79,14 @@ AudioOutputDevice::~AudioOutputDevice() { // The current design requires that the user calls Stop() before deleting // this class. DCHECK(audio_thread_.IsStopped()); + + // The following makes it possible for |current_switch_callback_| to release + // its bound parameters in the correct thread instead of implicitly releasing + // them in the thread where this destructor runs. + if (!current_switch_callback_.is_null()) { + base::ResetAndReturn(¤t_switch_callback_).Run( + SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE); + } } void AudioOutputDevice::Start() { @@ -117,6 +129,16 @@ bool AudioOutputDevice::SetVolume(double volume) { return true; } +void AudioOutputDevice::SwitchOutputDevice( + const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) { + DVLOG(1) << __FUNCTION__ << "(" << device_id << ")"; + task_runner()->PostTask( + FROM_HERE, base::Bind(&AudioOutputDevice::SwitchOutputDeviceOnIOThread, + this, device_id, security_origin, callback)); +} + void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) { DCHECK(task_runner()->BelongsToCurrentThread()); if (state_ == IDLE) { @@ -179,6 +201,21 @@ void AudioOutputDevice::SetVolumeOnIOThread(double volume) { ipc_->SetVolume(volume); } +void AudioOutputDevice::SwitchOutputDeviceOnIOThread( + const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) { + DCHECK(task_runner()->BelongsToCurrentThread()); + DVLOG(1) << __FUNCTION__ << "(" << device_id << "," << security_origin << ")"; + if (state_ >= CREATING_STREAM) { + SetCurrentSwitchRequest(callback); + ipc_->SwitchOutputDevice(device_id, security_origin, + current_switch_request_id_); + } else { + callback.Run(SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED); + } +} + void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegateState state) { DCHECK(task_runner()->BelongsToCurrentThread()); @@ -254,6 +291,32 @@ void AudioOutputDevice::OnStreamCreated( PlayOnIOThread(); } +void AudioOutputDevice::SetCurrentSwitchRequest( + const SwitchOutputDeviceCB& callback) { + DCHECK(task_runner()->BelongsToCurrentThread()); + DVLOG(1) << __FUNCTION__; + // If there is a previous unresolved request, resolve it as obsolete + if (!current_switch_callback_.is_null()) { + base::ResetAndReturn(¤t_switch_callback_).Run( + SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE); + } + current_switch_callback_ = callback; + current_switch_request_id_++; +} + +void AudioOutputDevice::OnOutputDeviceSwitched( + int request_id, + SwitchOutputDeviceResult result) { + DCHECK(task_runner()->BelongsToCurrentThread()); + DCHECK(request_id <= current_switch_request_id_); + DVLOG(1) << __FUNCTION__ + << "(" << request_id << ", " << result << ")"; + if (request_id != current_switch_request_id_) { + return; + } + base::ResetAndReturn(¤t_switch_callback_).Run(result); +} + void AudioOutputDevice::OnIPCClosed() { DCHECK(task_runner()->BelongsToCurrentThread()); state_ = IPC_CLOSED; diff --git a/media/audio/audio_output_device.h b/media/audio/audio_output_device.h index 6255c71..dc28352 100644 --- a/media/audio/audio_output_device.h +++ b/media/audio/audio_output_device.h @@ -58,6 +58,8 @@ #ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_ #define MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_ +#include <string> + #include "base/basictypes.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" @@ -95,6 +97,9 @@ class MEDIA_EXPORT AudioOutputDevice void Play() override; void Pause() override; bool SetVolume(double volume) override; + void SwitchOutputDevice(const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) override; // Methods called on IO thread ---------------------------------------------- // AudioOutputIPCDelegate methods. @@ -102,6 +107,8 @@ class MEDIA_EXPORT AudioOutputDevice void OnStreamCreated(base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, int length) override; + void OnOutputDeviceSwitched(int request_id, + SwitchOutputDeviceResult result) override; void OnIPCClosed() override; protected: @@ -129,11 +136,16 @@ class MEDIA_EXPORT AudioOutputDevice void PauseOnIOThread(); void ShutDownOnIOThread(); void SetVolumeOnIOThread(double volume); + void SwitchOutputDeviceOnIOThread(const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback); // base::MessageLoop::DestructionObserver implementation for the IO loop. // If the IO loop dies before we do, we shut down the audio thread from here. void WillDestroyCurrentMessageLoop() override; + void SetCurrentSwitchRequest(const SwitchOutputDeviceCB& callback); + AudioParameters audio_parameters_; RenderCallback* callback_; @@ -171,6 +183,9 @@ class MEDIA_EXPORT AudioOutputDevice // the callback via Start(). See http://crbug.com/151051 for details. bool stopping_hack_; + int current_switch_request_id_; + SwitchOutputDeviceCB current_switch_callback_; + DISALLOW_COPY_AND_ASSIGN(AudioOutputDevice); }; diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc index d803483..3b3ee51 100644 --- a/media/audio/audio_output_device_unittest.cc +++ b/media/audio/audio_output_device_unittest.cc @@ -5,11 +5,15 @@ #include <vector> #include "base/at_exit.h" +#include "base/bind_helpers.h" +#include "base/callback.h" #include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" #include "base/process/process_handle.h" #include "base/sync_socket.h" +#include "base/task_runner.h" #include "base/test/test_timeouts.h" +#include "base/thread_task_runner_handle.h" #include "media/audio/audio_output_device.h" #include "media/audio/sample_rates.h" #include "testing/gmock/include/gmock/gmock.h" @@ -52,6 +56,15 @@ class MockAudioOutputIPC : public AudioOutputIPC { MOCK_METHOD0(PauseStream, void()); MOCK_METHOD0(CloseStream, void()); MOCK_METHOD1(SetVolume, void(double volume)); + MOCK_METHOD3(SwitchOutputDevice, + void(const std::string& device_id, + const GURL& security_origin, + int request_id)); +}; + +class MockSwitchOutputDeviceCallback { + public: + MOCK_METHOD1(Callback, void(media::SwitchOutputDeviceResult result)); }; ACTION_P2(SendPendingBytes, socket, pending_bytes) { @@ -78,6 +91,7 @@ class AudioOutputDeviceTest void ExpectRenderCallback(); void WaitUntilRenderCallback(); void StopAudioDevice(); + void SwitchOutputDevice(); protected: // Used to clean up TLS pointers that the test(s) will initialize. @@ -88,9 +102,11 @@ class AudioOutputDeviceTest StrictMock<MockRenderCallback> callback_; MockAudioOutputIPC* audio_output_ipc_; // owned by audio_device_ scoped_refptr<AudioOutputDevice> audio_device_; + MockSwitchOutputDeviceCallback switch_output_device_callback_; private: int CalculateMemorySize(); + void SwitchOutputDeviceCallback(SwitchOutputDeviceResult result); SharedMemory shared_memory_; CancelableSyncSocket browser_socket_; @@ -196,6 +212,28 @@ void AudioOutputDeviceTest::StopAudioDevice() { io_loop_.RunUntilIdle(); } +void AudioOutputDeviceTest::SwitchOutputDevice() { + const GURL security_origin("http://localhost"); + const std::string device_id; + const int request_id = 1; + + // Switch the output device and check that the IPC message is sent + EXPECT_CALL(*audio_output_ipc_, + SwitchOutputDevice(device_id, security_origin, request_id)); + audio_device_->SwitchOutputDevice( + device_id, security_origin, + base::Bind(&MockSwitchOutputDeviceCallback::Callback, + base::Unretained(&switch_output_device_callback_))); + io_loop_.RunUntilIdle(); + + // Simulate the reception of a successful response from the browser + EXPECT_CALL(switch_output_device_callback_, + Callback(SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS)); + audio_device_->OnOutputDeviceSwitched(request_id, + SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS); + io_loop_.RunUntilIdle(); +} + TEST_P(AudioOutputDeviceTest, Initialize) { // Tests that the object can be constructed, initialized and destructed // without having ever been started/stopped. @@ -239,6 +277,13 @@ TEST_P(AudioOutputDeviceTest, CreateStream) { StopAudioDevice(); } +// Switch the output device +TEST_P(AudioOutputDeviceTest, SwitchOutputDevice) { + StartAudioDevice(); + SwitchOutputDevice(); + StopAudioDevice(); +} + INSTANTIATE_TEST_CASE_P(Render, AudioOutputDeviceTest, Values(false)); } // namespace media. diff --git a/media/audio/audio_output_ipc.h b/media/audio/audio_output_ipc.h index 9af1df4..2115508 100644 --- a/media/audio/audio_output_ipc.h +++ b/media/audio/audio_output_ipc.h @@ -57,6 +57,10 @@ class MEDIA_EXPORT AudioOutputIPCDelegate { base::SyncSocket::Handle socket_handle, int length) = 0; + // Called when an attempt to switch the output device has been completed + virtual void OnOutputDeviceSwitched(int request_id, + SwitchOutputDeviceResult result) = 0; + // Called when the AudioOutputIPC object is going away and/or when the IPC // channel has been closed and no more ipc requests can be made. // Implementations should delete their owned AudioOutputIPC instance @@ -99,6 +103,11 @@ class MEDIA_EXPORT AudioOutputIPC { // Sets the volume of the audio stream. virtual void SetVolume(double volume) = 0; + + // Switches the output device of the audio stream. + virtual void SwitchOutputDevice(const std::string& device_id, + const GURL& security_origin, + int request_id) = 0; }; } // namespace media diff --git a/media/audio/audio_output_stream_sink.cc b/media/audio/audio_output_stream_sink.cc index e233e99..e290e0f 100644 --- a/media/audio/audio_output_stream_sink.cc +++ b/media/audio/audio_output_stream_sink.cc @@ -57,7 +57,14 @@ bool AudioOutputStreamSink::SetVolume(double volume) { audio_task_runner_->PostTask( FROM_HERE, base::Bind(&AudioOutputStreamSink::DoSetVolume, this, volume)); return true; -}; +} + +void AudioOutputStreamSink::SwitchOutputDevice( + const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) { + callback.Run(SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED); +} int AudioOutputStreamSink::OnMoreData(AudioBus* dest, uint32 total_bytes_delay) { diff --git a/media/audio/audio_output_stream_sink.h b/media/audio/audio_output_stream_sink.h index c6f04a7..9d99fb7 100644 --- a/media/audio/audio_output_stream_sink.h +++ b/media/audio/audio_output_stream_sink.h @@ -5,6 +5,8 @@ #ifndef MEDIA_AUDIO_AUDIO_OUTPUT_STREAM_SINK_H_ #define MEDIA_AUDIO_AUDIO_OUTPUT_STREAM_SINK_H_ +#include <string> + #include "base/compiler_specific.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" @@ -34,6 +36,9 @@ class MEDIA_EXPORT AudioOutputStreamSink void Pause() override; void Play() override; bool SetVolume(double volume) override; + void SwitchOutputDevice(const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) override; // AudioSourceCallback implementation. int OnMoreData(AudioBus* dest, uint32 total_bytes_delay) override; diff --git a/media/audio/clockless_audio_sink.cc b/media/audio/clockless_audio_sink.cc index 44e9adb..60c839c 100644 --- a/media/audio/clockless_audio_sink.cc +++ b/media/audio/clockless_audio_sink.cc @@ -4,6 +4,9 @@ #include "media/audio/clockless_audio_sink.h" +#include "base/bind.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" #include "base/threading/simple_thread.h" namespace media { @@ -105,4 +108,11 @@ bool ClocklessAudioSink::SetVolume(double volume) { return volume == 0.0; } +void ClocklessAudioSink::SwitchOutputDevice( + const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) { + callback.Run(SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED); +} + } // namespace media diff --git a/media/audio/clockless_audio_sink.h b/media/audio/clockless_audio_sink.h index 075c55a..2251b00 100644 --- a/media/audio/clockless_audio_sink.h +++ b/media/audio/clockless_audio_sink.h @@ -5,6 +5,8 @@ #ifndef MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_ #define MEDIA_AUDIO_CLOCKLESS_AUDIO_SINK_H_ +#include <string> + #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "media/base/audio_renderer_sink.h" @@ -32,6 +34,9 @@ class MEDIA_EXPORT ClocklessAudioSink void Pause() override; void Play() override; bool SetVolume(double volume) override; + void SwitchOutputDevice(const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) override; // Returns the time taken to consume all the audio. base::TimeDelta render_time() { return playback_time_; } diff --git a/media/audio/null_audio_sink.cc b/media/audio/null_audio_sink.cc index e737c43..fc5e900 100644 --- a/media/audio/null_audio_sink.cc +++ b/media/audio/null_audio_sink.cc @@ -5,6 +5,7 @@ #include "media/audio/null_audio_sink.h" #include "base/bind.h" +#include "base/location.h" #include "base/single_thread_task_runner.h" #include "media/audio/fake_audio_worker.h" #include "media/base/audio_hash.h" @@ -70,6 +71,13 @@ bool NullAudioSink::SetVolume(double volume) { return volume == 0.0; } +void NullAudioSink::SwitchOutputDevice(const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) { + DCHECK(task_runner_->BelongsToCurrentThread()); + callback.Run(SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_SUPPORTED); +} + void NullAudioSink::CallRender() { DCHECK(task_runner_->BelongsToCurrentThread()); diff --git a/media/audio/null_audio_sink.h b/media/audio/null_audio_sink.h index 1abdb99..d8d5f04 100644 --- a/media/audio/null_audio_sink.h +++ b/media/audio/null_audio_sink.h @@ -32,6 +32,9 @@ class MEDIA_EXPORT NullAudioSink void Pause() override; void Play() override; bool SetVolume(double volume) override; + void SwitchOutputDevice(const std::string& device_id, + const GURL& security_origin, + const SwitchOutputDeviceCB& callback) override; // Enables audio frame hashing. Must be called prior to Initialize(). void StartAudioHashForTesting(); |