summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authorguidou <guidou@chromium.org>2015-06-15 17:53:56 -0700
committerCommit bot <commit-bot@chromium.org>2015-06-16 00:54:36 +0000
commita0037e4cbf52a6e92e3fa01fafbd7e3667cccf37 (patch)
treeddf1d4cc6765163cce37546e1179a80d60aceef1 /media/audio
parent1d2ecc2c0406ccbec246aa6e5eb041f0d5b2bbc8 (diff)
downloadchromium_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.gn1
-rw-r--r--media/audio/audio_output_device.cc65
-rw-r--r--media/audio/audio_output_device.h15
-rw-r--r--media/audio/audio_output_device_unittest.cc45
-rw-r--r--media/audio/audio_output_ipc.h9
-rw-r--r--media/audio/audio_output_stream_sink.cc9
-rw-r--r--media/audio/audio_output_stream_sink.h5
-rw-r--r--media/audio/clockless_audio_sink.cc10
-rw-r--r--media/audio/clockless_audio_sink.h5
-rw-r--r--media/audio/null_audio_sink.cc8
-rw-r--r--media/audio/null_audio_sink.h3
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(&current_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(&current_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(&current_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();