Chromium Code Reviews| Index: content/browser/renderer_host/media/audio_input_device_manager.cc |
| diff --git a/content/browser/renderer_host/media/audio_input_device_manager.cc b/content/browser/renderer_host/media/audio_input_device_manager.cc |
| index 4ad35755240e59932c8d60e8e8998c3eca558254..9aea4b3d4d8bb6e2d1049e21c9c5da1d0b48edb6 100644 |
| --- a/content/browser/renderer_host/media/audio_input_device_manager.cc |
| +++ b/content/browser/renderer_host/media/audio_input_device_manager.cc |
| @@ -8,13 +8,15 @@ |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| +#include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "build/build_config.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/media_stream_request.h" |
| #include "media/audio/audio_input_ipc.h" |
| -#include "media/audio/audio_manager_base.h" |
| +#include "media/audio/audio_system.h" |
| #include "media/base/audio_parameters.h" |
| +#include "media/base/bind_to_current_loop.h" |
| #include "media/base/channel_layout.h" |
| #include "media/base/media_switches.h" |
| @@ -29,20 +31,199 @@ const int AudioInputDeviceManager::kFakeOpenSessionId = 1; |
| namespace { |
| // Starting id for the first capture session. |
| const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1; |
| + |
| +// The object of this class lives on AudioSystem thread and at each given |
| +// moment is owned by a callback it posted to that thread. |
| +class DeviceOpener { |
| + public: |
| + using OnOpenedCallback = base::Callback<void(const StreamDeviceInfo&)>; |
| + |
| + ~DeviceOpener(); |
| + |
| + // Wwill reply with |on_opened_cb| on the thread Open() is called on. |
|
Max Morin
2017/03/22 17:46:24
Typo Wwill
|
| + static void Open(media::AudioSystem* audio_system, |
| + const MediaStreamDevice& device, |
| + int session_id, |
| + OnOpenedCallback on_opened_cb); |
| + |
| + protected: |
| + DeviceOpener(media::AudioSystem* audio_system, |
| + const MediaStreamDevice& device, |
| + int session_id, |
| + OnOpenedCallback on_opened_cb); |
| + |
| + private: |
| + void DoOpen(std::unique_ptr<DeviceOpener> owned_this); |
| + void OpenWithMatchedOutputId(std::unique_ptr<DeviceOpener> owned_this, |
| + const std::string& matched_output_id); |
| + void OpenWithInputParams(std::unique_ptr<DeviceOpener> owned_this, |
| + const media::AudioParameters& params); |
| + void OpenWithOutputParams(std::unique_ptr<DeviceOpener> owned_this, |
| + const media::AudioParameters& params); |
| + void ReplyOpened(); |
| + |
| + base::WeakPtr<media::AudioSystem> audio_system_; |
| + base::TimeTicks start_time_; |
| + OnOpenedCallback on_opened_cb_; |
| + StreamDeviceInfo info_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DeviceOpener); |
| +}; |
| + |
| +DeviceOpener::DeviceOpener(media::AudioSystem* audio_system, |
| + const MediaStreamDevice& device, |
| + int session_id, |
| + OnOpenedCallback on_opened_cb) |
| + : audio_system_(audio_system->GetWeakPtr()), |
| + on_opened_cb_(std::move(on_opened_cb)), |
| + info_(device.type, device.name, device.id, 0, 0, 0) { |
| + info_.session_id = session_id; |
| +} |
| + |
| +DeviceOpener::~DeviceOpener() { |
| + if (!audio_system_) |
| + return; |
| + DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + // Do not ReplyOpened() here, since destructor may be called on message |
| + // loop destruction before callback runs. |
| + |
| + DCHECK(start_time_ == base::TimeTicks() || |
| + (base::TimeTicks::Now() - start_time_).InSeconds() < 30) |
| + << "DeviceOpener hang"; |
| +} |
| + |
| +// static |
| +void DeviceOpener::Open(media::AudioSystem* audio_system, |
| + const MediaStreamDevice& device, |
| + int session_id, |
| + OnOpenedCallback on_opened_cb) { |
| + DCHECK(audio_system); |
| + |
| + std::unique_ptr<DeviceOpener> opener( |
| + new DeviceOpener(audio_system, device, session_id, |
| + media::BindToCurrentLoop(on_opened_cb))); |
| + |
| + audio_system->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&DeviceOpener::DoOpen, base::Unretained(opener.get()), |
| + base::Passed(&opener))); |
| +} |
| + |
| +void DeviceOpener::DoOpen(std::unique_ptr<DeviceOpener> owned_this) { |
| + if (!audio_system_) |
| + return; |
| + DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + start_time_ = base::TimeTicks::Now(); |
| + |
| + audio_system_->GetAssociatedOutputDeviceID( |
| + info_.device.id, |
| + base::Bind(&DeviceOpener::OpenWithMatchedOutputId, base::Unretained(this), |
| + base::Passed(&owned_this))); |
| +} |
| + |
| +void DeviceOpener::OpenWithMatchedOutputId( |
| + std::unique_ptr<DeviceOpener> owned_this, |
| + const std::string& matched_output_id) { |
| + if (!audio_system_) |
| + return; |
| + DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + info_.device.matched_output_device_id = matched_output_id; |
| + |
| + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kUseFakeDeviceForMediaStream)) { |
| + audio_system_->GetInputStreamParameters( |
| + info_.device.id, |
| + base::Bind(&DeviceOpener::OpenWithInputParams, base::Unretained(this), |
| + base::Passed(&owned_this))); |
| + return; |
| + } |
| + |
| + // Don't need to query the hardware information if using fake device. |
| + info_.device.input.sample_rate = 44100; |
| + info_.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO; |
| + if (!info_.device.matched_output_device_id.empty()) { |
| + info_.device.matched_output.sample_rate = 44100; |
| + info_.device.matched_output.channel_layout = media::CHANNEL_LAYOUT_STEREO; |
| + } |
| + ReplyOpened(); |
| +} |
| + |
| +void DeviceOpener::OpenWithInputParams(std::unique_ptr<DeviceOpener> owned_this, |
| + const media::AudioParameters& params) { |
| + if (!audio_system_) |
| + return; |
| + DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + // TODO(tommi): As is, we hit this code path when device.type is |
| + // MEDIA_TAB_AUDIO_CAPTURE and the device id is not a device that |
| + // the AudioManager can know about. This currently does not fail because |
| + // the implementation of GetInputStreamParameters returns valid parameters |
| + // by default for invalid devices. That behavior is problematic because it |
| + // causes other parts of the code to attempt to open truly invalid or |
| + // missing devices and falling back on alternate devices (and likely fail |
| + // twice in a row). Tab audio capture should not pass through here and |
| + // GetInputStreamParameters should return invalid parameters for invalid |
| + // devices. |
| + |
| + // Get the preferred sample rate and channel configuration for the |
| + // audio device. |
| + info_.device.input.sample_rate = params.sample_rate(); |
| + info_.device.input.channel_layout = params.channel_layout(); |
| + info_.device.input.frames_per_buffer = params.frames_per_buffer(); |
| + info_.device.input.effects = params.effects(); |
| + info_.device.input.mic_positions = params.mic_positions(); |
| + |
| + if (info_.device.matched_output_device_id.empty()) { |
| + ReplyOpened(); |
| + return; |
| + } |
| + |
| + audio_system_->GetOutputStreamParameters( |
| + info_.device.matched_output_device_id, |
| + base::Bind(&DeviceOpener::OpenWithOutputParams, base::Unretained(this), |
| + base::Passed(&owned_this))); |
| +} |
| + |
| +void DeviceOpener::OpenWithOutputParams( |
| + std::unique_ptr<DeviceOpener> owned_this, |
| + const media::AudioParameters& params) { |
| + if (!audio_system_) |
| + return; |
| + DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + info_.device.matched_output.sample_rate = params.sample_rate(); |
| + info_.device.matched_output.channel_layout = params.channel_layout(); |
| + info_.device.matched_output.frames_per_buffer = params.frames_per_buffer(); |
| + ReplyOpened(); |
| } |
| +void DeviceOpener::ReplyOpened() { |
| + if (!audio_system_) |
| + return; |
| + DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); |
| + |
| + DCHECK(start_time_ > base::TimeTicks()); |
| + UMA_HISTOGRAM_TIMES("Media.AudioInputDeviceManager.OpenOnDeviceThreadTime", |
|
o1ka
2017/03/22 17:09:33
TODO: Make sure UMA meaning is unchanged. Add UMA
|
| + base::TimeTicks::Now() - start_time_); |
| + std::move(on_opened_cb_).Run(info_); |
| + start_time_ = base::TimeTicks(); |
| +} |
| + |
| +} // namespace |
| + |
| AudioInputDeviceManager::AudioInputDeviceManager( |
| - media::AudioManager* audio_manager) |
| + media::AudioSystem* audio_system) |
| : next_capture_session_id_(kFirstSessionId), |
| #if defined(OS_CHROMEOS) |
| keyboard_mic_streams_count_(0), |
| #endif |
| - audio_manager_(audio_manager), |
| - device_task_runner_(audio_manager_->GetTaskRunner()) { |
| + audio_system_(audio_system) { |
| } |
| -AudioInputDeviceManager::~AudioInputDeviceManager() { |
| -} |
| +AudioInputDeviceManager::~AudioInputDeviceManager() {} |
| const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById( |
| int session_id) { |
| @@ -58,7 +239,6 @@ void AudioInputDeviceManager::RegisterListener( |
| MediaStreamProviderListener* listener) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(listener); |
| - DCHECK(device_task_runner_); |
| listeners_.AddObserver(listener); |
| } |
| @@ -73,10 +253,9 @@ int AudioInputDeviceManager::Open(const MediaStreamDevice& device) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| // Generate a new id for this device. |
| int session_id = next_capture_session_id_++; |
| - device_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread, |
| - this, session_id, device)); |
| + DeviceOpener::Open( |
| + audio_system_, device, session_id, |
| + base::Bind(&AudioInputDeviceManager::OpenedOnIOThread, this, session_id)); |
| return session_id; |
| } |
| @@ -135,72 +314,6 @@ void AudioInputDeviceManager::UnregisterKeyboardMicStream() { |
| } |
| #endif |
| -void AudioInputDeviceManager::OpenOnDeviceThread( |
| - int session_id, |
| - const MediaStreamDevice& device) { |
| - SCOPED_UMA_HISTOGRAM_TIMER( |
| - "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime"); |
| - DCHECK(IsOnDeviceThread()); |
| - |
| - StreamDeviceInfo out(device.type, device.name, device.id, 0, 0, 0); |
| - out.session_id = session_id; |
| - |
| - MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input; |
| - |
| - // Add preferred output device information if a matching output device |
| - // exists. |
| - out.device.matched_output_device_id = |
| - audio_manager_->GetAssociatedOutputDeviceID(device.id); |
| - |
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kUseFakeDeviceForMediaStream)) { |
| - // Don't need to query the hardware information if using fake device. |
| - input_params.sample_rate = 44100; |
| - input_params.channel_layout = media::CHANNEL_LAYOUT_STEREO; |
| - if (!out.device.matched_output_device_id.empty()) { |
| - out.device.matched_output.sample_rate = 44100; |
| - out.device.matched_output.channel_layout = media::CHANNEL_LAYOUT_STEREO; |
| - } |
| - } else { |
| - // TODO(tommi): As is, we hit this code path when device.type is |
| - // MEDIA_TAB_AUDIO_CAPTURE and the device id is not a device that |
| - // the AudioManager can know about. This currently does not fail because |
| - // the implementation of GetInputStreamParameters returns valid parameters |
| - // by default for invalid devices. That behavior is problematic because it |
| - // causes other parts of the code to attempt to open truly invalid or |
| - // missing devices and falling back on alternate devices (and likely fail |
| - // twice in a row). Tab audio capture should not pass through here and |
| - // GetInputStreamParameters should return invalid parameters for invalid |
| - // devices. |
| - |
| - // Get the preferred sample rate and channel configuration for the |
| - // audio device. |
| - media::AudioParameters params = |
| - audio_manager_->GetInputStreamParameters(device.id); |
| - input_params.sample_rate = params.sample_rate(); |
| - input_params.channel_layout = params.channel_layout(); |
| - input_params.frames_per_buffer = params.frames_per_buffer(); |
| - input_params.effects = params.effects(); |
| - input_params.mic_positions = params.mic_positions(); |
| - if (!out.device.matched_output_device_id.empty()) { |
| - params = audio_manager_->GetOutputStreamParameters( |
| - out.device.matched_output_device_id); |
| - MediaStreamDevice::AudioDeviceParameters& matched_output_params = |
| - out.device.matched_output; |
| - matched_output_params.sample_rate = params.sample_rate(); |
| - matched_output_params.channel_layout = params.channel_layout(); |
| - matched_output_params.frames_per_buffer = params.frames_per_buffer(); |
| - } |
| - } |
| - |
| - // 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, session_id, out)); |
| -} |
| - |
| void AudioInputDeviceManager::OpenedOnIOThread(int session_id, |
| const StreamDeviceInfo& info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| @@ -220,10 +333,6 @@ void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type, |
| listener.Closed(stream_type, session_id); |
| } |
| -bool AudioInputDeviceManager::IsOnDeviceThread() const { |
| - return device_task_runner_->BelongsToCurrentThread(); |
| -} |
| - |
| AudioInputDeviceManager::StreamDeviceList::iterator |
| AudioInputDeviceManager::GetDevice(int session_id) { |
| for (StreamDeviceList::iterator i(devices_.begin()); i != devices_.end(); |
| @@ -243,5 +352,4 @@ void AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread( |
| } |
| #endif |
| - |
| } // namespace content |