blob: 70bc0b030302e9b5ea6199856d49874bf7c3dc56 [file] [log] [blame]
[email protected]c9bdcbd2012-07-19 23:35:541// 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
3// found in the LICENSE file.
4
[email protected]91d3a7d2012-08-22 00:56:555#include "remoting/host/audio_capturer_win.h"
6
[email protected]c9bdcbd2012-07-19 23:35:547#include <avrt.h>
[email protected]c9bdcbd2012-07-19 23:35:548#include <mmreg.h>
9#include <mmsystem.h>
avic5960f32015-12-22 22:49:4810#include <stdint.h>
[email protected]07cb35f2012-07-31 23:10:1011#include <stdlib.h>
sergeyu1417e0132015-12-23 19:01:2212#include <windows.h>
13
avic5960f32015-12-22 22:49:4814#include <algorithm>
sergeyu1417e0132015-12-23 19:01:2215#include <utility>
[email protected]07cb35f2012-07-31 23:10:1016
[email protected]c9bdcbd2012-07-19 23:35:5417#include "base/logging.h"
dcheng0765c492016-04-06 22:41:5318#include "base/memory/ptr_util.h"
zijieheb40182a2016-07-24 19:28:1519#include "base/synchronization/lock.h"
[email protected]c9bdcbd2012-07-19 23:35:5420
21namespace {
22const int kChannels = 2;
[email protected]881de5e2013-01-02 23:11:1423const int kBytesPerSample = 2;
24const int kBitsPerSample = kBytesPerSample * 8;
[email protected]c9bdcbd2012-07-19 23:35:5425// Conversion factor from 100ns to 1ms.
[email protected]37a770b02012-08-17 00:13:1726const int k100nsPerMillisecond = 10000;
[email protected]07cb35f2012-07-31 23:10:1027
28// Tolerance for catching packets of silence. If all samples have absolute
29// value less than this threshold, the packet will be counted as a packet of
30// silence. A value of 2 was chosen, because Windows can give samples of 1 and
31// -1, even when no audio is playing.
32const int kSilenceThreshold = 2;
[email protected]37a770b02012-08-17 00:13:1733
34// Lower bound for timer intervals, in milliseconds.
35const int kMinTimerInterval = 30;
36
37// Upper bound for the timer precision error, in milliseconds.
38// Timers are supposed to be accurate to 20ms, so we use 30ms to be safe.
39const int kMaxExpectedTimerLag = 30;
[email protected]c9bdcbd2012-07-19 23:35:5440} // namespace
41
42namespace remoting {
43
zijieheb40182a2016-07-24 19:28:1544class 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
[email protected]ca1488912012-07-27 17:19:0497AudioCapturerWin::AudioCapturerWin()
[email protected]881de5e2013-01-02 23:11:1498 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID),
[email protected]a21883d2013-03-16 09:14:5199 silence_detector_(kSilenceThreshold),
zijieheb40182a2016-07-24 19:28:15100 mm_notification_client_(new MMNotificationClient()),
[email protected]a21883d2013-03-16 09:14:51101 last_capture_error_(S_OK) {
zijieheb40182a2016-07-24 19:28:15102 thread_checker_.DetachFromThread();
[email protected]c9bdcbd2012-07-19 23:35:54103}
104
105AudioCapturerWin::~AudioCapturerWin() {
sergeyu7bc87602015-02-13 20:33:28106 DCHECK(thread_checker_.CalledOnValidThread());
zijiehefc96e4f2016-04-11 22:29:56107 if (audio_client_) {
108 audio_client_->Stop();
109 }
[email protected]c9bdcbd2012-07-19 23:35:54110}
111
[email protected]339abeca2012-07-20 18:28:24112bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) {
zijieheb40182a2016-07-24 19:28:15113 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
129bool AudioCapturerWin::ResetAndInitialize() {
130 Deinitialize();
131 if (!Initialize()) {
132 Deinitialize();
133 return false;
134 }
135 return true;
136}
137
138void AudioCapturerWin::Deinitialize() {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 wave_format_ex_.Reset(nullptr);
robliao9212cb42017-04-06 17:43:39141 mm_device_enumerator_.Reset();
142 audio_capture_client_.Reset();
143 audio_client_.Reset();
144 mm_device_.Reset();
145 audio_volume_.Reset();
zijieheb40182a2016-07-24 19:28:15146}
147
148bool AudioCapturerWin::Initialize() {
robliaob44b9072017-04-19 00:28:09149 DCHECK(!audio_capture_client_.Get());
150 DCHECK(!audio_client_.Get());
151 DCHECK(!mm_device_.Get());
152 DCHECK(!audio_volume_.Get());
sergeyuc5f104b2015-01-09 19:33:24153 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr);
[email protected]c9bdcbd2012-07-19 23:35:54154 DCHECK(thread_checker_.CalledOnValidThread());
155
[email protected]c9bdcbd2012-07-19 23:35:54156 HRESULT hr = S_OK;
zijieheb40182a2016-07-24 19:28:15157 hr = mm_device_enumerator_.CreateInstance(__uuidof(MMDeviceEnumerator));
[email protected]c9bdcbd2012-07-19 23:35:54158 if (FAILED(hr)) {
159 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24160 return false;
[email protected]c9bdcbd2012-07-19 23:35:54161 }
162
zijieheb40182a2016-07-24 19:28:15163 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
[email protected]c9bdcbd2012-07-19 23:35:54171 // Get the audio endpoint.
zijieheb40182a2016-07-24 19:28:15172 hr = mm_device_enumerator_->GetDefaultAudioEndpoint(eRender, eConsole,
173 mm_device_.Receive());
[email protected]c9bdcbd2012-07-19 23:35:54174 if (FAILED(hr)) {
175 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24176 return false;
[email protected]c9bdcbd2012-07-19 23:35:54177 }
178
179 // Get an audio client.
180 hr = mm_device_->Activate(__uuidof(IAudioClient),
181 CLSCTX_ALL,
sergeyuc5f104b2015-01-09 19:33:24182 nullptr,
[email protected]c9bdcbd2012-07-19 23:35:54183 audio_client_.ReceiveVoid());
184 if (FAILED(hr)) {
185 LOG(ERROR) << "Failed to get an IAudioClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24186 return false;
[email protected]c9bdcbd2012-07-19 23:35:54187 }
188
189 REFERENCE_TIME device_period;
sergeyuc5f104b2015-01-09 19:33:24190 hr = audio_client_->GetDevicePeriod(&device_period, nullptr);
[email protected]c9bdcbd2012-07-19 23:35:54191 if (FAILED(hr)) {
192 LOG(ERROR) << "IAudioClient::GetDevicePeriod failed. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24193 return false;
[email protected]c9bdcbd2012-07-19 23:35:54194 }
[email protected]37a770b02012-08-17 00:13:17195 // We round up, if |device_period| / |k100nsPerMillisecond|
196 // is not a whole number.
197 int device_period_in_milliseconds =
198 1 + ((device_period - 1) / k100nsPerMillisecond);
[email protected]c9bdcbd2012-07-19 23:35:54199 audio_device_period_ = base::TimeDelta::FromMilliseconds(
[email protected]37a770b02012-08-17 00:13:17200 std::max(device_period_in_milliseconds, kMinTimerInterval));
[email protected]c9bdcbd2012-07-19 23:35:54201
202 // Get the wave format.
203 hr = audio_client_->GetMixFormat(&wave_format_ex_);
204 if (FAILED(hr)) {
205 LOG(ERROR) << "Failed to get WAVEFORMATEX. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24206 return false;
[email protected]c9bdcbd2012-07-19 23:35:54207 }
208
209 // Set the wave format
210 switch (wave_format_ex_->wFormatTag) {
211 case WAVE_FORMAT_IEEE_FLOAT:
212 // Intentional fall-through.
213 case WAVE_FORMAT_PCM:
[email protected]ca1488912012-07-27 17:19:04214 if (!AudioCapturer::IsValidSampleRate(wave_format_ex_->nSamplesPerSec)) {
215 LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
216 return false;
217 }
218 sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
219 wave_format_ex_->nSamplesPerSec);
220
[email protected]c9bdcbd2012-07-19 23:35:54221 wave_format_ex_->wFormatTag = WAVE_FORMAT_PCM;
222 wave_format_ex_->nChannels = kChannels;
[email protected]c9bdcbd2012-07-19 23:35:54223 wave_format_ex_->wBitsPerSample = kBitsPerSample;
[email protected]881de5e2013-01-02 23:11:14224 wave_format_ex_->nBlockAlign = kChannels * kBytesPerSample;
[email protected]c9bdcbd2012-07-19 23:35:54225 wave_format_ex_->nAvgBytesPerSec =
[email protected]881de5e2013-01-02 23:11:14226 sampling_rate_ * kChannels * kBytesPerSample;
[email protected]c9bdcbd2012-07-19 23:35:54227 break;
228 case WAVE_FORMAT_EXTENSIBLE: {
229 PWAVEFORMATEXTENSIBLE wave_format_extensible =
230 reinterpret_cast<WAVEFORMATEXTENSIBLE*>(
231 static_cast<WAVEFORMATEX*>(wave_format_ex_));
232 if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
233 wave_format_extensible->SubFormat)) {
[email protected]ca1488912012-07-27 17:19:04234 if (!AudioCapturer::IsValidSampleRate(
235 wave_format_extensible->Format.nSamplesPerSec)) {
236 LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
237 return false;
238 }
239 sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
240 wave_format_extensible->Format.nSamplesPerSec);
241
[email protected]c9bdcbd2012-07-19 23:35:54242 wave_format_extensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
243 wave_format_extensible->Samples.wValidBitsPerSample = kBitsPerSample;
244
245 wave_format_extensible->Format.nChannels = kChannels;
[email protected]ca1488912012-07-27 17:19:04246 wave_format_extensible->Format.nSamplesPerSec = sampling_rate_;
[email protected]c9bdcbd2012-07-19 23:35:54247 wave_format_extensible->Format.wBitsPerSample = kBitsPerSample;
248 wave_format_extensible->Format.nBlockAlign =
[email protected]881de5e2013-01-02 23:11:14249 kChannels * kBytesPerSample;
[email protected]c9bdcbd2012-07-19 23:35:54250 wave_format_extensible->Format.nAvgBytesPerSec =
[email protected]881de5e2013-01-02 23:11:14251 sampling_rate_ * kChannels * kBytesPerSample;
[email protected]c9bdcbd2012-07-19 23:35:54252 } else {
253 LOG(ERROR) << "Failed to force 16-bit samples";
[email protected]339abeca2012-07-20 18:28:24254 return false;
[email protected]c9bdcbd2012-07-19 23:35:54255 }
256 break;
257 }
258 default:
[email protected]ca1488912012-07-27 17:19:04259 LOG(ERROR) << "Failed to force 16-bit PCM";
[email protected]339abeca2012-07-20 18:28:24260 return false;
[email protected]c9bdcbd2012-07-19 23:35:54261 }
262
263 // Initialize the IAudioClient.
[email protected]37a770b02012-08-17 00:13:17264 hr = audio_client_->Initialize(
265 AUDCLNT_SHAREMODE_SHARED,
266 AUDCLNT_STREAMFLAGS_LOOPBACK,
267 (kMaxExpectedTimerLag + audio_device_period_.InMilliseconds()) *
268 k100nsPerMillisecond,
269 0,
270 wave_format_ex_,
sergeyuc5f104b2015-01-09 19:33:24271 nullptr);
[email protected]c9bdcbd2012-07-19 23:35:54272 if (FAILED(hr)) {
273 LOG(ERROR) << "Failed to initialize IAudioClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24274 return false;
[email protected]c9bdcbd2012-07-19 23:35:54275 }
276
277 // Get an IAudioCaptureClient.
278 hr = audio_client_->GetService(__uuidof(IAudioCaptureClient),
279 audio_capture_client_.ReceiveVoid());
280 if (FAILED(hr)) {
281 LOG(ERROR) << "Failed to get an IAudioCaptureClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24282 return false;
[email protected]c9bdcbd2012-07-19 23:35:54283 }
284
285 // Start the IAudioClient.
286 hr = audio_client_->Start();
287 if (FAILED(hr)) {
288 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24289 return false;
[email protected]c9bdcbd2012-07-19 23:35:54290 }
291
zijiehefc96e4f2016-04-11 22:29:56292 // Initialize IAudioEndpointVolume.
zijiehe3ba26c002016-03-10 20:49:24293 // TODO(zijiehe): Do we need to control per process volume?
zijiehefc96e4f2016-04-11 22:29:56294 hr = mm_device_->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, nullptr,
295 audio_volume_.ReceiveVoid());
zijiehe3ba26c002016-03-10 20:49:24296 if (FAILED(hr)) {
zijiehefc96e4f2016-04-11 22:29:56297 LOG(ERROR) << "Failed to get an IAudioEndpointVolume. Error " << hr;
zijiehe3ba26c002016-03-10 20:49:24298 return false;
299 }
300
[email protected]881de5e2013-01-02 23:11:14301 silence_detector_.Reset(sampling_rate_, kChannels);
302
[email protected]339abeca2012-07-20 18:28:24303 return true;
[email protected]c9bdcbd2012-07-19 23:35:54304}
305
zijieheb40182a2016-07-24 19:28:15306bool AudioCapturerWin::is_initialized() const {
307 // All Com components should be initialized / deinitialized together.
308 return !!audio_volume_;
309}
310
zijiehe3ba26c002016-03-10 20:49:24311float AudioCapturerWin::GetAudioLevel() {
312 BOOL mute;
313 HRESULT hr = audio_volume_->GetMute(&mute);
314 if (FAILED(hr)) {
zijiehefc96e4f2016-04-11 22:29:56315 LOG(ERROR) << "Failed to get mute status from IAudioEndpointVolume, error "
316 << hr;
zijiehe3ba26c002016-03-10 20:49:24317 return 1;
318 }
319 if (mute) {
320 return 0;
321 }
322
323 float level;
zijiehefc96e4f2016-04-11 22:29:56324 hr = audio_volume_->GetMasterVolumeLevelScalar(&level);
zijiehe3ba26c002016-03-10 20:49:24325 if (FAILED(hr) || level > 1) {
zijiehefc96e4f2016-04-11 22:29:56326 LOG(ERROR) << "Failed to get master volume from IAudioEndpointVolume, "
327 "error "
328 << hr;
zijiehe3ba26c002016-03-10 20:49:24329 return 1;
330 }
331 if (level < 0) {
332 return 0;
333 }
334 return level;
335}
336
zijiehefc96e4f2016-04-11 22:29:56337void AudioCapturerWin::ProcessSamples(uint8_t* data, size_t frames) {
zijiehe3ba26c002016-03-10 20:49:24338 if (frames == 0) {
339 return;
340 }
341
zijiehe3ba26c002016-03-10 20:49:24342 int16_t* samples = reinterpret_cast<int16_t*>(data);
343 static_assert(sizeof(samples[0]) == kBytesPerSample,
344 "expect 16 bits per sample");
345 size_t sample_count = frames * kChannels;
346 if (silence_detector_.IsSilence(samples, sample_count)) {
347 return;
348 }
349
zijiehefc96e4f2016-04-11 22:29:56350 // Windows API does not provide volume adjusted audio sample as Linux does.
351 // So we need to manually apply volume to the samples.
zijiehe3ba26c002016-03-10 20:49:24352 float level = GetAudioLevel();
353 if (level == 0) {
354 return;
355 }
356
357 if (level < 1) {
zijiehe3ba26c002016-03-10 20:49:24358 int32_t level_int = static_cast<int32_t>(level * 65536);
359 for (size_t i = 0; i < sample_count; i++) {
360 samples[i] = (static_cast<int32_t>(samples[i]) * level_int) >> 16;
361 }
362 }
363
dcheng0765c492016-04-06 22:41:53364 std::unique_ptr<AudioPacket> packet(new AudioPacket());
zijiehe3ba26c002016-03-10 20:49:24365 packet->add_data(data, frames * wave_format_ex_->nBlockAlign);
366 packet->set_encoding(AudioPacket::ENCODING_RAW);
367 packet->set_sampling_rate(sampling_rate_);
368 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
369 packet->set_channels(AudioPacket::CHANNELS_STEREO);
370
371 callback_.Run(std::move(packet));
372}
373
[email protected]c9bdcbd2012-07-19 23:35:54374void AudioCapturerWin::DoCapture() {
[email protected]ca1488912012-07-27 17:19:04375 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_));
[email protected]c9bdcbd2012-07-19 23:35:54376 DCHECK(thread_checker_.CalledOnValidThread());
[email protected]c9bdcbd2012-07-19 23:35:54377
zijieheb40182a2016-07-24 19:28:15378 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
[email protected]c9bdcbd2012-07-19 23:35:54386 // Fetch all packets from the audio capture endpoint buffer.
[email protected]a21883d2013-03-16 09:14:51387 HRESULT hr = S_OK;
[email protected]c9bdcbd2012-07-19 23:35:54388 while (true) {
389 UINT32 next_packet_size;
390 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size);
[email protected]a21883d2013-03-16 09:14:51391 if (FAILED(hr))
392 break;
[email protected]c9bdcbd2012-07-19 23:35:54393
394 if (next_packet_size <= 0) {
395 return;
396 }
397
398 BYTE* data;
399 UINT32 frames;
400 DWORD flags;
sergeyuc5f104b2015-01-09 19:33:24401 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr,
402 nullptr);
[email protected]a21883d2013-03-16 09:14:51403 if (FAILED(hr))
404 break;
[email protected]c9bdcbd2012-07-19 23:35:54405
zijiehefc96e4f2016-04-11 22:29:56406 ProcessSamples(data, frames);
[email protected]c9bdcbd2012-07-19 23:35:54407
408 hr = audio_capture_client_->ReleaseBuffer(frames);
[email protected]a21883d2013-03-16 09:14:51409 if (FAILED(hr))
410 break;
411 }
412
413 // There is nothing to capture if the audio endpoint device has been unplugged
414 // or disabled.
415 if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
416 return;
417
418 // Avoid reporting the same error multiple times.
419 if (FAILED(hr) && hr != last_capture_error_) {
420 last_capture_error_ = hr;
421 LOG(ERROR) << "Failed to capture an audio packet: 0x"
422 << std::hex << hr << std::dec << ".";
[email protected]c9bdcbd2012-07-19 23:35:54423 }
424}
425
[email protected]e8aaf2ca2012-09-11 18:07:24426bool AudioCapturer::IsSupported() {
427 return true;
428}
429
dcheng0765c492016-04-06 22:41:53430std::unique_ptr<AudioCapturer> AudioCapturer::Create() {
431 return base::WrapUnique(new AudioCapturerWin());
[email protected]c9bdcbd2012-07-19 23:35:54432}
433
434} // namespace remoting