// 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_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 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::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, bool initially_muted) { #if defined(OS_WIN) DCHECK(handle.IsValid()); DCHECK(socket_handle); #else DCHECK(base::SharedMemory::IsHandleValid(handle)); DCHECK_NE(-1, socket_handle); #endif DCHECK(handle.GetSize()); 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, handle, 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(handle, handle.GetSize(), socket_handle); } else { // Clean up the handles. base::SyncSocket temp_socket(socket_handle); base::SharedMemory temp_shared_memory(handle, false); } } } 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) {} 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; ipc_ = AudioInputIPCFactory::get()->CreateAudioInputIPC(render_frame_id); params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_MONO, sample_rate, ppapi::kBitsPerAudioInputSample, 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_) return; // We will be notified by OnStreamCreated(). create_stream_sent_ = true; ipc_->CreateStream(this, session_id, params_, false, 1); } void PepperPlatformAudioInput::StartCaptureOnIOThread() { DCHECK(io_task_runner_->BelongsToCurrentThread()); if (ipc_) ipc_->RecordStream(); } void PepperPlatformAudioInput::StopCaptureOnIOThread() { DCHECK(io_task_runner_->BelongsToCurrentThread()); // 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