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