blob: 8a256454fac9362d4a5774fb85a00c1cc1cd4328 [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
5#include <windows.h>
6#include <audioclient.h>
7#include <avrt.h>
8#include <mmdeviceapi.h>
9#include <mmreg.h>
10#include <mmsystem.h>
11
12#include "base/basictypes.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop.h"
16#include "base/timer.h"
17#include "base/win/scoped_co_mem.h"
18#include "base/win/scoped_com_initializer.h"
19#include "base/win/scoped_comptr.h"
20#include "remoting/host/audio_capturer.h"
21#include "remoting/proto/audio.pb.h"
22
23namespace {
24const int kChannels = 2;
25const int kBitsPerSample = 16;
[email protected]c9bdcbd2012-07-19 23:35:5426const int kBitsPerByte = 8;
27// Conversion factor from 100ns to 1ms.
28const int kHnsToMs = 10000;
29} // namespace
30
31namespace remoting {
32
33class AudioCapturerWin : public AudioCapturer {
34 public:
35 AudioCapturerWin();
36 virtual ~AudioCapturerWin();
37
38 // AudioCapturer interface.
[email protected]339abeca2012-07-20 18:28:2439 virtual bool Start(const PacketCapturedCallback& callback) OVERRIDE;
[email protected]c9bdcbd2012-07-19 23:35:5440 virtual void Stop() OVERRIDE;
41 virtual bool IsRunning() OVERRIDE;
42
43 private:
44 // Receives all packets from the audio capture endpoint buffer and pushes them
45 // to the network.
46 void DoCapture();
47
48 PacketCapturedCallback callback_;
49
[email protected]ca1488912012-07-27 17:19:0450 AudioPacket::SamplingRate sampling_rate_;
51
[email protected]c9bdcbd2012-07-19 23:35:5452 scoped_ptr<base::RepeatingTimer<AudioCapturerWin> > capture_timer_;
53 base::TimeDelta audio_device_period_;
54
55 base::win::ScopedCoMem<WAVEFORMATEX> wave_format_ex_;
56 base::win::ScopedComPtr<IAudioCaptureClient> audio_capture_client_;
57 base::win::ScopedComPtr<IAudioClient> audio_client_;
58 base::win::ScopedComPtr<IMMDevice> mm_device_;
59 scoped_ptr<base::win::ScopedCOMInitializer> com_initializer_;
60
61 base::ThreadChecker thread_checker_;
62
63 DISALLOW_COPY_AND_ASSIGN(AudioCapturerWin);
64};
65
[email protected]ca1488912012-07-27 17:19:0466AudioCapturerWin::AudioCapturerWin()
67 : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID) {
68 thread_checker_.DetachFromThread();
[email protected]c9bdcbd2012-07-19 23:35:5469}
70
71AudioCapturerWin::~AudioCapturerWin() {
72}
73
[email protected]339abeca2012-07-20 18:28:2474bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) {
[email protected]c9bdcbd2012-07-19 23:35:5475 DCHECK(!audio_capture_client_.get());
76 DCHECK(!audio_client_.get());
77 DCHECK(!mm_device_.get());
78 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == NULL);
79 DCHECK(thread_checker_.CalledOnValidThread());
80
81 callback_ = callback;
82
83 // Initialize the capture timer.
84 capture_timer_.reset(new base::RepeatingTimer<AudioCapturerWin>());
85
86 HRESULT hr = S_OK;
87 com_initializer_.reset(new base::win::ScopedCOMInitializer());
88
89 base::win::ScopedComPtr<IMMDeviceEnumerator> mm_device_enumerator;
90 hr = mm_device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator));
91 if (FAILED(hr)) {
92 LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr;
[email protected]339abeca2012-07-20 18:28:2493 return false;
[email protected]c9bdcbd2012-07-19 23:35:5494 }
95
96 // Get the audio endpoint.
97 hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender,
98 eConsole,
99 mm_device_.Receive());
100 if (FAILED(hr)) {
101 LOG(ERROR) << "Failed to get IMMDevice. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24102 return false;
[email protected]c9bdcbd2012-07-19 23:35:54103 }
104
105 // Get an audio client.
106 hr = mm_device_->Activate(__uuidof(IAudioClient),
107 CLSCTX_ALL,
108 NULL,
109 audio_client_.ReceiveVoid());
110 if (FAILED(hr)) {
111 LOG(ERROR) << "Failed to get an IAudioClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24112 return false;
[email protected]c9bdcbd2012-07-19 23:35:54113 }
114
115 REFERENCE_TIME device_period;
116 hr = audio_client_->GetDevicePeriod(&device_period, NULL);
117 if (FAILED(hr)) {
118 LOG(ERROR) << "IAudioClient::GetDevicePeriod failed. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24119 return false;
[email protected]c9bdcbd2012-07-19 23:35:54120 }
121 audio_device_period_ = base::TimeDelta::FromMilliseconds(
122 device_period / kChannels / kHnsToMs);
123
124 // Get the wave format.
125 hr = audio_client_->GetMixFormat(&wave_format_ex_);
126 if (FAILED(hr)) {
127 LOG(ERROR) << "Failed to get WAVEFORMATEX. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24128 return false;
[email protected]c9bdcbd2012-07-19 23:35:54129 }
130
131 // Set the wave format
132 switch (wave_format_ex_->wFormatTag) {
133 case WAVE_FORMAT_IEEE_FLOAT:
134 // Intentional fall-through.
135 case WAVE_FORMAT_PCM:
[email protected]ca1488912012-07-27 17:19:04136 if (!AudioCapturer::IsValidSampleRate(wave_format_ex_->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_ex_->nSamplesPerSec);
142
[email protected]c9bdcbd2012-07-19 23:35:54143 wave_format_ex_->wFormatTag = WAVE_FORMAT_PCM;
144 wave_format_ex_->nChannels = kChannels;
[email protected]c9bdcbd2012-07-19 23:35:54145 wave_format_ex_->wBitsPerSample = kBitsPerSample;
146 wave_format_ex_->nBlockAlign = kChannels * kBitsPerSample / kBitsPerByte;
147 wave_format_ex_->nAvgBytesPerSec =
[email protected]ca1488912012-07-27 17:19:04148 sampling_rate_ * kChannels * kBitsPerSample / kBitsPerByte;
[email protected]c9bdcbd2012-07-19 23:35:54149 break;
150 case WAVE_FORMAT_EXTENSIBLE: {
151 PWAVEFORMATEXTENSIBLE wave_format_extensible =
152 reinterpret_cast<WAVEFORMATEXTENSIBLE*>(
153 static_cast<WAVEFORMATEX*>(wave_format_ex_));
154 if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
155 wave_format_extensible->SubFormat)) {
[email protected]ca1488912012-07-27 17:19:04156 if (!AudioCapturer::IsValidSampleRate(
157 wave_format_extensible->Format.nSamplesPerSec)) {
158 LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
159 return false;
160 }
161 sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
162 wave_format_extensible->Format.nSamplesPerSec);
163
[email protected]c9bdcbd2012-07-19 23:35:54164 wave_format_extensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
165 wave_format_extensible->Samples.wValidBitsPerSample = kBitsPerSample;
166
167 wave_format_extensible->Format.nChannels = kChannels;
[email protected]ca1488912012-07-27 17:19:04168 wave_format_extensible->Format.nSamplesPerSec = sampling_rate_;
[email protected]c9bdcbd2012-07-19 23:35:54169 wave_format_extensible->Format.wBitsPerSample = kBitsPerSample;
170 wave_format_extensible->Format.nBlockAlign =
171 kChannels * kBitsPerSample / kBitsPerByte;
172 wave_format_extensible->Format.nAvgBytesPerSec =
[email protected]ca1488912012-07-27 17:19:04173 sampling_rate_ * kChannels * kBitsPerSample / kBitsPerByte;
[email protected]c9bdcbd2012-07-19 23:35:54174 } else {
175 LOG(ERROR) << "Failed to force 16-bit samples";
[email protected]339abeca2012-07-20 18:28:24176 return false;
[email protected]c9bdcbd2012-07-19 23:35:54177 }
178 break;
179 }
180 default:
[email protected]ca1488912012-07-27 17:19:04181 LOG(ERROR) << "Failed to force 16-bit PCM";
[email protected]339abeca2012-07-20 18:28:24182 return false;
[email protected]c9bdcbd2012-07-19 23:35:54183 }
184
185 // Initialize the IAudioClient.
186 hr = audio_client_->Initialize(AUDCLNT_SHAREMODE_SHARED,
187 AUDCLNT_STREAMFLAGS_LOOPBACK,
188 0,
189 0,
190 wave_format_ex_,
191 NULL);
192 if (FAILED(hr)) {
193 LOG(ERROR) << "Failed to initialize IAudioClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24194 return false;
[email protected]c9bdcbd2012-07-19 23:35:54195 }
196
197 // Get an IAudioCaptureClient.
198 hr = audio_client_->GetService(__uuidof(IAudioCaptureClient),
199 audio_capture_client_.ReceiveVoid());
200 if (FAILED(hr)) {
201 LOG(ERROR) << "Failed to get an IAudioCaptureClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24202 return false;
[email protected]c9bdcbd2012-07-19 23:35:54203 }
204
205 // Start the IAudioClient.
206 hr = audio_client_->Start();
207 if (FAILED(hr)) {
208 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr;
[email protected]339abeca2012-07-20 18:28:24209 return false;
[email protected]c9bdcbd2012-07-19 23:35:54210 }
211
212 // Start capturing.
213 capture_timer_->Start(FROM_HERE,
214 audio_device_period_,
215 this,
216 &AudioCapturerWin::DoCapture);
[email protected]339abeca2012-07-20 18:28:24217 return true;
[email protected]c9bdcbd2012-07-19 23:35:54218}
219
220void AudioCapturerWin::Stop() {
221 DCHECK(thread_checker_.CalledOnValidThread());
222 DCHECK(IsRunning());
223
224 capture_timer_.reset();
225 mm_device_.Release();
226 audio_client_.Release();
227 audio_capture_client_.Release();
228 wave_format_ex_.Reset(NULL);
229 com_initializer_.reset();
230
231 thread_checker_.DetachFromThread();
232}
233
234bool AudioCapturerWin::IsRunning() {
235 DCHECK(thread_checker_.CalledOnValidThread());
236 return capture_timer_.get() != NULL;
237}
238
239void AudioCapturerWin::DoCapture() {
[email protected]ca1488912012-07-27 17:19:04240 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_));
[email protected]c9bdcbd2012-07-19 23:35:54241 DCHECK(thread_checker_.CalledOnValidThread());
242 DCHECK(IsRunning());
243
244 // Fetch all packets from the audio capture endpoint buffer.
245 while (true) {
246 UINT32 next_packet_size;
247 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size);
248 if (FAILED(hr)) {
249 LOG(ERROR) << "Failed to GetNextPacketSize. Error " << hr;
250 return;
251 }
252
253 if (next_packet_size <= 0) {
254 return;
255 }
256
257 BYTE* data;
258 UINT32 frames;
259 DWORD flags;
260 hr = audio_capture_client_->GetBuffer(
261 &data, &frames, &flags, NULL, NULL);
262 if (FAILED(hr)) {
263 LOG(ERROR) << "Failed to GetBuffer. Error " << hr;
264 return;
265 }
266
267 scoped_ptr<AudioPacket> packet = scoped_ptr<AudioPacket>(new AudioPacket());
268 packet->set_data(data, frames * wave_format_ex_->nBlockAlign);
[email protected]ca1488912012-07-27 17:19:04269 packet->set_sampling_rate(sampling_rate_);
[email protected]c9bdcbd2012-07-19 23:35:54270
271 callback_.Run(packet.Pass());
272
273 hr = audio_capture_client_->ReleaseBuffer(frames);
274 if (FAILED(hr)) {
275 LOG(ERROR) << "Failed to ReleaseBuffer. Error " << hr;
276 return;
277 }
278 }
279}
280
281scoped_ptr<AudioCapturer> AudioCapturer::Create() {
282 return scoped_ptr<AudioCapturer>(new AudioCapturerWin());
283}
284
285} // namespace remoting