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 bool AudioCapturerWin::IsMuted(float* level) { | |
joedow
2016/03/02 15:25:52
Couldn't this function return a float (just return
Sergey Ulanov
2016/03/02 20:09:40
+1
Hzj_jie
2016/03/03 09:33:39
Yes, current logic treat 0 volume exactly the same
| |
217 DCHECK(level); | |
218 BOOL mute; | |
219 HRESULT hr = audio_volume_->GetMute(&mute); | |
220 if (!FAILED(hr) && mute) { | |
221 return true; | |
222 } | |
223 hr = audio_volume_->GetMasterVolume(level); | |
224 if (FAILED(hr) || *level > 1 || *level < 0) { | |
joedow
2016/03/02 15:25:52
If the level is less than zero, should you clamp i
Hzj_jie
2016/03/03 09:33:39
I am OK with both values, we definitely should not
| |
225 *level = 1; | |
joedow
2016/03/02 15:25:52
Per my previous comment, I think the decision to d
Sergey Ulanov
2016/03/02 20:09:40
look at it as the host's volume setting being igno
Hzj_jie
2016/03/03 09:33:39
Yes, if anything wrong, current design ignores hos
| |
226 } | |
227 return *level == 0; | |
228 } | |
229 | |
206 void AudioCapturerWin::DoCapture() { | 230 void AudioCapturerWin::DoCapture() { |
207 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); | 231 DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_)); |
208 DCHECK(thread_checker_.CalledOnValidThread()); | 232 DCHECK(thread_checker_.CalledOnValidThread()); |
209 | 233 |
210 // Fetch all packets from the audio capture endpoint buffer. | 234 // Fetch all packets from the audio capture endpoint buffer. |
211 HRESULT hr = S_OK; | 235 HRESULT hr = S_OK; |
212 while (true) { | 236 while (true) { |
213 UINT32 next_packet_size; | 237 UINT32 next_packet_size; |
214 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); | 238 HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); |
215 if (FAILED(hr)) | 239 if (FAILED(hr)) |
216 break; | 240 break; |
217 | 241 |
218 if (next_packet_size <= 0) { | 242 if (next_packet_size <= 0) { |
219 return; | 243 return; |
220 } | 244 } |
221 | 245 |
222 BYTE* data; | 246 BYTE* data; |
223 UINT32 frames; | 247 UINT32 frames; |
224 DWORD flags; | 248 DWORD flags; |
225 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr, | 249 hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, nullptr, |
226 nullptr); | 250 nullptr); |
227 if (FAILED(hr)) | 251 if (FAILED(hr)) |
228 break; | 252 break; |
229 | 253 |
230 if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0 && | 254 if (frames > 0 && (flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0) { |
231 !silence_detector_.IsSilence(reinterpret_cast<const int16_t*>(data), | 255 float level; |
232 frames * kChannels)) { | 256 if (!IsMuted(&level)) { |
Sergey Ulanov
2016/03/02 20:09:40
Sorry for not being clear about it on my previous
Hzj_jie
2016/03/03 09:33:39
Done.
| |
233 scoped_ptr<AudioPacket> packet(new AudioPacket()); | 257 int16_t* samples = reinterpret_cast<int16_t*>(data); |
234 packet->add_data(data, frames * wave_format_ex_->nBlockAlign); | 258 // BYTE to int16_t |
joedow
2016/03/02 15:25:51
I don't think this comment adds much value, remove
Hzj_jie
2016/03/03 09:33:39
Done.
| |
235 packet->set_encoding(AudioPacket::ENCODING_RAW); | 259 size_t sample_count = frames * kChannels * kBytesPerSample |
236 packet->set_sampling_rate(sampling_rate_); | 260 * sizeof(BYTE) / sizeof(int16_t); |
Sergey Ulanov
2016/03/02 20:09:39
I still think sizeof(BYTE) is useless here. Someth
Hzj_jie
2016/03/03 09:33:39
Good point, thank you.
| |
237 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | 261 static_assert(sizeof(samples[0]) == kBytesPerSample, |
238 packet->set_channels(AudioPacket::CHANNELS_STEREO); | 262 "expect 16 bits per sample"); |
263 if (level < 1) { | |
joedow
2016/03/02 15:25:52
A comment might be good here to describe why we ne
Hzj_jie
2016/03/03 09:33:39
Done.
| |
264 int32_t level_int = static_cast<int32_t>(level * 65536); | |
265 for (size_t i = 0; i < sample_count; i++) { | |
266 samples[i] = (static_cast<int32_t>(samples[i]) * level_int) >> 16; | |
267 } | |
268 } | |
269 if (!silence_detector_.IsSilence(samples, sample_count)) { | |
Sergey Ulanov
2016/03/02 20:09:40
Here almost all data is being dropped (as audio is
Hzj_jie
2016/03/03 09:33:39
I believe network is more precious than processor,
Sergey Ulanov
2016/03/04 21:30:03
You already have explicit check for volume=0, so s
Hzj_jie
2016/03/09 19:47:57
Not really, if the volume is close to 0, but sampl
| |
270 scoped_ptr<AudioPacket> packet(new AudioPacket()); | |
271 packet->add_data(data, frames * wave_format_ex_->nBlockAlign); | |
Sergey Ulanov
2016/03/02 20:09:39
So here the data is copied from data to a new buff
Hzj_jie
2016/03/03 09:33:39
Same as comment above.
| |
272 packet->set_encoding(AudioPacket::ENCODING_RAW); | |
273 packet->set_sampling_rate(sampling_rate_); | |
274 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | |
275 packet->set_channels(AudioPacket::CHANNELS_STEREO); | |
239 | 276 |
240 callback_.Run(std::move(packet)); | 277 callback_.Run(std::move(packet)); |
278 } | |
279 } | |
241 } | 280 } |
242 | 281 |
243 hr = audio_capture_client_->ReleaseBuffer(frames); | 282 hr = audio_capture_client_->ReleaseBuffer(frames); |
244 if (FAILED(hr)) | 283 if (FAILED(hr)) |
245 break; | 284 break; |
246 } | 285 } |
247 | 286 |
248 // There is nothing to capture if the audio endpoint device has been unplugged | 287 // There is nothing to capture if the audio endpoint device has been unplugged |
249 // or disabled. | 288 // or disabled. |
250 if (hr == AUDCLNT_E_DEVICE_INVALIDATED) | 289 if (hr == AUDCLNT_E_DEVICE_INVALIDATED) |
251 return; | 290 return; |
252 | 291 |
253 // Avoid reporting the same error multiple times. | 292 // Avoid reporting the same error multiple times. |
254 if (FAILED(hr) && hr != last_capture_error_) { | 293 if (FAILED(hr) && hr != last_capture_error_) { |
255 last_capture_error_ = hr; | 294 last_capture_error_ = hr; |
256 LOG(ERROR) << "Failed to capture an audio packet: 0x" | 295 LOG(ERROR) << "Failed to capture an audio packet: 0x" |
257 << std::hex << hr << std::dec << "."; | 296 << std::hex << hr << std::dec << "."; |
258 } | 297 } |
259 } | 298 } |
260 | 299 |
261 bool AudioCapturer::IsSupported() { | 300 bool AudioCapturer::IsSupported() { |
262 return true; | 301 return true; |
263 } | 302 } |
264 | 303 |
265 scoped_ptr<AudioCapturer> AudioCapturer::Create() { | 304 scoped_ptr<AudioCapturer> AudioCapturer::Create() { |
266 return make_scoped_ptr(new AudioCapturerWin()); | 305 return make_scoped_ptr(new AudioCapturerWin()); |
267 } | 306 } |
268 | 307 |
269 } // namespace remoting | 308 } // namespace remoting |
OLD | NEW |