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 |