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 "remoting/host/audio_capturer_win.h" | 5 #include "remoting/host/audio_capturer_win.h" |
| 6 | 6 |
| 7 #include <avrt.h> | 7 #include <avrt.h> |
| 8 #include <mmreg.h> | 8 #include <mmreg.h> |
| 9 #include <mmsystem.h> | 9 #include <mmsystem.h> |
| 10 #include <stdint.h> | 10 #include <stdint.h> |
| 11 #include <stdlib.h> | 11 #include <stdlib.h> |
| 12 #include <windows.h> | 12 #include <windows.h> |
| 13 | 13 |
| 14 #include <algorithm> | 14 #include <algorithm> |
| 15 #include <utility> | 15 #include <utility> |
| 16 | 16 |
| 17 #include "base/logging.h" | 17 #include "base/logging.h" |
| 18 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
| 19 #include "base/synchronization/lock.h" | |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 const int kChannels = 2; | 22 const int kChannels = 2; |
| 22 const int kBytesPerSample = 2; | 23 const int kBytesPerSample = 2; |
| 23 const int kBitsPerSample = kBytesPerSample * 8; | 24 const int kBitsPerSample = kBytesPerSample * 8; |
| 24 // Conversion factor from 100ns to 1ms. | 25 // Conversion factor from 100ns to 1ms. |
| 25 const int k100nsPerMillisecond = 10000; | 26 const int k100nsPerMillisecond = 10000; |
| 26 | 27 |
| 27 // Tolerance for catching packets of silence. If all samples have absolute | 28 // Tolerance for catching packets of silence. If all samples have absolute |
| 28 // value less than this threshold, the packet will be counted as a packet of | 29 // value less than this threshold, the packet will be counted as a packet of |
| 29 // silence. A value of 2 was chosen, because Windows can give samples of 1 and | 30 // silence. A value of 2 was chosen, because Windows can give samples of 1 and |
| 30 // -1, even when no audio is playing. | 31 // -1, even when no audio is playing. |
| 31 const int kSilenceThreshold = 2; | 32 const int kSilenceThreshold = 2; |
| 32 | 33 |
| 33 // Lower bound for timer intervals, in milliseconds. | 34 // Lower bound for timer intervals, in milliseconds. |
| 34 const int kMinTimerInterval = 30; | 35 const int kMinTimerInterval = 30; |
| 35 | 36 |
| 36 // Upper bound for the timer precision error, in milliseconds. | 37 // Upper bound for the timer precision error, in milliseconds. |
| 37 // Timers are supposed to be accurate to 20ms, so we use 30ms to be safe. | 38 // Timers are supposed to be accurate to 20ms, so we use 30ms to be safe. |
| 38 const int kMaxExpectedTimerLag = 30; | 39 const int kMaxExpectedTimerLag = 30; |
| 39 } // namespace | 40 } // namespace |
| 40 | 41 |
| 41 namespace remoting { | 42 namespace remoting { |
| 42 | 43 |
| 44 class AudioCapturerWin::MMNotificationClient : public IMMNotificationClient { | |
| 45 public: | |
| 46 MMNotificationClient(AudioCapturerWin* owner) | |
|
Sergey Ulanov
2016/07/22 17:38:37
owner_ is not needed anymore. Remove it?
Hzj_jie
2016/07/22 21:03:11
Yep, sorry.
| |
| 47 : owner_(owner) { | |
| 48 DCHECK(owner_); | |
| 49 } | |
| 50 | |
| 51 HRESULT __stdcall OnDefaultDeviceChanged( | |
| 52 EDataFlow flow, | |
| 53 ERole role, | |
| 54 LPCWSTR pwstrDefaultDevice) override { | |
| 55 SetDefaultAudioDeviceChanged(); | |
| 56 return S_OK; | |
| 57 } | |
| 58 | |
| 59 HRESULT __stdcall QueryInterface(REFIID iid, void** object) override { | |
| 60 if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) { | |
| 61 *object = static_cast<IMMNotificationClient*>(this); | |
| 62 return S_OK; | |
| 63 } | |
| 64 *object = nullptr; | |
| 65 return E_NOINTERFACE; | |
| 66 } | |
| 67 | |
| 68 // No Ops overrides. | |
| 69 HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override { | |
| 70 return S_OK; | |
| 71 } | |
| 72 HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override { | |
| 73 return S_OK; | |
| 74 } | |
| 75 HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId, | |
| 76 DWORD dwNewState) override { | |
| 77 return S_OK; | |
| 78 } | |
| 79 HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId, | |
| 80 const PROPERTYKEY key) override { | |
| 81 return S_OK; | |
| 82 } | |
| 83 ULONG __stdcall AddRef() override { return 1; } | |
| 84 ULONG __stdcall Release() override { return 1; } | |
| 85 | |
| 86 bool GetAndResetDefaultAudioDeviceChanged() { | |
| 87 base::AutoLock lock(lock_); | |
| 88 if (default_audio_device_changed_) { | |
| 89 default_audio_device_changed_ = false; | |
| 90 return true; | |
| 91 } | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 void SetDefaultAudioDeviceChanged() { | |
| 96 base::AutoLock lock(lock_); | |
| 97 default_audio_device_changed_ = true; | |
| 98 } | |
| 99 | |
| 100 private: | |
| 101 AudioCapturerWin* const owner_; | |
| 102 bool default_audio_device_changed_ = false; // GUARDED_BY(lock_) | |
|
Sergey Ulanov
2016/07/22 17:38:37
replace GUARDED_BY with a comment (e.g. "|lock_| m
Hzj_jie
2016/07/22 21:03:11
Done.
| |
| 103 base::Lock lock_; | |
| 104 }; | |
| 105 | |
| 43 AudioCapturerWin::AudioCapturerWin() | 106 AudioCapturerWin::AudioCapturerWin() |
| 44 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), | 107 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), |
| 45 silence_detector_(kSilenceThreshold), | 108 silence_detector_(kSilenceThreshold), |
| 109 mm_notification_client_(new MMNotificationClient(this)), | |
| 46 last_capture_error_(S_OK) { | 110 last_capture_error_(S_OK) { |
| 47 thread_checker_.DetachFromThread(); | 111 thread_checker_.DetachFromThread(); |
| 48 } | 112 } |
| 49 | 113 |
| 50 AudioCapturerWin::~AudioCapturerWin() { | 114 AudioCapturerWin::~AudioCapturerWin() { |
| 51 DCHECK(thread_checker_.CalledOnValidThread()); | 115 DCHECK(thread_checker_.CalledOnValidThread()); |
| 52 if (audio_client_) { | 116 if (audio_client_) { |
| 53 audio_client_->Stop(); | 117 audio_client_->Stop(); |
| 54 } | 118 } |
| 55 } | 119 } |
| 56 | 120 |
| 57 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { | 121 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { |
| 58 DCHECK(!audio_capture_client_.get()); | 122 DCHECK(!audio_capture_client_.get()); |
| 59 DCHECK(!audio_client_.get()); | 123 DCHECK(!audio_client_.get()); |
| 60 DCHECK(!mm_device_.get()); | 124 DCHECK(!mm_device_.get()); |
| 61 DCHECK(!audio_volume_.get()); | 125 DCHECK(!audio_volume_.get()); |
| 62 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); | 126 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); |
| 63 DCHECK(thread_checker_.CalledOnValidThread()); | 127 DCHECK(thread_checker_.CalledOnValidThread()); |
| 64 | 128 |
| 65 callback_ = callback; | 129 callback_ = callback; |
| 66 | 130 |
| 67 // Initialize the capture timer. | 131 if (!ResetAndInitialize()) { |
| 132 return false; | |
| 133 } | |
| 134 | |
| 135 // Initialize the capture timer and start capturing. | |
| 68 capture_timer_.reset(new base::RepeatingTimer()); | 136 capture_timer_.reset(new base::RepeatingTimer()); |
| 137 capture_timer_->Start(FROM_HERE, audio_device_period_, this, | |
| 138 &AudioCapturerWin::DoCapture); | |
| 139 return true; | |
| 140 } | |
| 141 | |
| 142 bool AudioCapturerWin::ResetAndInitialize() { | |
| 143 wave_format_ex_.Reset(nullptr); | |
| 144 mm_device_enumerator_.Release(); | |
| 145 audio_capture_client_.Release(); | |
| 146 audio_client_.Release(); | |
| 147 mm_device_.Release(); | |
| 148 audio_volume_.Release(); | |
| 69 | 149 |
| 70 HRESULT hr = S_OK; | 150 HRESULT hr = S_OK; |
| 71 | 151 hr = mm_device_enumerator_.CreateInstance(__uuidof(MMDeviceEnumerator)); |
| 72 base::win::ScopedComPtr<IMMDeviceEnumerator> mm_device_enumerator; | |
| 73 hr = mm_device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator)); | |
| 74 if (FAILED(hr)) { | 152 if (FAILED(hr)) { |
| 75 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; | 153 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; |
| 76 return false; | 154 return false; |
| 77 } | 155 } |
| 78 | 156 |
| 157 hr = mm_device_enumerator_->RegisterEndpointNotificationCallback( | |
| 158 mm_notification_client_.get()); | |
| 159 if (FAILED(hr)) { | |
| 160 // We cannot predict which kind of error the API may return, but this is | |
| 161 // not a fatal error. | |
| 162 LOG(ERROR) << "Failed to register IMMNotificationClient. Error " << hr; | |
| 163 } | |
| 164 | |
| 79 // Get the audio endpoint. | 165 // Get the audio endpoint. |
| 80 hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender, | 166 hr = mm_device_enumerator_->GetDefaultAudioEndpoint(eRender, eConsole, |
| 81 eConsole, | 167 mm_device_.Receive()); |
| 82 mm_device_.Receive()); | |
| 83 if (FAILED(hr)) { | 168 if (FAILED(hr)) { |
| 84 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; | 169 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; |
| 85 return false; | 170 return false; |
| 86 } | 171 } |
| 87 | 172 |
| 88 // Get an audio client. | 173 // Get an audio client. |
| 89 hr = mm_device_->Activate(__uuidof(IAudioClient), | 174 hr = mm_device_->Activate(__uuidof(IAudioClient), |
| 90 CLSCTX_ALL, | 175 CLSCTX_ALL, |
| 91 nullptr, | 176 nullptr, |
| 92 audio_client_.ReceiveVoid()); | 177 audio_client_.ReceiveVoid()); |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 202 // TODO(zijiehe): Do we need to control per process volume? | 287 // TODO(zijiehe): Do we need to control per process volume? |
| 203 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, | 288 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, |
| 204 audio_volume_.ReceiveVoid()); | 289 audio_volume_.ReceiveVoid()); |
| 205 if (FAILED(hr)) { | 290 if (FAILED(hr)) { |
| 206 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr; | 291 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr; |
| 207 return false; | 292 return false; |
| 208 } | 293 } |
| 209 | 294 |
| 210 silence_detector_.Reset(sampling_rate_, kChannels); | 295 silence_detector_.Reset(sampling_rate_, kChannels); |
| 211 | 296 |
| 212 // Start capturing. | |
| 213 capture_timer_->Start(FROM_HERE, | |
| 214 audio_device_period_, | |
| 215 this, | |
| 216 &AudioCapturerWin::DoCapture); | |
| 217 return true; | 297 return true; |
| 218 } | 298 } |
| 219 | 299 |
| 220 float AudioCapturerWin::GetAudioLevel() { | 300 float AudioCapturerWin::GetAudioLevel() { |
| 221 BOOL mute; | 301 BOOL mute; |
| 222 HRESULT hr = audio_volume_->GetMute(&mute); | 302 HRESULT hr = audio_volume_->GetMute(&mute); |
| 223 if (FAILED(hr)) { | 303 if (FAILED(hr)) { |
| 224 LOG(ERROR) << "Failed to get mute status from IAudioEndpointVolume, error " | 304 LOG(ERROR) << "Failed to get mute status from IAudioEndpointVolume, error " |
| 225 << hr; | 305 << hr; |
| 226 return 1; | 306 return 1; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 277 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | 357 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); |
| 278 packet->set_channels(AudioPacket::CHANNELS_STEREO); | 358 packet->set_channels(AudioPacket::CHANNELS_STEREO); |
| 279 | 359 |
| 280 callback_.Run(std::move(packet)); | 360 callback_.Run(std::move(packet)); |
| 281 } | 361 } |
| 282 | 362 |
| 283 void AudioCapturerWin::DoCapture() { | 363 void AudioCapturerWin::DoCapture() { |
| 284 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); | 364 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); |
| 285 DCHECK(thread_checker_.CalledOnValidThread()); | 365 DCHECK(thread_checker_.CalledOnValidThread()); |
| 286 | 366 |
| 367 if (mm_notification_client_->GetAndResetDefaultAudioDeviceChanged() && | |
| 368 !ResetAndInitialize()) { | |
|
Sergey Ulanov
2016/07/22 17:38:37
This expression is hard to read: it's not obvious
Hzj_jie
2016/07/22 21:03:11
Done.
| |
| 369 // Initialization failed, we should wait for next DoCapture call. | |
|
Sergey Ulanov
2016/07/22 17:38:37
When DoCapture() is called again this code won't c
Hzj_jie
2016/07/22 21:03:11
No, SetDefaultAudioDeviceChanged in line 370 will
Sergey Ulanov
2016/07/22 21:29:27
I see. It seems error-prone to piggy-back on that
| |
| 370 mm_notification_client_->SetDefaultAudioDeviceChanged(); | |
| 371 return; | |
| 372 } | |
| 373 | |
| 287 // Fetch all packets from the audio capture endpoint buffer. | 374 // Fetch all packets from the audio capture endpoint buffer. |
| 288 HRESULT hr = S_OK; | 375 HRESULT hr = S_OK; |
| 289 while (true) { | 376 while (true) { |
| 290 UINT32 next_packet_size; | 377 UINT32 next_packet_size; |
| 291 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); | 378 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); |
| 292 if (FAILED(hr)) | 379 if (FAILED(hr)) |
| 293 break; | 380 break; |
| 294 | 381 |
| 295 if (next_packet_size <= 0) { | 382 if (next_packet_size <= 0) { |
| 296 return; | 383 return; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 326 | 413 |
| 327 bool AudioCapturer::IsSupported() { | 414 bool AudioCapturer::IsSupported() { |
| 328 return true; | 415 return true; |
| 329 } | 416 } |
| 330 | 417 |
| 331 std::unique_ptr<AudioCapturer> AudioCapturer::Create() { | 418 std::unique_ptr<AudioCapturer> AudioCapturer::Create() { |
| 332 return base::WrapUnique(new AudioCapturerWin()); | 419 return base::WrapUnique(new AudioCapturerWin()); |
| 333 } | 420 } |
| 334 | 421 |
| 335 } // namespace remoting | 422 } // namespace remoting |
| OLD | NEW |