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

Unified Diff: content/renderer/pepper/pepper_platform_audio_output.cc

Issue 2755613002: Support audio output device enumeration and selection in PPAPI (Closed)
Patch Set: Should not include local change to ppapi/generators/idl_outfile.py Created 3 years, 9 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/renderer/pepper/pepper_platform_audio_output.cc
diff --git a/content/renderer/pepper/pepper_platform_audio_output.cc b/content/renderer/pepper/pepper_platform_audio_output.cc
index e847e72478061d57fdd996277bd766090285941a..c181a9f40ef05b6b97fa7db04c90a7ea3414e09b 100644
--- a/content/renderer/pepper/pepper_platform_audio_output.cc
+++ b/content/renderer/pepper/pepper_platform_audio_output.cc
@@ -9,12 +9,19 @@
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
#include "build/build_config.h"
#include "content/child/child_process.h"
+#include "content/common/content_constants_internal.h"
#include "content/common/media/audio_messages.h"
#include "content/renderer/media/audio_message_filter.h"
#include "content/renderer/pepper/audio_helper.h"
+#include "content/renderer/pepper/pepper_audio_output_host.h"
+#include "content/renderer/pepper/pepper_media_device_manager.h"
+#include "content/renderer/render_frame_impl.h"
#include "content/renderer/render_thread_impl.h"
+#include "media/audio/audio_device_description.h"
#include "ppapi/shared_impl/ppb_audio_config_shared.h"
namespace content {
@@ -39,6 +46,43 @@ PepperPlatformAudioOutput* PepperPlatformAudioOutput::Create(
return NULL;
}
+// static
+PepperPlatformAudioOutput* PepperPlatformAudioOutput::Create(
+ int render_frame_id,
+ const std::string& device_id,
+ const GURL& document_url,
+ int sample_rate,
+ int frames_per_buffer,
+ PepperAudioOutputHost* client) {
+ scoped_refptr<PepperPlatformAudioOutput> audio_output(
+ new PepperPlatformAudioOutput(render_frame_id, device_id, document_url,
+ // Set authorization request timeout at 80% of renderer hung timeout,
+ // but no more than kMaxAuthorizationTimeout.
+ base::TimeDelta::FromMilliseconds(
+ std::min(kHungRendererDelayMs * 8 / 10,
+ kMaxAuthorizationTimeoutMs))));
+
+ if (audio_output->Initialize(sample_rate,
+ frames_per_buffer,
+ client)) {
+ // Balanced by Release invoked in
+ // PepperPlatformAudioOutput::ShutDownOnIOThread().
+ audio_output->AddRef();
+ return audio_output.get();
+ }
+ return NULL;
+}
+
+void PepperPlatformAudioOutput::RequestDeviceAuthorization() {
+ if (ipc_) {
+ io_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &PepperPlatformAudioOutput::RequestDeviceAuthorizationOnIOThread,
+ this));
+ }
+}
+
bool PepperPlatformAudioOutput::StartPlayback() {
if (ipc_) {
io_task_runner_->PostTask(
@@ -74,18 +118,85 @@ void PepperPlatformAudioOutput::ShutDown() {
// Called on the main thread to stop all audio callbacks. We must only change
// the client on the main thread, and the delegates from the I/O thread.
client_ = NULL;
+ host_client_ = NULL;
io_task_runner_->PostTask(
FROM_HERE,
base::Bind(&PepperPlatformAudioOutput::ShutDownOnIOThread, this));
}
-void PepperPlatformAudioOutput::OnError() {}
+void PepperPlatformAudioOutput::OnError() {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+ // Do nothing if the stream has been closed.
+ if (state_ < CREATING_STREAM)
+ return;
+
+ DLOG(WARNING) << "PepperPlatformAudioOutput::OnError()";
+
+ main_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&PepperPlatformAudioOutput::NotifyStreamCreationFailed, this));
bbudge 2017/03/23 18:05:44 This looks like a change to the behavior of PPB_Au
Xing 2017/03/29 21:14:32 New implementation has been moved to pepper_platfo
+}
void PepperPlatformAudioOutput::OnDeviceAuthorized(
media::OutputDeviceStatus device_status,
const media::AudioParameters& output_params,
const std::string& matched_device_id) {
- NOTREACHED();
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+ auth_timeout_action_.reset();
+
+ // Do nothing if late authorization is received after timeout.
+ if (state_ == IPC_CLOSED)
+ return;
+
+ LOG_IF(WARNING, device_status == media::OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT)
+ << "Output device authorization timed out";
+
+ DCHECK_EQ(state_, AUTHORIZING);
+
+ // It may happen that a second authorization is received as a result to a
+ // call to StartPlayback() after Shutdown(). If the status for the second
+ // authorization differs from the first, it will not be reflected in
+ // |device_status_| to avoid a race.
+ // This scenario is unlikely. If it occurs, the new value will be
+ // different from OUTPUT_DEVICE_STATUS_OK, so the PepperPlatformAudioOutput
+ // will enter the IPC_CLOSED state anyway, which is the safe thing to do.
+ // This is preferable to holding a lock.
+ if (!did_receive_auth_.IsSignaled())
+ device_status_ = device_status;
+
+ if (device_status == media::OUTPUT_DEVICE_STATUS_OK) {
+ state_ = AUTHORIZED;
+ if (!did_receive_auth_.IsSignaled()) {
+ output_params_ = output_params;
+
+ // It's possible to not have a matched device obtained via session id. It
+ // means matching output device through |session_id_| failed and the
+ // default device is used.
+ DCHECK(media::AudioDeviceDescription::UseSessionIdToSelectDevice(
+ session_id_,
+ device_id_) ||
+ matched_device_id_.empty());
+ matched_device_id_ = matched_device_id;
+
+ DVLOG(1) << "PepperPlatformAudioOutput authorized, session_id: "
+ << session_id_ << ", device_id: " << device_id_
+ << ", matched_device_id: " << matched_device_id_;
+
+ did_receive_auth_.Signal();
+ }
+ if (start_on_authorized_)
+ CreateStreamOnIOThread(params_);
+ }
+ else {
+ // Closing IPC forces a Signal(), so no clients are locked waiting
+ // indefinitely after this method returns.
+ ipc_->CloseStream();
+ OnIPCClosed();
+ main_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&PepperPlatformAudioOutput::NotifyStreamCreationFailed,
+ this));
+ }
}
void PepperPlatformAudioOutput::OnStreamCreated(
@@ -106,26 +217,74 @@ void PepperPlatformAudioOutput::OnStreamCreated(
// occurred while the request was in-flight, so we need to NULL check.
if (client_)
client_->StreamCreated(handle, length, socket_handle);
+ else if (host_client_)
+ host_client_->StreamCreated(handle, length, socket_handle);
} else {
- main_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PepperPlatformAudioOutput::OnStreamCreated, this,
- handle, socket_handle, length));
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ if (state_ != CREATING_STREAM)
+ return;
+
+ state_ = PAUSED;
+ if (play_on_start_)
+ StartPlaybackOnIOThread();
+
+ main_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&PepperPlatformAudioOutput::OnStreamCreated, this,
+ handle, socket_handle, length));
}
}
-void PepperPlatformAudioOutput::OnIPCClosed() { ipc_.reset(); }
+void PepperPlatformAudioOutput::OnIPCClosed() {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ state_ = IPC_CLOSED;
+ ipc_.reset();
+
+ // Signal to unblock any blocked threads waiting for parameters
+ did_receive_auth_.Signal();
+}
PepperPlatformAudioOutput::~PepperPlatformAudioOutput() {
// Make sure we have been shut down. Warning: this will usually happen on
// the I/O thread!
DCHECK(!ipc_);
DCHECK(!client_);
+ DCHECK(!host_client_);
}
PepperPlatformAudioOutput::PepperPlatformAudioOutput()
: client_(NULL),
+ host_client_(NULL),
+ main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ io_task_runner_(ChildProcess::current()->io_task_runner()),
+ render_frame_id_(MSG_ROUTING_NONE),
+ state_(IDLE),
+ start_on_authorized_(true),
+ play_on_start_(false),
+ session_id_(0),
+ did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED),
+ device_status_(media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL) {
+}
+
+PepperPlatformAudioOutput::PepperPlatformAudioOutput(int render_frame_id,
+ const std::string& device_id,
+ const GURL& document_url,
+ base::TimeDelta authorization_timeout)
+ : client_(NULL),
+ host_client_(NULL),
main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
- io_task_runner_(ChildProcess::current()->io_task_runner()) {
+ io_task_runner_(ChildProcess::current()->io_task_runner()),
+ render_frame_id_(render_frame_id),
+ state_(IDLE),
+ start_on_authorized_(true),
+ play_on_start_(false),
+ session_id_(0),
+ device_id_(device_id),
+ security_origin_(document_url),
+ did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED),
+ device_status_(media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL),
+ auth_timeout_(authorization_timeout) {
}
bool PepperPlatformAudioOutput::Initialize(int sample_rate,
@@ -140,41 +299,162 @@ bool PepperPlatformAudioOutput::Initialize(int sample_rate,
source_render_frame_id);
CHECK(ipc_);
- media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
- media::CHANNEL_LAYOUT_STEREO,
- sample_rate,
- ppapi::kBitsPerAudioOutputSample,
- frames_per_buffer);
+ params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_STEREO,
+ sample_rate,
+ ppapi::kBitsPerAudioOutputSample,
+ frames_per_buffer);
io_task_runner_->PostTask(
FROM_HERE, base::Bind(&PepperPlatformAudioOutput::InitializeOnIOThread,
- this, params));
+ this, params_));
+
return true;
}
+bool PepperPlatformAudioOutput::Initialize(int sample_rate,
+ int frames_per_buffer,
+ PepperAudioOutputHost* client) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ RenderFrameImpl* const render_frame =
+ RenderFrameImpl::FromRoutingID(render_frame_id_);
+ if (!render_frame || !client)
+ return false;
+
+ host_client_ = client;
+
+ RenderThreadImpl* const render_thread = RenderThreadImpl::current();
+ ipc_ = render_thread->audio_message_filter()->CreateAudioOutputIPC(
+ render_frame_id_);
+ CHECK(ipc_);
+
+ params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ media::CHANNEL_LAYOUT_STEREO,
+ sample_rate,
+ ppapi::kBitsPerAudioOutputSample,
+ frames_per_buffer);
+
+ io_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PepperPlatformAudioOutput::CreateStreamOnIOThread,
+ this, params_));
+
+ return true;
+}
+
+void PepperPlatformAudioOutput::RequestDeviceAuthorizationOnIOThread() {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(state_, IDLE);
+
+ if (!ipc_)
+ return;
+
+ state_ = AUTHORIZING;
+ ipc_->RequestDeviceAuthorization(
+ this, session_id_, device_id_, security_origin_);
+
+ if (auth_timeout_ > base::TimeDelta()) {
+ // Create the timer on the thread it's used on. It's guaranteed to be
+ // deleted on the same thread since users must call ShutDown() before
+ // deleting PepperPlatformAudioOutput; see ShutDownOnIOThread().
+ auth_timeout_action_.reset(new base::OneShotTimer());
+ auth_timeout_action_->Start(
+ FROM_HERE, auth_timeout_,
+ base::Bind(&PepperPlatformAudioOutput::OnDeviceAuthorized, this,
+ media::OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT,
+ media::AudioParameters(), std::string()));
+ }
+}
+
+void PepperPlatformAudioOutput::CreateStreamOnIOThread(
+ const media::AudioParameters& params) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ switch (state_) {
+ case IPC_CLOSED:
+ main_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&PepperPlatformAudioOutput::NotifyStreamCreationFailed,
+ this));
+ break;
+
+ case IDLE:
+ if (did_receive_auth_.IsSignaled() && device_id_.empty() &&
+ security_origin_.unique()) {
+ state_ = CREATING_STREAM;
+ ipc_->CreateStream(this, params);
+ }
+ else {
+ RequestDeviceAuthorizationOnIOThread();
+ start_on_authorized_ = true;
+ }
+ break;
+
+ case AUTHORIZING:
+ start_on_authorized_ = true;
+ break;
+
+ case AUTHORIZED:
+ state_ = CREATING_STREAM;
+ ipc_->CreateStream(this, params);
+ start_on_authorized_ = false;
+ break;
+
+ case CREATING_STREAM:
+ case PAUSED:
+ case PLAYING:
+ NOTREACHED();
+ break;
+ }
+}
+
void PepperPlatformAudioOutput::InitializeOnIOThread(
const media::AudioParameters& params) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- if (ipc_)
+ if (ipc_) {
+ // To maintain the compatibility of the old Pepper audio interface,
+ // We bypass the device autherization process, and try to create audio
+ // stream directly.
+ state_ = CREATING_STREAM;
ipc_->CreateStream(this, params);
+ }
}
void PepperPlatformAudioOutput::StartPlaybackOnIOThread() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- if (ipc_)
+ if (!ipc_)
+ return;
+
+ if (state_ == PAUSED) {
ipc_->PlayStream();
-}
+ state_ = PLAYING;
+ play_on_start_ = false;
+ }
+ else {
+ if (state_ < CREATING_STREAM)
+ CreateStreamOnIOThread(params_);
-void PepperPlatformAudioOutput::SetVolumeOnIOThread(double volume) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
- if (ipc_)
- ipc_->SetVolume(volume);
+ play_on_start_ = true;
+ }
}
void PepperPlatformAudioOutput::StopPlaybackOnIOThread() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- if (ipc_)
+ if (!ipc_)
+ return;
+
+ if (state_ == PLAYING) {
ipc_->PauseStream();
+ state_ = PAUSED;
+ }
+ play_on_start_ = false;
+}
+
+void PepperPlatformAudioOutput::SetVolumeOnIOThread(double volume) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+ if (!ipc_)
+ return;
+
+ if (state_ >= CREATING_STREAM)
+ ipc_->SetVolume(volume);
}
void PepperPlatformAudioOutput::ShutDownOnIOThread() {
@@ -184,11 +464,27 @@ void PepperPlatformAudioOutput::ShutDownOnIOThread() {
if (!ipc_)
return;
- ipc_->CloseStream();
- ipc_.reset();
+ // Close the stream, if we haven't already.
+ if (state_ >= AUTHORIZING) {
+ ipc_->CloseStream();
+ ipc_.reset();
+ state_ = IDLE;
+ }
+ start_on_authorized_ = false;
+
+ // Destoy the timer on the thread it's used on.
+ auth_timeout_action_.reset();
+
+ // Release for the delegate, balances out the reference taken in
+ // PepperPlatformAudioOutput::Create.
+ Release();
+}
+
+void PepperPlatformAudioOutput::NotifyStreamCreationFailed() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
- Release(); // Release for the delegate, balances out the reference taken in
- // PepperPlatformAudioOutput::Create.
+ if (host_client_)
+ host_client_->StreamCreationFailed();
}
} // namespace content

Powered by Google App Engine
This is Rietveld 408576698