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