| // 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 |