// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/renderer_host/media/audio_input_device_manager.h" #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "content/browser/renderer_host/media/audio_input_device_manager_event_handler.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/media_stream_request.h" #include "media/audio/audio_device_name.h" #include "media/audio/audio_input_ipc.h" #include "media/audio/audio_manager_base.h" #include "media/audio/audio_util.h" #include "media/base/channel_layout.h" namespace content { const int AudioInputDeviceManager::kFakeOpenSessionId = 1; namespace { // Starting id for the first capture session. const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1; } AudioInputDeviceManager::AudioInputDeviceManager( media::AudioManager* audio_manager) : listener_(NULL), next_capture_session_id_(kFirstSessionId), use_fake_device_(false), audio_manager_(audio_manager) { } AudioInputDeviceManager::~AudioInputDeviceManager() { } void AudioInputDeviceManager::Register( MediaStreamProviderListener* listener, base::MessageLoopProxy* device_thread_loop) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(!listener_); DCHECK(!device_loop_); listener_ = listener; device_loop_ = device_thread_loop; } void AudioInputDeviceManager::Unregister() { DCHECK(listener_); listener_ = NULL; } void AudioInputDeviceManager::EnumerateDevices(MediaStreamType stream_type) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(listener_); device_loop_->PostTask( FROM_HERE, base::Bind(&AudioInputDeviceManager::EnumerateOnDeviceThread, this, stream_type)); } int AudioInputDeviceManager::Open(const StreamDeviceInfo& device) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Generate a new id for this device. int session_id = next_capture_session_id_++; device_loop_->PostTask( FROM_HERE, base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread, this, session_id, device)); return session_id; } void AudioInputDeviceManager::Close(int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(listener_); // Check if the device has been stopped, if not, send stop signal. EventHandlerMap::iterator it = event_handlers_.find(session_id); if (it != event_handlers_.end()) { it->second->OnDeviceStopped(session_id); event_handlers_.erase(it); } device_loop_->PostTask( FROM_HERE, base::Bind(&AudioInputDeviceManager::CloseOnDeviceThread, this, session_id)); } void AudioInputDeviceManager::Start( int session_id, AudioInputDeviceManagerEventHandler* handler) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(handler); // Solution for not using MediaStreamManager. This is needed when Start() is // called without using Open(), we post default device for test purpose. // And we do not store the info for the kFakeOpenSessionId but return // the callback immediately. if (session_id == kFakeOpenSessionId) { handler->OnDeviceStarted(session_id, media::AudioManagerBase::kDefaultDeviceId); return; } // Look up the device_id of the session so we can notify the renderer that // the device has started. If the session has not been started, // use the empty device_id to indicate that Start() failed. std::string device_id; if (event_handlers_.insert(std::make_pair(session_id, handler)).second) { StreamDeviceMap::const_iterator it = devices_.find(session_id); if (it != devices_.end()) device_id = it->second.device.id; } // Post a callback through the AudioInputRendererHost to notify the renderer // that the device has started. handler->OnDeviceStarted(session_id, device_id); } void AudioInputDeviceManager::Stop(int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Erase the event handler referenced by the session_id. event_handlers_.erase(session_id); } void AudioInputDeviceManager::UseFakeDevice() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); use_fake_device_ = true; } bool AudioInputDeviceManager::ShouldUseFakeDevice() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); return use_fake_device_; } void AudioInputDeviceManager::EnumerateOnDeviceThread( MediaStreamType stream_type) { DCHECK(IsOnDeviceThread()); media::AudioDeviceNames device_names; switch (stream_type) { case MEDIA_DEVICE_AUDIO_CAPTURE: // AudioManager is guaranteed to outlive MediaStreamManager in // BrowserMainloop. audio_manager_->GetAudioInputDeviceNames(&device_names); break; default: NOTREACHED(); break; } scoped_ptr devices(new StreamDeviceInfoArray()); for (media::AudioDeviceNames::iterator it = device_names.begin(); it != device_names.end(); ++it) { // Get the preferred sample rate and channel configuration for the // current audio device. int sample_rate = media::GetAudioInputHardwareSampleRate(it->unique_id); media::ChannelLayout channel_layout = media::GetAudioInputHardwareChannelLayout(it->unique_id); // Add device information to device vector. devices->push_back(StreamDeviceInfo( stream_type, it->device_name, it->unique_id, sample_rate, channel_layout, false)); } // Return the device list through the listener by posting a task on // IO thread since MediaStreamManager handles the callback asynchronously. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&AudioInputDeviceManager::DevicesEnumeratedOnIOThread, this, stream_type, base::Passed(&devices))); } void AudioInputDeviceManager::OpenOnDeviceThread( int session_id, const StreamDeviceInfo& device) { DCHECK(IsOnDeviceThread()); // Add the session_id and device to the map. if (!devices_.insert(std::make_pair(session_id, device)).second) { NOTREACHED(); devices_[session_id] = device; } // Return the |session_id| through the listener by posting a task on // IO thread since MediaStreamManager handles the callback asynchronously. BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&AudioInputDeviceManager::OpenedOnIOThread, this, device.device.type, session_id)); } void AudioInputDeviceManager::CloseOnDeviceThread(int session_id) { DCHECK(IsOnDeviceThread()); StreamDeviceMap::iterator it = devices_.find(session_id); if (it == devices_.end()) return; const MediaStreamType stream_type = it->second.device.type; devices_.erase(it); // Post a callback through the listener on IO thread since // MediaStreamManager handles the callback asynchronously. BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&AudioInputDeviceManager::ClosedOnIOThread, this, stream_type, session_id)); } void AudioInputDeviceManager::DevicesEnumeratedOnIOThread( MediaStreamType stream_type, scoped_ptr devices) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Ensure that |devices| gets deleted on exit. if (listener_) listener_->DevicesEnumerated(stream_type, *devices); } void AudioInputDeviceManager::OpenedOnIOThread(MediaStreamType stream_type, int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (listener_) listener_->Opened(stream_type, session_id); } void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type, int session_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (listener_) listener_->Closed(stream_type, session_id); } bool AudioInputDeviceManager::IsOnDeviceThread() const { return device_loop_->BelongsToCurrentThread(); } } // namespace content