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> |
11 #include <stdlib.h> | 11 #include <stdlib.h> |
12 #include <windows.h> | 12 #include <windows.h> |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <utility> | 15 #include <utility> |
16 | 16 |
17 #include "base/logging.h" | 17 #include "base/logging.h" |
18 | 18 |
19 namespace { | 19 namespace { |
20 const int kChannels = 2; | 20 const int kChannels = 2; |
21 // Following logic expects kBytesPerSample always be 2. | |
Sergey Ulanov
2016/03/01 21:48:17
I don't think you need this comment here. See my s
Hzj_jie
2016/03/02 08:16:33
Done.
| |
21 const int kBytesPerSample = 2; | 22 const int kBytesPerSample = 2; |
22 const int kBitsPerSample = kBytesPerSample * 8; | 23 const int kBitsPerSample = kBytesPerSample * 8; |
23 // Conversion factor from 100ns to 1ms. | 24 // Conversion factor from 100ns to 1ms. |
24 const int k100nsPerMillisecond = 10000; | 25 const int k100nsPerMillisecond = 10000; |
25 | 26 |
26 // Tolerance for catching packets of silence. If all samples have absolute | 27 // Tolerance for catching packets of silence. If all samples have absolute |
27 // value less than this threshold, the packet will be counted as a packet of | 28 // value less than this threshold, the packet will be counted as a packet of |
28 // silence. A value of 2 was chosen, because Windows can give samples of 1 and | 29 // silence. A value of 2 was chosen, because Windows can give samples of 1 and |
29 // -1, even when no audio is playing. | 30 // -1, even when no audio is playing. |
30 const int kSilenceThreshold = 2; | 31 const int kSilenceThreshold = 2; |
(...skipping 16 matching lines...) Expand all Loading... | |
47 } | 48 } |
48 | 49 |
49 AudioCapturerWin::~AudioCapturerWin() { | 50 AudioCapturerWin::~AudioCapturerWin() { |
50 DCHECK(thread_checker_.CalledOnValidThread()); | 51 DCHECK(thread_checker_.CalledOnValidThread()); |
51 } | 52 } |
52 | 53 |
53 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { | 54 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { |
54 DCHECK(!audio_capture_client_.get()); | 55 DCHECK(!audio_capture_client_.get()); |
55 DCHECK(!audio_client_.get()); | 56 DCHECK(!audio_client_.get()); |
56 DCHECK(!mm_device_.get()); | 57 DCHECK(!mm_device_.get()); |
58 DCHECK(!audio_volume_.get()); | |
57 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); | 59 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); |
58 DCHECK(thread_checker_.CalledOnValidThread()); | 60 DCHECK(thread_checker_.CalledOnValidThread()); |
59 | 61 |
60 callback_ = callback; | 62 callback_ = callback; |
61 | 63 |
62 // Initialize the capture timer. | 64 // Initialize the capture timer. |
63 capture_timer_.reset(new base::RepeatingTimer()); | 65 capture_timer_.reset(new base::RepeatingTimer()); |
64 | 66 |
65 HRESULT hr = S_OK; | 67 HRESULT hr = S_OK; |
66 | 68 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 return false; | 188 return false; |
187 } | 189 } |
188 | 190 |
189 // Start the IAudioClient. | 191 // Start the IAudioClient. |
190 hr = audio_client_->Start(); | 192 hr = audio_client_->Start(); |
191 if (FAILED(hr)) { | 193 if (FAILED(hr)) { |
192 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; | 194 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; |
193 return false; | 195 return false; |
194 } | 196 } |
195 | 197 |
198 // Initialize ISampleAudioVolume. TODO(zijiehe): Do we need to control per | |
joedow
2016/03/01 22:38:40
s/ISampleAudioVolume/ISimpleAudioVolume.
The TODO
Hzj_jie
2016/03/02 08:16:33
Done.
| |
199 // process volume? | |
200 hr = audio_client_->GetService(__uuidof(ISimpleAudioVolume), | |
201 audio_volume_.ReceiveVoid()); | |
202 if (FAILED(hr)) { | |
203 LOG(ERROR) << "Failed to get an ISimpleAudioVolume. Error " << hr; | |
204 return false; | |
205 } | |
206 | |
196 silence_detector_.Reset(sampling_rate_, kChannels); | 207 silence_detector_.Reset(sampling_rate_, kChannels); |
197 | 208 |
198 // Start capturing. | 209 // Start capturing. |
199 capture_timer_->Start(FROM_HERE, | 210 capture_timer_->Start(FROM_HERE, |
200 audio_device_period_, | 211 audio_device_period_, |
201 this, | 212 this, |
202 &AudioCapturerWin::DoCapture); | 213 &AudioCapturerWin::DoCapture); |
203 return true; | 214 return true; |
204 } | 215 } |
205 | 216 |
(...skipping 14 matching lines...) Expand all Loading... | |
220 } | 231 } |
221 | 232 |
222 BYTE* data; | 233 BYTE* data; |
223 UINT32 frames; | 234 UINT32 frames; |
224 DWORD flags; | 235 DWORD flags; |
225 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr, | 236 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr, |
226 nullptr); | 237 nullptr); |
227 if (FAILED(hr)) | 238 if (FAILED(hr)) |
228 break; | 239 break; |
229 | 240 |
230 if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0 && | 241 BOOL mute; |
231 !silence_detector_.IsSilence(reinterpret_cast<const int16_t*>(data), | 242 if (FAILED(audio_volume_->GetMute(&mute)) || !mute) { |
Sergey Ulanov
2016/03/01 21:48:16
Suggest moving this code to a separate function to
joedow
2016/03/01 22:38:40
Agree with Sergey, also GetMute looks like it woul
Hzj_jie
2016/03/02 08:16:32
I just want to make sure this change won't break e
| |
232 frames * kChannels)) { | 243 float level; |
233 scoped_ptr<AudioPacket> packet(new AudioPacket()); | 244 if (FAILED(audio_volume_->GetMasterVolume(&level) || |
234 packet->add_data(data, frames * wave_format_ex_->nBlockAlign); | 245 level > 1 || level < -1)) { |
Sergey Ulanov
2016/03/01 21:48:16
is the level allowed to be negative?
Hzj_jie
2016/03/02 08:16:33
No, the value should be [0.0, 1.0], but this check
| |
235 packet->set_encoding(AudioPacket::ENCODING_RAW); | 246 level = 1; |
joedow
2016/03/01 22:38:40
Should we default to 1 if the call above fails? T
Hzj_jie
2016/03/02 08:16:33
The reason is same as GetMute call, i.e. make sure
| |
236 packet->set_sampling_rate(sampling_rate_); | 247 } |
237 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | 248 if (level > 0 && (flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0) { |
238 packet->set_channels(AudioPacket::CHANNELS_STEREO); | 249 // NOTE: The samples may not be 16 bits align, if any constants changed. |
250 // i.e. samples[x] may be part of one sample of one channel, or may be | |
251 // the combination of one sample of two channels. | |
Sergey Ulanov
2016/03/01 21:48:16
I don't understand this comment. We always get dat
Hzj_jie
2016/03/02 08:16:33
Yes, 'We always get data in 16-bit format' is the
| |
252 int16_t* samples = reinterpret_cast<int16_t*>(data); | |
253 // BYTE to int16_t | |
254 size_t sample_count = frames * kChannels * kBytesPerSample | |
255 * sizeof(BYTE) / sizeof(int16_t); | |
Sergey Ulanov
2016/03/01 21:48:16
nit: don't need sizeof(BYTE). kBytesPerSample alre
Hzj_jie
2016/03/02 08:16:33
Though less possibility, what if sizeof(BYTE) != 1
| |
256 if (!silence_detector_.IsSilence(samples, sample_count)) { | |
257 scoped_ptr<AudioPacket> packet(new AudioPacket()); | |
258 if (level < 1) { | |
259 for (size_t i = 0; i < sample_count; i++) { | |
260 samples[i] *= level; | |
Sergey Ulanov
2016/03/01 21:48:16
This would be many times faster if you avoid float
Hzj_jie
2016/03/02 08:16:32
Done. But the performance impact would not be many
Sergey Ulanov
2016/03/02 20:09:39
FWIW I see 2.5x-3x difference on ia64 and on ia32
Hzj_jie
2016/03/03 09:33:38
I tried with only multiplication (which is on-par)
| |
261 } | |
262 } | |
263 packet->add_data(data, frames * wave_format_ex_->nBlockAlign); | |
264 packet->set_encoding(AudioPacket::ENCODING_RAW); | |
265 packet->set_sampling_rate(sampling_rate_); | |
266 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | |
267 packet->set_channels(AudioPacket::CHANNELS_STEREO); | |
239 | 268 |
240 callback_.Run(std::move(packet)); | 269 callback_.Run(std::move(packet)); |
270 } | |
271 } | |
241 } | 272 } |
242 | 273 |
243 hr = audio_capture_client_->ReleaseBuffer(frames); | 274 hr = audio_capture_client_->ReleaseBuffer(frames); |
244 if (FAILED(hr)) | 275 if (FAILED(hr)) |
245 break; | 276 break; |
246 } | 277 } |
247 | 278 |
248 // There is nothing to capture if the audio endpoint device has been unplugged | 279 // There is nothing to capture if the audio endpoint device has been unplugged |
249 // or disabled. | 280 // or disabled. |
250 if (hr == AUDCLNT_E_DEVICE_INVALIDATED) | 281 if (hr == AUDCLNT_E_DEVICE_INVALIDATED) |
251 return; | 282 return; |
252 | 283 |
253 // Avoid reporting the same error multiple times. | 284 // Avoid reporting the same error multiple times. |
254 if (FAILED(hr) && hr != last_capture_error_) { | 285 if (FAILED(hr) && hr != last_capture_error_) { |
255 last_capture_error_ = hr; | 286 last_capture_error_ = hr; |
256 LOG(ERROR) << "Failed to capture an audio packet: 0x" | 287 LOG(ERROR) << "Failed to capture an audio packet: 0x" |
257 << std::hex << hr << std::dec << "."; | 288 << std::hex << hr << std::dec << "."; |
258 } | 289 } |
259 } | 290 } |
260 | 291 |
261 bool AudioCapturer::IsSupported() { | 292 bool AudioCapturer::IsSupported() { |
262 return true; | 293 return true; |
263 } | 294 } |
264 | 295 |
265 scoped_ptr<AudioCapturer> AudioCapturer::Create() { | 296 scoped_ptr<AudioCapturer> AudioCapturer::Create() { |
266 return make_scoped_ptr(new AudioCapturerWin()); | 297 return make_scoped_ptr(new AudioCapturerWin()); |
267 } | 298 } |
268 | 299 |
269 } // namespace remoting | 300 } // namespace remoting |
OLD | NEW |