// 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/logging.h" #include "base/message_loop/message_loop_proxy.h" #include "build/build_config.h" #include "content/child/child_process.h" #include "content/renderer/media/audio_input_message_filter.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_manager_base.h" #include "ppapi/shared_impl/ppb_audio_config_shared.h" #include "url/gurl.h" namespace content { // static PepperPlatformAudioInput* PepperPlatformAudioInput::Create( int render_frame_id, const std::string& device_id, const GURL& document_url, int sample_rate, int frames_per_buffer, PepperAudioInputHost* client) { scoped_refptr audio_input( new PepperPlatformAudioInput()); if (audio_input->Initialize(render_frame_id, device_id, document_url, sample_rate, frames_per_buffer, client)) { // Balanced by Release invoked in // PepperPlatformAudioInput::ShutDownOnIOThread(). audio_input->AddRef(); return audio_input.get(); } return NULL; } void PepperPlatformAudioInput::StartCapture() { DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); io_message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this)); } void PepperPlatformAudioInput::StopCapture() { DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); io_message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread, this)); } void PepperPlatformAudioInput::ShutDown() { DCHECK(main_message_loop_proxy_->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_ = NULL; io_message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread, this)); } void PepperPlatformAudioInput::OnStreamCreated( base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, int length, int total_segments) { #if defined(OS_WIN) DCHECK(handle); DCHECK(socket_handle); #else DCHECK_NE(-1, handle.fd); DCHECK_NE(-1, socket_handle); #endif DCHECK(length); // TODO(yzshen): Make use of circular buffer scheme. crbug.com/181449. DCHECK_EQ(1, total_segments); if (base::MessageLoopProxy::current().get() != main_message_loop_proxy_.get()) { // If shutdown has occurred, |client_| will be NULL and the handles will be // cleaned up on the main thread. main_message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&PepperPlatformAudioInput::OnStreamCreated, this, handle, socket_handle, length, total_segments)); } 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, length, socket_handle); } else { // Clean up the handles. base::SyncSocket temp_socket(socket_handle); base::SharedMemory temp_shared_memory(handle, false); } } } void PepperPlatformAudioInput::OnVolume(double volume) {} void PepperPlatformAudioInput::OnStateChanged( media::AudioInputIPCDelegate::State state) {} 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_(NULL), main_message_loop_proxy_(base::MessageLoopProxy::current()), io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()), 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, const GURL& document_url, int sample_rate, int frames_per_buffer, PepperAudioInputHost* client) { DCHECK(main_message_loop_proxy_->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_ = RenderThreadImpl::current() ->audio_input_message_filter() ->CreateAudioInputIPC(render_frame->render_view()->GetRoutingID()); params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR, media::CHANNEL_LAYOUT_MONO, ppapi::kAudioInputChannels, 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::AudioManagerBase::kDefaultDeviceId : device_id, document_url, base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this)); pending_open_device_ = true; return true; } void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) { DCHECK(io_message_loop_proxy_->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_message_loop_proxy_->BelongsToCurrentThread()); if (ipc_) ipc_->RecordStream(); } void PepperPlatformAudioInput::StopCaptureOnIOThread() { DCHECK(io_message_loop_proxy_->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_message_loop_proxy_->BelongsToCurrentThread()); StopCaptureOnIOThread(); main_message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&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_message_loop_proxy_->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_message_loop_proxy_->PostTask( FROM_HERE, base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread, this, session_id)); } else { // Shutdown has occurred. CloseDevice(); } } else { NotifyStreamCreationFailed(); } } void PepperPlatformAudioInput::CloseDevice() { DCHECK(main_message_loop_proxy_->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_message_loop_proxy_->BelongsToCurrentThread()); if (client_) client_->StreamCreationFailed(); } PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() { DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); RenderFrameImpl* const render_frame = RenderFrameImpl::FromRoutingID(render_frame_id_); return render_frame ? PepperMediaDeviceManager::GetForRenderFrame(render_frame).get() : NULL; } } // namespace content