Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/renderer_host/media/audio_input_device_manager.h" | 5 #include "content/browser/renderer_host/media/audio_input_device_manager.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/memory/ptr_util.h" | |
| 11 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
| 12 #include "build/build_config.h" | 13 #include "build/build_config.h" |
| 13 #include "content/public/browser/browser_thread.h" | 14 #include "content/public/browser/browser_thread.h" |
| 14 #include "content/public/common/media_stream_request.h" | 15 #include "content/public/common/media_stream_request.h" |
| 15 #include "media/audio/audio_input_ipc.h" | 16 #include "media/audio/audio_input_ipc.h" |
| 16 #include "media/audio/audio_manager_base.h" | 17 #include "media/audio/audio_system.h" |
| 17 #include "media/base/audio_parameters.h" | 18 #include "media/base/audio_parameters.h" |
| 19 #include "media/base/bind_to_current_loop.h" | |
| 18 #include "media/base/channel_layout.h" | 20 #include "media/base/channel_layout.h" |
| 19 #include "media/base/media_switches.h" | 21 #include "media/base/media_switches.h" |
| 20 | 22 |
| 21 #if defined(OS_CHROMEOS) | 23 #if defined(OS_CHROMEOS) |
| 22 #include "chromeos/audio/cras_audio_handler.h" | 24 #include "chromeos/audio/cras_audio_handler.h" |
| 23 #endif | 25 #endif |
| 24 | 26 |
| 25 namespace content { | 27 namespace content { |
| 26 | 28 |
| 27 const int AudioInputDeviceManager::kFakeOpenSessionId = 1; | 29 const int AudioInputDeviceManager::kFakeOpenSessionId = 1; |
| 28 | 30 |
| 29 namespace { | 31 namespace { |
| 30 // Starting id for the first capture session. | 32 // Starting id for the first capture session. |
| 31 const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1; | 33 const int kFirstSessionId = AudioInputDeviceManager::kFakeOpenSessionId + 1; |
| 34 | |
| 35 // The object of this class lives on AudioSystem thread and at each given | |
| 36 // moment is owned by a callback it posted to that thread. | |
| 37 class DeviceOpener { | |
| 38 public: | |
| 39 using OnOpenedCallback = base::Callback<void(const StreamDeviceInfo&)>; | |
| 40 | |
| 41 ~DeviceOpener(); | |
| 42 | |
| 43 // Wwill reply with |on_opened_cb| on the thread Open() is called on. | |
|
Max Morin
2017/03/22 17:46:24
Typo Wwill
| |
| 44 static void Open(media::AudioSystem* audio_system, | |
| 45 const MediaStreamDevice& device, | |
| 46 int session_id, | |
| 47 OnOpenedCallback on_opened_cb); | |
| 48 | |
| 49 protected: | |
| 50 DeviceOpener(media::AudioSystem* audio_system, | |
| 51 const MediaStreamDevice& device, | |
| 52 int session_id, | |
| 53 OnOpenedCallback on_opened_cb); | |
| 54 | |
| 55 private: | |
| 56 void DoOpen(std::unique_ptr<DeviceOpener> owned_this); | |
| 57 void OpenWithMatchedOutputId(std::unique_ptr<DeviceOpener> owned_this, | |
| 58 const std::string& matched_output_id); | |
| 59 void OpenWithInputParams(std::unique_ptr<DeviceOpener> owned_this, | |
| 60 const media::AudioParameters& params); | |
| 61 void OpenWithOutputParams(std::unique_ptr<DeviceOpener> owned_this, | |
| 62 const media::AudioParameters& params); | |
| 63 void ReplyOpened(); | |
| 64 | |
| 65 base::WeakPtr<media::AudioSystem> audio_system_; | |
| 66 base::TimeTicks start_time_; | |
| 67 OnOpenedCallback on_opened_cb_; | |
| 68 StreamDeviceInfo info_; | |
| 69 | |
| 70 DISALLOW_COPY_AND_ASSIGN(DeviceOpener); | |
| 71 }; | |
| 72 | |
| 73 DeviceOpener::DeviceOpener(media::AudioSystem* audio_system, | |
| 74 const MediaStreamDevice& device, | |
| 75 int session_id, | |
| 76 OnOpenedCallback on_opened_cb) | |
| 77 : audio_system_(audio_system->GetWeakPtr()), | |
| 78 on_opened_cb_(std::move(on_opened_cb)), | |
| 79 info_(device.type, device.name, device.id, 0, 0, 0) { | |
| 80 info_.session_id = session_id; | |
| 32 } | 81 } |
| 33 | 82 |
| 83 DeviceOpener::~DeviceOpener() { | |
| 84 if (!audio_system_) | |
| 85 return; | |
| 86 DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 87 | |
| 88 // Do not ReplyOpened() here, since destructor may be called on message | |
| 89 // loop destruction before callback runs. | |
| 90 | |
| 91 DCHECK(start_time_ == base::TimeTicks() || | |
| 92 (base::TimeTicks::Now() - start_time_).InSeconds() < 30) | |
| 93 << "DeviceOpener hang"; | |
| 94 } | |
| 95 | |
| 96 // static | |
| 97 void DeviceOpener::Open(media::AudioSystem* audio_system, | |
| 98 const MediaStreamDevice& device, | |
| 99 int session_id, | |
| 100 OnOpenedCallback on_opened_cb) { | |
| 101 DCHECK(audio_system); | |
| 102 | |
| 103 std::unique_ptr<DeviceOpener> opener( | |
| 104 new DeviceOpener(audio_system, device, session_id, | |
| 105 media::BindToCurrentLoop(on_opened_cb))); | |
| 106 | |
| 107 audio_system->GetTaskRunner()->PostTask( | |
| 108 FROM_HERE, | |
| 109 base::Bind(&DeviceOpener::DoOpen, base::Unretained(opener.get()), | |
| 110 base::Passed(&opener))); | |
| 111 } | |
| 112 | |
| 113 void DeviceOpener::DoOpen(std::unique_ptr<DeviceOpener> owned_this) { | |
| 114 if (!audio_system_) | |
| 115 return; | |
| 116 DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 117 | |
| 118 start_time_ = base::TimeTicks::Now(); | |
| 119 | |
| 120 audio_system_->GetAssociatedOutputDeviceID( | |
| 121 info_.device.id, | |
| 122 base::Bind(&DeviceOpener::OpenWithMatchedOutputId, base::Unretained(this), | |
| 123 base::Passed(&owned_this))); | |
| 124 } | |
| 125 | |
| 126 void DeviceOpener::OpenWithMatchedOutputId( | |
| 127 std::unique_ptr<DeviceOpener> owned_this, | |
| 128 const std::string& matched_output_id) { | |
| 129 if (!audio_system_) | |
| 130 return; | |
| 131 DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 132 | |
| 133 info_.device.matched_output_device_id = matched_output_id; | |
| 134 | |
| 135 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 136 switches::kUseFakeDeviceForMediaStream)) { | |
| 137 audio_system_->GetInputStreamParameters( | |
| 138 info_.device.id, | |
| 139 base::Bind(&DeviceOpener::OpenWithInputParams, base::Unretained(this), | |
| 140 base::Passed(&owned_this))); | |
| 141 return; | |
| 142 } | |
| 143 | |
| 144 // Don't need to query the hardware information if using fake device. | |
| 145 info_.device.input.sample_rate = 44100; | |
| 146 info_.device.input.channel_layout = media::CHANNEL_LAYOUT_STEREO; | |
| 147 if (!info_.device.matched_output_device_id.empty()) { | |
| 148 info_.device.matched_output.sample_rate = 44100; | |
| 149 info_.device.matched_output.channel_layout = media::CHANNEL_LAYOUT_STEREO; | |
| 150 } | |
| 151 ReplyOpened(); | |
| 152 } | |
| 153 | |
| 154 void DeviceOpener::OpenWithInputParams(std::unique_ptr<DeviceOpener> owned_this, | |
| 155 const media::AudioParameters& params) { | |
| 156 if (!audio_system_) | |
| 157 return; | |
| 158 DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 159 | |
| 160 // TODO(tommi): As is, we hit this code path when device.type is | |
| 161 // MEDIA_TAB_AUDIO_CAPTURE and the device id is not a device that | |
| 162 // the AudioManager can know about. This currently does not fail because | |
| 163 // the implementation of GetInputStreamParameters returns valid parameters | |
| 164 // by default for invalid devices. That behavior is problematic because it | |
| 165 // causes other parts of the code to attempt to open truly invalid or | |
| 166 // missing devices and falling back on alternate devices (and likely fail | |
| 167 // twice in a row). Tab audio capture should not pass through here and | |
| 168 // GetInputStreamParameters should return invalid parameters for invalid | |
| 169 // devices. | |
| 170 | |
| 171 // Get the preferred sample rate and channel configuration for the | |
| 172 // audio device. | |
| 173 info_.device.input.sample_rate = params.sample_rate(); | |
| 174 info_.device.input.channel_layout = params.channel_layout(); | |
| 175 info_.device.input.frames_per_buffer = params.frames_per_buffer(); | |
| 176 info_.device.input.effects = params.effects(); | |
| 177 info_.device.input.mic_positions = params.mic_positions(); | |
| 178 | |
| 179 if (info_.device.matched_output_device_id.empty()) { | |
| 180 ReplyOpened(); | |
| 181 return; | |
| 182 } | |
| 183 | |
| 184 audio_system_->GetOutputStreamParameters( | |
| 185 info_.device.matched_output_device_id, | |
| 186 base::Bind(&DeviceOpener::OpenWithOutputParams, base::Unretained(this), | |
| 187 base::Passed(&owned_this))); | |
| 188 } | |
| 189 | |
| 190 void DeviceOpener::OpenWithOutputParams( | |
| 191 std::unique_ptr<DeviceOpener> owned_this, | |
| 192 const media::AudioParameters& params) { | |
| 193 if (!audio_system_) | |
| 194 return; | |
| 195 DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 196 | |
| 197 info_.device.matched_output.sample_rate = params.sample_rate(); | |
| 198 info_.device.matched_output.channel_layout = params.channel_layout(); | |
| 199 info_.device.matched_output.frames_per_buffer = params.frames_per_buffer(); | |
| 200 ReplyOpened(); | |
| 201 } | |
| 202 | |
| 203 void DeviceOpener::ReplyOpened() { | |
| 204 if (!audio_system_) | |
| 205 return; | |
| 206 DCHECK(audio_system_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 207 | |
| 208 DCHECK(start_time_ > base::TimeTicks()); | |
| 209 UMA_HISTOGRAM_TIMES("Media.AudioInputDeviceManager.OpenOnDeviceThreadTime", | |
|
o1ka
2017/03/22 17:09:33
TODO: Make sure UMA meaning is unchanged. Add UMA
| |
| 210 base::TimeTicks::Now() - start_time_); | |
| 211 std::move(on_opened_cb_).Run(info_); | |
| 212 start_time_ = base::TimeTicks(); | |
| 213 } | |
| 214 | |
| 215 } // namespace | |
| 216 | |
| 34 AudioInputDeviceManager::AudioInputDeviceManager( | 217 AudioInputDeviceManager::AudioInputDeviceManager( |
| 35 media::AudioManager* audio_manager) | 218 media::AudioSystem* audio_system) |
| 36 : next_capture_session_id_(kFirstSessionId), | 219 : next_capture_session_id_(kFirstSessionId), |
| 37 #if defined(OS_CHROMEOS) | 220 #if defined(OS_CHROMEOS) |
| 38 keyboard_mic_streams_count_(0), | 221 keyboard_mic_streams_count_(0), |
| 39 #endif | 222 #endif |
| 40 audio_manager_(audio_manager), | 223 audio_system_(audio_system) { |
| 41 device_task_runner_(audio_manager_->GetTaskRunner()) { | |
| 42 } | 224 } |
| 43 | 225 |
| 44 AudioInputDeviceManager::~AudioInputDeviceManager() { | 226 AudioInputDeviceManager::~AudioInputDeviceManager() {} |
| 45 } | |
| 46 | 227 |
| 47 const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById( | 228 const StreamDeviceInfo* AudioInputDeviceManager::GetOpenedDeviceInfoById( |
| 48 int session_id) { | 229 int session_id) { |
| 49 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 230 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 50 StreamDeviceList::iterator device = GetDevice(session_id); | 231 StreamDeviceList::iterator device = GetDevice(session_id); |
| 51 if (device == devices_.end()) | 232 if (device == devices_.end()) |
| 52 return nullptr; | 233 return nullptr; |
| 53 | 234 |
| 54 return &(*device); | 235 return &(*device); |
| 55 } | 236 } |
| 56 | 237 |
| 57 void AudioInputDeviceManager::RegisterListener( | 238 void AudioInputDeviceManager::RegisterListener( |
| 58 MediaStreamProviderListener* listener) { | 239 MediaStreamProviderListener* listener) { |
| 59 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 240 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 60 DCHECK(listener); | 241 DCHECK(listener); |
| 61 DCHECK(device_task_runner_); | |
| 62 listeners_.AddObserver(listener); | 242 listeners_.AddObserver(listener); |
| 63 } | 243 } |
| 64 | 244 |
| 65 void AudioInputDeviceManager::UnregisterListener( | 245 void AudioInputDeviceManager::UnregisterListener( |
| 66 MediaStreamProviderListener* listener) { | 246 MediaStreamProviderListener* listener) { |
| 67 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 247 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 68 DCHECK(listener); | 248 DCHECK(listener); |
| 69 listeners_.RemoveObserver(listener); | 249 listeners_.RemoveObserver(listener); |
| 70 } | 250 } |
| 71 | 251 |
| 72 int AudioInputDeviceManager::Open(const MediaStreamDevice& device) { | 252 int AudioInputDeviceManager::Open(const MediaStreamDevice& device) { |
| 73 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 253 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 74 // Generate a new id for this device. | 254 // Generate a new id for this device. |
| 75 int session_id = next_capture_session_id_++; | 255 int session_id = next_capture_session_id_++; |
| 76 device_task_runner_->PostTask( | 256 DeviceOpener::Open( |
| 77 FROM_HERE, | 257 audio_system_, device, session_id, |
| 78 base::Bind(&AudioInputDeviceManager::OpenOnDeviceThread, | 258 base::Bind(&AudioInputDeviceManager::OpenedOnIOThread, this, session_id)); |
| 79 this, session_id, device)); | |
| 80 | 259 |
| 81 return session_id; | 260 return session_id; |
| 82 } | 261 } |
| 83 | 262 |
| 84 void AudioInputDeviceManager::Close(int session_id) { | 263 void AudioInputDeviceManager::Close(int session_id) { |
| 85 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 264 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 86 StreamDeviceList::iterator device = GetDevice(session_id); | 265 StreamDeviceList::iterator device = GetDevice(session_id); |
| 87 if (device == devices_.end()) | 266 if (device == devices_.end()) |
| 88 return; | 267 return; |
| 89 const MediaStreamType stream_type = device->device.type; | 268 const MediaStreamType stream_type = device->device.type; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 128 BrowserThread::UI, | 307 BrowserThread::UI, |
| 129 FROM_HERE, | 308 FROM_HERE, |
| 130 base::Bind( | 309 base::Bind( |
| 131 &AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread, | 310 &AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread, |
| 132 this, | 311 this, |
| 133 false)); | 312 false)); |
| 134 } | 313 } |
| 135 } | 314 } |
| 136 #endif | 315 #endif |
| 137 | 316 |
| 138 void AudioInputDeviceManager::OpenOnDeviceThread( | |
| 139 int session_id, | |
| 140 const MediaStreamDevice& device) { | |
| 141 SCOPED_UMA_HISTOGRAM_TIMER( | |
| 142 "Media.AudioInputDeviceManager.OpenOnDeviceThreadTime"); | |
| 143 DCHECK(IsOnDeviceThread()); | |
| 144 | |
| 145 StreamDeviceInfo out(device.type, device.name, device.id, 0, 0, 0); | |
| 146 out.session_id = session_id; | |
| 147 | |
| 148 MediaStreamDevice::AudioDeviceParameters& input_params = out.device.input; | |
| 149 | |
| 150 // Add preferred output device information if a matching output device | |
| 151 // exists. | |
| 152 out.device.matched_output_device_id = | |
| 153 audio_manager_->GetAssociatedOutputDeviceID(device.id); | |
| 154 | |
| 155 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 156 switches::kUseFakeDeviceForMediaStream)) { | |
| 157 // Don't need to query the hardware information if using fake device. | |
| 158 input_params.sample_rate = 44100; | |
| 159 input_params.channel_layout = media::CHANNEL_LAYOUT_STEREO; | |
| 160 if (!out.device.matched_output_device_id.empty()) { | |
| 161 out.device.matched_output.sample_rate = 44100; | |
| 162 out.device.matched_output.channel_layout = media::CHANNEL_LAYOUT_STEREO; | |
| 163 } | |
| 164 } else { | |
| 165 // TODO(tommi): As is, we hit this code path when device.type is | |
| 166 // MEDIA_TAB_AUDIO_CAPTURE and the device id is not a device that | |
| 167 // the AudioManager can know about. This currently does not fail because | |
| 168 // the implementation of GetInputStreamParameters returns valid parameters | |
| 169 // by default for invalid devices. That behavior is problematic because it | |
| 170 // causes other parts of the code to attempt to open truly invalid or | |
| 171 // missing devices and falling back on alternate devices (and likely fail | |
| 172 // twice in a row). Tab audio capture should not pass through here and | |
| 173 // GetInputStreamParameters should return invalid parameters for invalid | |
| 174 // devices. | |
| 175 | |
| 176 // Get the preferred sample rate and channel configuration for the | |
| 177 // audio device. | |
| 178 media::AudioParameters params = | |
| 179 audio_manager_->GetInputStreamParameters(device.id); | |
| 180 input_params.sample_rate = params.sample_rate(); | |
| 181 input_params.channel_layout = params.channel_layout(); | |
| 182 input_params.frames_per_buffer = params.frames_per_buffer(); | |
| 183 input_params.effects = params.effects(); | |
| 184 input_params.mic_positions = params.mic_positions(); | |
| 185 if (!out.device.matched_output_device_id.empty()) { | |
| 186 params = audio_manager_->GetOutputStreamParameters( | |
| 187 out.device.matched_output_device_id); | |
| 188 MediaStreamDevice::AudioDeviceParameters& matched_output_params = | |
| 189 out.device.matched_output; | |
| 190 matched_output_params.sample_rate = params.sample_rate(); | |
| 191 matched_output_params.channel_layout = params.channel_layout(); | |
| 192 matched_output_params.frames_per_buffer = params.frames_per_buffer(); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 // Return the |session_id| through the listener by posting a task on | |
| 197 // IO thread since MediaStreamManager handles the callback asynchronously. | |
| 198 BrowserThread::PostTask(BrowserThread::IO, | |
| 199 FROM_HERE, | |
| 200 base::Bind(&AudioInputDeviceManager::OpenedOnIOThread, | |
| 201 this, session_id, out)); | |
| 202 } | |
| 203 | |
| 204 void AudioInputDeviceManager::OpenedOnIOThread(int session_id, | 317 void AudioInputDeviceManager::OpenedOnIOThread(int session_id, |
| 205 const StreamDeviceInfo& info) { | 318 const StreamDeviceInfo& info) { |
| 206 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 319 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 207 DCHECK_EQ(session_id, info.session_id); | 320 DCHECK_EQ(session_id, info.session_id); |
| 208 DCHECK(GetDevice(session_id) == devices_.end()); | 321 DCHECK(GetDevice(session_id) == devices_.end()); |
| 209 | 322 |
| 210 devices_.push_back(info); | 323 devices_.push_back(info); |
| 211 | 324 |
| 212 for (auto& listener : listeners_) | 325 for (auto& listener : listeners_) |
| 213 listener.Opened(info.device.type, session_id); | 326 listener.Opened(info.device.type, session_id); |
| 214 } | 327 } |
| 215 | 328 |
| 216 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type, | 329 void AudioInputDeviceManager::ClosedOnIOThread(MediaStreamType stream_type, |
| 217 int session_id) { | 330 int session_id) { |
| 218 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 331 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 219 for (auto& listener : listeners_) | 332 for (auto& listener : listeners_) |
| 220 listener.Closed(stream_type, session_id); | 333 listener.Closed(stream_type, session_id); |
| 221 } | 334 } |
| 222 | 335 |
| 223 bool AudioInputDeviceManager::IsOnDeviceThread() const { | |
| 224 return device_task_runner_->BelongsToCurrentThread(); | |
| 225 } | |
| 226 | |
| 227 AudioInputDeviceManager::StreamDeviceList::iterator | 336 AudioInputDeviceManager::StreamDeviceList::iterator |
| 228 AudioInputDeviceManager::GetDevice(int session_id) { | 337 AudioInputDeviceManager::GetDevice(int session_id) { |
| 229 for (StreamDeviceList::iterator i(devices_.begin()); i != devices_.end(); | 338 for (StreamDeviceList::iterator i(devices_.begin()); i != devices_.end(); |
| 230 ++i) { | 339 ++i) { |
| 231 if (i->session_id == session_id) | 340 if (i->session_id == session_id) |
| 232 return i; | 341 return i; |
| 233 } | 342 } |
| 234 | 343 |
| 235 return devices_.end(); | 344 return devices_.end(); |
| 236 } | 345 } |
| 237 | 346 |
| 238 #if defined(OS_CHROMEOS) | 347 #if defined(OS_CHROMEOS) |
| 239 void AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread( | 348 void AudioInputDeviceManager::SetKeyboardMicStreamActiveOnUIThread( |
| 240 bool active) { | 349 bool active) { |
| 241 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 350 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 242 chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(active); | 351 chromeos::CrasAudioHandler::Get()->SetKeyboardMicActive(active); |
| 243 } | 352 } |
| 244 #endif | 353 #endif |
| 245 | 354 |
| 246 | |
| 247 } // namespace content | 355 } // namespace content |
| OLD | NEW |