Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1067)

Unified Diff: content/browser/renderer_host/media/audio_output_impl.cc

Issue 2319493002: Add mojo interface for audio rendering. (Closed)
Patch Set: format Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698