blob: fe4a97789013bc2d08212b323c1c28f7de9a0c51 [file] [log] [blame]
// Copyright (c) 2012 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/pepper/pepper_platform_audio_input.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "content/child/child_process.h"
#include "content/renderer/media/audio/audio_input_ipc_factory.h"
#include "content/renderer/pepper/pepper_audio_input_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 "content/renderer/render_view_impl.h"
#include "media/audio/audio_device_description.h"
#include "ppapi/shared_impl/ppb_audio_config_shared.h"
namespace content {
// static
PepperPlatformAudioInput* PepperPlatformAudioInput::Create(
int render_frame_id,
const std::string& device_id,
int sample_rate,
int frames_per_buffer,
PepperAudioInputHost* client) {
scoped_refptr<PepperPlatformAudioInput> audio_input(
new PepperPlatformAudioInput());
if (audio_input->Initialize(render_frame_id,
device_id,
sample_rate,
frames_per_buffer,
client)) {
// Balanced by Release invoked in
// PepperPlatformAudioInput::ShutDownOnIOThread().
audio_input->AddRef();
return audio_input.get();
}
return nullptr;
}
void PepperPlatformAudioInput::StartCapture() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PepperPlatformAudioInput::StartCaptureOnIOThread, this));
}
void PepperPlatformAudioInput::StopCapture() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PepperPlatformAudioInput::StopCaptureOnIOThread, this));
}
void PepperPlatformAudioInput::ShutDown() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
// Make sure we don't call shutdown more than once.
if (!client_)
return;
// 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_ = nullptr;
io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PepperPlatformAudioInput::ShutDownOnIOThread, this));
}
void PepperPlatformAudioInput::OnStreamCreated(
base::ReadOnlySharedMemoryRegion shared_memory_region,
base::SyncSocket::Handle socket_handle,
bool initially_muted) {
DCHECK(shared_memory_region.IsValid());
#if defined(OS_WIN)
DCHECK(socket_handle);
#else
DCHECK_NE(-1, socket_handle);
#endif
DCHECK_GT(shared_memory_region.GetSize(), 0u);
if (base::ThreadTaskRunnerHandle::Get().get() != main_task_runner_.get()) {
// If shutdown has occurred, |client_| will be NULL and the handles will be
// cleaned up on the main thread.
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PepperPlatformAudioInput::OnStreamCreated,
this, std::move(shared_memory_region),
socket_handle, initially_muted));
} else {
// Must dereference the client only on the main thread. Shutdown may have
// occurred while the request was in-flight, so we need to NULL check.
if (client_) {
client_->StreamCreated(std::move(shared_memory_region), socket_handle);
} else {
// Clean up the handle.
base::SyncSocket temp_socket(socket_handle);
}
}
}
void PepperPlatformAudioInput::OnError() {}
void PepperPlatformAudioInput::OnMuted(bool is_muted) {}
void PepperPlatformAudioInput::OnIPCClosed() { ipc_.reset(); }
PepperPlatformAudioInput::~PepperPlatformAudioInput() {
// Make sure we have been shut down. Warning: this may happen on the I/O
// thread!
// Although these members should be accessed on a specific thread (either the
// main thread or the I/O thread), it should be fine to examine their value
// here.
DCHECK(!ipc_);
DCHECK(!client_);
DCHECK(label_.empty());
DCHECK(!pending_open_device_);
}
PepperPlatformAudioInput::PepperPlatformAudioInput()
: client_(nullptr),
main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
io_task_runner_(ChildProcess::current()->io_task_runner()),
render_frame_id_(MSG_ROUTING_NONE),
create_stream_sent_(false),
pending_open_device_(false),
pending_open_device_id_(-1),
ipc_startup_state_(kIdle) {}
bool PepperPlatformAudioInput::Initialize(
int render_frame_id,
const std::string& device_id,
int sample_rate,
int frames_per_buffer,
PepperAudioInputHost* client) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
RenderFrameImpl* const render_frame =
RenderFrameImpl::FromRoutingID(render_frame_id);
if (!render_frame || !client)
return false;
render_frame_id_ = render_frame_id;
client_ = client;
if (!GetMediaDeviceManager())
return false;
params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR,
media::CHANNEL_LAYOUT_MONO,
sample_rate,
frames_per_buffer);
// We need to open the device and obtain the label and session ID before
// initializing.
pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice(
PP_DEVICETYPE_DEV_AUDIOCAPTURE,
device_id.empty() ? media::AudioDeviceDescription::kDefaultDeviceId
: device_id,
client->pp_instance(),
base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this));
pending_open_device_ = true;
return true;
}
void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (ipc_startup_state_ != kStopped)
ipc_ = AudioInputIPCFactory::get()->CreateAudioInputIPC(
render_frame_id_, media::AudioSourceParameters(session_id));
if (!ipc_)
return;
// We will be notified by OnStreamCreated().
create_stream_sent_ = true;
ipc_->CreateStream(this, params_, false, 1);
if (ipc_startup_state_ == kStarted)
ipc_->RecordStream();
}
void PepperPlatformAudioInput::StartCaptureOnIOThread() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (!ipc_) {
ipc_startup_state_ = kStarted;
return;
}
ipc_->RecordStream();
}
void PepperPlatformAudioInput::StopCaptureOnIOThread() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
if (!ipc_) {
ipc_startup_state_ = kStopped;
return;
}
// TODO(yzshen): We cannot re-start capturing if the stream is closed.
if (ipc_ && create_stream_sent_) {
ipc_->CloseStream();
}
ipc_.reset();
}
void PepperPlatformAudioInput::ShutDownOnIOThread() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
StopCaptureOnIOThread();
main_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PepperPlatformAudioInput::CloseDevice, this));
Release(); // Release for the delegate, balances out the reference taken in
// PepperPlatformAudioInput::Create.
}
void PepperPlatformAudioInput::OnDeviceOpened(int request_id,
bool succeeded,
const std::string& label) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
pending_open_device_ = false;
pending_open_device_id_ = -1;
PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
if (succeeded && device_manager) {
DCHECK(!label.empty());
label_ = label;
if (client_) {
int session_id = device_manager->GetSessionID(
PP_DEVICETYPE_DEV_AUDIOCAPTURE, label);
io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PepperPlatformAudioInput::InitializeOnIOThread, this,
session_id));
} else {
// Shutdown has occurred.
CloseDevice();
}
} else {
NotifyStreamCreationFailed();
}
}
void PepperPlatformAudioInput::CloseDevice() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
if (!label_.empty()) {
PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
if (device_manager)
device_manager->CloseDevice(label_);
label_.clear();
}
if (pending_open_device_) {
PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager();
if (device_manager)
device_manager->CancelOpenDevice(pending_open_device_id_);
pending_open_device_ = false;
pending_open_device_id_ = -1;
}
}
void PepperPlatformAudioInput::NotifyStreamCreationFailed() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
if (client_)
client_->StreamCreationFailed();
}
PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
RenderFrameImpl* const render_frame =
RenderFrameImpl::FromRoutingID(render_frame_id_);
return render_frame
? PepperMediaDeviceManager::GetForRenderFrame(render_frame).get()
: nullptr;
}
} // namespace content