| 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,
|
| + 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;
|
| + 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
|
|
|