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