Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: remoting/host/audio_capturer_win.cc

Issue 2163473002: [Chromoting] Audio playback won't work after switching default playback device (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Resolve review comments Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/host/audio_capturer_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « remoting/host/audio_capturer_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698