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> |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
47 } | 47 } |
48 | 48 |
49 AudioCapturerWin::~AudioCapturerWin() { | 49 AudioCapturerWin::~AudioCapturerWin() { |
50 DCHECK(thread_checker_.CalledOnValidThread()); | 50 DCHECK(thread_checker_.CalledOnValidThread()); |
51 } | 51 } |
52 | 52 |
53 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { | 53 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) { |
54 DCHECK(!audio_capture_client_.get()); | 54 DCHECK(!audio_capture_client_.get()); |
55 DCHECK(!audio_client_.get()); | 55 DCHECK(!audio_client_.get()); |
56 DCHECK(!mm_device_.get()); | 56 DCHECK(!mm_device_.get()); |
57 DCHECK(!audio_volume_.get()); | |
57 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); | 58 DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == nullptr); |
58 DCHECK(thread_checker_.CalledOnValidThread()); | 59 DCHECK(thread_checker_.CalledOnValidThread()); |
59 | 60 |
60 callback_ = callback; | 61 callback_ = callback; |
61 | 62 |
62 // Initialize the capture timer. | 63 // Initialize the capture timer. |
63 capture_timer_.reset(new base::RepeatingTimer()); | 64 capture_timer_.reset(new base::RepeatingTimer()); |
64 | 65 |
65 HRESULT hr = S_OK; | 66 HRESULT hr = S_OK; |
66 | 67 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
186 return false; | 187 return false; |
187 } | 188 } |
188 | 189 |
189 // Start the IAudioClient. | 190 // Start the IAudioClient. |
190 hr = audio_client_->Start(); | 191 hr = audio_client_->Start(); |
191 if (FAILED(hr)) { | 192 if (FAILED(hr)) { |
192 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; | 193 LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; |
193 return false; | 194 return false; |
194 } | 195 } |
195 | 196 |
197 // Initialize ISimpleAudioVolume. | |
198 // TODO(zijiehe): Do we need to control per process volume? | |
199 hr = audio_client_->GetService(__uuidof(ISimpleAudioVolume), | |
200 audio_volume_.ReceiveVoid()); | |
201 if (FAILED(hr)) { | |
202 LOG(ERROR) << "Failed to get an ISimpleAudioVolume. Error " << hr; | |
203 return false; | |
204 } | |
205 | |
196 silence_detector_.Reset(sampling_rate_, kChannels); | 206 silence_detector_.Reset(sampling_rate_, kChannels); |
197 | 207 |
198 // Start capturing. | 208 // Start capturing. |
199 capture_timer_->Start(FROM_HERE, | 209 capture_timer_->Start(FROM_HERE, |
200 audio_device_period_, | 210 audio_device_period_, |
201 this, | 211 this, |
202 &AudioCapturerWin::DoCapture); | 212 &AudioCapturerWin::DoCapture); |
203 return true; | 213 return true; |
204 } | 214 } |
205 | 215 |
216 float AudioCapturerWin::GetAudioLevel() { | |
217 BOOL mute; | |
218 HRESULT hr = audio_volume_->GetMute(&mute); | |
219 if (FAILED(hr)) { | |
220 return 1; | |
joedow
2016/03/04 16:46:10
Is there an advantage to returning an int here ins
Sergey Ulanov
2016/03/04 21:30:03
FWIW IMO it makes no difference. Compiler will gen
Hzj_jie
2016/03/09 19:47:57
Done.
| |
221 } else if (mute) { | |
Sergey Ulanov
2016/03/04 21:30:03
don't need this else.
See https://www.chromium.org
Hzj_jie
2016/03/09 19:47:57
Done.
| |
222 return 0; | |
223 } | |
joedow
2016/03/04 16:46:10
nit: add a newline here to break up the mute and v
Hzj_jie
2016/03/09 19:47:57
Done.
| |
224 float level; | |
225 hr = audio_volume_->GetMasterVolume(&level); | |
226 if (FAILED(hr) || level > 1) { | |
227 return 1; | |
228 } else if (level < 0) { | |
229 return 0; | |
230 } else { | |
Sergey Ulanov
2016/03/04 21:30:03
don't need this else
Hzj_jie
2016/03/09 19:47:57
Done.
| |
231 return level; | |
232 } | |
233 } | |
234 | |
235 void AudioCapturerWin::ProcessSamples(BYTE* data, UINT32 frames, DWORD flags) { | |
236 if (frames == 0) { | |
237 return; | |
238 } | |
239 | |
240 if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0) { | |
241 return; | |
242 } | |
243 | |
244 float level = GetAudioLevel(); | |
245 if (level == 0) { | |
joedow
2016/03/04 16:46:10
Are there problems here if the level is 0.01 or 0.
Sergey Ulanov
2016/03/04 21:30:03
Compiler is required to convert ints to floats for
Hzj_jie
2016/03/09 19:47:57
I believe what Joe mentioned is the string display
| |
246 return; | |
247 } | |
248 | |
249 int16_t* samples = reinterpret_cast<int16_t*>(data); | |
250 static_assert(sizeof(samples[0]) == kBytesPerSample, | |
251 "expect 16 bits per sample"); | |
252 size_t sample_count = frames * kChannels; | |
253 if (level < 1) { | |
254 // Windows API does not provide volume adjusted audio sample as Linux does. | |
255 // So we need to manually append volume signal to the samples. | |
256 int32_t level_int = static_cast<int32_t>(level * 65536); | |
257 for (size_t i = 0; i < sample_count; i++) { | |
258 samples[i] = (static_cast<int32_t>(samples[i]) * level_int) >> 16; | |
259 } | |
260 } | |
261 if (!silence_detector_.IsSilence(samples, sample_count)) { | |
Sergey Ulanov
2016/03/04 21:30:03
this can be reformatted as follows
if (silence_de
Hzj_jie
2016/03/09 19:47:57
Done.
| |
262 scoped_ptr<AudioPacket> packet(new AudioPacket()); | |
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); | |
268 | |
269 callback_.Run(std::move(packet)); | |
270 } | |
271 } | |
272 | |
206 void AudioCapturerWin::DoCapture() { | 273 void AudioCapturerWin::DoCapture() { |
207 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); | 274 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); |
208 DCHECK(thread_checker_.CalledOnValidThread()); | 275 DCHECK(thread_checker_.CalledOnValidThread()); |
209 | 276 |
210 // Fetch all packets from the audio capture endpoint buffer. | 277 // Fetch all packets from the audio capture endpoint buffer. |
211 HRESULT hr = S_OK; | 278 HRESULT hr = S_OK; |
212 while (true) { | 279 while (true) { |
213 UINT32 next_packet_size; | 280 UINT32 next_packet_size; |
214 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); | 281 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); |
215 if (FAILED(hr)) | 282 if (FAILED(hr)) |
216 break; | 283 break; |
217 | 284 |
218 if (next_packet_size <= 0) { | 285 if (next_packet_size <= 0) { |
219 return; | 286 return; |
220 } | 287 } |
221 | 288 |
222 BYTE* data; | 289 BYTE* data; |
223 UINT32 frames; | 290 UINT32 frames; |
224 DWORD flags; | 291 DWORD flags; |
225 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr, | 292 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr, |
226 nullptr); | 293 nullptr); |
227 if (FAILED(hr)) | 294 if (FAILED(hr)) |
228 break; | 295 break; |
229 | 296 |
230 if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0 && | 297 ProcessSamples(data, frames, flags); |
231 !silence_detector_.IsSilence(reinterpret_cast<const int16_t*>(data), | |
232 frames * kChannels)) { | |
233 scoped_ptr<AudioPacket> packet(new AudioPacket()); | |
234 packet->add_data(data, frames * wave_format_ex_->nBlockAlign); | |
235 packet->set_encoding(AudioPacket::ENCODING_RAW); | |
236 packet->set_sampling_rate(sampling_rate_); | |
237 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | |
238 packet->set_channels(AudioPacket::CHANNELS_STEREO); | |
239 | |
240 callback_.Run(std::move(packet)); | |
241 } | |
242 | 298 |
243 hr = audio_capture_client_->ReleaseBuffer(frames); | 299 hr = audio_capture_client_->ReleaseBuffer(frames); |
244 if (FAILED(hr)) | 300 if (FAILED(hr)) |
245 break; | 301 break; |
246 } | 302 } |
247 | 303 |
248 // There is nothing to capture if the audio endpoint device has been unplugged | 304 // There is nothing to capture if the audio endpoint device has been unplugged |
249 // or disabled. | 305 // or disabled. |
250 if (hr == AUDCLNT_E_DEVICE_INVALIDATED) | 306 if (hr == AUDCLNT_E_DEVICE_INVALIDATED) |
251 return; | 307 return; |
252 | 308 |
253 // Avoid reporting the same error multiple times. | 309 // Avoid reporting the same error multiple times. |
254 if (FAILED(hr) && hr != last_capture_error_) { | 310 if (FAILED(hr) && hr != last_capture_error_) { |
255 last_capture_error_ = hr; | 311 last_capture_error_ = hr; |
256 LOG(ERROR) << "Failed to capture an audio packet: 0x" | 312 LOG(ERROR) << "Failed to capture an audio packet: 0x" |
257 << std::hex << hr << std::dec << "."; | 313 << std::hex << hr << std::dec << "."; |
258 } | 314 } |
259 } | 315 } |
260 | 316 |
261 bool AudioCapturer::IsSupported() { | 317 bool AudioCapturer::IsSupported() { |
262 return true; | 318 return true; |
263 } | 319 } |
264 | 320 |
265 scoped_ptr<AudioCapturer> AudioCapturer::Create() { | 321 scoped_ptr<AudioCapturer> AudioCapturer::Create() { |
266 return make_scoped_ptr(new AudioCapturerWin()); | 322 return make_scoped_ptr(new AudioCapturerWin()); |
267 } | 323 } |
268 | 324 |
269 } // namespace remoting | 325 } // namespace remoting |
OLD | NEW |