Chromium Code Reviews| Index: content/renderer/media/audio_output_ipc_factory.cc |
| diff --git a/content/renderer/media/audio_output_ipc_factory.cc b/content/renderer/media/audio_output_ipc_factory.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..6109466b409e62fb774d40f7185b48e5ec370246 |
| --- /dev/null |
| +++ b/content/renderer/media/audio_output_ipc_factory.cc |
| @@ -0,0 +1,284 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/renderer/media/audio_output_ipc_factory.h" |
| + |
| +#include <string> |
| +#include <utility> |
| + |
| +#include "base/files/file.h" |
| +#include "base/memory/shared_memory_handle.h" |
| +#include "base/single_thread_task_runner.h" |
| +#include "base/sync_socket.h" |
| +#include "content/renderer/media/webrtc_logging.h" |
| +#include "media/base/audio_parameters.h" |
| +#include "media/mojo/interfaces/audio_output.mojom.h" |
| +#include "mojo/edk/embedder/embedder.h" |
| +#include "mojo/public/c/system/buffer.h" |
| +#include "mojo/public/cpp/bindings/binding.h" |
| +#include "mojo/public/cpp/bindings/interface_request.h" |
| +#include "mojo/public/cpp/system/handle.h" |
| +#include "mojo/public/cpp/system/platform_handle.h" |
| +#include "services/shell/public/cpp/interface_provider.h" |
| + |
| +namespace content { |
| + |
| +class AudioOutputIPCFactory::MojoAudioOutputIPC |
| + : public media::AudioOutputIPC, |
| + private media::mojom::AudioOutputStreamClient { |
| + public: |
| + MojoAudioOutputIPC(const scoped_refptr<AudioOutputIPCFactory> output_client, |
| + const media::mojom::AudioOutputPtr* output_service, |
| + int render_frame_id) |
| + : output_client_(output_client), |
| + output_service_(output_service), |
| + delegate_(nullptr), |
| + render_frame_id_(render_frame_id), |
| + stream_created_(false), |
| + weak_ptr_factory_(this) {} |
| + |
| + ~MojoAudioOutputIPC() override { StopClient(false); } |
| + |
| + private: |
| + // media::AudioOutputIPC implementation |
| + void RequestDeviceAuthorization(media::AudioOutputIPCDelegate* delegate, |
|
o1ka
2016/09/02 07:31:46
Could you split declaration and definition of meth
Max Morin
2016/09/02 10:27:07
Yes. I'll go through this class later.
|
| + int session_id, |
| + const std::string& device_id, |
| + const url::Origin& origin) override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(delegate); |
| + DCHECK(!delegate_); // Request exists/completed |
| + DCHECK(!stream_created_); // Active stream exists |
| + |
| + delegate_ = delegate; |
| + stream_id_ = output_client_->delegates_.Add(delegate); |
| + (*output_service_) |
| + ->RequestDeviceAuthorization( |
| + stream_id_, render_frame_id_, session_id, device_id, origin, |
| + base::Bind(&MojoAudioOutputIPC::RequestDeviceAuthorizationCallback, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + } |
| + |
| + void CreateStream(media::AudioOutputIPCDelegate* delegate, |
| + const media::AudioParameters& params) override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(!stream_created_); |
| + DCHECK(delegate); |
| + DCHECK(!delegate_ || delegate_ == delegate); // Ensure same delegate. |
| + |
| + if (!delegate_) |
| + stream_id_ = output_client_->delegates_.Add(delegate); |
| + delegate_ = delegate; |
|
o1ka
2016/09/02 07:31:46
Do we really want to continue if (delegate_ == del
|
| + media::mojom::AudioOutputStreamClientPtr client; |
| + binding_.reset(new mojo::Binding<media::mojom::AudioOutputStreamClient>( |
| + this, mojo::GetProxy(&client))); |
| + binding_->set_connection_error_handler(base::Bind( |
| + &MojoAudioOutputIPC::OnError, weak_ptr_factory_.GetWeakPtr())); |
| + |
| + (*output_service_) |
| + ->CreateStream(stream_id_, render_frame_id_, std::move(client), params, |
| + base::Bind(&MojoAudioOutputIPC::CreateStreamCallback, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + stream_created_ = true; |
| + } |
| + |
| + void PlayStream() override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(stream_created_); |
| + |
| + output_stream_service_->Play(); |
| + } |
| + |
| + void PauseStream() override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(stream_created_); |
| + |
| + output_stream_service_->Pause(); |
| + } |
| + |
| + void SetVolume(double volume) override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(stream_created_); |
| + |
| + output_stream_service_->SetVolume(volume); |
| + } |
| + |
| + void CloseStream() override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + |
| + stream_created_ = false; |
| + StopClient(false); |
| + } |
| + |
| + // media::mojom::AudioOutputStreamClient implementation |
| + void OnStreamStateChange( |
| + media::mojom::AudioOutputStreamState state) override { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + |
| + if (state == media::mojom::AudioOutputStreamState::ERROR) { |
| + StopClient(true); |
| + } else if (delegate_) { |
| + // delegate_->OnStateChanged(state); |
| + } |
| + } |
| + |
| + // This error handler is used by |output_stream_service_| on a connection |
| + // error. |
| + void OnError() { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + |
| + OnStreamStateChange(media::mojom::AudioOutputStreamState::ERROR); |
| + } |
| + |
| + void StopClient(bool error) { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + |
| + if (delegate_) { |
| + if (error) |
| + delegate_->OnStateChanged(media::mojom::AudioOutputStreamState::ERROR); |
| + // |output_client_| may have called NotifyConnectionClose() |
| + if (output_client_->delegates_.Lookup(stream_id_) == delegate_) |
| + output_client_->delegates_.Remove(stream_id_); |
| + } |
| + |
| + if (output_stream_service_.is_bound()) |
| + (*output_service_)->CloseStream(stream_id_); |
| + |
| + delegate_ = nullptr; |
| + output_stream_service_.reset(); |
| + binding_.reset(); |
| + } |
| + |
| + // Callback implementations |
| + void RequestDeviceAuthorizationCallback( |
| + int device_status, |
| + const media::AudioParameters& output_params, |
| + mojo::String matched_device_id) { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + |
| + if (!delegate_) |
| + return; |
| + delegate_->OnDeviceAuthorized( |
| + static_cast<media::OutputDeviceStatus>(device_status), output_params, |
| + matched_device_id.get()); |
| + } |
| + |
| + void CreateStreamCallback(media::mojom::AudioOutputStreamPtr stream, |
| + mojo::ScopedSharedBufferHandle shared_buffer, |
| + mojo::ScopedHandle socket_descriptor) { |
| + DCHECK(output_client_->io_task_runner_->BelongsToCurrentThread()); |
| + |
| + // If stream creation failed, inform the delegate. |
| + if (!stream.is_bound()) |
| + return OnError(); |
| + output_stream_service_ = std::move(stream); |
| + output_stream_service_.set_connection_error_handler(base::Bind( |
| + &MojoAudioOutputIPC::OnError, weak_ptr_factory_.GetWeakPtr())); |
| + |
| + base::SharedMemoryHandle handle; |
| + size_t length; |
| + bool read_only; |
| + MojoResult unwrap_shared_memory_result = mojo::UnwrapSharedMemoryHandle( |
| + std::move(shared_buffer), &handle, &length, &read_only); |
| + |
| + if (unwrap_shared_memory_result != MOJO_RESULT_OK) { |
| + DLOG(ERROR) << "Failed to pass shared memory. Closing: " |
| + << unwrap_shared_memory_result; |
| + return OnError(); |
| + } |
| + |
| + base::SyncSocket::TransitDescriptor descriptor; |
| + MojoResult unwrap_platform_file_result = |
| + mojo::UnwrapPlatformFile(std::move(socket_descriptor), |
| +#if defined(OS_WIN) |
| + &descriptor); |
| +#else |
| + &descriptor.fd); |
| +#endif |
| + if (unwrap_platform_file_result != MOJO_RESULT_OK) { |
| + DLOG(ERROR) << "Failed to pass transit descriptor. Closing: " |
| + << unwrap_platform_file_result; |
| + return OnError(); |
| + } |
| + |
| + base::SyncSocket::Handle socket_handle = |
| + base::SyncSocket::UnwrapHandle(descriptor); |
| + if (!delegate_) { |
| + base::SharedMemory::CloseHandle(handle); |
| + base::SyncSocket socket(socket_handle); |
| + return; |
| + } |
| + delegate_->OnStreamCreated(handle, socket_handle, length); |
| + } |
| + |
| + const scoped_refptr<AudioOutputIPCFactory> output_client_; |
| + const media::mojom::AudioOutputPtr* output_service_; |
| + media::mojom::AudioOutputStreamPtr output_stream_service_; |
| + media::AudioOutputIPCDelegate* delegate_; |
| + |
| + const int render_frame_id_; |
| + int stream_id_; |
| + bool stream_created_; |
| + |
| + std::unique_ptr<mojo::Binding<media::mojom::AudioOutputStreamClient>> |
| + binding_; |
| + |
| + base::WeakPtrFactory<MojoAudioOutputIPC> weak_ptr_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(MojoAudioOutputIPC); |
| +}; |
| + |
| +AudioOutputIPCFactory::AudioOutputIPCFactory( |
| + const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, |
| + shell::InterfaceProvider* interface_provider) |
| + : io_task_runner_(io_task_runner) { |
| + DCHECK(!g_audio_output_ipc_factory); |
| + g_audio_output_ipc_factory = this; |
| + |
| + interface_provider->GetInterface(&output_service_); |
| + output_service_.Bind(output_service_.PassInterface(), io_task_runner_); |
| +} |
| + |
| +AudioOutputIPCFactory::~AudioOutputIPCFactory() { |
| + DCHECK(g_audio_output_ipc_factory == this); |
| + g_audio_output_ipc_factory = nullptr; |
| + |
| + io_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&AudioOutputIPCFactory::NotifyConnectionClose, |
| + base::RetainedRef(this))); |
| +} |
| + |
| +AudioOutputIPCFactory* AudioOutputIPCFactory::g_audio_output_ipc_factory = |
| + nullptr; |
| + |
| +// static |
| +const scoped_refptr<AudioOutputIPCFactory> AudioOutputIPCFactory::Get() { |
| + return g_audio_output_ipc_factory; |
| +} |
| + |
| +std::unique_ptr<media::AudioOutputIPC> |
| +AudioOutputIPCFactory::CreateAudioOutputIPC(int render_frame_id) { |
| + return std::unique_ptr<media::AudioOutputIPC>( |
| + new MojoAudioOutputIPC(this, &output_service_, render_frame_id)); |
| +} |
| + |
| +void AudioOutputIPCFactory::OnConnectionError() { |
| + DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| + |
| + NotifyConnectionClose(); |
| +} |
| + |
| +void AudioOutputIPCFactory::NotifyConnectionClose() { |
| + DCHECK(io_task_runner_->BelongsToCurrentThread()); |
| + |
| + output_service_.reset(); |
| + IDMap<media::AudioOutputIPCDelegate>::iterator it(&delegates_); |
| + while (!it.IsAtEnd()) { |
| + it.GetCurrentValue()->OnIPCClosed(); |
| + delegates_.Remove(it.GetCurrentKey()); |
| + it.Advance(); |
| + } |
| +} |
| + |
| +} // namespace content |