0279135e0830b5d9da3d05e874d18ec29e07ebbe
[platform/framework/web/crosswalk.git] / src / remoting / host / audio_capturer_win.cc
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.
4
5 #include "remoting/host/audio_capturer_win.h"
6
7 #include <windows.h>
8 #include <avrt.h>
9 #include <mmreg.h>
10 #include <mmsystem.h>
11
12 #include <algorithm>
13 #include <stdlib.h>
14
15 #include "base/logging.h"
16
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;
23
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;
29
30 // Lower bound for timer intervals, in milliseconds.
31 const int kMinTimerInterval = 30;
32
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
37
38 namespace remoting {
39
40 AudioCapturerWin::AudioCapturerWin()
41     : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID),
42       silence_detector_(kSilenceThreshold),
43       last_capture_error_(S_OK) {
44     thread_checker_.DetachFromThread();
45 }
46
47 AudioCapturerWin::~AudioCapturerWin() {
48 }
49
50 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) {
51   DCHECK(!audio_capture_client_.get());
52   DCHECK(!audio_client_.get());
53   DCHECK(!mm_device_.get());
54   DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == NULL);
55   DCHECK(thread_checker_.CalledOnValidThread());
56
57   callback_ = callback;
58
59   // Initialize the capture timer.
60   capture_timer_.reset(new base::RepeatingTimer<AudioCapturerWin>());
61
62   HRESULT hr = S_OK;
63
64   base::win::ScopedComPtr<IMMDeviceEnumerator> mm_device_enumerator;
65   hr = mm_device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator));
66   if (FAILED(hr)) {
67     LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr;
68     return false;
69   }
70
71   // Get the audio endpoint.
72   hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender,
73                                                      eConsole,
74                                                      mm_device_.Receive());
75   if (FAILED(hr)) {
76     LOG(ERROR) << "Failed to get IMMDevice. Error " << hr;
77     return false;
78   }
79
80   // Get an audio client.
81   hr = mm_device_->Activate(__uuidof(IAudioClient),
82                             CLSCTX_ALL,
83                             NULL,
84                             audio_client_.ReceiveVoid());
85   if (FAILED(hr)) {
86     LOG(ERROR) << "Failed to get an IAudioClient. Error " << hr;
87     return false;
88   }
89
90   REFERENCE_TIME device_period;
91   hr = audio_client_->GetDevicePeriod(&device_period, NULL);
92   if (FAILED(hr)) {
93     LOG(ERROR) << "IAudioClient::GetDevicePeriod failed. Error " << hr;
94     return false;
95   }
96   // We round up, if |device_period| / |k100nsPerMillisecond|
97   // is not a whole number.
98   int device_period_in_milliseconds =
99       1 + ((device_period - 1) / k100nsPerMillisecond);
100   audio_device_period_ = base::TimeDelta::FromMilliseconds(
101       std::max(device_period_in_milliseconds, kMinTimerInterval));
102
103   // Get the wave format.
104   hr = audio_client_->GetMixFormat(&wave_format_ex_);
105   if (FAILED(hr)) {
106     LOG(ERROR) << "Failed to get WAVEFORMATEX. Error " << hr;
107     return false;
108   }
109
110   // Set the wave format
111   switch (wave_format_ex_->wFormatTag) {
112     case WAVE_FORMAT_IEEE_FLOAT:
113       // Intentional fall-through.
114     case WAVE_FORMAT_PCM:
115       if (!AudioCapturer::IsValidSampleRate(wave_format_ex_->nSamplesPerSec)) {
116         LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
117         return false;
118       }
119       sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
120           wave_format_ex_->nSamplesPerSec);
121
122       wave_format_ex_->wFormatTag = WAVE_FORMAT_PCM;
123       wave_format_ex_->nChannels = kChannels;
124       wave_format_ex_->wBitsPerSample = kBitsPerSample;
125       wave_format_ex_->nBlockAlign = kChannels * kBytesPerSample;
126       wave_format_ex_->nAvgBytesPerSec =
127           sampling_rate_ * kChannels * kBytesPerSample;
128       break;
129     case WAVE_FORMAT_EXTENSIBLE: {
130       PWAVEFORMATEXTENSIBLE wave_format_extensible =
131           reinterpret_cast<WAVEFORMATEXTENSIBLE*>(
132           static_cast<WAVEFORMATEX*>(wave_format_ex_));
133       if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
134                       wave_format_extensible->SubFormat)) {
135         if (!AudioCapturer::IsValidSampleRate(
136                 wave_format_extensible->Format.nSamplesPerSec)) {
137           LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
138           return false;
139         }
140         sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
141             wave_format_extensible->Format.nSamplesPerSec);
142
143         wave_format_extensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
144         wave_format_extensible->Samples.wValidBitsPerSample = kBitsPerSample;
145
146         wave_format_extensible->Format.nChannels = kChannels;
147         wave_format_extensible->Format.nSamplesPerSec = sampling_rate_;
148         wave_format_extensible->Format.wBitsPerSample = kBitsPerSample;
149         wave_format_extensible->Format.nBlockAlign =
150             kChannels * kBytesPerSample;
151         wave_format_extensible->Format.nAvgBytesPerSec =
152             sampling_rate_ * kChannels * kBytesPerSample;
153       } else {
154         LOG(ERROR) << "Failed to force 16-bit samples";
155         return false;
156       }
157       break;
158     }
159     default:
160       LOG(ERROR) << "Failed to force 16-bit PCM";
161       return false;
162   }
163
164   // Initialize the IAudioClient.
165   hr = audio_client_->Initialize(
166       AUDCLNT_SHAREMODE_SHARED,
167       AUDCLNT_STREAMFLAGS_LOOPBACK,
168       (kMaxExpectedTimerLag + audio_device_period_.InMilliseconds()) *
169       k100nsPerMillisecond,
170       0,
171       wave_format_ex_,
172       NULL);
173   if (FAILED(hr)) {
174     LOG(ERROR) << "Failed to initialize IAudioClient. Error " << hr;
175     return false;
176   }
177
178   // Get an IAudioCaptureClient.
179   hr = audio_client_->GetService(__uuidof(IAudioCaptureClient),
180                                  audio_capture_client_.ReceiveVoid());
181   if (FAILED(hr)) {
182     LOG(ERROR) << "Failed to get an IAudioCaptureClient. Error " << hr;
183     return false;
184   }
185
186   // Start the IAudioClient.
187   hr = audio_client_->Start();
188   if (FAILED(hr)) {
189     LOG(ERROR) << "Failed to start IAudioClient. Error " << hr;
190     return false;
191   }
192
193   silence_detector_.Reset(sampling_rate_, kChannels);
194
195   // Start capturing.
196   capture_timer_->Start(FROM_HERE,
197                         audio_device_period_,
198                         this,
199                         &AudioCapturerWin::DoCapture);
200   return true;
201 }
202
203 void AudioCapturerWin::Stop() {
204   DCHECK(thread_checker_.CalledOnValidThread());
205   DCHECK(IsStarted());
206
207   capture_timer_.reset();
208   mm_device_.Release();
209   audio_client_.Release();
210   audio_capture_client_.Release();
211   wave_format_ex_.Reset(NULL);
212
213   thread_checker_.DetachFromThread();
214 }
215
216 bool AudioCapturerWin::IsStarted() {
217   DCHECK(thread_checker_.CalledOnValidThread());
218   return capture_timer_.get() != NULL;
219 }
220
221 void AudioCapturerWin::DoCapture() {
222   DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_));
223   DCHECK(thread_checker_.CalledOnValidThread());
224   DCHECK(IsStarted());
225
226   // Fetch all packets from the audio capture endpoint buffer.
227   HRESULT hr = S_OK;
228   while (true) {
229     UINT32 next_packet_size;
230     HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size);
231     if (FAILED(hr))
232       break;
233
234     if (next_packet_size <= 0) {
235       return;
236     }
237
238     BYTE* data;
239     UINT32 frames;
240     DWORD flags;
241     hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, NULL, NULL);
242     if (FAILED(hr))
243       break;
244
245     if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0 &&
246         !silence_detector_.IsSilence(
247             reinterpret_cast<const int16*>(data), frames * kChannels)) {
248       scoped_ptr<AudioPacket> packet(new AudioPacket());
249       packet->add_data(data, frames * wave_format_ex_->nBlockAlign);
250       packet->set_encoding(AudioPacket::ENCODING_RAW);
251       packet->set_sampling_rate(sampling_rate_);
252       packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
253       packet->set_channels(AudioPacket::CHANNELS_STEREO);
254
255       callback_.Run(packet.Pass());
256     }
257
258     hr = audio_capture_client_->ReleaseBuffer(frames);
259     if (FAILED(hr))
260       break;
261   }
262
263   // There is nothing to capture if the audio endpoint device has been unplugged
264   // or disabled.
265   if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
266     return;
267
268   // Avoid reporting the same error multiple times.
269   if (FAILED(hr) && hr != last_capture_error_) {
270     last_capture_error_ = hr;
271     LOG(ERROR) << "Failed to capture an audio packet: 0x"
272                << std::hex << hr << std::dec << ".";
273   }
274 }
275
276 bool AudioCapturer::IsSupported() {
277   return true;
278 }
279
280 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
281   return make_scoped_ptr(new AudioCapturerWin());
282 }
283
284 }  // namespace remoting