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 |