audio_input_renderer_host.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/browser/renderer_host/media/audio_input_renderer_host.h" 6 7#include "base/bind.h" 8#include "base/metrics/histogram.h" 9#include "base/process.h" 10#include "base/shared_memory.h" 11#include "content/browser/renderer_host/media/audio_input_device_manager.h" 12#include "content/browser/renderer_host/media/audio_input_sync_writer.h" 13#include "content/browser/renderer_host/media/media_stream_manager.h" 14#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h" 15#include "content/browser/renderer_host/media/web_contents_capture_util.h" 16#include "media/audio/audio_manager_base.h" 17 18namespace content { 19 20struct AudioInputRendererHost::AudioEntry { 21 AudioEntry(); 22 ~AudioEntry(); 23 24 // The AudioInputController that manages the audio input stream. 25 scoped_refptr<media::AudioInputController> controller; 26 27 // The audio input stream ID in the render view. 28 int stream_id; 29 30 // Shared memory for transmission of the audio data. It has 31 // |shared_memory_segment_count| equal lengthed segments. 32 base::SharedMemory shared_memory; 33 int shared_memory_segment_count; 34 35 // The synchronous writer to be used by the controller. We have the 36 // ownership of the writer. 37 scoped_ptr<media::AudioInputController::SyncWriter> writer; 38 39 // Set to true after we called Close() for the controller. 40 bool pending_close; 41}; 42 43AudioInputRendererHost::AudioEntry::AudioEntry() 44 : stream_id(0), 45 shared_memory_segment_count(0), 46 pending_close(false) { 47} 48 49AudioInputRendererHost::AudioEntry::~AudioEntry() {} 50 51AudioInputRendererHost::AudioInputRendererHost( 52 media::AudioManager* audio_manager, 53 MediaStreamManager* media_stream_manager) 54 : audio_manager_(audio_manager), 55 media_stream_manager_(media_stream_manager) { 56} 57 58AudioInputRendererHost::~AudioInputRendererHost() { 59 DCHECK(audio_entries_.empty()); 60} 61 62void AudioInputRendererHost::OnChannelClosing() { 63 BrowserMessageFilter::OnChannelClosing(); 64 65 // Since the IPC channel is gone, close all requested audio streams. 66 DeleteEntries(); 67} 68 69void AudioInputRendererHost::OnDestruct() const { 70 BrowserThread::DeleteOnIOThread::Destruct(this); 71} 72 73void AudioInputRendererHost::OnCreated( 74 media::AudioInputController* controller) { 75 BrowserThread::PostTask( 76 BrowserThread::IO, 77 FROM_HERE, 78 base::Bind( 79 &AudioInputRendererHost::DoCompleteCreation, 80 this, 81 make_scoped_refptr(controller))); 82} 83 84void AudioInputRendererHost::OnRecording( 85 media::AudioInputController* controller) { 86 BrowserThread::PostTask( 87 BrowserThread::IO, 88 FROM_HERE, 89 base::Bind( 90 &AudioInputRendererHost::DoSendRecordingMessage, 91 this, 92 make_scoped_refptr(controller))); 93} 94 95void AudioInputRendererHost::OnError(media::AudioInputController* controller) { 96 BrowserThread::PostTask( 97 BrowserThread::IO, 98 FROM_HERE, 99 base::Bind( 100 &AudioInputRendererHost::DoHandleError, 101 this, 102 make_scoped_refptr(controller))); 103} 104 105void AudioInputRendererHost::OnData(media::AudioInputController* controller, 106 const uint8* data, 107 uint32 size) { 108 NOTREACHED() << "Only low-latency mode is supported."; 109} 110 111void AudioInputRendererHost::DoCompleteCreation( 112 media::AudioInputController* controller) { 113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 114 115 AudioEntry* entry = LookupByController(controller); 116 if (!entry) 117 return; 118 119 if (!peer_handle()) { 120 NOTREACHED() << "Renderer process handle is invalid."; 121 DeleteEntryOnError(entry); 122 return; 123 } 124 125 if (!entry->controller->LowLatencyMode()) { 126 NOTREACHED() << "Only low-latency mode is supported."; 127 DeleteEntryOnError(entry); 128 return; 129 } 130 131 // Once the audio stream is created then complete the creation process by 132 // mapping shared memory and sharing with the renderer process. 133 base::SharedMemoryHandle foreign_memory_handle; 134 if (!entry->shared_memory.ShareToProcess(peer_handle(), 135 &foreign_memory_handle)) { 136 // If we failed to map and share the shared memory then close the audio 137 // stream and send an error message. 138 DeleteEntryOnError(entry); 139 return; 140 } 141 142 AudioInputSyncWriter* writer = 143 static_cast<AudioInputSyncWriter*>(entry->writer.get()); 144 145#if defined(OS_WIN) 146 base::SyncSocket::Handle foreign_socket_handle; 147#else 148 base::FileDescriptor foreign_socket_handle; 149#endif 150 151 // If we failed to prepare the sync socket for the renderer then we fail 152 // the construction of audio input stream. 153 if (!writer->PrepareForeignSocketHandle(peer_handle(), 154 &foreign_socket_handle)) { 155 DeleteEntryOnError(entry); 156 return; 157 } 158 159 Send(new AudioInputMsg_NotifyStreamCreated(entry->stream_id, 160 foreign_memory_handle, foreign_socket_handle, 161 entry->shared_memory.requested_size(), 162 entry->shared_memory_segment_count)); 163} 164 165void AudioInputRendererHost::DoSendRecordingMessage( 166 media::AudioInputController* controller) { 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 168 // TODO(henrika): See crbug.com/115262 for details on why this method 169 // should be implemented. 170} 171 172void AudioInputRendererHost::DoHandleError( 173 media::AudioInputController* controller) { 174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 175 176 AudioEntry* entry = LookupByController(controller); 177 if (!entry) 178 return; 179 180 DeleteEntryOnError(entry); 181} 182 183bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message, 184 bool* message_was_ok) { 185 bool handled = true; 186 IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost, message, *message_was_ok) 187 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream) 188 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream) 189 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream) 190 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume) 191 IPC_MESSAGE_UNHANDLED(handled = false) 192 IPC_END_MESSAGE_MAP_EX() 193 194 return handled; 195} 196 197void AudioInputRendererHost::OnCreateStream( 198 int stream_id, 199 int render_view_id, 200 int session_id, 201 const AudioInputHostMsg_CreateStream_Config& config) { 202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 203 204 DVLOG(1) << "AudioInputRendererHost@" << this 205 << "::OnCreateStream(stream_id=" << stream_id 206 << ", render_view_id=" << render_view_id 207 << ", session_id=" << session_id << ")"; 208 DCHECK_GT(render_view_id, 0); 209 210 // media::AudioParameters is validated in the deserializer. 211 if (LookupById(stream_id) != NULL) { 212 SendErrorMessage(stream_id); 213 return; 214 } 215 216 media::AudioParameters audio_params(config.params); 217 if (media_stream_manager_->audio_input_device_manager()-> 218 ShouldUseFakeDevice()) { 219 audio_params.Reset( 220 media::AudioParameters::AUDIO_FAKE, 221 config.params.channel_layout(), config.params.channels(), 0, 222 config.params.sample_rate(), config.params.bits_per_sample(), 223 config.params.frames_per_buffer()); 224 } 225 226 // Check if we have the permission to open the device and which device to use. 227 std::string device_id = media::AudioManagerBase::kDefaultDeviceId; 228 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) { 229 const StreamDeviceInfo* info = media_stream_manager_-> 230 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); 231 if (!info) { 232 SendErrorMessage(stream_id); 233 DLOG(WARNING) << "No permission has been granted to input stream with " 234 << "session_id=" << session_id; 235 return; 236 } 237 238 device_id = info->device.id; 239 } 240 241 // Create a new AudioEntry structure. 242 scoped_ptr<AudioEntry> entry(new AudioEntry()); 243 244 const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) + 245 audio_params.GetBytesPerBuffer()); 246 entry->shared_memory_segment_count = config.shared_memory_count; 247 248 // Create the shared memory and share it with the renderer process 249 // using a new SyncWriter object. 250 if (!entry->shared_memory.CreateAndMapAnonymous( 251 segment_size * entry->shared_memory_segment_count)) { 252 // If creation of shared memory failed then send an error message. 253 SendErrorMessage(stream_id); 254 return; 255 } 256 257 scoped_ptr<AudioInputSyncWriter> writer( 258 new AudioInputSyncWriter(&entry->shared_memory, 259 entry->shared_memory_segment_count)); 260 261 if (!writer->Init()) { 262 SendErrorMessage(stream_id); 263 return; 264 } 265 266 // If we have successfully created the SyncWriter then assign it to the 267 // entry and construct an AudioInputController. 268 entry->writer.reset(writer.release()); 269 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) { 270 entry->controller = media::AudioInputController::CreateForStream( 271 audio_manager_->GetWorkerLoop(), 272 this, 273 WebContentsAudioInputStream::Create( 274 device_id, audio_params, audio_manager_->GetWorkerLoop()), 275 entry->writer.get()); 276 } else { 277 // TODO(henrika): replace CreateLowLatency() with Create() as soon 278 // as satish has ensured that Speech Input also uses the default low- 279 // latency path. See crbug.com/112472 for details. 280 entry->controller = media::AudioInputController::CreateLowLatency( 281 audio_manager_, 282 this, 283 audio_params, 284 device_id, 285 entry->writer.get()); 286 } 287 288 if (!entry->controller.get()) { 289 SendErrorMessage(stream_id); 290 return; 291 } 292 293 // Set the initial AGC state for the audio input stream. Note that, the AGC 294 // is only supported in AUDIO_PCM_LOW_LATENCY mode. 295 if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) 296 entry->controller->SetAutomaticGainControl(config.automatic_gain_control); 297 298 // Since the controller was created successfully, create an entry and add it 299 // to the map. 300 entry->stream_id = stream_id; 301 audio_entries_.insert(std::make_pair(stream_id, entry.release())); 302} 303 304void AudioInputRendererHost::OnRecordStream(int stream_id) { 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 306 307 AudioEntry* entry = LookupById(stream_id); 308 if (!entry) { 309 SendErrorMessage(stream_id); 310 return; 311 } 312 313 entry->controller->Record(); 314} 315 316void AudioInputRendererHost::OnCloseStream(int stream_id) { 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 318 319 AudioEntry* entry = LookupById(stream_id); 320 321 if (entry) 322 CloseAndDeleteStream(entry); 323} 324 325void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) { 326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 327 328 AudioEntry* entry = LookupById(stream_id); 329 if (!entry) { 330 SendErrorMessage(stream_id); 331 return; 332 } 333 334 entry->controller->SetVolume(volume); 335} 336 337void AudioInputRendererHost::SendErrorMessage(int stream_id) { 338 Send(new AudioInputMsg_NotifyStreamStateChanged( 339 stream_id, media::AudioInputIPCDelegate::kError)); 340} 341 342void AudioInputRendererHost::DeleteEntries() { 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 344 345 for (AudioEntryMap::iterator i = audio_entries_.begin(); 346 i != audio_entries_.end(); ++i) { 347 CloseAndDeleteStream(i->second); 348 } 349} 350 351void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) { 352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 353 354 if (!entry->pending_close) { 355 entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry, 356 this, entry)); 357 entry->pending_close = true; 358 } 359} 360 361void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) { 362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 363 364 // Delete the entry when this method goes out of scope. 365 scoped_ptr<AudioEntry> entry_deleter(entry); 366 367 // Erase the entry from the map. 368 audio_entries_.erase(entry->stream_id); 369} 370 371void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) { 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 373 374 // Sends the error message first before we close the stream because 375 // |entry| is destroyed in DeleteEntry(). 376 SendErrorMessage(entry->stream_id); 377 CloseAndDeleteStream(entry); 378} 379 380AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById( 381 int stream_id) { 382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 383 384 AudioEntryMap::iterator i = audio_entries_.find(stream_id); 385 if (i != audio_entries_.end()) 386 return i->second; 387 return NULL; 388} 389 390AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController( 391 media::AudioInputController* controller) { 392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 393 394 // Iterate the map of entries. 395 // TODO(hclam): Implement a faster look up method. 396 for (AudioEntryMap::iterator i = audio_entries_.begin(); 397 i != audio_entries_.end(); ++i) { 398 if (controller == i->second->controller.get()) 399 return i->second; 400 } 401 return NULL; 402} 403 404} // namespace content 405