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 HRESULT __stdcall OnDefaultDeviceChanged( |
| 47 EDataFlow flow, |
| 48 ERole role, |
| 49 LPCWSTR pwstrDefaultDevice) override { |
| 50 base::AutoLock lock(lock_); |
| 51 default_audio_device_changed_ = true; |
| 52 return S_OK; |
| 53 } |
| 54 |
| 55 HRESULT __stdcall QueryInterface(REFIID iid, void** object) override { |
| 56 if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) { |
| 57 *object = static_cast<IMMNotificationClient*>(this); |
| 58 return S_OK; |
| 59 } |
| 60 *object = nullptr; |
| 61 return E_NOINTERFACE; |
| 62 } |
| 63 |
| 64 // No Ops overrides. |
| 65 HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override { |
| 66 return S_OK; |
| 67 } |
| 68 HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override { |
| 69 return S_OK; |
| 70 } |
| 71 HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId, |
| 72 DWORD dwNewState) override { |
| 73 return S_OK; |
| 74 } |
| 75 HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId, |
| 76 const PROPERTYKEY key) override { |
| 77 return S_OK; |
| 78 } |
| 79 ULONG __stdcall AddRef() override { return 1; } |
| 80 ULONG __stdcall Release() override { return 1; } |
| 81 |
| 82 bool GetAndResetDefaultAudioDeviceChanged() { |
| 83 base::AutoLock lock(lock_); |
| 84 if (default_audio_device_changed_) { |
| 85 default_audio_device_changed_ = false; |
| 86 return true; |
| 87 } |
| 88 return false; |
| 89 } |
| 90 |
| 91 private: |
| 92 // |lock_| musted be locked when accessing |default_audio_device_changed_|. |
| 93 bool default_audio_device_changed_ = false; |
| 94 base::Lock lock_; |
| 95 }; |
| 96 |
43 AudioCapturerWin::AudioCapturerWin() | 97 AudioCapturerWin::AudioCapturerWin() |
44 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), | 98 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), |
45 silence_detector_(kSilenceThreshold), | 99 silence_detector_(kSilenceThreshold), |
| 100 mm_notification_client_(new MMNotificationClient()), |
46 last_capture_error_(S_OK) { | 101 last_capture_error_(S_OK) { |
47 thread_checker_.DetachFromThread(); | 102 thread_checker_.DetachFromThread(); |
48 } | 103 } |
49 | 104 |
50 AudioCapturerWin::~AudioCapturerWin() { | 105 AudioCapturerWin::~AudioCapturerWin() { |
51 DCHECK(thread_checker_.CalledOnValidThread()); | 106 DCHECK(thread_checker_.CalledOnValidThread()); |
52 if (audio_client_) { | 107 if (audio_client_) { |
53 audio_client_->Stop(); | 108 audio_client_->Stop(); |
54 } | 109 } |
55 } | 110 } |
56 | 111 |
57 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { | 112 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { |
| 113 callback_ = callback; |
| 114 |
| 115 if (!Initialize()) { |
| 116 return false; |
| 117 } |
| 118 |
| 119 // Initialize the capture timer and start capturing. Note, this timer won't |
| 120 // be reset or restarted in ResetAndInitialize() function. Which means we |
| 121 // expect the audio_device_period_ is a system wide configuration, it would |
| 122 // not be changed with the default audio device. |
| 123 capture_timer_.reset(new base::RepeatingTimer()); |
| 124 capture_timer_->Start(FROM_HERE, audio_device_period_, this, |
| 125 &AudioCapturerWin::DoCapture); |
| 126 return true; |
| 127 } |
| 128 |
| 129 bool AudioCapturerWin::ResetAndInitialize() { |
| 130 Deinitialize(); |
| 131 if (!Initialize()) { |
| 132 Deinitialize(); |
| 133 return false; |
| 134 } |
| 135 return true; |
| 136 } |
| 137 |
| 138 void AudioCapturerWin::Deinitialize() { |
| 139 DCHECK(thread_checker_.CalledOnValidThread()); |
| 140 wave_format_ex_.Reset(nullptr); |
| 141 mm_device_enumerator_.Release(); |
| 142 audio_capture_client_.Release(); |
| 143 audio_client_.Release(); |
| 144 mm_device_.Release(); |
| 145 audio_volume_.Release(); |
| 146 } |
| 147 |
| 148 bool AudioCapturerWin::Initialize() { |
58 DCHECK(!audio_capture_client_.get()); | 149 DCHECK(!audio_capture_client_.get()); |
59 DCHECK(!audio_client_.get()); | 150 DCHECK(!audio_client_.get()); |
60 DCHECK(!mm_device_.get()); | 151 DCHECK(!mm_device_.get()); |
61 DCHECK(!audio_volume_.get()); | 152 DCHECK(!audio_volume_.get()); |
62 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); | 153 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); |
63 DCHECK(thread_checker_.CalledOnValidThread()); | 154 DCHECK(thread_checker_.CalledOnValidThread()); |
64 | 155 |
65 callback_ = callback; | |
66 | |
67 // Initialize the capture timer. | |
68 capture_timer_.reset(new base::RepeatingTimer()); | |
69 | |
70 HRESULT hr = S_OK; | 156 HRESULT hr = S_OK; |
71 | 157 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)) { | 158 if (FAILED(hr)) { |
75 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; | 159 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; |
76 return false; | 160 return false; |
77 } | 161 } |
78 | 162 |
| 163 hr = mm_device_enumerator_->RegisterEndpointNotificationCallback( |
| 164 mm_notification_client_.get()); |
| 165 if (FAILED(hr)) { |
| 166 // We cannot predict which kind of error the API may return, but this is |
| 167 // not a fatal error. |
| 168 LOG(ERROR) << "Failed to register IMMNotificationClient. Error " << hr; |
| 169 } |
| 170 |
79 // Get the audio endpoint. | 171 // Get the audio endpoint. |
80 hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender, | 172 hr = mm_device_enumerator_->GetDefaultAudioEndpoint(eRender, eConsole, |
81 eConsole, | 173 mm_device_.Receive()); |
82 mm_device_.Receive()); | |
83 if (FAILED(hr)) { | 174 if (FAILED(hr)) { |
84 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; | 175 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; |
85 return false; | 176 return false; |
86 } | 177 } |
87 | 178 |
88 // Get an audio client. | 179 // Get an audio client. |
89 hr = mm_device_->Activate(__uuidof(IAudioClient), | 180 hr = mm_device_->Activate(__uuidof(IAudioClient), |
90 CLSCTX_ALL, | 181 CLSCTX_ALL, |
91 nullptr, | 182 nullptr, |
92 audio_client_.ReceiveVoid()); | 183 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? | 293 // TODO(zijiehe): Do we need to control per process volume? |
203 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, | 294 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, |
204 audio_volume_.ReceiveVoid()); | 295 audio_volume_.ReceiveVoid()); |
205 if (FAILED(hr)) { | 296 if (FAILED(hr)) { |
206 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr; | 297 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr; |
207 return false; | 298 return false; |
208 } | 299 } |
209 | 300 |
210 silence_detector_.Reset(sampling_rate_, kChannels); | 301 silence_detector_.Reset(sampling_rate_, kChannels); |
211 | 302 |
212 // Start capturing. | |
213 capture_timer_->Start(FROM_HERE, | |
214 audio_device_period_, | |
215 this, | |
216 &AudioCapturerWin::DoCapture); | |
217 return true; | 303 return true; |
218 } | 304 } |
219 | 305 |
| 306 bool AudioCapturerWin::is_initialized() const { |
| 307 // All Com components should be initialized / deinitialized together. |
| 308 return !!audio_volume_; |
| 309 } |
| 310 |
220 float AudioCapturerWin::GetAudioLevel() { | 311 float AudioCapturerWin::GetAudioLevel() { |
221 BOOL mute; | 312 BOOL mute; |
222 HRESULT hr = audio_volume_->GetMute(&mute); | 313 HRESULT hr = audio_volume_->GetMute(&mute); |
223 if (FAILED(hr)) { | 314 if (FAILED(hr)) { |
224 LOG(ERROR) << "Failed to get mute status from IAudioEndpointVolume, error " | 315 LOG(ERROR) << "Failed to get mute status from IAudioEndpointVolume, error " |
225 << hr; | 316 << hr; |
226 return 1; | 317 return 1; |
227 } | 318 } |
228 if (mute) { | 319 if (mute) { |
229 return 0; | 320 return 0; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | 368 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); |
278 packet->set_channels(AudioPacket::CHANNELS_STEREO); | 369 packet->set_channels(AudioPacket::CHANNELS_STEREO); |
279 | 370 |
280 callback_.Run(std::move(packet)); | 371 callback_.Run(std::move(packet)); |
281 } | 372 } |
282 | 373 |
283 void AudioCapturerWin::DoCapture() { | 374 void AudioCapturerWin::DoCapture() { |
284 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); | 375 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); |
285 DCHECK(thread_checker_.CalledOnValidThread()); | 376 DCHECK(thread_checker_.CalledOnValidThread()); |
286 | 377 |
| 378 if (!is_initialized() || |
| 379 mm_notification_client_->GetAndResetDefaultAudioDeviceChanged()) { |
| 380 if (!ResetAndInitialize()) { |
| 381 // Initialization failed, we should wait for next DoCapture call. |
| 382 return; |
| 383 } |
| 384 } |
| 385 |
287 // Fetch all packets from the audio capture endpoint buffer. | 386 // Fetch all packets from the audio capture endpoint buffer. |
288 HRESULT hr = S_OK; | 387 HRESULT hr = S_OK; |
289 while (true) { | 388 while (true) { |
290 UINT32 next_packet_size; | 389 UINT32 next_packet_size; |
291 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); | 390 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); |
292 if (FAILED(hr)) | 391 if (FAILED(hr)) |
293 break; | 392 break; |
294 | 393 |
295 if (next_packet_size <= 0) { | 394 if (next_packet_size <= 0) { |
296 return; | 395 return; |
(...skipping 29 matching lines...) Expand all Loading... |
326 | 425 |
327 bool AudioCapturer::IsSupported() { | 426 bool AudioCapturer::IsSupported() { |
328 return true; | 427 return true; |
329 } | 428 } |
330 | 429 |
331 std::unique_ptr<AudioCapturer> AudioCapturer::Create() { | 430 std::unique_ptr<AudioCapturer> AudioCapturer::Create() { |
332 return base::WrapUnique(new AudioCapturerWin()); | 431 return base::WrapUnique(new AudioCapturerWin()); |
333 } | 432 } |
334 | 433 |
335 } // namespace remoting | 434 } // namespace remoting |
OLD | NEW |