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> |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 // Lower bound for timer intervals, in milliseconds. | 33 // Lower bound for timer intervals, in milliseconds. |
| 34 const int kMinTimerInterval = 30; | 34 const int kMinTimerInterval = 30; |
| 35 | 35 |
| 36 // Upper bound for the timer precision error, in milliseconds. | 36 // 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. | 37 // Timers are supposed to be accurate to 20ms, so we use 30ms to be safe. |
| 38 const int kMaxExpectedTimerLag = 30; | 38 const int kMaxExpectedTimerLag = 30; |
| 39 } // namespace | 39 } // namespace |
| 40 | 40 |
| 41 namespace remoting { | 41 namespace remoting { |
| 42 | 42 |
| 43 class AudioCapturerWin::MMNotificationClient : public IMMNotificationClient { | |
|
Sergey Ulanov
2016/07/18 23:09:24
Do we need this as a separate class? Maybe just im
Hzj_jie
2016/07/19 02:36:39
Then AudioCapturerWin will export a set of unrelat
| |
| 44 public: | |
| 45 MMNotificationClient(AudioCapturerWin* owner); | |
| 46 | |
| 47 HRESULT __stdcall OnDefaultDeviceChanged(EDataFlow flow, | |
| 48 ERole role, | |
| 49 LPCWSTR pwstrDefaultDevice) override; | |
| 50 | |
| 51 HRESULT __stdcall QueryInterface(REFIID iid, void** object) override; | |
| 52 | |
| 53 // No Ops overrides. | |
| 54 HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override { | |
| 55 return S_OK; } | |
|
Sergey Ulanov
2016/07/18 23:09:24
here and below: formatting
Hzj_jie
2016/07/19 02:36:39
Done.
| |
| 56 HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override { | |
| 57 return S_OK; } | |
| 58 HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId, | |
| 59 DWORD dwNewState) override { | |
| 60 return S_OK; } | |
| 61 HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId, | |
| 62 const PROPERTYKEY key) override { | |
| 63 return S_OK; } | |
| 64 ULONG __stdcall AddRef() override { return 1; } | |
| 65 ULONG __stdcall Release() override { return 1; } | |
| 66 | |
| 67 private: | |
| 68 AudioCapturerWin* owner_; | |
| 69 }; | |
| 70 | |
| 71 AudioCapturerWin::MMNotificationClient::MMNotificationClient( | |
| 72 AudioCapturerWin* owner) : owner_(owner) { | |
| 73 DCHECK(owner_); | |
| 74 } | |
| 75 | |
| 76 HRESULT AudioCapturerWin::MMNotificationClient::OnDefaultDeviceChanged( | |
|
Sergey Ulanov
2016/07/18 23:09:24
Which thread is this called on?
Hzj_jie
2016/07/19 02:36:40
Totally no idea, no document about it on MSDN.
Sergey Ulanov
2016/07/20 17:28:34
According to comments in audio/win/audio_device_li
Hzj_jie
2016/07/20 20:43:14
Assignment of a bool value is atomic, so the worst
Sergey Ulanov
2016/07/21 18:54:01
No types are atomic in C++ by default. You cannot
Hzj_jie
2016/07/21 23:38:50
Done.
Emmm, I believe the way I am using here is
| |
| 77 EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDevice) { | |
|
Sergey Ulanov
2016/07/18 23:09:24
One argument per line please
git cl format
Hzj_jie
2016/07/19 02:36:40
Done.
| |
| 78 owner_->default_audio_device_changed_ = true; | |
| 79 return S_OK; | |
| 80 } | |
| 81 | |
| 82 HRESULT AudioCapturerWin::MMNotificationClient::QueryInterface( | |
| 83 REFIID iid, void** object) { | |
| 84 if (iid == IID_IUnknown || iid == __uuidof(IMMNotificationClient)) { | |
| 85 *object = static_cast<IMMNotificationClient*>(this); | |
| 86 return S_OK; | |
| 87 } | |
| 88 *object = nullptr; | |
| 89 return E_NOINTERFACE; | |
| 90 } | |
| 91 | |
| 43 AudioCapturerWin::AudioCapturerWin() | 92 AudioCapturerWin::AudioCapturerWin() |
| 44 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), | 93 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), |
| 45 silence_detector_(kSilenceThreshold), | 94 silence_detector_(kSilenceThreshold), |
| 95 default_audio_device_changed_(false), | |
| 96 notificator_(new MMNotificationClient(this)), | |
|
Sergey Ulanov
2016/07/18 23:09:24
I don't think that |notificator_| is the right nam
Hzj_jie
2016/07/19 02:36:40
Done.
| |
| 46 last_capture_error_(S_OK) { | 97 last_capture_error_(S_OK) { |
| 47 thread_checker_.DetachFromThread(); | 98 thread_checker_.DetachFromThread(); |
| 48 } | 99 } |
| 49 | 100 |
| 50 AudioCapturerWin::~AudioCapturerWin() { | 101 AudioCapturerWin::~AudioCapturerWin() { |
| 51 DCHECK(thread_checker_.CalledOnValidThread()); | 102 DCHECK(thread_checker_.CalledOnValidThread()); |
| 103 | |
| 104 HRESULT hr = mm_device_enumerator_->UnregisterEndpointNotificationCallback( | |
| 105 notificator_.get()); | |
| 106 if (FAILED(hr)) { | |
| 107 LOG(ERROR) << "Failed to unregister notificator. Error " << hr; | |
| 108 } | |
| 109 | |
| 52 if (audio_client_) { | 110 if (audio_client_) { |
| 53 audio_client_->Stop(); | 111 audio_client_->Stop(); |
| 54 } | 112 } |
| 55 } | 113 } |
| 56 | 114 |
| 57 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { | 115 bool AudioCapturerWin::Initialize() { |
|
Sergey Ulanov
2016/07/18 23:09:24
Call this ResetAndInitialize() as it doesn't just
Hzj_jie
2016/07/19 02:36:40
Done.
| |
| 58 DCHECK(!audio_capture_client_.get()); | 116 default_audio_device_changed_ = false; |
| 59 DCHECK(!audio_client_.get()); | 117 wave_format_ex_.Reset(nullptr); |
| 60 DCHECK(!mm_device_.get()); | 118 mm_device_enumerator_.Release(); |
| 61 DCHECK(!audio_volume_.get()); | 119 audio_capture_client_.Release(); |
| 62 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); | 120 audio_client_.Release(); |
| 63 DCHECK(thread_checker_.CalledOnValidThread()); | 121 mm_device_.Release(); |
| 64 | 122 audio_volume_.Release(); |
| 65 callback_ = callback; | |
| 66 | |
| 67 // Initialize the capture timer. | |
| 68 capture_timer_.reset(new base::RepeatingTimer()); | |
| 69 | 123 |
| 70 HRESULT hr = S_OK; | 124 HRESULT hr = S_OK; |
| 71 | 125 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)) { | 126 if (FAILED(hr)) { |
| 75 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; | 127 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; |
| 76 return false; | 128 return false; |
| 77 } | 129 } |
| 78 | 130 |
| 131 hr = mm_device_enumerator_->RegisterEndpointNotificationCallback( | |
|
Sergey Ulanov
2016/07/18 23:09:24
Do we also need to unregister the callback when ca
Hzj_jie
2016/07/19 02:36:40
Done.
| |
| 132 notificator_.get()); | |
| 133 if (FAILED(hr)) { | |
| 134 // We cannot predict which kind of error the API may return, but this is | |
| 135 // not a fatal error. | |
| 136 LOG(ERROR) << "Failed to register notificator. Error " << hr; | |
| 137 } | |
| 138 | |
| 79 // Get the audio endpoint. | 139 // Get the audio endpoint. |
| 80 hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender, | 140 hr = mm_device_enumerator_->GetDefaultAudioEndpoint(eRender, |
| 81 eConsole, | 141 eConsole, |
| 82 mm_device_.Receive()); | 142 mm_device_.Receive()); |
| 83 if (FAILED(hr)) { | 143 if (FAILED(hr)) { |
| 84 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; | 144 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; |
| 85 return false; | 145 return false; |
| 86 } | 146 } |
| 87 | 147 |
| 88 // Get an audio client. | 148 // Get an audio client. |
| 89 hr = mm_device_->Activate(__uuidof(IAudioClient), | 149 hr = mm_device_->Activate(__uuidof(IAudioClient), |
| 90 CLSCTX_ALL, | 150 CLSCTX_ALL, |
| 91 nullptr, | 151 nullptr, |
| 92 audio_client_.ReceiveVoid()); | 152 audio_client_.ReceiveVoid()); |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 | 253 |
| 194 // Start the IAudioClient. | 254 // Start the IAudioClient. |
| 195 hr = audio_client_->Start(); | 255 hr = audio_client_->Start(); |
| 196 if (FAILED(hr)) { | 256 if (FAILED(hr)) { |
| 197 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; | 257 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; |
| 198 return false; | 258 return false; |
| 199 } | 259 } |
| 200 | 260 |
| 201 // Initialize IAudioEndpointVolume. | 261 // Initialize IAudioEndpointVolume. |
| 202 // TODO(zijiehe): Do we need to control per process volume? | 262 // TODO(zijiehe): Do we need to control per process volume? |
| 203 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr, | 263 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), |
| 264 CLSCTX_ALL, | |
| 265 nullptr, | |
| 204 audio_volume_.ReceiveVoid()); | 266 audio_volume_.ReceiveVoid()); |
| 205 if (FAILED(hr)) { | 267 if (FAILED(hr)) { |
| 206 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr; | 268 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr; |
| 207 return false; | 269 return false; |
| 208 } | 270 } |
| 209 | 271 |
| 210 silence_detector_.Reset(sampling_rate_, kChannels); | 272 silence_detector_.Reset(sampling_rate_, kChannels); |
| 211 | 273 |
| 274 return true; | |
| 275 } | |
| 276 | |
| 277 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { | |
|
Sergey Ulanov
2016/07/18 23:09:24
Move this before initialize, so the order of metho
Hzj_jie
2016/07/19 02:36:39
Done.
| |
| 278 DCHECK(!audio_capture_client_.get()); | |
| 279 DCHECK(!audio_client_.get()); | |
| 280 DCHECK(!mm_device_.get()); | |
| 281 DCHECK(!audio_volume_.get()); | |
| 282 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); | |
| 283 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 284 | |
| 285 callback_ = callback; | |
| 286 | |
| 287 // Initialize the capture timer. | |
| 288 capture_timer_.reset(new base::RepeatingTimer()); | |
|
Sergey Ulanov
2016/07/18 23:09:24
nit: move this below, next to the capture_timer_->
Hzj_jie
2016/07/19 02:36:40
Done.
| |
| 289 | |
| 290 if (!Initialize()) { | |
| 291 return false; | |
| 292 } | |
| 293 | |
| 212 // Start capturing. | 294 // Start capturing. |
| 213 capture_timer_->Start(FROM_HERE, | 295 capture_timer_->Start(FROM_HERE, |
| 214 audio_device_period_, | 296 audio_device_period_, |
| 215 this, | 297 this, |
| 216 &AudioCapturerWin::DoCapture); | 298 &AudioCapturerWin::DoCapture); |
| 217 return true; | 299 return true; |
| 218 } | 300 } |
| 219 | 301 |
| 220 float AudioCapturerWin::GetAudioLevel() { | 302 float AudioCapturerWin::GetAudioLevel() { |
| 221 BOOL mute; | 303 BOOL mute; |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 277 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | 359 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); |
| 278 packet->set_channels(AudioPacket::CHANNELS_STEREO); | 360 packet->set_channels(AudioPacket::CHANNELS_STEREO); |
| 279 | 361 |
| 280 callback_.Run(std::move(packet)); | 362 callback_.Run(std::move(packet)); |
| 281 } | 363 } |
| 282 | 364 |
| 283 void AudioCapturerWin::DoCapture() { | 365 void AudioCapturerWin::DoCapture() { |
| 284 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); | 366 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); |
| 285 DCHECK(thread_checker_.CalledOnValidThread()); | 367 DCHECK(thread_checker_.CalledOnValidThread()); |
| 286 | 368 |
| 369 if (default_audio_device_changed_ && !Initialize()) { | |
| 370 // Initialization failed, we should wait for next DoCapture call. | |
| 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 |