Chromium Code Reviews| Index: content/browser/renderer_host/media/audio_output_impl.cc |
| diff --git a/content/browser/renderer_host/media/audio_output_impl.cc b/content/browser/renderer_host/media/audio_output_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e4a1f3b68c3fbe99acd24992ef41ccefe6449afc |
| --- /dev/null |
| +++ b/content/browser/renderer_host/media/audio_output_impl.cc |
| @@ -0,0 +1,215 @@ |
| +// 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/browser/renderer_host/media/audio_output_impl.h" |
| + |
| +#include <algorithm> |
| +#include <memory> |
| +#include <string> |
| +#include <utility> |
| + |
| +#include "base/memory/shared_memory.h" |
| +#include "content/browser/media/capture/audio_mirroring_manager.h" |
| +#include "mojo/public/cpp/system/platform_handle.h" |
| + |
| +namespace content { |
| + |
| +// static |
| +AudioOutputImpl::UniquePtr AudioOutputImpl::Create( |
| + base::SingleThreadTaskRunner* task_runner, |
| + Host* host, |
| + int32_t id, |
| + int render_frame_id, |
| + int render_process_id, |
| + const std::string& output_device_id) { |
| + return UniquePtr(new AudioOutputImpl(task_runner, host, id, render_frame_id, |
| + render_process_id, output_device_id), |
| + Deleter()); |
| +} |
| + |
| +AudioOutputImpl::~AudioOutputImpl() { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| +} |
| + |
| +void AudioOutputImpl::Bind(media::mojom::AudioOutputRequest request) { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + binding_.Bind(std::move(request)); |
| + binding_.set_connection_error_handler( |
| + base::Bind(&AudioOutputImpl::OnError, base::Unretained(this))); |
| +} |
| + |
| +void AudioOutputImpl::Close() { |
| + // Since AudioOutputController is closed asynchronously, we need to take |
| + // extra care when AudioOutputImpl is deleted. We use a custom deleter for |
| + // our unique_ptrs to make this extra trouble transparent to the owner. |
| + // The entire termination sequence is like this: |
| + // 1. Close is called and we unbind ourselves, if bound. |
| + // 2. We ask the AudioOutputService to remove us. |
| + // 3. AudioOutputService removes its reference, causing Deleter::operator() |
| + // to run. |
| + // 4. a. Deleter closes controller_, which then deletes us by callback. |
| + // b. There is no controller, so Deleter just deletes us. |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + if (is_closing) |
| + return; |
| + is_closing = true; |
| + binding_.Close(); |
| + if (IsStarted()) |
| + host_->NotifyStreamClosed(id_); |
| + host_->Remove(id_); |
| +} |
| + |
| +void AudioOutputImpl::Start(const media::AudioParameters& params, |
| + const StartCallback& callback) { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + DCHECK(!is_closing); |
| + if (IsStarted()) { |
| + Close(); |
| + return; |
| + } |
| + Init(params); |
| + if (!IsStarted()) { |
| + Close(); |
| + return; |
| + } |
| + |
| + host_->NotifyStreamCreated(id_, controller_.get(), render_frame_id_); |
| + // We add the controller to AudioMirroringManager in this class since we |
| + // have to remove it in this class. |
| + content::AudioMirroringManager::GetInstance()->AddDiverter( |
| + render_process_id_, render_frame_id_, controller_.get()); |
| + |
| + controller_->Play(); |
| + start_callback_ = std::move(callback); |
| +} |
| + |
| +void AudioOutputImpl::Play() { |
| + DCHECK(!is_closing); |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + if (!IsStarted()) |
| + Close(); |
|
o1ka
2016/09/28 10:24:06
Here and in other places: what is the purpose of C
Max Morin
2016/09/28 12:47:33
Ok, in this case the client might call Play() befo
|
| + else |
| + controller_->Play(); |
| +} |
| + |
| +void AudioOutputImpl::Pause() { |
| + DCHECK(!is_closing); |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + if (!IsStarted()) |
| + Close(); |
| + else |
| + controller_->Pause(); |
| +} |
| + |
| +void AudioOutputImpl::SetVolume(double volume) { |
| + DCHECK(!is_closing); |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + if (!IsStarted() || volume < 0.0 || volume > 1.0) |
|
o1ka
2016/09/28 10:24:07
Where these volume limits come from? And what if t
Max Morin
2016/09/28 12:47:33
Good question. Needs better comments.
|
| + Close(); |
| + else |
| + controller_->SetVolume(volume); |
| +} |
| + |
| +void AudioOutputImpl::OnCreated() { |
| + task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioOutputImpl::DoCompleteCreation, base::Unretained(this))); |
|
o1ka
2016/09/28 10:24:07
"this" won't survive
|
| +} |
| + |
| +void AudioOutputImpl::OnPlaying() { |
|
o1ka
2016/09/28 10:24:07
Thread check? and what if it is called after Clos
|
| + host_->NotifyStreamStateChanged(id_, true); |
| +} |
| +void AudioOutputImpl::OnPaused() { |
| + host_->NotifyStreamStateChanged(id_, false); |
|
o1ka
2016/09/28 10:24:06
Same as above
|
| +} |
| +void AudioOutputImpl::OnError() { |
| + task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&AudioOutputImpl::Close, base::Unretained(this))); |
|
o1ka
2016/09/28 10:24:06
What is the guarantee that "this" will survive unt
Max Morin
2016/09/28 12:47:33
Ok, so we should maybe have weak pointers to *this
|
| +} |
| + |
| +AudioOutputImpl::AudioOutputImpl(base::SingleThreadTaskRunner* task_runner, |
| + Host* host, |
| + int32_t id, |
| + int render_frame_id, |
| + int render_process_id, |
| + const std::string& output_device_id) |
| + : task_runner_(task_runner), |
| + host_(host), |
| + id_(id), |
| + render_frame_id_(render_frame_id), |
| + render_process_id_(render_process_id), |
| + output_device_id_(output_device_id), |
| + binding_(this) { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
|
o1ka
2016/09/28 10:24:07
Do you really need it to be constructed on this th
|
| +} |
| + |
| +void AudioOutputImpl::DoCompleteCreation() { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + if (is_closing) |
|
o1ka
2016/09/28 10:24:06
is it actually "closing" or "closed"?
Max Morin
2016/09/28 12:47:33
I'll rework this bit.
|
| + return; |
| + DCHECK(reader_); |
| + DCHECK(binding_.is_bound()); |
| + |
| + // Now construction is done and we are ready to send the shared memory and the |
| + // sync socket to the renderer. |
| + base::SharedMemory* shared_memory = reader_->shared_memory(); |
| + base::CancelableSyncSocket* foreign_socket = reader_->foreign_socket(); |
| + DCHECK(shared_memory); |
| + DCHECK(foreign_socket); |
| + |
| + // Wrap the shared memory for sending: |
| + base::SharedMemoryHandle foreign_memory_handle = |
| + base::SharedMemory::DuplicateHandle(shared_memory->handle()); |
| + // TODO: When does this fail? |
| + DCHECK(base::SharedMemory::IsHandleValid(foreign_memory_handle)) |
| + << "Invalid memory handle."; |
| + mojo::ScopedSharedBufferHandle shared_buffer_handle = |
| + mojo::WrapSharedMemoryHandle(foreign_memory_handle, |
| + shared_memory->requested_size(), false); |
| + |
| + // TODO: When does this fail? |
| + DCHECK(shared_buffer_handle.is_valid()); |
| + |
| + // TODO: take ownership of sync socket handle, crbug.com/647659 |
| + start_callback_.Run(std::move(shared_buffer_handle), |
| + mojo::WrapPlatformFile(foreign_socket->handle())); |
| +} |
| + |
| +void AudioOutputImpl::Init(const media::AudioParameters& params) { |
| + DCHECK(!is_closing); |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + reader_ = AudioSyncReader::Create(params); |
| + if (!reader_) |
| + return; |
|
o1ka
2016/09/28 10:24:06
Is it a normal situation? If not - DCHECK? (we can
Max Morin
2016/09/28 12:47:33
Should probably be a DCHECK, it happens if we can'
|
| + controller_ = media::AudioOutputController::Create( |
| + media::AudioManager::Get(), this, params, output_device_id_, |
| + reader_.get()); |
| + DCHECK(controller_); |
| +} |
| + |
| +bool AudioOutputImpl::IsStarted() const { |
| + DCHECK(task_runner_->BelongsToCurrentThread()); |
| + return controller_ != nullptr; |
| +} |
| + |
| +void AudioOutputImpl::Deleter::operator()(AudioOutputImpl* output) { |
| + DCHECK(output->task_runner_->BelongsToCurrentThread()); |
| + DCHECK(output->is_closing); |
| + if (output->controller_) |
| + output->controller_->Close(base::Bind(&AudioOutputImpl::DeleteAndUnregister, |
| + base::Unretained(output))); |
| + else |
| + delete output; |
| +} |
| + |
| +// static |
| +void AudioOutputImpl::DeleteAndUnregister(AudioOutputImpl* output) { |
| + // This must be done after controller finished closing, see |
| + // http://crbug.com/474432. |
| + AudioMirroringManager::GetInstance()->RemoveDiverter( |
| + output->controller_.get()); |
| + output->task_runner_->DeleteSoon(FROM_HERE, output); |
|
o1ka
2016/09/28 10:24:06
This deletion is a lot of fun, but can we probably
|
| +} |
| + |
| +} // namespace content |