Remove dependency on content from remoting_host.
[chromium-blink-merge.git] / remoting / host / audio_capturer_win.cc
blobf5d71390193b32f4f5d89a2c7e1f37f7c74ebd82
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
3 // found in the LICENSE file.
5 #include "remoting/host/audio_capturer_win.h"
7 #include <windows.h>
8 #include <avrt.h>
9 #include <mmreg.h>
10 #include <mmsystem.h>
12 #include <algorithm>
13 #include <stdlib.h>
15 #include "base/logging.h"
17 namespace {
18 const int kChannels = 2;
19 const int kBytesPerSample = 2;
20 const int kBitsPerSample = kBytesPerSample * 8;
21 // Conversion factor from 100ns to 1ms.
22 const int k100nsPerMillisecond = 10000;
24 // Tolerance for catching packets of silence. If all samples have absolute
25 // value less than this threshold, the packet will be counted as a packet of
26 // silence. A value of 2 was chosen, because Windows can give samples of 1 and
27 // -1, even when no audio is playing.
28 const int kSilenceThreshold = 2;
30 // Lower bound for timer intervals, in milliseconds.
31 const int kMinTimerInterval = 30;
33 // Upper bound for the timer precision error, in milliseconds.
34 // Timers are supposed to be accurate to 20ms, so we use 30ms to be safe.
35 const int kMaxExpectedTimerLag = 30;
36 } // namespace
38 namespace remoting {
40 AudioCapturerWin::AudioCapturerWin()
41 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID),
42 silence_detector_(kSilenceThreshold),
43 last_capture_error_(S_OK) {
44 thread_checker_.DetachFromThread();
47 AudioCapturerWin::~AudioCapturerWin() {
48 DCHECK(thread_checker_.CalledOnValidThread());
51 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) {
52 DCHECK(!audio_capture_client_.get());
53 DCHECK(!audio_client_.get());
54 DCHECK(!mm_device_.get());
55 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr);
56 DCHECK(thread_checker_.CalledOnValidThread());
58 callback_ = callback;
60 // Initialize the capture timer.
61 capture_timer_.reset(new base::RepeatingTimer<AudioCapturerWin>());
63 HRESULT hr = S_OK;
65 base::win::ScopedComPtr<IMMDeviceEnumerator> mm_device_enumerator;
66 hr = mm_device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator));
67 if (FAILED(hr)) {
68 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr;
69 return false;
72 // Get the audio endpoint.
73 hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender,
74 eConsole,
75 mm_device_.Receive());
76 if (FAILED(hr)) {
77 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr;
78 return false;
81 // Get an audio client.
82 hr = mm_device_->Activate(__uuidof(IAudioClient),
83 CLSCTX_ALL,
84 nullptr,
85 audio_client_.ReceiveVoid());
86 if (FAILED(hr)) {
87 LOG(ERROR) << "Failed to get an IAudioClient. Error " << hr;
88 return false;
91 REFERENCE_TIME device_period;
92 hr = audio_client_->GetDevicePeriod(&device_period, nullptr);
93 if (FAILED(hr)) {
94 LOG(ERROR) << "IAudioClient::GetDevicePeriod failed. Error " << hr;
95 return false;
97 // We round up, if |device_period| / |k100nsPerMillisecond|
98 // is not a whole number.
99 int device_period_in_milliseconds =
100 1 + ((device_period - 1) / k100nsPerMillisecond);
101 audio_device_period_ = base::TimeDelta::FromMilliseconds(
102 std::max(device_period_in_milliseconds, kMinTimerInterval));
104 // Get the wave format.
105 hr = audio_client_->GetMixFormat(&wave_format_ex_);
106 if (FAILED(hr)) {
107 LOG(ERROR) << "Failed to get WAVEFORMATEX. Error " << hr;
108 return false;
111 // Set the wave format
112 switch (wave_format_ex_->wFormatTag) {
113 case WAVE_FORMAT_IEEE_FLOAT:
114 // Intentional fall-through.
115 case WAVE_FORMAT_PCM:
116 if (!AudioCapturer::IsValidSampleRate(wave_format_ex_->nSamplesPerSec)) {
117 LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
118 return false;
120 sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
121 wave_format_ex_->nSamplesPerSec);
123 wave_format_ex_->wFormatTag = WAVE_FORMAT_PCM;
124 wave_format_ex_->nChannels = kChannels;
125 wave_format_ex_->wBitsPerSample = kBitsPerSample;
126 wave_format_ex_->nBlockAlign = kChannels * kBytesPerSample;
127 wave_format_ex_->nAvgBytesPerSec =
128 sampling_rate_ * kChannels * kBytesPerSample;
129 break;
130 case WAVE_FORMAT_EXTENSIBLE: {
131 PWAVEFORMATEXTENSIBLE wave_format_extensible =
132 reinterpret_cast<WAVEFORMATEXTENSIBLE*>(
133 static_cast<WAVEFORMATEX*>(wave_format_ex_));
134 if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
135 wave_format_extensible->SubFormat)) {
136 if (!AudioCapturer::IsValidSampleRate(
137 wave_format_extensible->Format.nSamplesPerSec)) {
138 LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
139 return false;
141 sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
142 wave_format_extensible->Format.nSamplesPerSec);
144 wave_format_extensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
145 wave_format_extensible->Samples.wValidBitsPerSample = kBitsPerSample;
147 wave_format_extensible->Format.nChannels = kChannels;
148 wave_format_extensible->Format.nSamplesPerSec = sampling_rate_;
149 wave_format_extensible->Format.wBitsPerSample = kBitsPerSample;
150 wave_format_extensible->Format.nBlockAlign =
151 kChannels * kBytesPerSample;
152 wave_format_extensible->Format.nAvgBytesPerSec =
153 sampling_rate_ * kChannels * kBytesPerSample;
154 } else {
155 LOG(ERROR) << "Failed to force 16-bit samples";
156 return false;
158 break;
160 default:
161 LOG(ERROR) << "Failed to force 16-bit PCM";
162 return false;
165 // Initialize the IAudioClient.
166 hr = audio_client_->Initialize(
167 AUDCLNT_SHAREMODE_SHARED,
168 AUDCLNT_STREAMFLAGS_LOOPBACK,
169 (kMaxExpectedTimerLag + audio_device_period_.InMilliseconds()) *
170 k100nsPerMillisecond,
172 wave_format_ex_,
173 nullptr);
174 if (FAILED(hr)) {
175 LOG(ERROR) << "Failed to initialize IAudioClient. Error " << hr;
176 return false;
179 // Get an IAudioCaptureClient.
180 hr = audio_client_->GetService(__uuidof(IAudioCaptureClient),
181 audio_capture_client_.ReceiveVoid());
182 if (FAILED(hr)) {
183 LOG(ERROR) << "Failed to get an IAudioCaptureClient. Error " << hr;
184 return false;
187 // Start the IAudioClient.
188 hr = audio_client_->Start();
189 if (FAILED(hr)) {
190 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr;
191 return false;
194 silence_detector_.Reset(sampling_rate_, kChannels);
196 // Start capturing.
197 capture_timer_->Start(FROM_HERE,
198 audio_device_period_,
199 this,
200 &AudioCapturerWin::DoCapture);
201 return true;
204 void AudioCapturerWin::DoCapture() {
205 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_));
206 DCHECK(thread_checker_.CalledOnValidThread());
208 // Fetch all packets from the audio capture endpoint buffer.
209 HRESULT hr = S_OK;
210 while (true) {
211 UINT32 next_packet_size;
212 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size);
213 if (FAILED(hr))
214 break;
216 if (next_packet_size <= 0) {
217 return;
220 BYTE* data;
221 UINT32 frames;
222 DWORD flags;
223 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr,
224 nullptr);
225 if (FAILED(hr))
226 break;
228 if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0 &&
229 !silence_detector_.IsSilence(
230 reinterpret_cast<const int16*>(data), frames * kChannels)) {
231 scoped_ptr<AudioPacket> packet(new AudioPacket());
232 packet->add_data(data, frames * wave_format_ex_->nBlockAlign);
233 packet->set_encoding(AudioPacket::ENCODING_RAW);
234 packet->set_sampling_rate(sampling_rate_);
235 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
236 packet->set_channels(AudioPacket::CHANNELS_STEREO);
238 callback_.Run(packet.Pass());
241 hr = audio_capture_client_->ReleaseBuffer(frames);
242 if (FAILED(hr))
243 break;
246 // There is nothing to capture if the audio endpoint device has been unplugged
247 // or disabled.
248 if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
249 return;
251 // Avoid reporting the same error multiple times.
252 if (FAILED(hr) && hr != last_capture_error_) {
253 last_capture_error_ = hr;
254 LOG(ERROR) << "Failed to capture an audio packet: 0x"
255 << std::hex << hr << std::dec << ".";
259 bool AudioCapturer::IsSupported() {
260 return true;
263 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
264 return make_scoped_ptr(new AudioCapturerWin());
267 } // namespace remoting