1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/renderer/pepper/pepper_platform_audio_input.h" 6 7#include "base/bind.h" 8#include "base/logging.h" 9#include "base/message_loop/message_loop_proxy.h" 10#include "build/build_config.h" 11#include "content/child/child_process.h" 12#include "content/renderer/media/audio_input_message_filter.h" 13#include "content/renderer/pepper/pepper_audio_input_host.h" 14#include "content/renderer/pepper/pepper_media_device_manager.h" 15#include "content/renderer/render_frame_impl.h" 16#include "content/renderer/render_thread_impl.h" 17#include "content/renderer/render_view_impl.h" 18#include "media/audio/audio_manager_base.h" 19#include "ppapi/shared_impl/ppb_audio_config_shared.h" 20#include "url/gurl.h" 21 22namespace content { 23 24// static 25PepperPlatformAudioInput* PepperPlatformAudioInput::Create( 26 int render_frame_id, 27 const std::string& device_id, 28 const GURL& document_url, 29 int sample_rate, 30 int frames_per_buffer, 31 PepperAudioInputHost* client) { 32 scoped_refptr<PepperPlatformAudioInput> audio_input( 33 new PepperPlatformAudioInput()); 34 if (audio_input->Initialize(render_frame_id, 35 device_id, 36 document_url, 37 sample_rate, 38 frames_per_buffer, 39 client)) { 40 // Balanced by Release invoked in 41 // PepperPlatformAudioInput::ShutDownOnIOThread(). 42 audio_input->AddRef(); 43 return audio_input.get(); 44 } 45 return NULL; 46} 47 48void PepperPlatformAudioInput::StartCapture() { 49 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 50 51 io_message_loop_proxy_->PostTask( 52 FROM_HERE, 53 base::Bind(&PepperPlatformAudioInput::StartCaptureOnIOThread, this)); 54} 55 56void PepperPlatformAudioInput::StopCapture() { 57 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 58 59 io_message_loop_proxy_->PostTask( 60 FROM_HERE, 61 base::Bind(&PepperPlatformAudioInput::StopCaptureOnIOThread, this)); 62} 63 64void PepperPlatformAudioInput::ShutDown() { 65 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 66 67 // Make sure we don't call shutdown more than once. 68 if (!client_) 69 return; 70 71 // Called on the main thread to stop all audio callbacks. We must only change 72 // the client on the main thread, and the delegates from the I/O thread. 73 client_ = NULL; 74 io_message_loop_proxy_->PostTask( 75 FROM_HERE, 76 base::Bind(&PepperPlatformAudioInput::ShutDownOnIOThread, this)); 77} 78 79void PepperPlatformAudioInput::OnStreamCreated( 80 base::SharedMemoryHandle handle, 81 base::SyncSocket::Handle socket_handle, 82 int length, 83 int total_segments) { 84#if defined(OS_WIN) 85 DCHECK(handle); 86 DCHECK(socket_handle); 87#else 88 DCHECK_NE(-1, handle.fd); 89 DCHECK_NE(-1, socket_handle); 90#endif 91 DCHECK(length); 92 // TODO(yzshen): Make use of circular buffer scheme. crbug.com/181449. 93 DCHECK_EQ(1, total_segments); 94 95 if (base::MessageLoopProxy::current().get() != 96 main_message_loop_proxy_.get()) { 97 // If shutdown has occurred, |client_| will be NULL and the handles will be 98 // cleaned up on the main thread. 99 main_message_loop_proxy_->PostTask( 100 FROM_HERE, 101 base::Bind(&PepperPlatformAudioInput::OnStreamCreated, 102 this, 103 handle, 104 socket_handle, 105 length, 106 total_segments)); 107 } else { 108 // Must dereference the client only on the main thread. Shutdown may have 109 // occurred while the request was in-flight, so we need to NULL check. 110 if (client_) { 111 client_->StreamCreated(handle, length, socket_handle); 112 } else { 113 // Clean up the handles. 114 base::SyncSocket temp_socket(socket_handle); 115 base::SharedMemory temp_shared_memory(handle, false); 116 } 117 } 118} 119 120void PepperPlatformAudioInput::OnVolume(double volume) {} 121 122void PepperPlatformAudioInput::OnStateChanged( 123 media::AudioInputIPCDelegate::State state) {} 124 125void PepperPlatformAudioInput::OnIPCClosed() { ipc_.reset(); } 126 127PepperPlatformAudioInput::~PepperPlatformAudioInput() { 128 // Make sure we have been shut down. Warning: this may happen on the I/O 129 // thread! 130 // Although these members should be accessed on a specific thread (either the 131 // main thread or the I/O thread), it should be fine to examine their value 132 // here. 133 DCHECK(!ipc_); 134 DCHECK(!client_); 135 DCHECK(label_.empty()); 136 DCHECK(!pending_open_device_); 137} 138 139PepperPlatformAudioInput::PepperPlatformAudioInput() 140 : client_(NULL), 141 main_message_loop_proxy_(base::MessageLoopProxy::current()), 142 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()), 143 render_frame_id_(MSG_ROUTING_NONE), 144 create_stream_sent_(false), 145 pending_open_device_(false), 146 pending_open_device_id_(-1) {} 147 148bool PepperPlatformAudioInput::Initialize( 149 int render_frame_id, 150 const std::string& device_id, 151 const GURL& document_url, 152 int sample_rate, 153 int frames_per_buffer, 154 PepperAudioInputHost* client) { 155 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 156 157 RenderFrameImpl* const render_frame = 158 RenderFrameImpl::FromRoutingID(render_frame_id); 159 if (!render_frame || !client) 160 return false; 161 162 render_frame_id_ = render_frame_id; 163 client_ = client; 164 165 if (!GetMediaDeviceManager()) 166 return false; 167 168 ipc_ = RenderThreadImpl::current() 169 ->audio_input_message_filter() 170 ->CreateAudioInputIPC(render_frame->render_view()->GetRoutingID()); 171 172 params_.Reset(media::AudioParameters::AUDIO_PCM_LINEAR, 173 media::CHANNEL_LAYOUT_MONO, 174 ppapi::kAudioInputChannels, 175 sample_rate, 176 ppapi::kBitsPerAudioInputSample, 177 frames_per_buffer); 178 179 // We need to open the device and obtain the label and session ID before 180 // initializing. 181 pending_open_device_id_ = GetMediaDeviceManager()->OpenDevice( 182 PP_DEVICETYPE_DEV_AUDIOCAPTURE, 183 device_id.empty() ? media::AudioManagerBase::kDefaultDeviceId : device_id, 184 document_url, 185 base::Bind(&PepperPlatformAudioInput::OnDeviceOpened, this)); 186 pending_open_device_ = true; 187 188 return true; 189} 190 191void PepperPlatformAudioInput::InitializeOnIOThread(int session_id) { 192 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); 193 194 if (!ipc_) 195 return; 196 197 // We will be notified by OnStreamCreated(). 198 create_stream_sent_ = true; 199 ipc_->CreateStream(this, session_id, params_, false, 1); 200} 201 202void PepperPlatformAudioInput::StartCaptureOnIOThread() { 203 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); 204 205 if (ipc_) 206 ipc_->RecordStream(); 207} 208 209void PepperPlatformAudioInput::StopCaptureOnIOThread() { 210 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); 211 212 // TODO(yzshen): We cannot re-start capturing if the stream is closed. 213 if (ipc_ && create_stream_sent_) { 214 ipc_->CloseStream(); 215 } 216 ipc_.reset(); 217} 218 219void PepperPlatformAudioInput::ShutDownOnIOThread() { 220 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); 221 222 StopCaptureOnIOThread(); 223 224 main_message_loop_proxy_->PostTask( 225 FROM_HERE, base::Bind(&PepperPlatformAudioInput::CloseDevice, this)); 226 227 Release(); // Release for the delegate, balances out the reference taken in 228 // PepperPlatformAudioInput::Create. 229} 230 231void PepperPlatformAudioInput::OnDeviceOpened(int request_id, 232 bool succeeded, 233 const std::string& label) { 234 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 235 236 pending_open_device_ = false; 237 pending_open_device_id_ = -1; 238 239 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager(); 240 if (succeeded && device_manager) { 241 DCHECK(!label.empty()); 242 label_ = label; 243 244 if (client_) { 245 int session_id = device_manager->GetSessionID( 246 PP_DEVICETYPE_DEV_AUDIOCAPTURE, label); 247 io_message_loop_proxy_->PostTask( 248 FROM_HERE, 249 base::Bind(&PepperPlatformAudioInput::InitializeOnIOThread, 250 this, 251 session_id)); 252 } else { 253 // Shutdown has occurred. 254 CloseDevice(); 255 } 256 } else { 257 NotifyStreamCreationFailed(); 258 } 259} 260 261void PepperPlatformAudioInput::CloseDevice() { 262 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 263 264 if (!label_.empty()) { 265 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager(); 266 if (device_manager) 267 device_manager->CloseDevice(label_); 268 label_.clear(); 269 } 270 if (pending_open_device_) { 271 PepperMediaDeviceManager* const device_manager = GetMediaDeviceManager(); 272 if (device_manager) 273 device_manager->CancelOpenDevice(pending_open_device_id_); 274 pending_open_device_ = false; 275 pending_open_device_id_ = -1; 276 } 277} 278 279void PepperPlatformAudioInput::NotifyStreamCreationFailed() { 280 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 281 282 if (client_) 283 client_->StreamCreationFailed(); 284} 285 286PepperMediaDeviceManager* PepperPlatformAudioInput::GetMediaDeviceManager() { 287 DCHECK(main_message_loop_proxy_->BelongsToCurrentThread()); 288 289 RenderFrameImpl* const render_frame = 290 RenderFrameImpl::FromRoutingID(render_frame_id_); 291 return render_frame ? 292 PepperMediaDeviceManager::GetForRenderFrame(render_frame).get() : NULL; 293} 294 295} // namespace content 296