OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include <assert.h> | 11 #include <assert.h> |
12 | 12 |
13 #include "webrtc/base/checks.h" | 13 #include "webrtc/base/checks.h" |
14 #include "webrtc/base/logging.h" | 14 #include "webrtc/base/logging.h" |
15 #include "webrtc/modules/audio_device/audio_device_config.h" | 15 #include "webrtc/modules/audio_device/audio_device_config.h" |
16 #include "webrtc/modules/audio_device/linux/audio_device_pulse_linux.h" | 16 #include "webrtc/modules/audio_device/linux/audio_device_pulse_linux.h" |
17 #include "webrtc/system_wrappers/include/event_wrapper.h" | 17 #include "webrtc/system_wrappers/include/event_wrapper.h" |
18 #include "webrtc/system_wrappers/include/trace.h" | 18 #include "webrtc/system_wrappers/include/trace.h" |
19 | 19 |
20 webrtc::adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; | 20 webrtc::adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; |
21 | 21 |
22 // Accesses Pulse functions through our late-binding symbol table instead of | 22 // Accesses Pulse functions through our late-binding symbol table instead of |
23 // directly. This way we don't have to link to libpulse, which means our binary | 23 // directly. This way we don't have to link to libpulse, which means our binary |
24 // will work on systems that don't have it. | 24 // will work on systems that don't have it. |
25 #define LATE(sym) \ | 25 #define LATE(sym) \ |
26 LATESYM_GET(webrtc::adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, \ | 26 LATESYM_GET(webrtc::adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, \ |
27 sym) | 27 sym) |
28 | 28 |
29 namespace webrtc | 29 namespace webrtc { |
30 { | |
31 | 30 |
32 AudioDeviceLinuxPulse::AudioDeviceLinuxPulse(const int32_t id) : | 31 AudioDeviceLinuxPulse::AudioDeviceLinuxPulse(const int32_t id) |
33 _ptrAudioBuffer(NULL), | 32 : _ptrAudioBuffer(NULL), |
34 _timeEventRec(*EventWrapper::Create()), | 33 _timeEventRec(*EventWrapper::Create()), |
35 _timeEventPlay(*EventWrapper::Create()), | 34 _timeEventPlay(*EventWrapper::Create()), |
36 _recStartEvent(*EventWrapper::Create()), | 35 _recStartEvent(*EventWrapper::Create()), |
37 _playStartEvent(*EventWrapper::Create()), | 36 _playStartEvent(*EventWrapper::Create()), |
38 _id(id), | 37 _id(id), |
39 _mixerManager(id), | 38 _mixerManager(id), |
40 _inputDeviceIndex(0), | 39 _inputDeviceIndex(0), |
41 _outputDeviceIndex(0), | 40 _outputDeviceIndex(0), |
42 _inputDeviceIsSpecified(false), | 41 _inputDeviceIsSpecified(false), |
43 _outputDeviceIsSpecified(false), | 42 _outputDeviceIsSpecified(false), |
44 sample_rate_hz_(0), | 43 sample_rate_hz_(0), |
45 _recChannels(1), | 44 _recChannels(1), |
46 _playChannels(1), | 45 _playChannels(1), |
47 _playBufType(AudioDeviceModule::kFixedBufferSize), | 46 _playBufType(AudioDeviceModule::kFixedBufferSize), |
48 _initialized(false), | 47 _initialized(false), |
49 _recording(false), | 48 _recording(false), |
50 _playing(false), | 49 _playing(false), |
51 _recIsInitialized(false), | 50 _recIsInitialized(false), |
52 _playIsInitialized(false), | 51 _playIsInitialized(false), |
53 _startRec(false), | 52 _startRec(false), |
54 _stopRec(false), | 53 _stopRec(false), |
55 _startPlay(false), | 54 _startPlay(false), |
56 _stopPlay(false), | 55 _stopPlay(false), |
57 _AGC(false), | 56 _AGC(false), |
58 update_speaker_volume_at_startup_(false), | 57 update_speaker_volume_at_startup_(false), |
59 _playBufDelayFixed(20), | 58 _playBufDelayFixed(20), |
60 _sndCardPlayDelay(0), | 59 _sndCardPlayDelay(0), |
61 _sndCardRecDelay(0), | 60 _sndCardRecDelay(0), |
62 _writeErrors(0), | 61 _writeErrors(0), |
63 _playWarning(0), | 62 _playWarning(0), |
64 _playError(0), | 63 _playError(0), |
65 _recWarning(0), | 64 _recWarning(0), |
66 _recError(0), | 65 _recError(0), |
67 _deviceIndex(-1), | 66 _deviceIndex(-1), |
68 _numPlayDevices(0), | 67 _numPlayDevices(0), |
69 _numRecDevices(0), | 68 _numRecDevices(0), |
70 _playDeviceName(NULL), | 69 _playDeviceName(NULL), |
71 _recDeviceName(NULL), | 70 _recDeviceName(NULL), |
72 _playDisplayDeviceName(NULL), | 71 _playDisplayDeviceName(NULL), |
73 _recDisplayDeviceName(NULL), | 72 _recDisplayDeviceName(NULL), |
74 _playBuffer(NULL), | 73 _playBuffer(NULL), |
75 _playbackBufferSize(0), | 74 _playbackBufferSize(0), |
76 _playbackBufferUnused(0), | 75 _playbackBufferUnused(0), |
77 _tempBufferSpace(0), | 76 _tempBufferSpace(0), |
78 _recBuffer(NULL), | 77 _recBuffer(NULL), |
79 _recordBufferSize(0), | 78 _recordBufferSize(0), |
80 _recordBufferUsed(0), | 79 _recordBufferUsed(0), |
81 _tempSampleData(NULL), | 80 _tempSampleData(NULL), |
82 _tempSampleDataSize(0), | 81 _tempSampleDataSize(0), |
83 _configuredLatencyPlay(0), | 82 _configuredLatencyPlay(0), |
84 _configuredLatencyRec(0), | 83 _configuredLatencyRec(0), |
85 _paDeviceIndex(-1), | 84 _paDeviceIndex(-1), |
86 _paStateChanged(false), | 85 _paStateChanged(false), |
87 _paMainloop(NULL), | 86 _paMainloop(NULL), |
88 _paMainloopApi(NULL), | 87 _paMainloopApi(NULL), |
89 _paContext(NULL), | 88 _paContext(NULL), |
90 _recStream(NULL), | 89 _recStream(NULL), |
91 _playStream(NULL), | 90 _playStream(NULL), |
92 _recStreamFlags(0), | 91 _recStreamFlags(0), |
93 _playStreamFlags(0) | 92 _playStreamFlags(0) { |
94 { | 93 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__); |
95 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, | |
96 "%s created", __FUNCTION__); | |
97 | 94 |
98 memset(_paServerVersion, 0, sizeof(_paServerVersion)); | 95 memset(_paServerVersion, 0, sizeof(_paServerVersion)); |
99 memset(&_playBufferAttr, 0, sizeof(_playBufferAttr)); | 96 memset(&_playBufferAttr, 0, sizeof(_playBufferAttr)); |
100 memset(&_recBufferAttr, 0, sizeof(_recBufferAttr)); | 97 memset(&_recBufferAttr, 0, sizeof(_recBufferAttr)); |
101 memset(_oldKeyState, 0, sizeof(_oldKeyState)); | 98 memset(_oldKeyState, 0, sizeof(_oldKeyState)); |
102 } | 99 } |
103 | 100 |
104 AudioDeviceLinuxPulse::~AudioDeviceLinuxPulse() | 101 AudioDeviceLinuxPulse::~AudioDeviceLinuxPulse() { |
105 { | 102 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed", |
106 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, | 103 __FUNCTION__); |
107 "%s destroyed", __FUNCTION__); | 104 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
108 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 105 Terminate(); |
109 Terminate(); | |
110 | 106 |
111 if (_recBuffer) | 107 if (_recBuffer) { |
112 { | 108 delete[] _recBuffer; |
113 delete [] _recBuffer; | 109 _recBuffer = NULL; |
114 _recBuffer = NULL; | 110 } |
115 } | 111 if (_playBuffer) { |
116 if (_playBuffer) | 112 delete[] _playBuffer; |
117 { | 113 _playBuffer = NULL; |
118 delete [] _playBuffer; | 114 } |
119 _playBuffer = NULL; | 115 if (_playDeviceName) { |
120 } | 116 delete[] _playDeviceName; |
121 if (_playDeviceName) | 117 _playDeviceName = NULL; |
122 { | 118 } |
123 delete [] _playDeviceName; | 119 if (_recDeviceName) { |
124 _playDeviceName = NULL; | 120 delete[] _recDeviceName; |
125 } | 121 _recDeviceName = NULL; |
126 if (_recDeviceName) | 122 } |
127 { | |
128 delete [] _recDeviceName; | |
129 _recDeviceName = NULL; | |
130 } | |
131 | 123 |
132 delete &_recStartEvent; | 124 delete &_recStartEvent; |
133 delete &_playStartEvent; | 125 delete &_playStartEvent; |
134 delete &_timeEventRec; | 126 delete &_timeEventRec; |
135 delete &_timeEventPlay; | 127 delete &_timeEventPlay; |
136 } | 128 } |
137 | 129 |
138 void AudioDeviceLinuxPulse::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) | 130 void AudioDeviceLinuxPulse::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { |
139 { | 131 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
140 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
141 | 132 |
142 _ptrAudioBuffer = audioBuffer; | 133 _ptrAudioBuffer = audioBuffer; |
143 | 134 |
144 // Inform the AudioBuffer about default settings for this implementation. | 135 // Inform the AudioBuffer about default settings for this implementation. |
145 // Set all values to zero here since the actual settings will be done by | 136 // Set all values to zero here since the actual settings will be done by |
146 // InitPlayout and InitRecording later. | 137 // InitPlayout and InitRecording later. |
147 _ptrAudioBuffer->SetRecordingSampleRate(0); | 138 _ptrAudioBuffer->SetRecordingSampleRate(0); |
148 _ptrAudioBuffer->SetPlayoutSampleRate(0); | 139 _ptrAudioBuffer->SetPlayoutSampleRate(0); |
149 _ptrAudioBuffer->SetRecordingChannels(0); | 140 _ptrAudioBuffer->SetRecordingChannels(0); |
150 _ptrAudioBuffer->SetPlayoutChannels(0); | 141 _ptrAudioBuffer->SetPlayoutChannels(0); |
151 } | 142 } |
152 | 143 |
153 // ---------------------------------------------------------------------------- | 144 // ---------------------------------------------------------------------------- |
154 // ActiveAudioLayer | 145 // ActiveAudioLayer |
155 // ---------------------------------------------------------------------------- | 146 // ---------------------------------------------------------------------------- |
156 | 147 |
157 int32_t AudioDeviceLinuxPulse::ActiveAudioLayer( | 148 int32_t AudioDeviceLinuxPulse::ActiveAudioLayer( |
158 AudioDeviceModule::AudioLayer& audioLayer) const | 149 AudioDeviceModule::AudioLayer& audioLayer) const { |
159 { | 150 audioLayer = AudioDeviceModule::kLinuxPulseAudio; |
160 audioLayer = AudioDeviceModule::kLinuxPulseAudio; | 151 return 0; |
161 return 0; | |
162 } | 152 } |
163 | 153 |
164 AudioDeviceGeneric::InitStatus AudioDeviceLinuxPulse::Init() { | 154 AudioDeviceGeneric::InitStatus AudioDeviceLinuxPulse::Init() { |
165 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 155 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
166 if (_initialized) { | 156 if (_initialized) { |
167 return InitStatus::OK; | 157 return InitStatus::OK; |
168 } | 158 } |
169 | 159 |
170 // Initialize PulseAudio | 160 // Initialize PulseAudio |
171 if (InitPulseAudio() < 0) { | 161 if (InitPulseAudio() < 0) { |
(...skipping 27 matching lines...) Expand all Loading... |
199 _ptrThreadPlay.reset(new rtc::PlatformThread( | 189 _ptrThreadPlay.reset(new rtc::PlatformThread( |
200 PlayThreadFunc, this, "webrtc_audio_module_play_thread")); | 190 PlayThreadFunc, this, "webrtc_audio_module_play_thread")); |
201 _ptrThreadPlay->Start(); | 191 _ptrThreadPlay->Start(); |
202 _ptrThreadPlay->SetPriority(rtc::kRealtimePriority); | 192 _ptrThreadPlay->SetPriority(rtc::kRealtimePriority); |
203 | 193 |
204 _initialized = true; | 194 _initialized = true; |
205 | 195 |
206 return InitStatus::OK; | 196 return InitStatus::OK; |
207 } | 197 } |
208 | 198 |
209 int32_t AudioDeviceLinuxPulse::Terminate() | 199 int32_t AudioDeviceLinuxPulse::Terminate() { |
210 { | 200 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
211 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 201 if (!_initialized) { |
212 if (!_initialized) | 202 return 0; |
213 { | 203 } |
214 return 0; | 204 |
215 } | 205 _mixerManager.Close(); |
216 | 206 |
217 _mixerManager.Close(); | 207 // RECORDING |
218 | 208 if (_ptrThreadRec) { |
219 // RECORDING | 209 rtc::PlatformThread* tmpThread = _ptrThreadRec.release(); |
220 if (_ptrThreadRec) | 210 |
221 { | 211 _timeEventRec.Set(); |
222 rtc::PlatformThread* tmpThread = _ptrThreadRec.release(); | 212 tmpThread->Stop(); |
223 | 213 delete tmpThread; |
224 _timeEventRec.Set(); | 214 } |
225 tmpThread->Stop(); | 215 |
226 delete tmpThread; | 216 // PLAYOUT |
227 } | 217 if (_ptrThreadPlay) { |
228 | 218 rtc::PlatformThread* tmpThread = _ptrThreadPlay.release(); |
229 // PLAYOUT | 219 |
230 if (_ptrThreadPlay) | 220 _timeEventPlay.Set(); |
231 { | 221 tmpThread->Stop(); |
232 rtc::PlatformThread* tmpThread = _ptrThreadPlay.release(); | 222 delete tmpThread; |
233 | 223 } |
234 _timeEventPlay.Set(); | 224 |
235 tmpThread->Stop(); | 225 // Terminate PulseAudio |
236 delete tmpThread; | 226 if (TerminatePulseAudio() < 0) { |
237 } | 227 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
238 | 228 " failed to terminate PulseAudio"); |
239 // Terminate PulseAudio | 229 return -1; |
240 if (TerminatePulseAudio() < 0) | 230 } |
241 { | 231 |
242 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 232 if (_XDisplay) { |
243 " failed to terminate PulseAudio"); | 233 XCloseDisplay(_XDisplay); |
244 return -1; | 234 _XDisplay = NULL; |
245 } | 235 } |
246 | 236 |
247 if (_XDisplay) | 237 _initialized = false; |
248 { | 238 _outputDeviceIsSpecified = false; |
249 XCloseDisplay(_XDisplay); | 239 _inputDeviceIsSpecified = false; |
250 _XDisplay = NULL; | 240 |
251 } | 241 return 0; |
252 | 242 } |
253 _initialized = false; | 243 |
254 _outputDeviceIsSpecified = false; | 244 bool AudioDeviceLinuxPulse::Initialized() const { |
255 _inputDeviceIsSpecified = false; | 245 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
256 | 246 return (_initialized); |
257 return 0; | 247 } |
258 } | 248 |
259 | 249 int32_t AudioDeviceLinuxPulse::InitSpeaker() { |
260 bool AudioDeviceLinuxPulse::Initialized() const | 250 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
261 { | 251 |
262 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 252 if (_playing) { |
263 return (_initialized); | 253 return -1; |
264 } | 254 } |
265 | 255 |
266 int32_t AudioDeviceLinuxPulse::InitSpeaker() | 256 if (!_outputDeviceIsSpecified) { |
267 { | 257 return -1; |
268 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 258 } |
269 | 259 |
270 if (_playing) | 260 // check if default device |
271 { | 261 if (_outputDeviceIndex == 0) { |
272 return -1; | 262 uint16_t deviceIndex = 0; |
273 } | 263 GetDefaultDeviceInfo(false, NULL, deviceIndex); |
274 | 264 _paDeviceIndex = deviceIndex; |
275 if (!_outputDeviceIsSpecified) | 265 } else { |
276 { | 266 // get the PA device index from |
277 return -1; | 267 // the callback |
278 } | 268 _deviceIndex = _outputDeviceIndex; |
279 | 269 |
280 // check if default device | 270 // get playout devices |
281 if (_outputDeviceIndex == 0) | 271 PlayoutDevices(); |
282 { | 272 } |
283 uint16_t deviceIndex = 0; | 273 |
284 GetDefaultDeviceInfo(false, NULL, deviceIndex); | 274 // the callback has now set the _paDeviceIndex to |
285 _paDeviceIndex = deviceIndex; | 275 // the PulseAudio index of the device |
286 } else | 276 if (_mixerManager.OpenSpeaker(_paDeviceIndex) == -1) { |
287 { | 277 return -1; |
288 // get the PA device index from | 278 } |
289 // the callback | 279 |
290 _deviceIndex = _outputDeviceIndex; | 280 // clear _deviceIndex |
291 | 281 _deviceIndex = -1; |
292 // get playout devices | 282 _paDeviceIndex = -1; |
293 PlayoutDevices(); | 283 |
294 } | 284 return 0; |
295 | 285 } |
296 // the callback has now set the _paDeviceIndex to | 286 |
297 // the PulseAudio index of the device | 287 int32_t AudioDeviceLinuxPulse::InitMicrophone() { |
298 if (_mixerManager.OpenSpeaker(_paDeviceIndex) == -1) | 288 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
299 { | 289 if (_recording) { |
300 return -1; | 290 return -1; |
301 } | 291 } |
302 | 292 |
303 // clear _deviceIndex | 293 if (!_inputDeviceIsSpecified) { |
304 _deviceIndex = -1; | 294 return -1; |
305 _paDeviceIndex = -1; | 295 } |
306 | 296 |
307 return 0; | 297 // Check if default device |
308 } | 298 if (_inputDeviceIndex == 0) { |
309 | 299 uint16_t deviceIndex = 0; |
310 int32_t AudioDeviceLinuxPulse::InitMicrophone() | 300 GetDefaultDeviceInfo(true, NULL, deviceIndex); |
311 { | 301 _paDeviceIndex = deviceIndex; |
312 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 302 } else { |
313 if (_recording) | 303 // Get the PA device index from |
314 { | 304 // the callback |
315 return -1; | 305 _deviceIndex = _inputDeviceIndex; |
316 } | 306 |
317 | 307 // get recording devices |
318 if (!_inputDeviceIsSpecified) | 308 RecordingDevices(); |
319 { | 309 } |
320 return -1; | 310 |
321 } | 311 // The callback has now set the _paDeviceIndex to |
322 | 312 // the PulseAudio index of the device |
323 // Check if default device | 313 if (_mixerManager.OpenMicrophone(_paDeviceIndex) == -1) { |
324 if (_inputDeviceIndex == 0) | 314 return -1; |
325 { | 315 } |
326 uint16_t deviceIndex = 0; | 316 |
327 GetDefaultDeviceInfo(true, NULL, deviceIndex); | 317 // Clear _deviceIndex |
328 _paDeviceIndex = deviceIndex; | 318 _deviceIndex = -1; |
329 } else | 319 _paDeviceIndex = -1; |
330 { | 320 |
331 // Get the PA device index from | 321 return 0; |
332 // the callback | 322 } |
333 _deviceIndex = _inputDeviceIndex; | 323 |
334 | 324 bool AudioDeviceLinuxPulse::SpeakerIsInitialized() const { |
335 // get recording devices | 325 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
336 RecordingDevices(); | 326 return (_mixerManager.SpeakerIsInitialized()); |
337 } | 327 } |
338 | 328 |
339 // The callback has now set the _paDeviceIndex to | 329 bool AudioDeviceLinuxPulse::MicrophoneIsInitialized() const { |
340 // the PulseAudio index of the device | 330 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
341 if (_mixerManager.OpenMicrophone(_paDeviceIndex) == -1) | 331 return (_mixerManager.MicrophoneIsInitialized()); |
342 { | 332 } |
343 return -1; | 333 |
344 } | 334 int32_t AudioDeviceLinuxPulse::SpeakerVolumeIsAvailable(bool& available) { |
345 | 335 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
346 // Clear _deviceIndex | 336 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); |
347 _deviceIndex = -1; | 337 |
348 _paDeviceIndex = -1; | 338 // Make an attempt to open up the |
349 | 339 // output mixer corresponding to the currently selected output device. |
350 return 0; | 340 if (!wasInitialized && InitSpeaker() == -1) { |
351 } | 341 // If we end up here it means that the selected speaker has no volume |
352 | 342 // control. |
353 bool AudioDeviceLinuxPulse::SpeakerIsInitialized() const | 343 available = false; |
354 { | 344 return 0; |
355 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 345 } |
356 return (_mixerManager.SpeakerIsInitialized()); | 346 |
357 } | 347 // Given that InitSpeaker was successful, we know volume control exists. |
358 | 348 available = true; |
359 bool AudioDeviceLinuxPulse::MicrophoneIsInitialized() const | 349 |
360 { | 350 // Close the initialized output mixer |
361 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 351 if (!wasInitialized) { |
362 return (_mixerManager.MicrophoneIsInitialized()); | 352 _mixerManager.CloseSpeaker(); |
363 } | 353 } |
364 | 354 |
365 int32_t AudioDeviceLinuxPulse::SpeakerVolumeIsAvailable(bool& available) | 355 return 0; |
366 { | 356 } |
367 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 357 |
368 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); | 358 int32_t AudioDeviceLinuxPulse::SetSpeakerVolume(uint32_t volume) { |
369 | 359 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
370 // Make an attempt to open up the | 360 if (!_playing) { |
371 // output mixer corresponding to the currently selected output device. | 361 // Only update the volume if it's been set while we weren't playing. |
372 if (!wasInitialized && InitSpeaker() == -1) | 362 update_speaker_volume_at_startup_ = true; |
373 { | 363 } |
374 // If we end up here it means that the selected speaker has no volume | 364 return (_mixerManager.SetSpeakerVolume(volume)); |
375 // control. | 365 } |
376 available = false; | 366 |
377 return 0; | 367 int32_t AudioDeviceLinuxPulse::SpeakerVolume(uint32_t& volume) const { |
378 } | 368 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
379 | 369 uint32_t level(0); |
380 // Given that InitSpeaker was successful, we know volume control exists. | 370 |
| 371 if (_mixerManager.SpeakerVolume(level) == -1) { |
| 372 return -1; |
| 373 } |
| 374 |
| 375 volume = level; |
| 376 |
| 377 return 0; |
| 378 } |
| 379 |
| 380 int32_t AudioDeviceLinuxPulse::SetWaveOutVolume(uint16_t volumeLeft, |
| 381 uint16_t volumeRight) { |
| 382 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 383 " API call not supported on this platform"); |
| 384 return -1; |
| 385 } |
| 386 |
| 387 int32_t AudioDeviceLinuxPulse::WaveOutVolume(uint16_t& /*volumeLeft*/, |
| 388 uint16_t& /*volumeRight*/) const { |
| 389 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 390 " API call not supported on this platform"); |
| 391 return -1; |
| 392 } |
| 393 |
| 394 int32_t AudioDeviceLinuxPulse::MaxSpeakerVolume(uint32_t& maxVolume) const { |
| 395 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 396 uint32_t maxVol(0); |
| 397 |
| 398 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) { |
| 399 return -1; |
| 400 } |
| 401 |
| 402 maxVolume = maxVol; |
| 403 |
| 404 return 0; |
| 405 } |
| 406 |
| 407 int32_t AudioDeviceLinuxPulse::MinSpeakerVolume(uint32_t& minVolume) const { |
| 408 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 409 uint32_t minVol(0); |
| 410 |
| 411 if (_mixerManager.MinSpeakerVolume(minVol) == -1) { |
| 412 return -1; |
| 413 } |
| 414 |
| 415 minVolume = minVol; |
| 416 |
| 417 return 0; |
| 418 } |
| 419 |
| 420 int32_t AudioDeviceLinuxPulse::SpeakerVolumeStepSize(uint16_t& stepSize) const { |
| 421 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 422 uint16_t delta(0); |
| 423 |
| 424 if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) { |
| 425 return -1; |
| 426 } |
| 427 |
| 428 stepSize = delta; |
| 429 |
| 430 return 0; |
| 431 } |
| 432 |
| 433 int32_t AudioDeviceLinuxPulse::SpeakerMuteIsAvailable(bool& available) { |
| 434 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 435 bool isAvailable(false); |
| 436 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); |
| 437 |
| 438 // Make an attempt to open up the |
| 439 // output mixer corresponding to the currently selected output device. |
| 440 // |
| 441 if (!wasInitialized && InitSpeaker() == -1) { |
| 442 // If we end up here it means that the selected speaker has no volume |
| 443 // control, hence it is safe to state that there is no mute control |
| 444 // already at this stage. |
| 445 available = false; |
| 446 return 0; |
| 447 } |
| 448 |
| 449 // Check if the selected speaker has a mute control |
| 450 _mixerManager.SpeakerMuteIsAvailable(isAvailable); |
| 451 |
| 452 available = isAvailable; |
| 453 |
| 454 // Close the initialized output mixer |
| 455 if (!wasInitialized) { |
| 456 _mixerManager.CloseSpeaker(); |
| 457 } |
| 458 |
| 459 return 0; |
| 460 } |
| 461 |
| 462 int32_t AudioDeviceLinuxPulse::SetSpeakerMute(bool enable) { |
| 463 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 464 return (_mixerManager.SetSpeakerMute(enable)); |
| 465 } |
| 466 |
| 467 int32_t AudioDeviceLinuxPulse::SpeakerMute(bool& enabled) const { |
| 468 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 469 bool muted(0); |
| 470 if (_mixerManager.SpeakerMute(muted) == -1) { |
| 471 return -1; |
| 472 } |
| 473 |
| 474 enabled = muted; |
| 475 return 0; |
| 476 } |
| 477 |
| 478 int32_t AudioDeviceLinuxPulse::MicrophoneMuteIsAvailable(bool& available) { |
| 479 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 480 bool isAvailable(false); |
| 481 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); |
| 482 |
| 483 // Make an attempt to open up the |
| 484 // input mixer corresponding to the currently selected input device. |
| 485 // |
| 486 if (!wasInitialized && InitMicrophone() == -1) { |
| 487 // If we end up here it means that the selected microphone has no |
| 488 // volume control, hence it is safe to state that there is no |
| 489 // boost control already at this stage. |
| 490 available = false; |
| 491 return 0; |
| 492 } |
| 493 |
| 494 // Check if the selected microphone has a mute control |
| 495 // |
| 496 _mixerManager.MicrophoneMuteIsAvailable(isAvailable); |
| 497 available = isAvailable; |
| 498 |
| 499 // Close the initialized input mixer |
| 500 // |
| 501 if (!wasInitialized) { |
| 502 _mixerManager.CloseMicrophone(); |
| 503 } |
| 504 |
| 505 return 0; |
| 506 } |
| 507 |
| 508 int32_t AudioDeviceLinuxPulse::SetMicrophoneMute(bool enable) { |
| 509 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 510 return (_mixerManager.SetMicrophoneMute(enable)); |
| 511 } |
| 512 |
| 513 int32_t AudioDeviceLinuxPulse::MicrophoneMute(bool& enabled) const { |
| 514 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 515 bool muted(0); |
| 516 if (_mixerManager.MicrophoneMute(muted) == -1) { |
| 517 return -1; |
| 518 } |
| 519 |
| 520 enabled = muted; |
| 521 return 0; |
| 522 } |
| 523 |
| 524 int32_t AudioDeviceLinuxPulse::MicrophoneBoostIsAvailable(bool& available) { |
| 525 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 526 bool isAvailable(false); |
| 527 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); |
| 528 |
| 529 // Enumerate all avaliable microphone and make an attempt to open up the |
| 530 // input mixer corresponding to the currently selected input device. |
| 531 // |
| 532 if (!wasInitialized && InitMicrophone() == -1) { |
| 533 // If we end up here it means that the selected microphone has no |
| 534 // volume control, hence it is safe to state that there is no |
| 535 // boost control already at this stage. |
| 536 available = false; |
| 537 return 0; |
| 538 } |
| 539 |
| 540 // Check if the selected microphone has a boost control |
| 541 _mixerManager.MicrophoneBoostIsAvailable(isAvailable); |
| 542 available = isAvailable; |
| 543 |
| 544 // Close the initialized input mixer |
| 545 if (!wasInitialized) { |
| 546 _mixerManager.CloseMicrophone(); |
| 547 } |
| 548 |
| 549 return 0; |
| 550 } |
| 551 |
| 552 int32_t AudioDeviceLinuxPulse::SetMicrophoneBoost(bool enable) { |
| 553 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 554 return (_mixerManager.SetMicrophoneBoost(enable)); |
| 555 } |
| 556 |
| 557 int32_t AudioDeviceLinuxPulse::MicrophoneBoost(bool& enabled) const { |
| 558 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 559 bool onOff(0); |
| 560 |
| 561 if (_mixerManager.MicrophoneBoost(onOff) == -1) { |
| 562 return -1; |
| 563 } |
| 564 |
| 565 enabled = onOff; |
| 566 |
| 567 return 0; |
| 568 } |
| 569 |
| 570 int32_t AudioDeviceLinuxPulse::StereoRecordingIsAvailable(bool& available) { |
| 571 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 572 if (_recChannels == 2 && _recording) { |
381 available = true; | 573 available = true; |
382 | 574 return 0; |
383 // Close the initialized output mixer | 575 } |
384 if (!wasInitialized) | 576 |
385 { | 577 available = false; |
386 _mixerManager.CloseSpeaker(); | 578 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); |
387 } | 579 int error = 0; |
388 | 580 |
389 return 0; | 581 if (!wasInitialized && InitMicrophone() == -1) { |
390 } | 582 // Cannot open the specified device |
391 | 583 available = false; |
392 int32_t AudioDeviceLinuxPulse::SetSpeakerVolume(uint32_t volume) | 584 return 0; |
393 { | 585 } |
394 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 586 |
395 if (!_playing) { | 587 // Check if the selected microphone can record stereo. |
396 // Only update the volume if it's been set while we weren't playing. | 588 bool isAvailable(false); |
397 update_speaker_volume_at_startup_ = true; | 589 error = _mixerManager.StereoRecordingIsAvailable(isAvailable); |
398 } | 590 if (!error) |
399 return (_mixerManager.SetSpeakerVolume(volume)); | 591 available = isAvailable; |
400 } | 592 |
401 | 593 // Close the initialized input mixer |
402 int32_t AudioDeviceLinuxPulse::SpeakerVolume(uint32_t& volume) const | 594 if (!wasInitialized) { |
403 { | 595 _mixerManager.CloseMicrophone(); |
404 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 596 } |
405 uint32_t level(0); | 597 |
406 | 598 return error; |
407 if (_mixerManager.SpeakerVolume(level) == -1) | 599 } |
408 { | 600 |
409 return -1; | 601 int32_t AudioDeviceLinuxPulse::SetStereoRecording(bool enable) { |
410 } | 602 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
411 | 603 if (enable) |
412 volume = level; | 604 _recChannels = 2; |
413 | 605 else |
414 return 0; | 606 _recChannels = 1; |
415 } | 607 |
416 | 608 return 0; |
417 int32_t AudioDeviceLinuxPulse::SetWaveOutVolume( | 609 } |
418 uint16_t volumeLeft, | 610 |
419 uint16_t volumeRight) | 611 int32_t AudioDeviceLinuxPulse::StereoRecording(bool& enabled) const { |
420 { | 612 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
421 | 613 if (_recChannels == 2) |
| 614 enabled = true; |
| 615 else |
| 616 enabled = false; |
| 617 |
| 618 return 0; |
| 619 } |
| 620 |
| 621 int32_t AudioDeviceLinuxPulse::StereoPlayoutIsAvailable(bool& available) { |
| 622 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 623 if (_playChannels == 2 && _playing) { |
| 624 available = true; |
| 625 return 0; |
| 626 } |
| 627 |
| 628 available = false; |
| 629 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); |
| 630 int error = 0; |
| 631 |
| 632 if (!wasInitialized && InitSpeaker() == -1) { |
| 633 // Cannot open the specified device. |
| 634 return -1; |
| 635 } |
| 636 |
| 637 // Check if the selected speaker can play stereo. |
| 638 bool isAvailable(false); |
| 639 error = _mixerManager.StereoPlayoutIsAvailable(isAvailable); |
| 640 if (!error) |
| 641 available = isAvailable; |
| 642 |
| 643 // Close the initialized input mixer |
| 644 if (!wasInitialized) { |
| 645 _mixerManager.CloseSpeaker(); |
| 646 } |
| 647 |
| 648 return error; |
| 649 } |
| 650 |
| 651 int32_t AudioDeviceLinuxPulse::SetStereoPlayout(bool enable) { |
| 652 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 653 if (enable) |
| 654 _playChannels = 2; |
| 655 else |
| 656 _playChannels = 1; |
| 657 |
| 658 return 0; |
| 659 } |
| 660 |
| 661 int32_t AudioDeviceLinuxPulse::StereoPlayout(bool& enabled) const { |
| 662 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 663 if (_playChannels == 2) |
| 664 enabled = true; |
| 665 else |
| 666 enabled = false; |
| 667 |
| 668 return 0; |
| 669 } |
| 670 |
| 671 int32_t AudioDeviceLinuxPulse::SetAGC(bool enable) { |
| 672 rtc::CritScope lock(&_critSect); |
| 673 _AGC = enable; |
| 674 |
| 675 return 0; |
| 676 } |
| 677 |
| 678 bool AudioDeviceLinuxPulse::AGC() const { |
| 679 rtc::CritScope lock(&_critSect); |
| 680 return _AGC; |
| 681 } |
| 682 |
| 683 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeIsAvailable(bool& available) { |
| 684 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 685 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); |
| 686 |
| 687 // Make an attempt to open up the |
| 688 // input mixer corresponding to the currently selected output device. |
| 689 if (!wasInitialized && InitMicrophone() == -1) { |
| 690 // If we end up here it means that the selected microphone has no |
| 691 // volume control. |
| 692 available = false; |
| 693 return 0; |
| 694 } |
| 695 |
| 696 // Given that InitMicrophone was successful, we know that a volume control |
| 697 // exists. |
| 698 available = true; |
| 699 |
| 700 // Close the initialized input mixer |
| 701 if (!wasInitialized) { |
| 702 _mixerManager.CloseMicrophone(); |
| 703 } |
| 704 |
| 705 return 0; |
| 706 } |
| 707 |
| 708 int32_t AudioDeviceLinuxPulse::SetMicrophoneVolume(uint32_t volume) { |
| 709 return (_mixerManager.SetMicrophoneVolume(volume)); |
| 710 } |
| 711 |
| 712 int32_t AudioDeviceLinuxPulse::MicrophoneVolume(uint32_t& volume) const { |
| 713 uint32_t level(0); |
| 714 |
| 715 if (_mixerManager.MicrophoneVolume(level) == -1) { |
422 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | 716 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
423 " API call not supported on this platform"); | 717 " failed to retrive current microphone level"); |
424 return -1; | 718 return -1; |
425 } | 719 } |
426 | 720 |
427 int32_t AudioDeviceLinuxPulse::WaveOutVolume( | 721 volume = level; |
428 uint16_t& /*volumeLeft*/, | 722 |
429 uint16_t& /*volumeRight*/) const | 723 return 0; |
430 { | 724 } |
431 | 725 |
432 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | 726 int32_t AudioDeviceLinuxPulse::MaxMicrophoneVolume(uint32_t& maxVolume) const { |
433 " API call not supported on this platform"); | 727 uint32_t maxVol(0); |
434 return -1; | 728 |
435 } | 729 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) { |
436 | 730 return -1; |
437 int32_t AudioDeviceLinuxPulse::MaxSpeakerVolume( | 731 } |
438 uint32_t& maxVolume) const | 732 |
439 { | 733 maxVolume = maxVol; |
440 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 734 |
441 uint32_t maxVol(0); | 735 return 0; |
442 | 736 } |
443 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) | 737 |
444 { | 738 int32_t AudioDeviceLinuxPulse::MinMicrophoneVolume(uint32_t& minVolume) const { |
445 return -1; | 739 uint32_t minVol(0); |
446 } | 740 |
447 | 741 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) { |
448 maxVolume = maxVol; | 742 return -1; |
449 | 743 } |
450 return 0; | 744 |
451 } | 745 minVolume = minVol; |
452 | 746 |
453 int32_t AudioDeviceLinuxPulse::MinSpeakerVolume( | 747 return 0; |
454 uint32_t& minVolume) const | |
455 { | |
456 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
457 uint32_t minVol(0); | |
458 | |
459 if (_mixerManager.MinSpeakerVolume(minVol) == -1) | |
460 { | |
461 return -1; | |
462 } | |
463 | |
464 minVolume = minVol; | |
465 | |
466 return 0; | |
467 } | |
468 | |
469 int32_t AudioDeviceLinuxPulse::SpeakerVolumeStepSize( | |
470 uint16_t& stepSize) const | |
471 { | |
472 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
473 uint16_t delta(0); | |
474 | |
475 if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) | |
476 { | |
477 return -1; | |
478 } | |
479 | |
480 stepSize = delta; | |
481 | |
482 return 0; | |
483 } | |
484 | |
485 int32_t AudioDeviceLinuxPulse::SpeakerMuteIsAvailable(bool& available) | |
486 { | |
487 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
488 bool isAvailable(false); | |
489 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); | |
490 | |
491 // Make an attempt to open up the | |
492 // output mixer corresponding to the currently selected output device. | |
493 // | |
494 if (!wasInitialized && InitSpeaker() == -1) | |
495 { | |
496 // If we end up here it means that the selected speaker has no volume | |
497 // control, hence it is safe to state that there is no mute control | |
498 // already at this stage. | |
499 available = false; | |
500 return 0; | |
501 } | |
502 | |
503 // Check if the selected speaker has a mute control | |
504 _mixerManager.SpeakerMuteIsAvailable(isAvailable); | |
505 | |
506 available = isAvailable; | |
507 | |
508 // Close the initialized output mixer | |
509 if (!wasInitialized) | |
510 { | |
511 _mixerManager.CloseSpeaker(); | |
512 } | |
513 | |
514 return 0; | |
515 } | |
516 | |
517 int32_t AudioDeviceLinuxPulse::SetSpeakerMute(bool enable) | |
518 { | |
519 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
520 return (_mixerManager.SetSpeakerMute(enable)); | |
521 } | |
522 | |
523 int32_t AudioDeviceLinuxPulse::SpeakerMute(bool& enabled) const | |
524 { | |
525 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
526 bool muted(0); | |
527 if (_mixerManager.SpeakerMute(muted) == -1) | |
528 { | |
529 return -1; | |
530 } | |
531 | |
532 enabled = muted; | |
533 return 0; | |
534 } | |
535 | |
536 int32_t AudioDeviceLinuxPulse::MicrophoneMuteIsAvailable(bool& available) | |
537 { | |
538 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
539 bool isAvailable(false); | |
540 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); | |
541 | |
542 // Make an attempt to open up the | |
543 // input mixer corresponding to the currently selected input device. | |
544 // | |
545 if (!wasInitialized && InitMicrophone() == -1) | |
546 { | |
547 // If we end up here it means that the selected microphone has no | |
548 // volume control, hence it is safe to state that there is no | |
549 // boost control already at this stage. | |
550 available = false; | |
551 return 0; | |
552 } | |
553 | |
554 // Check if the selected microphone has a mute control | |
555 // | |
556 _mixerManager.MicrophoneMuteIsAvailable(isAvailable); | |
557 available = isAvailable; | |
558 | |
559 // Close the initialized input mixer | |
560 // | |
561 if (!wasInitialized) | |
562 { | |
563 _mixerManager.CloseMicrophone(); | |
564 } | |
565 | |
566 return 0; | |
567 } | |
568 | |
569 int32_t AudioDeviceLinuxPulse::SetMicrophoneMute(bool enable) | |
570 { | |
571 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
572 return (_mixerManager.SetMicrophoneMute(enable)); | |
573 } | |
574 | |
575 int32_t AudioDeviceLinuxPulse::MicrophoneMute(bool& enabled) const | |
576 { | |
577 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
578 bool muted(0); | |
579 if (_mixerManager.MicrophoneMute(muted) == -1) | |
580 { | |
581 return -1; | |
582 } | |
583 | |
584 enabled = muted; | |
585 return 0; | |
586 } | |
587 | |
588 int32_t AudioDeviceLinuxPulse::MicrophoneBoostIsAvailable(bool& available) | |
589 { | |
590 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
591 bool isAvailable(false); | |
592 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); | |
593 | |
594 // Enumerate all avaliable microphone and make an attempt to open up the | |
595 // input mixer corresponding to the currently selected input device. | |
596 // | |
597 if (!wasInitialized && InitMicrophone() == -1) | |
598 { | |
599 // If we end up here it means that the selected microphone has no | |
600 // volume control, hence it is safe to state that there is no | |
601 // boost control already at this stage. | |
602 available = false; | |
603 return 0; | |
604 } | |
605 | |
606 // Check if the selected microphone has a boost control | |
607 _mixerManager.MicrophoneBoostIsAvailable(isAvailable); | |
608 available = isAvailable; | |
609 | |
610 // Close the initialized input mixer | |
611 if (!wasInitialized) | |
612 { | |
613 _mixerManager.CloseMicrophone(); | |
614 } | |
615 | |
616 return 0; | |
617 } | |
618 | |
619 int32_t AudioDeviceLinuxPulse::SetMicrophoneBoost(bool enable) | |
620 { | |
621 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
622 return (_mixerManager.SetMicrophoneBoost(enable)); | |
623 } | |
624 | |
625 int32_t AudioDeviceLinuxPulse::MicrophoneBoost(bool& enabled) const | |
626 { | |
627 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
628 bool onOff(0); | |
629 | |
630 if (_mixerManager.MicrophoneBoost(onOff) == -1) | |
631 { | |
632 return -1; | |
633 } | |
634 | |
635 enabled = onOff; | |
636 | |
637 return 0; | |
638 } | |
639 | |
640 int32_t AudioDeviceLinuxPulse::StereoRecordingIsAvailable(bool& available) | |
641 { | |
642 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
643 if (_recChannels == 2 && _recording) { | |
644 available = true; | |
645 return 0; | |
646 } | |
647 | |
648 available = false; | |
649 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); | |
650 int error = 0; | |
651 | |
652 if (!wasInitialized && InitMicrophone() == -1) | |
653 { | |
654 // Cannot open the specified device | |
655 available = false; | |
656 return 0; | |
657 } | |
658 | |
659 // Check if the selected microphone can record stereo. | |
660 bool isAvailable(false); | |
661 error = _mixerManager.StereoRecordingIsAvailable(isAvailable); | |
662 if (!error) | |
663 available = isAvailable; | |
664 | |
665 // Close the initialized input mixer | |
666 if (!wasInitialized) | |
667 { | |
668 _mixerManager.CloseMicrophone(); | |
669 } | |
670 | |
671 return error; | |
672 } | |
673 | |
674 int32_t AudioDeviceLinuxPulse::SetStereoRecording(bool enable) | |
675 { | |
676 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
677 if (enable) | |
678 _recChannels = 2; | |
679 else | |
680 _recChannels = 1; | |
681 | |
682 return 0; | |
683 } | |
684 | |
685 int32_t AudioDeviceLinuxPulse::StereoRecording(bool& enabled) const | |
686 { | |
687 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
688 if (_recChannels == 2) | |
689 enabled = true; | |
690 else | |
691 enabled = false; | |
692 | |
693 return 0; | |
694 } | |
695 | |
696 int32_t AudioDeviceLinuxPulse::StereoPlayoutIsAvailable(bool& available) | |
697 { | |
698 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
699 if (_playChannels == 2 && _playing) { | |
700 available = true; | |
701 return 0; | |
702 } | |
703 | |
704 available = false; | |
705 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); | |
706 int error = 0; | |
707 | |
708 if (!wasInitialized && InitSpeaker() == -1) | |
709 { | |
710 // Cannot open the specified device. | |
711 return -1; | |
712 } | |
713 | |
714 // Check if the selected speaker can play stereo. | |
715 bool isAvailable(false); | |
716 error = _mixerManager.StereoPlayoutIsAvailable(isAvailable); | |
717 if (!error) | |
718 available = isAvailable; | |
719 | |
720 // Close the initialized input mixer | |
721 if (!wasInitialized) | |
722 { | |
723 _mixerManager.CloseSpeaker(); | |
724 } | |
725 | |
726 return error; | |
727 } | |
728 | |
729 int32_t AudioDeviceLinuxPulse::SetStereoPlayout(bool enable) | |
730 { | |
731 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
732 if (enable) | |
733 _playChannels = 2; | |
734 else | |
735 _playChannels = 1; | |
736 | |
737 return 0; | |
738 } | |
739 | |
740 int32_t AudioDeviceLinuxPulse::StereoPlayout(bool& enabled) const | |
741 { | |
742 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
743 if (_playChannels == 2) | |
744 enabled = true; | |
745 else | |
746 enabled = false; | |
747 | |
748 return 0; | |
749 } | |
750 | |
751 int32_t AudioDeviceLinuxPulse::SetAGC(bool enable) | |
752 { | |
753 rtc::CritScope lock(&_critSect); | |
754 _AGC = enable; | |
755 | |
756 return 0; | |
757 } | |
758 | |
759 bool AudioDeviceLinuxPulse::AGC() const | |
760 { | |
761 rtc::CritScope lock(&_critSect); | |
762 return _AGC; | |
763 } | |
764 | |
765 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeIsAvailable( | |
766 bool& available) | |
767 { | |
768 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
769 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); | |
770 | |
771 // Make an attempt to open up the | |
772 // input mixer corresponding to the currently selected output device. | |
773 if (!wasInitialized && InitMicrophone() == -1) | |
774 { | |
775 // If we end up here it means that the selected microphone has no | |
776 // volume control. | |
777 available = false; | |
778 return 0; | |
779 } | |
780 | |
781 // Given that InitMicrophone was successful, we know that a volume control | |
782 // exists. | |
783 available = true; | |
784 | |
785 // Close the initialized input mixer | |
786 if (!wasInitialized) | |
787 { | |
788 _mixerManager.CloseMicrophone(); | |
789 } | |
790 | |
791 return 0; | |
792 } | |
793 | |
794 int32_t AudioDeviceLinuxPulse::SetMicrophoneVolume(uint32_t volume) | |
795 { | |
796 return (_mixerManager.SetMicrophoneVolume(volume)); | |
797 } | |
798 | |
799 int32_t AudioDeviceLinuxPulse::MicrophoneVolume( | |
800 uint32_t& volume) const | |
801 { | |
802 | |
803 uint32_t level(0); | |
804 | |
805 if (_mixerManager.MicrophoneVolume(level) == -1) | |
806 { | |
807 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
808 " failed to retrive current microphone level"); | |
809 return -1; | |
810 } | |
811 | |
812 volume = level; | |
813 | |
814 return 0; | |
815 } | |
816 | |
817 int32_t AudioDeviceLinuxPulse::MaxMicrophoneVolume( | |
818 uint32_t& maxVolume) const | |
819 { | |
820 | |
821 uint32_t maxVol(0); | |
822 | |
823 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) | |
824 { | |
825 return -1; | |
826 } | |
827 | |
828 maxVolume = maxVol; | |
829 | |
830 return 0; | |
831 } | |
832 | |
833 int32_t AudioDeviceLinuxPulse::MinMicrophoneVolume( | |
834 uint32_t& minVolume) const | |
835 { | |
836 | |
837 uint32_t minVol(0); | |
838 | |
839 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) | |
840 { | |
841 return -1; | |
842 } | |
843 | |
844 minVolume = minVol; | |
845 | |
846 return 0; | |
847 } | 748 } |
848 | 749 |
849 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeStepSize( | 750 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeStepSize( |
850 uint16_t& stepSize) const | 751 uint16_t& stepSize) const { |
851 { | 752 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
852 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 753 uint16_t delta(0); |
853 uint16_t delta(0); | 754 |
854 | 755 if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) { |
855 if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) | 756 return -1; |
856 { | 757 } |
857 return -1; | 758 |
858 } | 759 stepSize = delta; |
859 | 760 |
860 stepSize = delta; | 761 return 0; |
861 | 762 } |
862 return 0; | 763 |
863 } | 764 int16_t AudioDeviceLinuxPulse::PlayoutDevices() { |
864 | 765 PaLock(); |
865 int16_t AudioDeviceLinuxPulse::PlayoutDevices() | 766 |
866 { | 767 pa_operation* paOperation = NULL; |
867 PaLock(); | 768 _numPlayDevices = 1; // init to 1 to account for "default" |
868 | 769 |
869 pa_operation* paOperation = NULL; | 770 // get the whole list of devices and update _numPlayDevices |
870 _numPlayDevices = 1; // init to 1 to account for "default" | 771 paOperation = |
871 | 772 LATE(pa_context_get_sink_info_list)(_paContext, PaSinkInfoCallback, this); |
872 // get the whole list of devices and update _numPlayDevices | 773 |
873 paOperation = LATE(pa_context_get_sink_info_list)(_paContext, | 774 WaitForOperationCompletion(paOperation); |
874 PaSinkInfoCallback, | 775 |
875 this); | 776 PaUnLock(); |
876 | 777 |
877 WaitForOperationCompletion(paOperation); | 778 return _numPlayDevices; |
878 | 779 } |
879 PaUnLock(); | 780 |
880 | 781 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice(uint16_t index) { |
881 return _numPlayDevices; | 782 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
882 } | 783 if (_playIsInitialized) { |
883 | 784 return -1; |
884 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice(uint16_t index) | 785 } |
885 { | 786 |
886 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 787 const uint16_t nDevices = PlayoutDevices(); |
887 if (_playIsInitialized) | 788 |
888 { | 789 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
889 return -1; | 790 " number of availiable output devices is %u", nDevices); |
890 } | 791 |
891 | 792 if (index > (nDevices - 1)) { |
892 const uint16_t nDevices = PlayoutDevices(); | 793 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
893 | 794 " device index is out of range [0,%u]", (nDevices - 1)); |
894 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, | 795 return -1; |
895 " number of availiable output devices is %u", nDevices); | 796 } |
896 | 797 |
897 if (index > (nDevices - 1)) | 798 _outputDeviceIndex = index; |
898 { | 799 _outputDeviceIsSpecified = true; |
899 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 800 |
900 " device index is out of range [0,%u]", (nDevices - 1)); | 801 return 0; |
901 return -1; | |
902 } | |
903 | |
904 _outputDeviceIndex = index; | |
905 _outputDeviceIsSpecified = true; | |
906 | |
907 return 0; | |
908 } | 802 } |
909 | 803 |
910 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice( | 804 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice( |
911 AudioDeviceModule::WindowsDeviceType /*device*/) | 805 AudioDeviceModule::WindowsDeviceType /*device*/) { |
912 { | 806 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
913 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 807 "WindowsDeviceType not supported"); |
914 "WindowsDeviceType not supported"); | 808 return -1; |
915 return -1; | |
916 } | 809 } |
917 | 810 |
918 int32_t AudioDeviceLinuxPulse::PlayoutDeviceName( | 811 int32_t AudioDeviceLinuxPulse::PlayoutDeviceName( |
919 uint16_t index, | 812 uint16_t index, |
920 char name[kAdmMaxDeviceNameSize], | 813 char name[kAdmMaxDeviceNameSize], |
921 char guid[kAdmMaxGuidSize]) | 814 char guid[kAdmMaxGuidSize]) { |
922 { | 815 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
923 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 816 const uint16_t nDevices = PlayoutDevices(); |
924 const uint16_t nDevices = PlayoutDevices(); | 817 |
925 | 818 if ((index > (nDevices - 1)) || (name == NULL)) { |
926 if ((index > (nDevices - 1)) || (name == NULL)) | 819 return -1; |
927 { | 820 } |
928 return -1; | 821 |
929 } | 822 memset(name, 0, kAdmMaxDeviceNameSize); |
930 | 823 |
931 memset(name, 0, kAdmMaxDeviceNameSize); | 824 if (guid != NULL) { |
932 | 825 memset(guid, 0, kAdmMaxGuidSize); |
933 if (guid != NULL) | 826 } |
934 { | 827 |
935 memset(guid, 0, kAdmMaxGuidSize); | 828 // Check if default device |
936 } | 829 if (index == 0) { |
937 | 830 uint16_t deviceIndex = 0; |
938 // Check if default device | 831 return GetDefaultDeviceInfo(false, name, deviceIndex); |
939 if (index == 0) | 832 } |
940 { | 833 |
941 uint16_t deviceIndex = 0; | 834 // Tell the callback that we want |
942 return GetDefaultDeviceInfo(false, name, deviceIndex); | 835 // The name for this device |
943 } | 836 _playDisplayDeviceName = name; |
944 | 837 _deviceIndex = index; |
945 // Tell the callback that we want | 838 |
946 // The name for this device | 839 // get playout devices |
947 _playDisplayDeviceName = name; | 840 PlayoutDevices(); |
948 _deviceIndex = index; | 841 |
949 | 842 // clear device name and index |
950 // get playout devices | 843 _playDisplayDeviceName = NULL; |
951 PlayoutDevices(); | 844 _deviceIndex = -1; |
952 | 845 |
953 // clear device name and index | 846 return 0; |
954 _playDisplayDeviceName = NULL; | |
955 _deviceIndex = -1; | |
956 | |
957 return 0; | |
958 } | 847 } |
959 | 848 |
960 int32_t AudioDeviceLinuxPulse::RecordingDeviceName( | 849 int32_t AudioDeviceLinuxPulse::RecordingDeviceName( |
961 uint16_t index, | 850 uint16_t index, |
962 char name[kAdmMaxDeviceNameSize], | 851 char name[kAdmMaxDeviceNameSize], |
963 char guid[kAdmMaxGuidSize]) | 852 char guid[kAdmMaxGuidSize]) { |
964 { | 853 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
965 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 854 const uint16_t nDevices(RecordingDevices()); |
966 const uint16_t nDevices(RecordingDevices()); | 855 |
967 | 856 if ((index > (nDevices - 1)) || (name == NULL)) { |
968 if ((index > (nDevices - 1)) || (name == NULL)) | 857 return -1; |
| 858 } |
| 859 |
| 860 memset(name, 0, kAdmMaxDeviceNameSize); |
| 861 |
| 862 if (guid != NULL) { |
| 863 memset(guid, 0, kAdmMaxGuidSize); |
| 864 } |
| 865 |
| 866 // Check if default device |
| 867 if (index == 0) { |
| 868 uint16_t deviceIndex = 0; |
| 869 return GetDefaultDeviceInfo(true, name, deviceIndex); |
| 870 } |
| 871 |
| 872 // Tell the callback that we want |
| 873 // the name for this device |
| 874 _recDisplayDeviceName = name; |
| 875 _deviceIndex = index; |
| 876 |
| 877 // Get recording devices |
| 878 RecordingDevices(); |
| 879 |
| 880 // Clear device name and index |
| 881 _recDisplayDeviceName = NULL; |
| 882 _deviceIndex = -1; |
| 883 |
| 884 return 0; |
| 885 } |
| 886 |
| 887 int16_t AudioDeviceLinuxPulse::RecordingDevices() { |
| 888 PaLock(); |
| 889 |
| 890 pa_operation* paOperation = NULL; |
| 891 _numRecDevices = 1; // Init to 1 to account for "default" |
| 892 |
| 893 // Get the whole list of devices and update _numRecDevices |
| 894 paOperation = LATE(pa_context_get_source_info_list)( |
| 895 _paContext, PaSourceInfoCallback, this); |
| 896 |
| 897 WaitForOperationCompletion(paOperation); |
| 898 |
| 899 PaUnLock(); |
| 900 |
| 901 return _numRecDevices; |
| 902 } |
| 903 |
| 904 int32_t AudioDeviceLinuxPulse::SetRecordingDevice(uint16_t index) { |
| 905 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 906 if (_recIsInitialized) { |
| 907 return -1; |
| 908 } |
| 909 |
| 910 const uint16_t nDevices(RecordingDevices()); |
| 911 |
| 912 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| 913 " number of availiable input devices is %u", nDevices); |
| 914 |
| 915 if (index > (nDevices - 1)) { |
| 916 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 917 " device index is out of range [0,%u]", (nDevices - 1)); |
| 918 return -1; |
| 919 } |
| 920 |
| 921 _inputDeviceIndex = index; |
| 922 _inputDeviceIsSpecified = true; |
| 923 |
| 924 return 0; |
| 925 } |
| 926 |
| 927 int32_t AudioDeviceLinuxPulse::SetRecordingDevice( |
| 928 AudioDeviceModule::WindowsDeviceType /*device*/) { |
| 929 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 930 "WindowsDeviceType not supported"); |
| 931 return -1; |
| 932 } |
| 933 |
| 934 int32_t AudioDeviceLinuxPulse::PlayoutIsAvailable(bool& available) { |
| 935 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 936 available = false; |
| 937 |
| 938 // Try to initialize the playout side |
| 939 int32_t res = InitPlayout(); |
| 940 |
| 941 // Cancel effect of initialization |
| 942 StopPlayout(); |
| 943 |
| 944 if (res != -1) { |
| 945 available = true; |
| 946 } |
| 947 |
| 948 return res; |
| 949 } |
| 950 |
| 951 int32_t AudioDeviceLinuxPulse::RecordingIsAvailable(bool& available) { |
| 952 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 953 available = false; |
| 954 |
| 955 // Try to initialize the playout side |
| 956 int32_t res = InitRecording(); |
| 957 |
| 958 // Cancel effect of initialization |
| 959 StopRecording(); |
| 960 |
| 961 if (res != -1) { |
| 962 available = true; |
| 963 } |
| 964 |
| 965 return res; |
| 966 } |
| 967 |
| 968 int32_t AudioDeviceLinuxPulse::InitPlayout() { |
| 969 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 970 |
| 971 if (_playing) { |
| 972 return -1; |
| 973 } |
| 974 |
| 975 if (!_outputDeviceIsSpecified) { |
| 976 return -1; |
| 977 } |
| 978 |
| 979 if (_playIsInitialized) { |
| 980 return 0; |
| 981 } |
| 982 |
| 983 // Initialize the speaker (devices might have been added or removed) |
| 984 if (InitSpeaker() == -1) { |
| 985 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 986 " InitSpeaker() failed"); |
| 987 } |
| 988 |
| 989 // Set the play sample specification |
| 990 pa_sample_spec playSampleSpec; |
| 991 playSampleSpec.channels = _playChannels; |
| 992 playSampleSpec.format = PA_SAMPLE_S16LE; |
| 993 playSampleSpec.rate = sample_rate_hz_; |
| 994 |
| 995 // Create a new play stream |
| 996 _playStream = |
| 997 LATE(pa_stream_new)(_paContext, "playStream", &playSampleSpec, NULL); |
| 998 |
| 999 if (!_playStream) { |
| 1000 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1001 " failed to create play stream, err=%d", |
| 1002 LATE(pa_context_errno)(_paContext)); |
| 1003 return -1; |
| 1004 } |
| 1005 |
| 1006 // Provide the playStream to the mixer |
| 1007 _mixerManager.SetPlayStream(_playStream); |
| 1008 |
| 1009 if (_ptrAudioBuffer) { |
| 1010 // Update audio buffer with the selected parameters |
| 1011 _ptrAudioBuffer->SetPlayoutSampleRate(sample_rate_hz_); |
| 1012 _ptrAudioBuffer->SetPlayoutChannels((uint8_t)_playChannels); |
| 1013 } |
| 1014 |
| 1015 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " stream state %d\n", |
| 1016 LATE(pa_stream_get_state)(_playStream)); |
| 1017 |
| 1018 // Set stream flags |
| 1019 _playStreamFlags = (pa_stream_flags_t)(PA_STREAM_AUTO_TIMING_UPDATE | |
| 1020 PA_STREAM_INTERPOLATE_TIMING); |
| 1021 |
| 1022 if (_configuredLatencyPlay != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) { |
| 1023 // If configuring a specific latency then we want to specify |
| 1024 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters |
| 1025 // automatically to reach that target latency. However, that flag |
| 1026 // doesn't exist in Ubuntu 8.04 and many people still use that, |
| 1027 // so we have to check the protocol version of libpulse. |
| 1028 if (LATE(pa_context_get_protocol_version)(_paContext) >= |
| 1029 WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) { |
| 1030 _playStreamFlags |= PA_STREAM_ADJUST_LATENCY; |
| 1031 } |
| 1032 |
| 1033 const pa_sample_spec* spec = LATE(pa_stream_get_sample_spec)(_playStream); |
| 1034 if (!spec) { |
| 1035 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1036 " pa_stream_get_sample_spec()"); |
| 1037 return -1; |
| 1038 } |
| 1039 |
| 1040 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); |
| 1041 uint32_t latency = bytesPerSec * WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS / |
| 1042 WEBRTC_PA_MSECS_PER_SEC; |
| 1043 |
| 1044 // Set the play buffer attributes |
| 1045 _playBufferAttr.maxlength = latency; // num bytes stored in the buffer |
| 1046 _playBufferAttr.tlength = latency; // target fill level of play buffer |
| 1047 // minimum free num bytes before server request more data |
| 1048 _playBufferAttr.minreq = latency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; |
| 1049 // prebuffer tlength before starting playout |
| 1050 _playBufferAttr.prebuf = _playBufferAttr.tlength - _playBufferAttr.minreq; |
| 1051 |
| 1052 _configuredLatencyPlay = latency; |
| 1053 } |
| 1054 |
| 1055 // num samples in bytes * num channels |
| 1056 _playbackBufferSize = sample_rate_hz_ / 100 * 2 * _playChannels; |
| 1057 _playbackBufferUnused = _playbackBufferSize; |
| 1058 _playBuffer = new int8_t[_playbackBufferSize]; |
| 1059 |
| 1060 // Enable underflow callback |
| 1061 LATE(pa_stream_set_underflow_callback) |
| 1062 (_playStream, PaStreamUnderflowCallback, this); |
| 1063 |
| 1064 // Set the state callback function for the stream |
| 1065 LATE(pa_stream_set_state_callback)(_playStream, PaStreamStateCallback, this); |
| 1066 |
| 1067 // Mark playout side as initialized |
| 1068 _playIsInitialized = true; |
| 1069 _sndCardPlayDelay = 0; |
| 1070 _sndCardRecDelay = 0; |
| 1071 |
| 1072 return 0; |
| 1073 } |
| 1074 |
| 1075 int32_t AudioDeviceLinuxPulse::InitRecording() { |
| 1076 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1077 |
| 1078 if (_recording) { |
| 1079 return -1; |
| 1080 } |
| 1081 |
| 1082 if (!_inputDeviceIsSpecified) { |
| 1083 return -1; |
| 1084 } |
| 1085 |
| 1086 if (_recIsInitialized) { |
| 1087 return 0; |
| 1088 } |
| 1089 |
| 1090 // Initialize the microphone (devices might have been added or removed) |
| 1091 if (InitMicrophone() == -1) { |
| 1092 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 1093 " InitMicrophone() failed"); |
| 1094 } |
| 1095 |
| 1096 // Set the rec sample specification |
| 1097 pa_sample_spec recSampleSpec; |
| 1098 recSampleSpec.channels = _recChannels; |
| 1099 recSampleSpec.format = PA_SAMPLE_S16LE; |
| 1100 recSampleSpec.rate = sample_rate_hz_; |
| 1101 |
| 1102 // Create a new rec stream |
| 1103 _recStream = |
| 1104 LATE(pa_stream_new)(_paContext, "recStream", &recSampleSpec, NULL); |
| 1105 if (!_recStream) { |
| 1106 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1107 " failed to create rec stream, err=%d", |
| 1108 LATE(pa_context_errno)(_paContext)); |
| 1109 return -1; |
| 1110 } |
| 1111 |
| 1112 // Provide the recStream to the mixer |
| 1113 _mixerManager.SetRecStream(_recStream); |
| 1114 |
| 1115 if (_ptrAudioBuffer) { |
| 1116 // Update audio buffer with the selected parameters |
| 1117 _ptrAudioBuffer->SetRecordingSampleRate(sample_rate_hz_); |
| 1118 _ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels); |
| 1119 } |
| 1120 |
| 1121 if (_configuredLatencyRec != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) { |
| 1122 _recStreamFlags = (pa_stream_flags_t)(PA_STREAM_AUTO_TIMING_UPDATE | |
| 1123 PA_STREAM_INTERPOLATE_TIMING); |
| 1124 |
| 1125 // If configuring a specific latency then we want to specify |
| 1126 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters |
| 1127 // automatically to reach that target latency. However, that flag |
| 1128 // doesn't exist in Ubuntu 8.04 and many people still use that, |
| 1129 // so we have to check the protocol version of libpulse. |
| 1130 if (LATE(pa_context_get_protocol_version)(_paContext) >= |
| 1131 WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) { |
| 1132 _recStreamFlags |= PA_STREAM_ADJUST_LATENCY; |
| 1133 } |
| 1134 |
| 1135 const pa_sample_spec* spec = LATE(pa_stream_get_sample_spec)(_recStream); |
| 1136 if (!spec) { |
| 1137 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1138 " pa_stream_get_sample_spec(rec)"); |
| 1139 return -1; |
| 1140 } |
| 1141 |
| 1142 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); |
| 1143 uint32_t latency = bytesPerSec * WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS / |
| 1144 WEBRTC_PA_MSECS_PER_SEC; |
| 1145 |
| 1146 // Set the rec buffer attributes |
| 1147 // Note: fragsize specifies a maximum transfer size, not a minimum, so |
| 1148 // it is not possible to force a high latency setting, only a low one. |
| 1149 _recBufferAttr.fragsize = latency; // size of fragment |
| 1150 _recBufferAttr.maxlength = |
| 1151 latency + bytesPerSec * WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS / |
| 1152 WEBRTC_PA_MSECS_PER_SEC; |
| 1153 |
| 1154 _configuredLatencyRec = latency; |
| 1155 } |
| 1156 |
| 1157 _recordBufferSize = sample_rate_hz_ / 100 * 2 * _recChannels; |
| 1158 _recordBufferUsed = 0; |
| 1159 _recBuffer = new int8_t[_recordBufferSize]; |
| 1160 |
| 1161 // Enable overflow callback |
| 1162 LATE(pa_stream_set_overflow_callback) |
| 1163 (_recStream, PaStreamOverflowCallback, this); |
| 1164 |
| 1165 // Set the state callback function for the stream |
| 1166 LATE(pa_stream_set_state_callback)(_recStream, PaStreamStateCallback, this); |
| 1167 |
| 1168 // Mark recording side as initialized |
| 1169 _recIsInitialized = true; |
| 1170 |
| 1171 return 0; |
| 1172 } |
| 1173 |
| 1174 int32_t AudioDeviceLinuxPulse::StartRecording() { |
| 1175 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1176 if (!_recIsInitialized) { |
| 1177 return -1; |
| 1178 } |
| 1179 |
| 1180 if (_recording) { |
| 1181 return 0; |
| 1182 } |
| 1183 |
| 1184 // Set state to ensure that the recording starts from the audio thread. |
| 1185 _startRec = true; |
| 1186 |
| 1187 // The audio thread will signal when recording has started. |
| 1188 _timeEventRec.Set(); |
| 1189 if (kEventTimeout == _recStartEvent.Wait(10000)) { |
969 { | 1190 { |
970 return -1; | 1191 rtc::CritScope lock(&_critSect); |
| 1192 _startRec = false; |
971 } | 1193 } |
972 | 1194 StopRecording(); |
973 memset(name, 0, kAdmMaxDeviceNameSize); | 1195 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
974 | 1196 " failed to activate recording"); |
975 if (guid != NULL) | 1197 return -1; |
| 1198 } |
| 1199 |
| 1200 { |
| 1201 rtc::CritScope lock(&_critSect); |
| 1202 if (_recording) { |
| 1203 // The recording state is set by the audio thread after recording |
| 1204 // has started. |
| 1205 } else { |
| 1206 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1207 " failed to activate recording"); |
| 1208 return -1; |
| 1209 } |
| 1210 } |
| 1211 |
| 1212 return 0; |
| 1213 } |
| 1214 |
| 1215 int32_t AudioDeviceLinuxPulse::StopRecording() { |
| 1216 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1217 rtc::CritScope lock(&_critSect); |
| 1218 |
| 1219 if (!_recIsInitialized) { |
| 1220 return 0; |
| 1221 } |
| 1222 |
| 1223 if (_recStream == NULL) { |
| 1224 return -1; |
| 1225 } |
| 1226 |
| 1227 _recIsInitialized = false; |
| 1228 _recording = false; |
| 1229 |
| 1230 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " stopping recording"); |
| 1231 |
| 1232 // Stop Recording |
| 1233 PaLock(); |
| 1234 |
| 1235 DisableReadCallback(); |
| 1236 LATE(pa_stream_set_overflow_callback)(_recStream, NULL, NULL); |
| 1237 |
| 1238 // Unset this here so that we don't get a TERMINATED callback |
| 1239 LATE(pa_stream_set_state_callback)(_recStream, NULL, NULL); |
| 1240 |
| 1241 if (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_UNCONNECTED) { |
| 1242 // Disconnect the stream |
| 1243 if (LATE(pa_stream_disconnect)(_recStream) != PA_OK) { |
| 1244 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1245 " failed to disconnect rec stream, err=%d\n", |
| 1246 LATE(pa_context_errno)(_paContext)); |
| 1247 PaUnLock(); |
| 1248 return -1; |
| 1249 } |
| 1250 |
| 1251 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, |
| 1252 " disconnected recording"); |
| 1253 } |
| 1254 |
| 1255 LATE(pa_stream_unref)(_recStream); |
| 1256 _recStream = NULL; |
| 1257 |
| 1258 PaUnLock(); |
| 1259 |
| 1260 // Provide the recStream to the mixer |
| 1261 _mixerManager.SetRecStream(_recStream); |
| 1262 |
| 1263 if (_recBuffer) { |
| 1264 delete[] _recBuffer; |
| 1265 _recBuffer = NULL; |
| 1266 } |
| 1267 |
| 1268 return 0; |
| 1269 } |
| 1270 |
| 1271 bool AudioDeviceLinuxPulse::RecordingIsInitialized() const { |
| 1272 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1273 return (_recIsInitialized); |
| 1274 } |
| 1275 |
| 1276 bool AudioDeviceLinuxPulse::Recording() const { |
| 1277 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1278 return (_recording); |
| 1279 } |
| 1280 |
| 1281 bool AudioDeviceLinuxPulse::PlayoutIsInitialized() const { |
| 1282 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1283 return (_playIsInitialized); |
| 1284 } |
| 1285 |
| 1286 int32_t AudioDeviceLinuxPulse::StartPlayout() { |
| 1287 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
| 1288 |
| 1289 if (!_playIsInitialized) { |
| 1290 return -1; |
| 1291 } |
| 1292 |
| 1293 if (_playing) { |
| 1294 return 0; |
| 1295 } |
| 1296 |
| 1297 // Set state to ensure that playout starts from the audio thread. |
| 1298 { |
| 1299 rtc::CritScope lock(&_critSect); |
| 1300 _startPlay = true; |
| 1301 } |
| 1302 |
| 1303 // Both |_startPlay| and |_playing| needs protction since they are also |
| 1304 // accessed on the playout thread. |
| 1305 |
| 1306 // The audio thread will signal when playout has started. |
| 1307 _timeEventPlay.Set(); |
| 1308 if (kEventTimeout == _playStartEvent.Wait(10000)) { |
976 { | 1309 { |
977 memset(guid, 0, kAdmMaxGuidSize); | 1310 rtc::CritScope lock(&_critSect); |
| 1311 _startPlay = false; |
978 } | 1312 } |
979 | 1313 StopPlayout(); |
980 // Check if default device | 1314 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
981 if (index == 0) | 1315 " failed to activate playout"); |
982 { | 1316 return -1; |
983 uint16_t deviceIndex = 0; | 1317 } |
984 return GetDefaultDeviceInfo(true, name, deviceIndex); | 1318 |
| 1319 { |
| 1320 rtc::CritScope lock(&_critSect); |
| 1321 if (_playing) { |
| 1322 // The playing state is set by the audio thread after playout |
| 1323 // has started. |
| 1324 } else { |
| 1325 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1326 " failed to activate playing"); |
| 1327 return -1; |
985 } | 1328 } |
986 | 1329 } |
987 // Tell the callback that we want | 1330 |
988 // the name for this device | 1331 return 0; |
989 _recDisplayDeviceName = name; | 1332 } |
990 _deviceIndex = index; | 1333 |
991 | 1334 int32_t AudioDeviceLinuxPulse::StopPlayout() { |
992 // Get recording devices | 1335 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
993 RecordingDevices(); | 1336 rtc::CritScope lock(&_critSect); |
994 | 1337 |
995 // Clear device name and index | 1338 if (!_playIsInitialized) { |
996 _recDisplayDeviceName = NULL; | 1339 return 0; |
997 _deviceIndex = -1; | 1340 } |
998 | 1341 |
999 return 0; | 1342 if (_playStream == NULL) { |
1000 } | 1343 return -1; |
1001 | 1344 } |
1002 int16_t AudioDeviceLinuxPulse::RecordingDevices() | 1345 |
1003 { | 1346 _playIsInitialized = false; |
1004 PaLock(); | 1347 _playing = false; |
1005 | 1348 _sndCardPlayDelay = 0; |
1006 pa_operation* paOperation = NULL; | 1349 _sndCardRecDelay = 0; |
1007 _numRecDevices = 1; // Init to 1 to account for "default" | 1350 |
1008 | 1351 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " stopping playback"); |
1009 // Get the whole list of devices and update _numRecDevices | 1352 |
1010 paOperation = LATE(pa_context_get_source_info_list)(_paContext, | 1353 // Stop Playout |
1011 PaSourceInfoCallback, | 1354 PaLock(); |
1012 this); | 1355 |
1013 | 1356 DisableWriteCallback(); |
1014 WaitForOperationCompletion(paOperation); | 1357 LATE(pa_stream_set_underflow_callback)(_playStream, NULL, NULL); |
1015 | 1358 |
1016 PaUnLock(); | 1359 // Unset this here so that we don't get a TERMINATED callback |
1017 | 1360 LATE(pa_stream_set_state_callback)(_playStream, NULL, NULL); |
1018 return _numRecDevices; | 1361 |
1019 } | 1362 if (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_UNCONNECTED) { |
1020 | 1363 // Disconnect the stream |
1021 int32_t AudioDeviceLinuxPulse::SetRecordingDevice(uint16_t index) | 1364 if (LATE(pa_stream_disconnect)(_playStream) != PA_OK) { |
1022 { | 1365 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
1023 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 1366 " failed to disconnect play stream, err=%d", |
1024 if (_recIsInitialized) | 1367 LATE(pa_context_errno)(_paContext)); |
1025 { | 1368 PaUnLock(); |
1026 return -1; | 1369 return -1; |
1027 } | 1370 } |
1028 | 1371 |
1029 const uint16_t nDevices(RecordingDevices()); | |
1030 | |
1031 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, | |
1032 " number of availiable input devices is %u", nDevices); | |
1033 | |
1034 if (index > (nDevices - 1)) | |
1035 { | |
1036 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1037 " device index is out of range [0,%u]", (nDevices - 1)); | |
1038 return -1; | |
1039 } | |
1040 | |
1041 _inputDeviceIndex = index; | |
1042 _inputDeviceIsSpecified = true; | |
1043 | |
1044 return 0; | |
1045 } | |
1046 | |
1047 int32_t AudioDeviceLinuxPulse::SetRecordingDevice( | |
1048 AudioDeviceModule::WindowsDeviceType /*device*/) | |
1049 { | |
1050 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1051 "WindowsDeviceType not supported"); | |
1052 return -1; | |
1053 } | |
1054 | |
1055 int32_t AudioDeviceLinuxPulse::PlayoutIsAvailable(bool& available) | |
1056 { | |
1057 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1058 available = false; | |
1059 | |
1060 // Try to initialize the playout side | |
1061 int32_t res = InitPlayout(); | |
1062 | |
1063 // Cancel effect of initialization | |
1064 StopPlayout(); | |
1065 | |
1066 if (res != -1) | |
1067 { | |
1068 available = true; | |
1069 } | |
1070 | |
1071 return res; | |
1072 } | |
1073 | |
1074 int32_t AudioDeviceLinuxPulse::RecordingIsAvailable(bool& available) | |
1075 { | |
1076 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1077 available = false; | |
1078 | |
1079 // Try to initialize the playout side | |
1080 int32_t res = InitRecording(); | |
1081 | |
1082 // Cancel effect of initialization | |
1083 StopRecording(); | |
1084 | |
1085 if (res != -1) | |
1086 { | |
1087 available = true; | |
1088 } | |
1089 | |
1090 return res; | |
1091 } | |
1092 | |
1093 int32_t AudioDeviceLinuxPulse::InitPlayout() | |
1094 { | |
1095 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1096 | |
1097 if (_playing) | |
1098 { | |
1099 return -1; | |
1100 } | |
1101 | |
1102 if (!_outputDeviceIsSpecified) | |
1103 { | |
1104 return -1; | |
1105 } | |
1106 | |
1107 if (_playIsInitialized) | |
1108 { | |
1109 return 0; | |
1110 } | |
1111 | |
1112 // Initialize the speaker (devices might have been added or removed) | |
1113 if (InitSpeaker() == -1) | |
1114 { | |
1115 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
1116 " InitSpeaker() failed"); | |
1117 } | |
1118 | |
1119 // Set the play sample specification | |
1120 pa_sample_spec playSampleSpec; | |
1121 playSampleSpec.channels = _playChannels; | |
1122 playSampleSpec.format = PA_SAMPLE_S16LE; | |
1123 playSampleSpec.rate = sample_rate_hz_; | |
1124 | |
1125 // Create a new play stream | |
1126 _playStream = LATE(pa_stream_new)(_paContext, "playStream", | |
1127 &playSampleSpec, NULL); | |
1128 | |
1129 if (!_playStream) | |
1130 { | |
1131 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1132 " failed to create play stream, err=%d", | |
1133 LATE(pa_context_errno)(_paContext)); | |
1134 return -1; | |
1135 } | |
1136 | |
1137 // Provide the playStream to the mixer | |
1138 _mixerManager.SetPlayStream(_playStream); | |
1139 | |
1140 if (_ptrAudioBuffer) | |
1141 { | |
1142 // Update audio buffer with the selected parameters | |
1143 _ptrAudioBuffer->SetPlayoutSampleRate(sample_rate_hz_); | |
1144 _ptrAudioBuffer->SetPlayoutChannels((uint8_t) _playChannels); | |
1145 } | |
1146 | |
1147 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1372 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, |
1148 " stream state %d\n", | 1373 " disconnected playback"); |
1149 LATE(pa_stream_get_state)(_playStream)); | 1374 } |
1150 | 1375 |
1151 // Set stream flags | 1376 LATE(pa_stream_unref)(_playStream); |
1152 _playStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE | 1377 _playStream = NULL; |
1153 | PA_STREAM_INTERPOLATE_TIMING); | 1378 |
1154 | 1379 PaUnLock(); |
1155 if (_configuredLatencyPlay != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) | 1380 |
1156 { | 1381 // Provide the playStream to the mixer |
1157 // If configuring a specific latency then we want to specify | 1382 _mixerManager.SetPlayStream(_playStream); |
1158 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters | 1383 |
1159 // automatically to reach that target latency. However, that flag | 1384 if (_playBuffer) { |
1160 // doesn't exist in Ubuntu 8.04 and many people still use that, | 1385 delete[] _playBuffer; |
1161 // so we have to check the protocol version of libpulse. | 1386 _playBuffer = NULL; |
1162 if (LATE(pa_context_get_protocol_version)(_paContext) | 1387 } |
1163 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) | 1388 |
1164 { | 1389 return 0; |
1165 _playStreamFlags |= PA_STREAM_ADJUST_LATENCY; | 1390 } |
1166 } | 1391 |
1167 | 1392 int32_t AudioDeviceLinuxPulse::PlayoutDelay(uint16_t& delayMS) const { |
1168 const pa_sample_spec *spec = | 1393 rtc::CritScope lock(&_critSect); |
1169 LATE(pa_stream_get_sample_spec)(_playStream); | 1394 delayMS = (uint16_t)_sndCardPlayDelay; |
1170 if (!spec) | 1395 return 0; |
1171 { | 1396 } |
1172 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1397 |
1173 " pa_stream_get_sample_spec()"); | 1398 int32_t AudioDeviceLinuxPulse::RecordingDelay(uint16_t& delayMS) const { |
1174 return -1; | 1399 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
1175 } | 1400 delayMS = (uint16_t)_sndCardRecDelay; |
1176 | 1401 return 0; |
1177 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); | 1402 } |
1178 uint32_t latency = bytesPerSec * | 1403 |
1179 WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS / | 1404 bool AudioDeviceLinuxPulse::Playing() const { |
1180 WEBRTC_PA_MSECS_PER_SEC; | 1405 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
1181 | 1406 return (_playing); |
1182 // Set the play buffer attributes | |
1183 _playBufferAttr.maxlength = latency; // num bytes stored in the buffer | |
1184 _playBufferAttr.tlength = latency; // target fill level of play buffer | |
1185 // minimum free num bytes before server request more data | |
1186 _playBufferAttr.minreq = latency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; | |
1187 // prebuffer tlength before starting playout | |
1188 _playBufferAttr.prebuf = _playBufferAttr.tlength - | |
1189 _playBufferAttr.minreq; | |
1190 | |
1191 _configuredLatencyPlay = latency; | |
1192 } | |
1193 | |
1194 // num samples in bytes * num channels | |
1195 _playbackBufferSize = sample_rate_hz_ / 100 * 2 * _playChannels; | |
1196 _playbackBufferUnused = _playbackBufferSize; | |
1197 _playBuffer = new int8_t[_playbackBufferSize]; | |
1198 | |
1199 // Enable underflow callback | |
1200 LATE(pa_stream_set_underflow_callback)(_playStream, | |
1201 PaStreamUnderflowCallback, this); | |
1202 | |
1203 // Set the state callback function for the stream | |
1204 LATE(pa_stream_set_state_callback)(_playStream, | |
1205 PaStreamStateCallback, this); | |
1206 | |
1207 // Mark playout side as initialized | |
1208 _playIsInitialized = true; | |
1209 _sndCardPlayDelay = 0; | |
1210 _sndCardRecDelay = 0; | |
1211 | |
1212 return 0; | |
1213 } | |
1214 | |
1215 int32_t AudioDeviceLinuxPulse::InitRecording() | |
1216 { | |
1217 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1218 | |
1219 if (_recording) | |
1220 { | |
1221 return -1; | |
1222 } | |
1223 | |
1224 if (!_inputDeviceIsSpecified) | |
1225 { | |
1226 return -1; | |
1227 } | |
1228 | |
1229 if (_recIsInitialized) | |
1230 { | |
1231 return 0; | |
1232 } | |
1233 | |
1234 // Initialize the microphone (devices might have been added or removed) | |
1235 if (InitMicrophone() == -1) | |
1236 { | |
1237 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
1238 " InitMicrophone() failed"); | |
1239 } | |
1240 | |
1241 // Set the rec sample specification | |
1242 pa_sample_spec recSampleSpec; | |
1243 recSampleSpec.channels = _recChannels; | |
1244 recSampleSpec.format = PA_SAMPLE_S16LE; | |
1245 recSampleSpec.rate = sample_rate_hz_; | |
1246 | |
1247 // Create a new rec stream | |
1248 _recStream = LATE(pa_stream_new)(_paContext, "recStream", &recSampleSpec, | |
1249 NULL); | |
1250 if (!_recStream) | |
1251 { | |
1252 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1253 " failed to create rec stream, err=%d", | |
1254 LATE(pa_context_errno)(_paContext)); | |
1255 return -1; | |
1256 } | |
1257 | |
1258 // Provide the recStream to the mixer | |
1259 _mixerManager.SetRecStream(_recStream); | |
1260 | |
1261 if (_ptrAudioBuffer) | |
1262 { | |
1263 // Update audio buffer with the selected parameters | |
1264 _ptrAudioBuffer->SetRecordingSampleRate(sample_rate_hz_); | |
1265 _ptrAudioBuffer->SetRecordingChannels((uint8_t) _recChannels); | |
1266 } | |
1267 | |
1268 if (_configuredLatencyRec != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) | |
1269 { | |
1270 _recStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE | |
1271 | PA_STREAM_INTERPOLATE_TIMING); | |
1272 | |
1273 // If configuring a specific latency then we want to specify | |
1274 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters | |
1275 // automatically to reach that target latency. However, that flag | |
1276 // doesn't exist in Ubuntu 8.04 and many people still use that, | |
1277 // so we have to check the protocol version of libpulse. | |
1278 if (LATE(pa_context_get_protocol_version)(_paContext) | |
1279 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) | |
1280 { | |
1281 _recStreamFlags |= PA_STREAM_ADJUST_LATENCY; | |
1282 } | |
1283 | |
1284 const pa_sample_spec *spec = | |
1285 LATE(pa_stream_get_sample_spec)(_recStream); | |
1286 if (!spec) | |
1287 { | |
1288 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1289 " pa_stream_get_sample_spec(rec)"); | |
1290 return -1; | |
1291 } | |
1292 | |
1293 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); | |
1294 uint32_t latency = bytesPerSec | |
1295 * WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS / WEBRTC_PA_MSECS_PER_SEC; | |
1296 | |
1297 // Set the rec buffer attributes | |
1298 // Note: fragsize specifies a maximum transfer size, not a minimum, so | |
1299 // it is not possible to force a high latency setting, only a low one. | |
1300 _recBufferAttr.fragsize = latency; // size of fragment | |
1301 _recBufferAttr.maxlength = latency + bytesPerSec | |
1302 * WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS / WEBRTC_PA_MSECS_PER_SEC; | |
1303 | |
1304 _configuredLatencyRec = latency; | |
1305 } | |
1306 | |
1307 _recordBufferSize = sample_rate_hz_ / 100 * 2 * _recChannels; | |
1308 _recordBufferUsed = 0; | |
1309 _recBuffer = new int8_t[_recordBufferSize]; | |
1310 | |
1311 // Enable overflow callback | |
1312 LATE(pa_stream_set_overflow_callback)(_recStream, | |
1313 PaStreamOverflowCallback, | |
1314 this); | |
1315 | |
1316 // Set the state callback function for the stream | |
1317 LATE(pa_stream_set_state_callback)(_recStream, | |
1318 PaStreamStateCallback, | |
1319 this); | |
1320 | |
1321 // Mark recording side as initialized | |
1322 _recIsInitialized = true; | |
1323 | |
1324 return 0; | |
1325 } | |
1326 | |
1327 int32_t AudioDeviceLinuxPulse::StartRecording() | |
1328 { | |
1329 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1330 if (!_recIsInitialized) | |
1331 { | |
1332 return -1; | |
1333 } | |
1334 | |
1335 if (_recording) | |
1336 { | |
1337 return 0; | |
1338 } | |
1339 | |
1340 // Set state to ensure that the recording starts from the audio thread. | |
1341 _startRec = true; | |
1342 | |
1343 // The audio thread will signal when recording has started. | |
1344 _timeEventRec.Set(); | |
1345 if (kEventTimeout == _recStartEvent.Wait(10000)) | |
1346 { | |
1347 { | |
1348 rtc::CritScope lock(&_critSect); | |
1349 _startRec = false; | |
1350 } | |
1351 StopRecording(); | |
1352 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1353 " failed to activate recording"); | |
1354 return -1; | |
1355 } | |
1356 | |
1357 { | |
1358 rtc::CritScope lock(&_critSect); | |
1359 if (_recording) | |
1360 { | |
1361 // The recording state is set by the audio thread after recording | |
1362 // has started. | |
1363 } else | |
1364 { | |
1365 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1366 " failed to activate recording"); | |
1367 return -1; | |
1368 } | |
1369 } | |
1370 | |
1371 return 0; | |
1372 } | |
1373 | |
1374 int32_t AudioDeviceLinuxPulse::StopRecording() | |
1375 { | |
1376 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1377 rtc::CritScope lock(&_critSect); | |
1378 | |
1379 if (!_recIsInitialized) | |
1380 { | |
1381 return 0; | |
1382 } | |
1383 | |
1384 if (_recStream == NULL) | |
1385 { | |
1386 return -1; | |
1387 } | |
1388 | |
1389 _recIsInitialized = false; | |
1390 _recording = false; | |
1391 | |
1392 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
1393 " stopping recording"); | |
1394 | |
1395 // Stop Recording | |
1396 PaLock(); | |
1397 | |
1398 DisableReadCallback(); | |
1399 LATE(pa_stream_set_overflow_callback)(_recStream, NULL, NULL); | |
1400 | |
1401 // Unset this here so that we don't get a TERMINATED callback | |
1402 LATE(pa_stream_set_state_callback)(_recStream, NULL, NULL); | |
1403 | |
1404 if (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_UNCONNECTED) | |
1405 { | |
1406 // Disconnect the stream | |
1407 if (LATE(pa_stream_disconnect)(_recStream) != PA_OK) | |
1408 { | |
1409 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1410 " failed to disconnect rec stream, err=%d\n", | |
1411 LATE(pa_context_errno)(_paContext)); | |
1412 PaUnLock(); | |
1413 return -1; | |
1414 } | |
1415 | |
1416 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
1417 " disconnected recording"); | |
1418 } | |
1419 | |
1420 LATE(pa_stream_unref)(_recStream); | |
1421 _recStream = NULL; | |
1422 | |
1423 PaUnLock(); | |
1424 | |
1425 // Provide the recStream to the mixer | |
1426 _mixerManager.SetRecStream(_recStream); | |
1427 | |
1428 if (_recBuffer) | |
1429 { | |
1430 delete [] _recBuffer; | |
1431 _recBuffer = NULL; | |
1432 } | |
1433 | |
1434 return 0; | |
1435 } | |
1436 | |
1437 bool AudioDeviceLinuxPulse::RecordingIsInitialized() const | |
1438 { | |
1439 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1440 return (_recIsInitialized); | |
1441 } | |
1442 | |
1443 bool AudioDeviceLinuxPulse::Recording() const | |
1444 { | |
1445 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1446 return (_recording); | |
1447 } | |
1448 | |
1449 bool AudioDeviceLinuxPulse::PlayoutIsInitialized() const | |
1450 { | |
1451 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1452 return (_playIsInitialized); | |
1453 } | |
1454 | |
1455 int32_t AudioDeviceLinuxPulse::StartPlayout() | |
1456 { | |
1457 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1458 | |
1459 if (!_playIsInitialized) | |
1460 { | |
1461 return -1; | |
1462 } | |
1463 | |
1464 if (_playing) | |
1465 { | |
1466 return 0; | |
1467 } | |
1468 | |
1469 // Set state to ensure that playout starts from the audio thread. | |
1470 { | |
1471 rtc::CritScope lock(&_critSect); | |
1472 _startPlay = true; | |
1473 } | |
1474 | |
1475 // Both |_startPlay| and |_playing| needs protction since they are also | |
1476 // accessed on the playout thread. | |
1477 | |
1478 // The audio thread will signal when playout has started. | |
1479 _timeEventPlay.Set(); | |
1480 if (kEventTimeout == _playStartEvent.Wait(10000)) | |
1481 { | |
1482 { | |
1483 rtc::CritScope lock(&_critSect); | |
1484 _startPlay = false; | |
1485 } | |
1486 StopPlayout(); | |
1487 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1488 " failed to activate playout"); | |
1489 return -1; | |
1490 } | |
1491 | |
1492 { | |
1493 rtc::CritScope lock(&_critSect); | |
1494 if (_playing) | |
1495 { | |
1496 // The playing state is set by the audio thread after playout | |
1497 // has started. | |
1498 } else | |
1499 { | |
1500 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1501 " failed to activate playing"); | |
1502 return -1; | |
1503 } | |
1504 } | |
1505 | |
1506 return 0; | |
1507 } | |
1508 | |
1509 int32_t AudioDeviceLinuxPulse::StopPlayout() | |
1510 { | |
1511 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1512 rtc::CritScope lock(&_critSect); | |
1513 | |
1514 if (!_playIsInitialized) | |
1515 { | |
1516 return 0; | |
1517 } | |
1518 | |
1519 if (_playStream == NULL) | |
1520 { | |
1521 return -1; | |
1522 } | |
1523 | |
1524 _playIsInitialized = false; | |
1525 _playing = false; | |
1526 _sndCardPlayDelay = 0; | |
1527 _sndCardRecDelay = 0; | |
1528 | |
1529 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
1530 " stopping playback"); | |
1531 | |
1532 // Stop Playout | |
1533 PaLock(); | |
1534 | |
1535 DisableWriteCallback(); | |
1536 LATE(pa_stream_set_underflow_callback)(_playStream, NULL, NULL); | |
1537 | |
1538 // Unset this here so that we don't get a TERMINATED callback | |
1539 LATE(pa_stream_set_state_callback)(_playStream, NULL, NULL); | |
1540 | |
1541 if (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_UNCONNECTED) | |
1542 { | |
1543 // Disconnect the stream | |
1544 if (LATE(pa_stream_disconnect)(_playStream) != PA_OK) | |
1545 { | |
1546 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
1547 " failed to disconnect play stream, err=%d", | |
1548 LATE(pa_context_errno)(_paContext)); | |
1549 PaUnLock(); | |
1550 return -1; | |
1551 } | |
1552 | |
1553 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
1554 " disconnected playback"); | |
1555 } | |
1556 | |
1557 LATE(pa_stream_unref)(_playStream); | |
1558 _playStream = NULL; | |
1559 | |
1560 PaUnLock(); | |
1561 | |
1562 // Provide the playStream to the mixer | |
1563 _mixerManager.SetPlayStream(_playStream); | |
1564 | |
1565 if (_playBuffer) | |
1566 { | |
1567 delete [] _playBuffer; | |
1568 _playBuffer = NULL; | |
1569 } | |
1570 | |
1571 return 0; | |
1572 } | |
1573 | |
1574 int32_t AudioDeviceLinuxPulse::PlayoutDelay(uint16_t& delayMS) const | |
1575 { | |
1576 rtc::CritScope lock(&_critSect); | |
1577 delayMS = (uint16_t) _sndCardPlayDelay; | |
1578 return 0; | |
1579 } | |
1580 | |
1581 int32_t AudioDeviceLinuxPulse::RecordingDelay(uint16_t& delayMS) const | |
1582 { | |
1583 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1584 delayMS = (uint16_t) _sndCardRecDelay; | |
1585 return 0; | |
1586 } | |
1587 | |
1588 bool AudioDeviceLinuxPulse::Playing() const | |
1589 { | |
1590 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
1591 return (_playing); | |
1592 } | 1407 } |
1593 | 1408 |
1594 int32_t AudioDeviceLinuxPulse::SetPlayoutBuffer( | 1409 int32_t AudioDeviceLinuxPulse::SetPlayoutBuffer( |
1595 const AudioDeviceModule::BufferType type, | 1410 const AudioDeviceModule::BufferType type, |
1596 uint16_t sizeMS) | 1411 uint16_t sizeMS) { |
1597 { | 1412 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
1598 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 1413 if (type != AudioDeviceModule::kFixedBufferSize) { |
1599 if (type != AudioDeviceModule::kFixedBufferSize) | 1414 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
1600 { | 1415 " Adaptive buffer size not supported on this platform"); |
1601 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1416 return -1; |
1602 " Adaptive buffer size not supported on this platform"); | 1417 } |
1603 return -1; | 1418 |
1604 } | 1419 _playBufType = type; |
1605 | 1420 _playBufDelayFixed = sizeMS; |
1606 _playBufType = type; | 1421 |
1607 _playBufDelayFixed = sizeMS; | 1422 return 0; |
1608 | |
1609 return 0; | |
1610 } | 1423 } |
1611 | 1424 |
1612 int32_t AudioDeviceLinuxPulse::PlayoutBuffer( | 1425 int32_t AudioDeviceLinuxPulse::PlayoutBuffer( |
1613 AudioDeviceModule::BufferType& type, | 1426 AudioDeviceModule::BufferType& type, |
1614 uint16_t& sizeMS) const | 1427 uint16_t& sizeMS) const { |
1615 { | 1428 RTC_DCHECK(thread_checker_.CalledOnValidThread()); |
1616 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | 1429 type = _playBufType; |
1617 type = _playBufType; | 1430 sizeMS = _playBufDelayFixed; |
1618 sizeMS = _playBufDelayFixed; | 1431 |
1619 | 1432 return 0; |
1620 return 0; | 1433 } |
1621 } | 1434 |
1622 | 1435 int32_t AudioDeviceLinuxPulse::CPULoad(uint16_t& /*load*/) const { |
1623 int32_t AudioDeviceLinuxPulse::CPULoad(uint16_t& /*load*/) const | 1436 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
1624 { | 1437 " API call not supported on this platform"); |
1625 | 1438 return -1; |
1626 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | 1439 } |
1627 " API call not supported on this platform"); | 1440 |
1628 return -1; | 1441 bool AudioDeviceLinuxPulse::PlayoutWarning() const { |
1629 } | |
1630 | |
1631 bool AudioDeviceLinuxPulse::PlayoutWarning() const | |
1632 { | |
1633 rtc::CritScope lock(&_critSect); | 1442 rtc::CritScope lock(&_critSect); |
1634 return (_playWarning > 0); | 1443 return (_playWarning > 0); |
1635 } | 1444 } |
1636 | 1445 |
1637 bool AudioDeviceLinuxPulse::PlayoutError() const | 1446 bool AudioDeviceLinuxPulse::PlayoutError() const { |
1638 { | |
1639 rtc::CritScope lock(&_critSect); | 1447 rtc::CritScope lock(&_critSect); |
1640 return (_playError > 0); | 1448 return (_playError > 0); |
1641 } | 1449 } |
1642 | 1450 |
1643 bool AudioDeviceLinuxPulse::RecordingWarning() const | 1451 bool AudioDeviceLinuxPulse::RecordingWarning() const { |
1644 { | |
1645 rtc::CritScope lock(&_critSect); | 1452 rtc::CritScope lock(&_critSect); |
1646 return (_recWarning > 0); | 1453 return (_recWarning > 0); |
1647 } | 1454 } |
1648 | 1455 |
1649 bool AudioDeviceLinuxPulse::RecordingError() const | 1456 bool AudioDeviceLinuxPulse::RecordingError() const { |
1650 { | |
1651 rtc::CritScope lock(&_critSect); | 1457 rtc::CritScope lock(&_critSect); |
1652 return (_recError > 0); | 1458 return (_recError > 0); |
1653 } | 1459 } |
1654 | 1460 |
1655 void AudioDeviceLinuxPulse::ClearPlayoutWarning() | 1461 void AudioDeviceLinuxPulse::ClearPlayoutWarning() { |
1656 { | |
1657 rtc::CritScope lock(&_critSect); | 1462 rtc::CritScope lock(&_critSect); |
1658 _playWarning = 0; | 1463 _playWarning = 0; |
1659 } | 1464 } |
1660 | 1465 |
1661 void AudioDeviceLinuxPulse::ClearPlayoutError() | 1466 void AudioDeviceLinuxPulse::ClearPlayoutError() { |
1662 { | |
1663 rtc::CritScope lock(&_critSect); | 1467 rtc::CritScope lock(&_critSect); |
1664 _playError = 0; | 1468 _playError = 0; |
1665 } | 1469 } |
1666 | 1470 |
1667 void AudioDeviceLinuxPulse::ClearRecordingWarning() | 1471 void AudioDeviceLinuxPulse::ClearRecordingWarning() { |
1668 { | |
1669 rtc::CritScope lock(&_critSect); | 1472 rtc::CritScope lock(&_critSect); |
1670 _recWarning = 0; | 1473 _recWarning = 0; |
1671 } | 1474 } |
1672 | 1475 |
1673 void AudioDeviceLinuxPulse::ClearRecordingError() | 1476 void AudioDeviceLinuxPulse::ClearRecordingError() { |
1674 { | |
1675 rtc::CritScope lock(&_critSect); | 1477 rtc::CritScope lock(&_critSect); |
1676 _recError = 0; | 1478 _recError = 0; |
1677 } | 1479 } |
1678 | 1480 |
1679 // ============================================================================ | 1481 // ============================================================================ |
1680 // Private Methods | 1482 // Private Methods |
1681 // ============================================================================ | 1483 // ============================================================================ |
1682 | 1484 |
1683 void AudioDeviceLinuxPulse::PaContextStateCallback(pa_context *c, void *pThis) | 1485 void AudioDeviceLinuxPulse::PaContextStateCallback(pa_context* c, void* pThis) { |
1684 { | 1486 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaContextStateCallbackHandler(c); |
1685 static_cast<AudioDeviceLinuxPulse*> (pThis)-> | |
1686 PaContextStateCallbackHandler(c); | |
1687 } | 1487 } |
1688 | 1488 |
1689 // ---------------------------------------------------------------------------- | 1489 // ---------------------------------------------------------------------------- |
1690 // PaSinkInfoCallback | 1490 // PaSinkInfoCallback |
1691 // ---------------------------------------------------------------------------- | 1491 // ---------------------------------------------------------------------------- |
1692 | 1492 |
1693 void AudioDeviceLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, | 1493 void AudioDeviceLinuxPulse::PaSinkInfoCallback(pa_context* /*c*/, |
1694 const pa_sink_info *i, int eol, | 1494 const pa_sink_info* i, |
1695 void *pThis) | 1495 int eol, |
1696 { | 1496 void* pThis) { |
1697 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaSinkInfoCallbackHandler( | 1497 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaSinkInfoCallbackHandler(i, eol); |
1698 i, eol); | 1498 } |
1699 } | 1499 |
1700 | 1500 void AudioDeviceLinuxPulse::PaSourceInfoCallback(pa_context* /*c*/, |
1701 void AudioDeviceLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, | 1501 const pa_source_info* i, |
1702 const pa_source_info *i, | 1502 int eol, |
1703 int eol, void *pThis) | 1503 void* pThis) { |
1704 { | 1504 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaSourceInfoCallbackHandler(i, |
1705 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaSourceInfoCallbackHandler( | 1505 eol); |
1706 i, eol); | 1506 } |
1707 } | 1507 |
1708 | 1508 void AudioDeviceLinuxPulse::PaServerInfoCallback(pa_context* /*c*/, |
1709 void AudioDeviceLinuxPulse::PaServerInfoCallback(pa_context */*c*/, | 1509 const pa_server_info* i, |
1710 const pa_server_info *i, | 1510 void* pThis) { |
1711 void *pThis) | 1511 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaServerInfoCallbackHandler(i); |
1712 { | 1512 } |
1713 static_cast<AudioDeviceLinuxPulse*> (pThis)-> | 1513 |
1714 PaServerInfoCallbackHandler(i); | 1514 void AudioDeviceLinuxPulse::PaStreamStateCallback(pa_stream* p, void* pThis) { |
1715 } | 1515 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaStreamStateCallbackHandler(p); |
1716 | 1516 } |
1717 void AudioDeviceLinuxPulse::PaStreamStateCallback(pa_stream *p, void *pThis) | 1517 |
1718 { | 1518 void AudioDeviceLinuxPulse::PaContextStateCallbackHandler(pa_context* c) { |
1719 static_cast<AudioDeviceLinuxPulse*> (pThis)-> | 1519 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " context state cb"); |
1720 PaStreamStateCallbackHandler(p); | 1520 |
1721 } | 1521 pa_context_state_t state = LATE(pa_context_get_state)(c); |
1722 | 1522 switch (state) { |
1723 void AudioDeviceLinuxPulse::PaContextStateCallbackHandler(pa_context *c) | 1523 case PA_CONTEXT_UNCONNECTED: |
1724 { | 1524 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " unconnected"); |
1725 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1525 break; |
1726 " context state cb"); | 1526 case PA_CONTEXT_CONNECTING: |
1727 | 1527 case PA_CONTEXT_AUTHORIZING: |
1728 pa_context_state_t state = LATE(pa_context_get_state)(c); | 1528 case PA_CONTEXT_SETTING_NAME: |
1729 switch (state) | 1529 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " no state"); |
1730 { | 1530 break; |
1731 case PA_CONTEXT_UNCONNECTED: | 1531 case PA_CONTEXT_FAILED: |
1732 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1532 case PA_CONTEXT_TERMINATED: |
1733 " unconnected"); | 1533 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " failed"); |
1734 break; | 1534 _paStateChanged = true; |
1735 case PA_CONTEXT_CONNECTING: | 1535 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
1736 case PA_CONTEXT_AUTHORIZING: | 1536 break; |
1737 case PA_CONTEXT_SETTING_NAME: | 1537 case PA_CONTEXT_READY: |
1738 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1538 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " ready"); |
1739 " no state"); | 1539 _paStateChanged = true; |
1740 break; | 1540 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
1741 case PA_CONTEXT_FAILED: | 1541 break; |
1742 case PA_CONTEXT_TERMINATED: | 1542 } |
1743 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1543 } |
1744 " failed"); | 1544 |
1745 _paStateChanged = true; | 1545 void AudioDeviceLinuxPulse::PaSinkInfoCallbackHandler(const pa_sink_info* i, |
1746 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); | 1546 int eol) { |
1747 break; | 1547 if (eol) { |
1748 case PA_CONTEXT_READY: | 1548 // Signal that we are done |
1749 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1549 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
1750 " ready"); | 1550 return; |
1751 _paStateChanged = true; | 1551 } |
1752 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); | 1552 |
1753 break; | 1553 if (_numPlayDevices == _deviceIndex) { |
1754 } | 1554 // Convert the device index to the one of the sink |
1755 } | 1555 _paDeviceIndex = i->index; |
1756 | 1556 |
1757 void AudioDeviceLinuxPulse::PaSinkInfoCallbackHandler(const pa_sink_info *i, | 1557 if (_playDeviceName) { |
1758 int eol) | 1558 // Copy the sink name |
1759 { | 1559 strncpy(_playDeviceName, i->name, kAdmMaxDeviceNameSize); |
1760 if (eol) | 1560 _playDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; |
1761 { | 1561 } |
1762 // Signal that we are done | 1562 if (_playDisplayDeviceName) { |
1763 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); | 1563 // Copy the sink display name |
1764 return; | 1564 strncpy(_playDisplayDeviceName, i->description, kAdmMaxDeviceNameSize); |
1765 } | 1565 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; |
1766 | 1566 } |
1767 if (_numPlayDevices == _deviceIndex) | 1567 } |
1768 { | 1568 |
1769 // Convert the device index to the one of the sink | 1569 _numPlayDevices++; |
1770 _paDeviceIndex = i->index; | 1570 } |
1771 | 1571 |
1772 if (_playDeviceName) | 1572 void AudioDeviceLinuxPulse::PaSourceInfoCallbackHandler(const pa_source_info* i, |
1773 { | 1573 int eol) { |
1774 // Copy the sink name | 1574 if (eol) { |
1775 strncpy(_playDeviceName, i->name, kAdmMaxDeviceNameSize); | 1575 // Signal that we are done |
1776 _playDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; | 1576 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
1777 } | 1577 return; |
1778 if (_playDisplayDeviceName) | 1578 } |
1779 { | 1579 |
1780 // Copy the sink display name | 1580 // We don't want to list output devices |
1781 strncpy(_playDisplayDeviceName, i->description, | 1581 if (i->monitor_of_sink == PA_INVALID_INDEX) { |
1782 kAdmMaxDeviceNameSize); | 1582 if (_numRecDevices == _deviceIndex) { |
1783 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; | 1583 // Convert the device index to the one of the source |
1784 } | 1584 _paDeviceIndex = i->index; |
1785 } | 1585 |
1786 | 1586 if (_recDeviceName) { |
1787 _numPlayDevices++; | 1587 // copy the source name |
1788 } | 1588 strncpy(_recDeviceName, i->name, kAdmMaxDeviceNameSize); |
1789 | 1589 _recDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; |
1790 void AudioDeviceLinuxPulse::PaSourceInfoCallbackHandler( | 1590 } |
1791 const pa_source_info *i, | 1591 if (_recDisplayDeviceName) { |
1792 int eol) | 1592 // Copy the source display name |
1793 { | 1593 strncpy(_recDisplayDeviceName, i->description, kAdmMaxDeviceNameSize); |
1794 if (eol) | 1594 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; |
1795 { | 1595 } |
1796 // Signal that we are done | 1596 } |
1797 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); | 1597 |
1798 return; | 1598 _numRecDevices++; |
1799 } | 1599 } |
1800 | |
1801 // We don't want to list output devices | |
1802 if (i->monitor_of_sink == PA_INVALID_INDEX) | |
1803 { | |
1804 if (_numRecDevices == _deviceIndex) | |
1805 { | |
1806 // Convert the device index to the one of the source | |
1807 _paDeviceIndex = i->index; | |
1808 | |
1809 if (_recDeviceName) | |
1810 { | |
1811 // copy the source name | |
1812 strncpy(_recDeviceName, i->name, kAdmMaxDeviceNameSize); | |
1813 _recDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; | |
1814 } | |
1815 if (_recDisplayDeviceName) | |
1816 { | |
1817 // Copy the source display name | |
1818 strncpy(_recDisplayDeviceName, i->description, | |
1819 kAdmMaxDeviceNameSize); | |
1820 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; | |
1821 } | |
1822 } | |
1823 | |
1824 _numRecDevices++; | |
1825 } | |
1826 } | 1600 } |
1827 | 1601 |
1828 void AudioDeviceLinuxPulse::PaServerInfoCallbackHandler( | 1602 void AudioDeviceLinuxPulse::PaServerInfoCallbackHandler( |
1829 const pa_server_info *i) | 1603 const pa_server_info* i) { |
1830 { | 1604 // Use PA native sampling rate |
1831 // Use PA native sampling rate | 1605 sample_rate_hz_ = i->sample_spec.rate; |
1832 sample_rate_hz_ = i->sample_spec.rate; | 1606 |
1833 | 1607 // Copy the PA server version |
1834 // Copy the PA server version | 1608 strncpy(_paServerVersion, i->server_version, 31); |
1835 strncpy(_paServerVersion, i->server_version, 31); | 1609 _paServerVersion[31] = '\0'; |
1836 _paServerVersion[31] = '\0'; | 1610 |
1837 | 1611 if (_recDisplayDeviceName) { |
1838 if (_recDisplayDeviceName) | 1612 // Copy the source name |
1839 { | 1613 strncpy(_recDisplayDeviceName, i->default_source_name, |
1840 // Copy the source name | 1614 kAdmMaxDeviceNameSize); |
1841 strncpy(_recDisplayDeviceName, i->default_source_name, | 1615 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; |
1842 kAdmMaxDeviceNameSize); | 1616 } |
1843 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; | 1617 |
1844 } | 1618 if (_playDisplayDeviceName) { |
1845 | 1619 // Copy the sink name |
1846 if (_playDisplayDeviceName) | 1620 strncpy(_playDisplayDeviceName, i->default_sink_name, |
1847 { | 1621 kAdmMaxDeviceNameSize); |
1848 // Copy the sink name | 1622 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; |
1849 strncpy(_playDisplayDeviceName, i->default_sink_name, | 1623 } |
1850 kAdmMaxDeviceNameSize); | 1624 |
1851 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; | 1625 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
1852 } | 1626 } |
1853 | 1627 |
1854 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); | 1628 void AudioDeviceLinuxPulse::PaStreamStateCallbackHandler(pa_stream* p) { |
1855 } | 1629 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " stream state cb"); |
1856 | 1630 |
1857 void AudioDeviceLinuxPulse::PaStreamStateCallbackHandler(pa_stream *p) | 1631 pa_stream_state_t state = LATE(pa_stream_get_state)(p); |
1858 { | 1632 switch (state) { |
1859 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1633 case PA_STREAM_UNCONNECTED: |
1860 " stream state cb"); | 1634 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " unconnected"); |
1861 | 1635 break; |
1862 pa_stream_state_t state = LATE(pa_stream_get_state)(p); | 1636 case PA_STREAM_CREATING: |
1863 switch (state) | 1637 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " creating"); |
1864 { | 1638 break; |
1865 case PA_STREAM_UNCONNECTED: | 1639 case PA_STREAM_FAILED: |
1866 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1640 case PA_STREAM_TERMINATED: |
1867 " unconnected"); | 1641 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " failed"); |
1868 break; | 1642 break; |
1869 case PA_STREAM_CREATING: | 1643 case PA_STREAM_READY: |
1870 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1644 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " ready"); |
1871 " creating"); | 1645 break; |
1872 break; | 1646 } |
1873 case PA_STREAM_FAILED: | 1647 |
1874 case PA_STREAM_TERMINATED: | 1648 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
1875 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1649 } |
1876 " failed"); | 1650 |
1877 break; | 1651 int32_t AudioDeviceLinuxPulse::CheckPulseAudioVersion() { |
1878 case PA_STREAM_READY: | 1652 PaLock(); |
1879 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | 1653 |
1880 " ready"); | 1654 pa_operation* paOperation = NULL; |
1881 break; | 1655 |
1882 } | 1656 // get the server info and update deviceName |
1883 | 1657 paOperation = |
1884 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); | 1658 LATE(pa_context_get_server_info)(_paContext, PaServerInfoCallback, this); |
1885 } | 1659 |
1886 | 1660 WaitForOperationCompletion(paOperation); |
1887 int32_t AudioDeviceLinuxPulse::CheckPulseAudioVersion() | 1661 |
1888 { | 1662 PaUnLock(); |
1889 PaLock(); | 1663 |
1890 | 1664 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, |
1891 pa_operation* paOperation = NULL; | 1665 " checking PulseAudio version: %s", _paServerVersion); |
1892 | 1666 |
1893 // get the server info and update deviceName | 1667 return 0; |
1894 paOperation = LATE(pa_context_get_server_info)(_paContext, | 1668 } |
1895 PaServerInfoCallback, | 1669 |
1896 this); | 1670 int32_t AudioDeviceLinuxPulse::InitSamplingFrequency() { |
1897 | 1671 PaLock(); |
1898 WaitForOperationCompletion(paOperation); | 1672 |
1899 | 1673 pa_operation* paOperation = NULL; |
1900 PaUnLock(); | 1674 |
1901 | 1675 // Get the server info and update sample_rate_hz_ |
1902 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, | 1676 paOperation = |
1903 " checking PulseAudio version: %s", _paServerVersion); | 1677 LATE(pa_context_get_server_info)(_paContext, PaServerInfoCallback, this); |
1904 | 1678 |
1905 return 0; | 1679 WaitForOperationCompletion(paOperation); |
1906 } | 1680 |
1907 | 1681 PaUnLock(); |
1908 int32_t AudioDeviceLinuxPulse::InitSamplingFrequency() | 1682 |
1909 { | 1683 return 0; |
1910 PaLock(); | |
1911 | |
1912 pa_operation* paOperation = NULL; | |
1913 | |
1914 // Get the server info and update sample_rate_hz_ | |
1915 paOperation = LATE(pa_context_get_server_info)(_paContext, | |
1916 PaServerInfoCallback, | |
1917 this); | |
1918 | |
1919 WaitForOperationCompletion(paOperation); | |
1920 | |
1921 PaUnLock(); | |
1922 | |
1923 return 0; | |
1924 } | 1684 } |
1925 | 1685 |
1926 int32_t AudioDeviceLinuxPulse::GetDefaultDeviceInfo(bool recDevice, | 1686 int32_t AudioDeviceLinuxPulse::GetDefaultDeviceInfo(bool recDevice, |
1927 char* name, | 1687 char* name, |
1928 uint16_t& index) | 1688 uint16_t& index) { |
1929 { | 1689 char tmpName[kAdmMaxDeviceNameSize] = {0}; |
1930 char tmpName[kAdmMaxDeviceNameSize] = {0}; | 1690 // subtract length of "default: " |
1931 // subtract length of "default: " | 1691 uint16_t nameLen = kAdmMaxDeviceNameSize - 9; |
1932 uint16_t nameLen = kAdmMaxDeviceNameSize - 9; | 1692 char* pName = NULL; |
1933 char* pName = NULL; | 1693 |
1934 | 1694 if (name) { |
1935 if (name) | 1695 // Add "default: " |
1936 { | 1696 strcpy(name, "default: "); |
1937 // Add "default: " | 1697 pName = &name[9]; |
1938 strcpy(name, "default: "); | 1698 } |
1939 pName = &name[9]; | 1699 |
1940 } | 1700 // Tell the callback that we want |
1941 | 1701 // the name for this device |
1942 // Tell the callback that we want | 1702 if (recDevice) { |
1943 // the name for this device | 1703 _recDisplayDeviceName = tmpName; |
1944 if (recDevice) | 1704 } else { |
1945 { | 1705 _playDisplayDeviceName = tmpName; |
1946 _recDisplayDeviceName = tmpName; | 1706 } |
1947 } else | 1707 |
1948 { | 1708 // Set members |
1949 _playDisplayDeviceName = tmpName; | 1709 _paDeviceIndex = -1; |
1950 } | 1710 _deviceIndex = 0; |
1951 | 1711 _numPlayDevices = 0; |
1952 // Set members | 1712 _numRecDevices = 0; |
1953 _paDeviceIndex = -1; | 1713 |
1954 _deviceIndex = 0; | 1714 PaLock(); |
1955 _numPlayDevices = 0; | 1715 |
1956 _numRecDevices = 0; | 1716 pa_operation* paOperation = NULL; |
1957 | 1717 |
1958 PaLock(); | 1718 // Get the server info and update deviceName |
1959 | 1719 paOperation = |
1960 pa_operation* paOperation = NULL; | 1720 LATE(pa_context_get_server_info)(_paContext, PaServerInfoCallback, this); |
1961 | 1721 |
1962 // Get the server info and update deviceName | 1722 WaitForOperationCompletion(paOperation); |
1963 paOperation = LATE(pa_context_get_server_info)(_paContext, | 1723 |
1964 PaServerInfoCallback, | 1724 // Get the device index |
1965 this); | 1725 if (recDevice) { |
1966 | 1726 paOperation = LATE(pa_context_get_source_info_by_name)( |
1967 WaitForOperationCompletion(paOperation); | 1727 _paContext, (char*)tmpName, PaSourceInfoCallback, this); |
1968 | 1728 } else { |
1969 // Get the device index | 1729 paOperation = LATE(pa_context_get_sink_info_by_name)( |
1970 if (recDevice) | 1730 _paContext, (char*)tmpName, PaSinkInfoCallback, this); |
1971 { | 1731 } |
1972 paOperation | 1732 |
1973 = LATE(pa_context_get_source_info_by_name)(_paContext, | 1733 WaitForOperationCompletion(paOperation); |
1974 (char *) tmpName, | 1734 |
1975 PaSourceInfoCallback, | 1735 PaUnLock(); |
1976 this); | 1736 |
1977 } else | 1737 // Set the index |
1978 { | 1738 index = _paDeviceIndex; |
1979 paOperation | 1739 |
1980 = LATE(pa_context_get_sink_info_by_name)(_paContext, | 1740 if (name) { |
1981 (char *) tmpName, | 1741 // Copy to name string |
1982 PaSinkInfoCallback, | 1742 strncpy(pName, tmpName, nameLen); |
1983 this); | 1743 } |
1984 } | 1744 |
1985 | 1745 // Clear members |
1986 WaitForOperationCompletion(paOperation); | 1746 _playDisplayDeviceName = NULL; |
1987 | 1747 _recDisplayDeviceName = NULL; |
| 1748 _paDeviceIndex = -1; |
| 1749 _deviceIndex = -1; |
| 1750 _numPlayDevices = 0; |
| 1751 _numRecDevices = 0; |
| 1752 |
| 1753 return 0; |
| 1754 } |
| 1755 |
| 1756 int32_t AudioDeviceLinuxPulse::InitPulseAudio() { |
| 1757 int retVal = 0; |
| 1758 |
| 1759 // Load libpulse |
| 1760 if (!PaSymbolTable.Load()) { |
| 1761 // Most likely the Pulse library and sound server are not installed on |
| 1762 // this system |
| 1763 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1764 " failed to load symbol table"); |
| 1765 return -1; |
| 1766 } |
| 1767 |
| 1768 // Create a mainloop API and connection to the default server |
| 1769 // the mainloop is the internal asynchronous API event loop |
| 1770 if (_paMainloop) { |
| 1771 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1772 " PA mainloop has already existed"); |
| 1773 return -1; |
| 1774 } |
| 1775 _paMainloop = LATE(pa_threaded_mainloop_new)(); |
| 1776 if (!_paMainloop) { |
| 1777 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1778 " could not create mainloop"); |
| 1779 return -1; |
| 1780 } |
| 1781 |
| 1782 // Start the threaded main loop |
| 1783 retVal = LATE(pa_threaded_mainloop_start)(_paMainloop); |
| 1784 if (retVal != PA_OK) { |
| 1785 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1786 " failed to start main loop, error=%d", retVal); |
| 1787 return -1; |
| 1788 } |
| 1789 |
| 1790 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " mainloop running!"); |
| 1791 |
| 1792 PaLock(); |
| 1793 |
| 1794 _paMainloopApi = LATE(pa_threaded_mainloop_get_api)(_paMainloop); |
| 1795 if (!_paMainloopApi) { |
| 1796 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1797 " could not create mainloop API"); |
1988 PaUnLock(); | 1798 PaUnLock(); |
1989 | 1799 return -1; |
1990 // Set the index | 1800 } |
1991 index = _paDeviceIndex; | 1801 |
1992 | 1802 // Create a new PulseAudio context |
1993 if (name) | 1803 if (_paContext) { |
1994 { | 1804 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
1995 // Copy to name string | 1805 " PA context has already existed"); |
1996 strncpy(pName, tmpName, nameLen); | 1806 PaUnLock(); |
1997 } | 1807 return -1; |
1998 | 1808 } |
1999 // Clear members | 1809 _paContext = LATE(pa_context_new)(_paMainloopApi, "WEBRTC VoiceEngine"); |
2000 _playDisplayDeviceName = NULL; | 1810 |
2001 _recDisplayDeviceName = NULL; | 1811 if (!_paContext) { |
2002 _paDeviceIndex = -1; | 1812 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
2003 _deviceIndex = -1; | 1813 " could not create context"); |
2004 _numPlayDevices = 0; | 1814 PaUnLock(); |
2005 _numRecDevices = 0; | 1815 return -1; |
2006 | 1816 } |
| 1817 |
| 1818 // Set state callback function |
| 1819 LATE(pa_context_set_state_callback)(_paContext, PaContextStateCallback, this); |
| 1820 |
| 1821 // Connect the context to a server (default) |
| 1822 _paStateChanged = false; |
| 1823 retVal = |
| 1824 LATE(pa_context_connect)(_paContext, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL); |
| 1825 |
| 1826 if (retVal != PA_OK) { |
| 1827 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1828 " failed to connect context, error=%d", retVal); |
| 1829 PaUnLock(); |
| 1830 return -1; |
| 1831 } |
| 1832 |
| 1833 // Wait for state change |
| 1834 while (!_paStateChanged) { |
| 1835 LATE(pa_threaded_mainloop_wait)(_paMainloop); |
| 1836 } |
| 1837 |
| 1838 // Now check to see what final state we reached. |
| 1839 pa_context_state_t state = LATE(pa_context_get_state)(_paContext); |
| 1840 |
| 1841 if (state != PA_CONTEXT_READY) { |
| 1842 if (state == PA_CONTEXT_FAILED) { |
| 1843 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1844 " failed to connect to PulseAudio sound server"); |
| 1845 } else if (state == PA_CONTEXT_TERMINATED) { |
| 1846 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1847 " PulseAudio connection terminated early"); |
| 1848 } else { |
| 1849 // Shouldn't happen, because we only signal on one of those three |
| 1850 // states |
| 1851 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1852 " unknown problem connecting to PulseAudio"); |
| 1853 } |
| 1854 PaUnLock(); |
| 1855 return -1; |
| 1856 } |
| 1857 |
| 1858 PaUnLock(); |
| 1859 |
| 1860 // Give the objects to the mixer manager |
| 1861 _mixerManager.SetPulseAudioObjects(_paMainloop, _paContext); |
| 1862 |
| 1863 // Check the version |
| 1864 if (CheckPulseAudioVersion() < 0) { |
| 1865 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1866 " PulseAudio version %s not supported", _paServerVersion); |
| 1867 return -1; |
| 1868 } |
| 1869 |
| 1870 // Initialize sampling frequency |
| 1871 if (InitSamplingFrequency() < 0 || sample_rate_hz_ == 0) { |
| 1872 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 1873 " failed to initialize sampling frequency," |
| 1874 " set to %d Hz", |
| 1875 sample_rate_hz_); |
| 1876 return -1; |
| 1877 } |
| 1878 |
| 1879 return 0; |
| 1880 } |
| 1881 |
| 1882 int32_t AudioDeviceLinuxPulse::TerminatePulseAudio() { |
| 1883 // Do nothing if the instance doesn't exist |
| 1884 // likely PaSymbolTable.Load() fails |
| 1885 if (!_paMainloop) { |
2007 return 0; | 1886 return 0; |
2008 } | 1887 } |
2009 | 1888 |
2010 int32_t AudioDeviceLinuxPulse::InitPulseAudio() | 1889 PaLock(); |
2011 { | 1890 |
2012 int retVal = 0; | 1891 // Disconnect the context |
2013 | 1892 if (_paContext) { |
2014 // Load libpulse | 1893 LATE(pa_context_disconnect)(_paContext); |
2015 if (!PaSymbolTable.Load()) | 1894 } |
2016 { | 1895 |
2017 // Most likely the Pulse library and sound server are not installed on | 1896 // Unreference the context |
2018 // this system | 1897 if (_paContext) { |
2019 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1898 LATE(pa_context_unref)(_paContext); |
2020 " failed to load symbol table"); | 1899 } |
2021 return -1; | 1900 |
2022 } | 1901 PaUnLock(); |
2023 | 1902 _paContext = NULL; |
2024 // Create a mainloop API and connection to the default server | 1903 |
2025 // the mainloop is the internal asynchronous API event loop | 1904 // Stop the threaded main loop |
2026 if (_paMainloop) { | 1905 if (_paMainloop) { |
2027 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1906 LATE(pa_threaded_mainloop_stop)(_paMainloop); |
2028 " PA mainloop has already existed"); | 1907 } |
2029 return -1; | 1908 |
2030 } | 1909 // Free the mainloop |
2031 _paMainloop = LATE(pa_threaded_mainloop_new)(); | 1910 if (_paMainloop) { |
2032 if (!_paMainloop) | 1911 LATE(pa_threaded_mainloop_free)(_paMainloop); |
2033 { | 1912 } |
2034 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1913 |
2035 " could not create mainloop"); | 1914 _paMainloop = NULL; |
2036 return -1; | 1915 |
2037 } | 1916 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " PulseAudio terminated"); |
2038 | 1917 |
2039 // Start the threaded main loop | 1918 return 0; |
2040 retVal = LATE(pa_threaded_mainloop_start)(_paMainloop); | 1919 } |
2041 if (retVal != PA_OK) | 1920 |
2042 { | 1921 void AudioDeviceLinuxPulse::PaLock() { |
2043 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1922 LATE(pa_threaded_mainloop_lock)(_paMainloop); |
2044 " failed to start main loop, error=%d", retVal); | 1923 } |
2045 return -1; | 1924 |
2046 } | 1925 void AudioDeviceLinuxPulse::PaUnLock() { |
2047 | 1926 LATE(pa_threaded_mainloop_unlock)(_paMainloop); |
2048 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2049 " mainloop running!"); | |
2050 | |
2051 PaLock(); | |
2052 | |
2053 _paMainloopApi = LATE(pa_threaded_mainloop_get_api)(_paMainloop); | |
2054 if (!_paMainloopApi) | |
2055 { | |
2056 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2057 " could not create mainloop API"); | |
2058 PaUnLock(); | |
2059 return -1; | |
2060 } | |
2061 | |
2062 // Create a new PulseAudio context | |
2063 if (_paContext){ | |
2064 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2065 " PA context has already existed"); | |
2066 PaUnLock(); | |
2067 return -1; | |
2068 } | |
2069 _paContext = LATE(pa_context_new)(_paMainloopApi, "WEBRTC VoiceEngine"); | |
2070 | |
2071 if (!_paContext) | |
2072 { | |
2073 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2074 " could not create context"); | |
2075 PaUnLock(); | |
2076 return -1; | |
2077 } | |
2078 | |
2079 // Set state callback function | |
2080 LATE(pa_context_set_state_callback)(_paContext, PaContextStateCallback, | |
2081 this); | |
2082 | |
2083 // Connect the context to a server (default) | |
2084 _paStateChanged = false; | |
2085 retVal = LATE(pa_context_connect)(_paContext, | |
2086 NULL, | |
2087 PA_CONTEXT_NOAUTOSPAWN, | |
2088 NULL); | |
2089 | |
2090 if (retVal != PA_OK) | |
2091 { | |
2092 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2093 " failed to connect context, error=%d", retVal); | |
2094 PaUnLock(); | |
2095 return -1; | |
2096 } | |
2097 | |
2098 // Wait for state change | |
2099 while (!_paStateChanged) | |
2100 { | |
2101 LATE(pa_threaded_mainloop_wait)(_paMainloop); | |
2102 } | |
2103 | |
2104 // Now check to see what final state we reached. | |
2105 pa_context_state_t state = LATE(pa_context_get_state)(_paContext); | |
2106 | |
2107 if (state != PA_CONTEXT_READY) | |
2108 { | |
2109 if (state == PA_CONTEXT_FAILED) | |
2110 { | |
2111 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2112 " failed to connect to PulseAudio sound server"); | |
2113 } else if (state == PA_CONTEXT_TERMINATED) | |
2114 { | |
2115 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2116 " PulseAudio connection terminated early"); | |
2117 } else | |
2118 { | |
2119 // Shouldn't happen, because we only signal on one of those three | |
2120 // states | |
2121 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2122 " unknown problem connecting to PulseAudio"); | |
2123 } | |
2124 PaUnLock(); | |
2125 return -1; | |
2126 } | |
2127 | |
2128 PaUnLock(); | |
2129 | |
2130 // Give the objects to the mixer manager | |
2131 _mixerManager.SetPulseAudioObjects(_paMainloop, _paContext); | |
2132 | |
2133 // Check the version | |
2134 if (CheckPulseAudioVersion() < 0) | |
2135 { | |
2136 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2137 " PulseAudio version %s not supported", | |
2138 _paServerVersion); | |
2139 return -1; | |
2140 } | |
2141 | |
2142 // Initialize sampling frequency | |
2143 if (InitSamplingFrequency() < 0 || sample_rate_hz_ == 0) | |
2144 { | |
2145 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2146 " failed to initialize sampling frequency," | |
2147 " set to %d Hz", | |
2148 sample_rate_hz_); | |
2149 return -1; | |
2150 } | |
2151 | |
2152 return 0; | |
2153 } | |
2154 | |
2155 int32_t AudioDeviceLinuxPulse::TerminatePulseAudio() | |
2156 { | |
2157 // Do nothing if the instance doesn't exist | |
2158 // likely PaSymbolTable.Load() fails | |
2159 if (!_paMainloop) { | |
2160 return 0; | |
2161 } | |
2162 | |
2163 PaLock(); | |
2164 | |
2165 // Disconnect the context | |
2166 if (_paContext) | |
2167 { | |
2168 LATE(pa_context_disconnect)(_paContext); | |
2169 } | |
2170 | |
2171 // Unreference the context | |
2172 if (_paContext) | |
2173 { | |
2174 LATE(pa_context_unref)(_paContext); | |
2175 } | |
2176 | |
2177 PaUnLock(); | |
2178 _paContext = NULL; | |
2179 | |
2180 // Stop the threaded main loop | |
2181 if (_paMainloop) | |
2182 { | |
2183 LATE(pa_threaded_mainloop_stop)(_paMainloop); | |
2184 } | |
2185 | |
2186 // Free the mainloop | |
2187 if (_paMainloop) | |
2188 { | |
2189 LATE(pa_threaded_mainloop_free)(_paMainloop); | |
2190 } | |
2191 | |
2192 _paMainloop = NULL; | |
2193 | |
2194 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2195 " PulseAudio terminated"); | |
2196 | |
2197 return 0; | |
2198 } | |
2199 | |
2200 void AudioDeviceLinuxPulse::PaLock() | |
2201 { | |
2202 LATE(pa_threaded_mainloop_lock)(_paMainloop); | |
2203 } | |
2204 | |
2205 void AudioDeviceLinuxPulse::PaUnLock() | |
2206 { | |
2207 LATE(pa_threaded_mainloop_unlock)(_paMainloop); | |
2208 } | 1927 } |
2209 | 1928 |
2210 void AudioDeviceLinuxPulse::WaitForOperationCompletion( | 1929 void AudioDeviceLinuxPulse::WaitForOperationCompletion( |
2211 pa_operation* paOperation) const | 1930 pa_operation* paOperation) const { |
2212 { | 1931 if (!paOperation) { |
2213 if (!paOperation) | 1932 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
2214 { | 1933 "paOperation NULL in WaitForOperationCompletion"); |
2215 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 1934 return; |
2216 "paOperation NULL in WaitForOperationCompletion"); | 1935 } |
2217 return; | 1936 |
2218 } | 1937 while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) { |
2219 | 1938 LATE(pa_threaded_mainloop_wait)(_paMainloop); |
2220 while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) | 1939 } |
2221 { | 1940 |
2222 LATE(pa_threaded_mainloop_wait)(_paMainloop); | 1941 LATE(pa_operation_unref)(paOperation); |
2223 } | |
2224 | |
2225 LATE(pa_operation_unref)(paOperation); | |
2226 } | 1942 } |
2227 | 1943 |
2228 // ============================================================================ | 1944 // ============================================================================ |
2229 // Thread Methods | 1945 // Thread Methods |
2230 // ============================================================================ | 1946 // ============================================================================ |
2231 | 1947 |
2232 void AudioDeviceLinuxPulse::EnableWriteCallback() | 1948 void AudioDeviceLinuxPulse::EnableWriteCallback() { |
2233 { | 1949 if (LATE(pa_stream_get_state)(_playStream) == PA_STREAM_READY) { |
2234 if (LATE(pa_stream_get_state)(_playStream) == PA_STREAM_READY) | 1950 // May already have available space. Must check. |
2235 { | 1951 _tempBufferSpace = LATE(pa_stream_writable_size)(_playStream); |
2236 // May already have available space. Must check. | 1952 if (_tempBufferSpace > 0) { |
2237 _tempBufferSpace = LATE(pa_stream_writable_size)(_playStream); | 1953 // Yup, there is already space available, so if we register a |
2238 if (_tempBufferSpace > 0) | 1954 // write callback then it will not receive any event. So dispatch |
2239 { | 1955 // one ourself instead. |
2240 // Yup, there is already space available, so if we register a | 1956 _timeEventPlay.Set(); |
2241 // write callback then it will not receive any event. So dispatch | 1957 return; |
2242 // one ourself instead. | 1958 } |
2243 _timeEventPlay.Set(); | 1959 } |
2244 return; | 1960 |
| 1961 LATE(pa_stream_set_write_callback)(_playStream, &PaStreamWriteCallback, this); |
| 1962 } |
| 1963 |
| 1964 void AudioDeviceLinuxPulse::DisableWriteCallback() { |
| 1965 LATE(pa_stream_set_write_callback)(_playStream, NULL, NULL); |
| 1966 } |
| 1967 |
| 1968 void AudioDeviceLinuxPulse::PaStreamWriteCallback(pa_stream* /*unused*/, |
| 1969 size_t buffer_space, |
| 1970 void* pThis) { |
| 1971 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaStreamWriteCallbackHandler( |
| 1972 buffer_space); |
| 1973 } |
| 1974 |
| 1975 void AudioDeviceLinuxPulse::PaStreamWriteCallbackHandler(size_t bufferSpace) { |
| 1976 _tempBufferSpace = bufferSpace; |
| 1977 |
| 1978 // Since we write the data asynchronously on a different thread, we have |
| 1979 // to temporarily disable the write callback or else Pulse will call it |
| 1980 // continuously until we write the data. We re-enable it below. |
| 1981 DisableWriteCallback(); |
| 1982 _timeEventPlay.Set(); |
| 1983 } |
| 1984 |
| 1985 void AudioDeviceLinuxPulse::PaStreamUnderflowCallback(pa_stream* /*unused*/, |
| 1986 void* pThis) { |
| 1987 static_cast<AudioDeviceLinuxPulse*>(pThis) |
| 1988 ->PaStreamUnderflowCallbackHandler(); |
| 1989 } |
| 1990 |
| 1991 void AudioDeviceLinuxPulse::PaStreamUnderflowCallbackHandler() { |
| 1992 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " Playout underflow"); |
| 1993 |
| 1994 if (_configuredLatencyPlay == WEBRTC_PA_NO_LATENCY_REQUIREMENTS) { |
| 1995 // We didn't configure a pa_buffer_attr before, so switching to |
| 1996 // one now would be questionable. |
| 1997 return; |
| 1998 } |
| 1999 |
| 2000 // Otherwise reconfigure the stream with a higher target latency. |
| 2001 |
| 2002 const pa_sample_spec* spec = LATE(pa_stream_get_sample_spec)(_playStream); |
| 2003 if (!spec) { |
| 2004 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 2005 " pa_stream_get_sample_spec()"); |
| 2006 return; |
| 2007 } |
| 2008 |
| 2009 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); |
| 2010 uint32_t newLatency = |
| 2011 _configuredLatencyPlay + bytesPerSec * |
| 2012 WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS / |
| 2013 WEBRTC_PA_MSECS_PER_SEC; |
| 2014 |
| 2015 // Set the play buffer attributes |
| 2016 _playBufferAttr.maxlength = newLatency; |
| 2017 _playBufferAttr.tlength = newLatency; |
| 2018 _playBufferAttr.minreq = newLatency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; |
| 2019 _playBufferAttr.prebuf = _playBufferAttr.tlength - _playBufferAttr.minreq; |
| 2020 |
| 2021 pa_operation* op = LATE(pa_stream_set_buffer_attr)( |
| 2022 _playStream, &_playBufferAttr, NULL, NULL); |
| 2023 if (!op) { |
| 2024 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 2025 " pa_stream_set_buffer_attr()"); |
| 2026 return; |
| 2027 } |
| 2028 |
| 2029 // Don't need to wait for this to complete. |
| 2030 LATE(pa_operation_unref)(op); |
| 2031 |
| 2032 // Save the new latency in case we underflow again. |
| 2033 _configuredLatencyPlay = newLatency; |
| 2034 } |
| 2035 |
| 2036 void AudioDeviceLinuxPulse::EnableReadCallback() { |
| 2037 LATE(pa_stream_set_read_callback)(_recStream, &PaStreamReadCallback, this); |
| 2038 } |
| 2039 |
| 2040 void AudioDeviceLinuxPulse::DisableReadCallback() { |
| 2041 LATE(pa_stream_set_read_callback)(_recStream, NULL, NULL); |
| 2042 } |
| 2043 |
| 2044 void AudioDeviceLinuxPulse::PaStreamReadCallback(pa_stream* /*unused1*/, |
| 2045 size_t /*unused2*/, |
| 2046 void* pThis) { |
| 2047 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaStreamReadCallbackHandler(); |
| 2048 } |
| 2049 |
| 2050 void AudioDeviceLinuxPulse::PaStreamReadCallbackHandler() { |
| 2051 // We get the data pointer and size now in order to save one Lock/Unlock |
| 2052 // in the worker thread. |
| 2053 if (LATE(pa_stream_peek)(_recStream, &_tempSampleData, |
| 2054 &_tempSampleDataSize) != 0) { |
| 2055 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " Can't read data!"); |
| 2056 return; |
| 2057 } |
| 2058 |
| 2059 // Since we consume the data asynchronously on a different thread, we have |
| 2060 // to temporarily disable the read callback or else Pulse will call it |
| 2061 // continuously until we consume the data. We re-enable it below. |
| 2062 DisableReadCallback(); |
| 2063 _timeEventRec.Set(); |
| 2064 } |
| 2065 |
| 2066 void AudioDeviceLinuxPulse::PaStreamOverflowCallback(pa_stream* /*unused*/, |
| 2067 void* pThis) { |
| 2068 static_cast<AudioDeviceLinuxPulse*>(pThis)->PaStreamOverflowCallbackHandler(); |
| 2069 } |
| 2070 |
| 2071 void AudioDeviceLinuxPulse::PaStreamOverflowCallbackHandler() { |
| 2072 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, " Recording overflow"); |
| 2073 } |
| 2074 |
| 2075 int32_t AudioDeviceLinuxPulse::LatencyUsecs(pa_stream* stream) { |
| 2076 if (!WEBRTC_PA_REPORT_LATENCY) { |
| 2077 return 0; |
| 2078 } |
| 2079 |
| 2080 if (!stream) { |
| 2081 return 0; |
| 2082 } |
| 2083 |
| 2084 pa_usec_t latency; |
| 2085 int negative; |
| 2086 if (LATE(pa_stream_get_latency)(stream, &latency, &negative) != 0) { |
| 2087 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, " Can't query latency"); |
| 2088 // We'd rather continue playout/capture with an incorrect delay than |
| 2089 // stop it altogether, so return a valid value. |
| 2090 return 0; |
| 2091 } |
| 2092 |
| 2093 if (negative) { |
| 2094 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, |
| 2095 " warning: pa_stream_get_latency reported negative " |
| 2096 "delay"); |
| 2097 |
| 2098 // The delay can be negative for monitoring streams if the captured |
| 2099 // samples haven't been played yet. In such a case, "latency" |
| 2100 // contains the magnitude, so we must negate it to get the real value. |
| 2101 int32_t tmpLatency = (int32_t)-latency; |
| 2102 if (tmpLatency < 0) { |
| 2103 // Make sure that we don't use a negative delay. |
| 2104 tmpLatency = 0; |
| 2105 } |
| 2106 |
| 2107 return tmpLatency; |
| 2108 } else { |
| 2109 return (int32_t)latency; |
| 2110 } |
| 2111 } |
| 2112 |
| 2113 int32_t AudioDeviceLinuxPulse::ReadRecordedData(const void* bufferData, |
| 2114 size_t bufferSize) |
| 2115 EXCLUSIVE_LOCKS_REQUIRED(_critSect) { |
| 2116 size_t size = bufferSize; |
| 2117 uint32_t numRecSamples = _recordBufferSize / (2 * _recChannels); |
| 2118 |
| 2119 // Account for the peeked data and the used data. |
| 2120 uint32_t recDelay = |
| 2121 (uint32_t)((LatencyUsecs(_recStream) / 1000) + |
| 2122 10 * ((size + _recordBufferUsed) / _recordBufferSize)); |
| 2123 |
| 2124 _sndCardRecDelay = recDelay; |
| 2125 |
| 2126 if (_playStream) { |
| 2127 // Get the playout delay. |
| 2128 _sndCardPlayDelay = (uint32_t)(LatencyUsecs(_playStream) / 1000); |
| 2129 } |
| 2130 |
| 2131 if (_recordBufferUsed > 0) { |
| 2132 // Have to copy to the buffer until it is full. |
| 2133 size_t copy = _recordBufferSize - _recordBufferUsed; |
| 2134 if (size < copy) { |
| 2135 copy = size; |
| 2136 } |
| 2137 |
| 2138 memcpy(&_recBuffer[_recordBufferUsed], bufferData, copy); |
| 2139 _recordBufferUsed += copy; |
| 2140 bufferData = static_cast<const char*>(bufferData) + copy; |
| 2141 size -= copy; |
| 2142 |
| 2143 if (_recordBufferUsed != _recordBufferSize) { |
| 2144 // Not enough data yet to pass to VoE. |
| 2145 return 0; |
| 2146 } |
| 2147 |
| 2148 // Provide data to VoiceEngine. |
| 2149 if (ProcessRecordedData(_recBuffer, numRecSamples, recDelay) == -1) { |
| 2150 // We have stopped recording. |
| 2151 return -1; |
| 2152 } |
| 2153 |
| 2154 _recordBufferUsed = 0; |
| 2155 } |
| 2156 |
| 2157 // Now process full 10ms sample sets directly from the input. |
| 2158 while (size >= _recordBufferSize) { |
| 2159 // Provide data to VoiceEngine. |
| 2160 if (ProcessRecordedData(static_cast<int8_t*>(const_cast<void*>(bufferData)), |
| 2161 numRecSamples, recDelay) == -1) { |
| 2162 // We have stopped recording. |
| 2163 return -1; |
| 2164 } |
| 2165 |
| 2166 bufferData = static_cast<const char*>(bufferData) + _recordBufferSize; |
| 2167 size -= _recordBufferSize; |
| 2168 |
| 2169 // We have consumed 10ms of data. |
| 2170 recDelay -= 10; |
| 2171 } |
| 2172 |
| 2173 // Now save any leftovers for later. |
| 2174 if (size > 0) { |
| 2175 memcpy(_recBuffer, bufferData, size); |
| 2176 _recordBufferUsed = size; |
| 2177 } |
| 2178 |
| 2179 return 0; |
| 2180 } |
| 2181 |
| 2182 int32_t AudioDeviceLinuxPulse::ProcessRecordedData(int8_t* bufferData, |
| 2183 uint32_t bufferSizeInSamples, |
| 2184 uint32_t recDelay) |
| 2185 EXCLUSIVE_LOCKS_REQUIRED(_critSect) { |
| 2186 uint32_t currentMicLevel(0); |
| 2187 uint32_t newMicLevel(0); |
| 2188 |
| 2189 _ptrAudioBuffer->SetRecordedBuffer(bufferData, bufferSizeInSamples); |
| 2190 |
| 2191 if (AGC()) { |
| 2192 // Store current mic level in the audio buffer if AGC is enabled |
| 2193 if (MicrophoneVolume(currentMicLevel) == 0) { |
| 2194 // This call does not affect the actual microphone volume |
| 2195 _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); |
| 2196 } |
| 2197 } |
| 2198 |
| 2199 const uint32_t clockDrift(0); |
| 2200 // TODO(andrew): this is a temporary hack, to avoid non-causal far- and |
| 2201 // near-end signals at the AEC for PulseAudio. I think the system delay is |
| 2202 // being correctly calculated here, but for legacy reasons we add +10 ms |
| 2203 // to the value in the AEC. The real fix will be part of a larger |
| 2204 // investigation into managing system delay in the AEC. |
| 2205 if (recDelay > 10) |
| 2206 recDelay -= 10; |
| 2207 else |
| 2208 recDelay = 0; |
| 2209 _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, recDelay, clockDrift); |
| 2210 _ptrAudioBuffer->SetTypingStatus(KeyPressed()); |
| 2211 // Deliver recorded samples at specified sample rate, |
| 2212 // mic level etc. to the observer using callback. |
| 2213 UnLock(); |
| 2214 _ptrAudioBuffer->DeliverRecordedData(); |
| 2215 Lock(); |
| 2216 |
| 2217 // We have been unlocked - check the flag again. |
| 2218 if (!_recording) { |
| 2219 return -1; |
| 2220 } |
| 2221 |
| 2222 if (AGC()) { |
| 2223 newMicLevel = _ptrAudioBuffer->NewMicLevel(); |
| 2224 if (newMicLevel != 0) { |
| 2225 // The VQE will only deliver non-zero microphone levels when a |
| 2226 // change is needed. |
| 2227 // Set this new mic level (received from the observer as return |
| 2228 // value in the callback). |
| 2229 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, |
| 2230 " AGC change of volume: old=%u => new=%u", currentMicLevel, |
| 2231 newMicLevel); |
| 2232 if (SetMicrophoneVolume(newMicLevel) == -1) { |
| 2233 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 2234 " the required modification of the microphone " |
| 2235 "volume failed"); |
| 2236 } |
| 2237 } |
| 2238 } |
| 2239 |
| 2240 return 0; |
| 2241 } |
| 2242 |
| 2243 bool AudioDeviceLinuxPulse::PlayThreadFunc(void* pThis) { |
| 2244 return (static_cast<AudioDeviceLinuxPulse*>(pThis)->PlayThreadProcess()); |
| 2245 } |
| 2246 |
| 2247 bool AudioDeviceLinuxPulse::RecThreadFunc(void* pThis) { |
| 2248 return (static_cast<AudioDeviceLinuxPulse*>(pThis)->RecThreadProcess()); |
| 2249 } |
| 2250 |
| 2251 bool AudioDeviceLinuxPulse::PlayThreadProcess() { |
| 2252 switch (_timeEventPlay.Wait(1000)) { |
| 2253 case kEventSignaled: |
| 2254 break; |
| 2255 case kEventError: |
| 2256 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 2257 "EventWrapper::Wait() failed"); |
| 2258 return true; |
| 2259 case kEventTimeout: |
| 2260 return true; |
| 2261 } |
| 2262 |
| 2263 rtc::CritScope lock(&_critSect); |
| 2264 |
| 2265 if (_startPlay) { |
| 2266 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| 2267 "_startPlay true, performing initial actions"); |
| 2268 |
| 2269 _startPlay = false; |
| 2270 _playDeviceName = NULL; |
| 2271 |
| 2272 // Set if not default device |
| 2273 if (_outputDeviceIndex > 0) { |
| 2274 // Get the playout device name |
| 2275 _playDeviceName = new char[kAdmMaxDeviceNameSize]; |
| 2276 _deviceIndex = _outputDeviceIndex; |
| 2277 PlayoutDevices(); |
| 2278 } |
| 2279 |
| 2280 // Start muted only supported on 0.9.11 and up |
| 2281 if (LATE(pa_context_get_protocol_version)(_paContext) >= |
| 2282 WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) { |
| 2283 // Get the currently saved speaker mute status |
| 2284 // and set the initial mute status accordingly |
| 2285 bool enabled(false); |
| 2286 _mixerManager.SpeakerMute(enabled); |
| 2287 if (enabled) { |
| 2288 _playStreamFlags |= PA_STREAM_START_MUTED; |
| 2289 } |
| 2290 } |
| 2291 |
| 2292 // Get the currently saved speaker volume |
| 2293 uint32_t volume = 0; |
| 2294 if (update_speaker_volume_at_startup_) |
| 2295 _mixerManager.SpeakerVolume(volume); |
| 2296 |
| 2297 PaLock(); |
| 2298 |
| 2299 // NULL gives PA the choice of startup volume. |
| 2300 pa_cvolume* ptr_cvolume = NULL; |
| 2301 if (update_speaker_volume_at_startup_) { |
| 2302 pa_cvolume cVolumes; |
| 2303 ptr_cvolume = &cVolumes; |
| 2304 |
| 2305 // Set the same volume for all channels |
| 2306 const pa_sample_spec* spec = LATE(pa_stream_get_sample_spec)(_playStream); |
| 2307 LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); |
| 2308 update_speaker_volume_at_startup_ = false; |
| 2309 } |
| 2310 |
| 2311 // Connect the stream to a sink |
| 2312 if (LATE(pa_stream_connect_playback)( |
| 2313 _playStream, _playDeviceName, &_playBufferAttr, |
| 2314 (pa_stream_flags_t)_playStreamFlags, ptr_cvolume, NULL) != PA_OK) { |
| 2315 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 2316 " failed to connect play stream, err=%d", |
| 2317 LATE(pa_context_errno)(_paContext)); |
| 2318 } |
| 2319 |
| 2320 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, |
| 2321 " play stream connected"); |
| 2322 |
| 2323 // Wait for state change |
| 2324 while (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_READY) { |
| 2325 LATE(pa_threaded_mainloop_wait)(_paMainloop); |
| 2326 } |
| 2327 |
| 2328 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " play stream ready"); |
| 2329 |
| 2330 // We can now handle write callbacks |
| 2331 EnableWriteCallback(); |
| 2332 |
| 2333 PaUnLock(); |
| 2334 |
| 2335 // Clear device name |
| 2336 if (_playDeviceName) { |
| 2337 delete[] _playDeviceName; |
| 2338 _playDeviceName = NULL; |
| 2339 } |
| 2340 |
| 2341 _playing = true; |
| 2342 _playStartEvent.Set(); |
| 2343 |
| 2344 return true; |
| 2345 } |
| 2346 |
| 2347 if (_playing) { |
| 2348 if (!_recording) { |
| 2349 // Update the playout delay |
| 2350 _sndCardPlayDelay = (uint32_t)(LatencyUsecs(_playStream) / 1000); |
| 2351 } |
| 2352 |
| 2353 if (_playbackBufferUnused < _playbackBufferSize) { |
| 2354 size_t write = _playbackBufferSize - _playbackBufferUnused; |
| 2355 if (_tempBufferSpace < write) { |
| 2356 write = _tempBufferSpace; |
| 2357 } |
| 2358 |
| 2359 PaLock(); |
| 2360 if (LATE(pa_stream_write)( |
| 2361 _playStream, (void*)&_playBuffer[_playbackBufferUnused], write, |
| 2362 NULL, (int64_t)0, PA_SEEK_RELATIVE) != PA_OK) { |
| 2363 _writeErrors++; |
| 2364 if (_writeErrors > 10) { |
| 2365 if (_playError == 1) { |
| 2366 WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, |
| 2367 " pending playout error exists"); |
| 2368 } |
| 2369 // Triggers callback from module process thread. |
| 2370 _playError = 1; |
| 2371 WEBRTC_TRACE(kTraceError, kTraceUtility, _id, |
| 2372 " kPlayoutError message posted: " |
| 2373 "_writeErrors=%u, error=%d", |
| 2374 _writeErrors, LATE(pa_context_errno)(_paContext)); |
| 2375 _writeErrors = 0; |
2245 } | 2376 } |
2246 } | 2377 } |
2247 | 2378 PaUnLock(); |
2248 LATE(pa_stream_set_write_callback)(_playStream, &PaStreamWriteCallback, | 2379 |
2249 this); | 2380 _playbackBufferUnused += write; |
2250 } | 2381 _tempBufferSpace -= write; |
2251 | 2382 } |
2252 void AudioDeviceLinuxPulse::DisableWriteCallback() | 2383 |
2253 { | 2384 uint32_t numPlaySamples = _playbackBufferSize / (2 * _playChannels); |
2254 LATE(pa_stream_set_write_callback)(_playStream, NULL, NULL); | 2385 // Might have been reduced to zero by the above. |
2255 } | 2386 if (_tempBufferSpace > 0) { |
2256 | 2387 // Ask for new PCM data to be played out using the |
2257 void AudioDeviceLinuxPulse::PaStreamWriteCallback(pa_stream */*unused*/, | 2388 // AudioDeviceBuffer ensure that this callback is executed |
2258 size_t buffer_space, | 2389 // without taking the audio-thread lock. |
2259 void *pThis) | 2390 UnLock(); |
2260 { | 2391 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " requesting data"); |
2261 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamWriteCallbackHandler( | 2392 uint32_t nSamples = _ptrAudioBuffer->RequestPlayoutData(numPlaySamples); |
2262 buffer_space); | 2393 Lock(); |
2263 } | 2394 |
2264 | 2395 // We have been unlocked - check the flag again. |
2265 void AudioDeviceLinuxPulse::PaStreamWriteCallbackHandler(size_t bufferSpace) | 2396 if (!_playing) { |
2266 { | 2397 return true; |
2267 _tempBufferSpace = bufferSpace; | 2398 } |
2268 | 2399 |
2269 // Since we write the data asynchronously on a different thread, we have | 2400 nSamples = _ptrAudioBuffer->GetPlayoutData(_playBuffer); |
2270 // to temporarily disable the write callback or else Pulse will call it | 2401 if (nSamples != numPlaySamples) { |
2271 // continuously until we write the data. We re-enable it below. | |
2272 DisableWriteCallback(); | |
2273 _timeEventPlay.Set(); | |
2274 } | |
2275 | |
2276 void AudioDeviceLinuxPulse::PaStreamUnderflowCallback(pa_stream */*unused*/, | |
2277 void *pThis) | |
2278 { | |
2279 static_cast<AudioDeviceLinuxPulse*> (pThis)-> | |
2280 PaStreamUnderflowCallbackHandler(); | |
2281 } | |
2282 | |
2283 void AudioDeviceLinuxPulse::PaStreamUnderflowCallbackHandler() | |
2284 { | |
2285 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
2286 " Playout underflow"); | |
2287 | |
2288 if (_configuredLatencyPlay == WEBRTC_PA_NO_LATENCY_REQUIREMENTS) | |
2289 { | |
2290 // We didn't configure a pa_buffer_attr before, so switching to | |
2291 // one now would be questionable. | |
2292 return; | |
2293 } | |
2294 | |
2295 // Otherwise reconfigure the stream with a higher target latency. | |
2296 | |
2297 const pa_sample_spec *spec = LATE(pa_stream_get_sample_spec)(_playStream); | |
2298 if (!spec) | |
2299 { | |
2300 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 2402 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
2301 " pa_stream_get_sample_spec()"); | 2403 " invalid number of output samples(%d)", nSamples); |
2302 return; | 2404 } |
2303 } | 2405 |
2304 | 2406 size_t write = _playbackBufferSize; |
2305 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); | 2407 if (_tempBufferSpace < write) { |
2306 uint32_t newLatency = _configuredLatencyPlay + bytesPerSec * | 2408 write = _tempBufferSpace; |
2307 WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS / | 2409 } |
2308 WEBRTC_PA_MSECS_PER_SEC; | 2410 |
2309 | 2411 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " will write"); |
2310 // Set the play buffer attributes | 2412 PaLock(); |
2311 _playBufferAttr.maxlength = newLatency; | 2413 if (LATE(pa_stream_write)(_playStream, (void*)&_playBuffer[0], write, |
2312 _playBufferAttr.tlength = newLatency; | 2414 NULL, (int64_t)0, PA_SEEK_RELATIVE) != PA_OK) { |
2313 _playBufferAttr.minreq = newLatency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; | 2415 _writeErrors++; |
2314 _playBufferAttr.prebuf = _playBufferAttr.tlength - _playBufferAttr.minreq; | 2416 if (_writeErrors > 10) { |
2315 | 2417 if (_playError == 1) { |
2316 pa_operation *op = LATE(pa_stream_set_buffer_attr)(_playStream, | 2418 WEBRTC_TRACE(kTraceWarning, kTraceUtility, _id, |
2317 &_playBufferAttr, NULL, | 2419 " pending playout error exists"); |
2318 NULL); | 2420 } |
2319 if (!op) | 2421 // Triggers callback from module process thread. |
2320 { | 2422 _playError = 1; |
| 2423 WEBRTC_TRACE(kTraceError, kTraceUtility, _id, |
| 2424 " kPlayoutError message posted: " |
| 2425 "_writeErrors=%u, error=%d", |
| 2426 _writeErrors, LATE(pa_context_errno)(_paContext)); |
| 2427 _writeErrors = 0; |
| 2428 } |
| 2429 } |
| 2430 PaUnLock(); |
| 2431 |
| 2432 _playbackBufferUnused = write; |
| 2433 } |
| 2434 |
| 2435 _tempBufferSpace = 0; |
| 2436 PaLock(); |
| 2437 EnableWriteCallback(); |
| 2438 PaUnLock(); |
| 2439 |
| 2440 } // _playing |
| 2441 |
| 2442 return true; |
| 2443 } |
| 2444 |
| 2445 bool AudioDeviceLinuxPulse::RecThreadProcess() { |
| 2446 switch (_timeEventRec.Wait(1000)) { |
| 2447 case kEventSignaled: |
| 2448 break; |
| 2449 case kEventError: |
| 2450 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 2451 "EventWrapper::Wait() failed"); |
| 2452 return true; |
| 2453 case kEventTimeout: |
| 2454 return true; |
| 2455 } |
| 2456 |
| 2457 rtc::CritScope lock(&_critSect); |
| 2458 |
| 2459 if (_startRec) { |
| 2460 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| 2461 "_startRec true, performing initial actions"); |
| 2462 |
| 2463 _recDeviceName = NULL; |
| 2464 |
| 2465 // Set if not default device |
| 2466 if (_inputDeviceIndex > 0) { |
| 2467 // Get the recording device name |
| 2468 _recDeviceName = new char[kAdmMaxDeviceNameSize]; |
| 2469 _deviceIndex = _inputDeviceIndex; |
| 2470 RecordingDevices(); |
| 2471 } |
| 2472 |
| 2473 PaLock(); |
| 2474 |
| 2475 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " connecting stream"); |
| 2476 |
| 2477 // Connect the stream to a source |
| 2478 if (LATE(pa_stream_connect_record)( |
| 2479 _recStream, _recDeviceName, &_recBufferAttr, |
| 2480 (pa_stream_flags_t)_recStreamFlags) != PA_OK) { |
| 2481 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| 2482 " failed to connect rec stream, err=%d", |
| 2483 LATE(pa_context_errno)(_paContext)); |
| 2484 } |
| 2485 |
| 2486 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " connected"); |
| 2487 |
| 2488 // Wait for state change |
| 2489 while (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_READY) { |
| 2490 LATE(pa_threaded_mainloop_wait)(_paMainloop); |
| 2491 } |
| 2492 |
| 2493 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " done"); |
| 2494 |
| 2495 // We can now handle read callbacks |
| 2496 EnableReadCallback(); |
| 2497 |
| 2498 PaUnLock(); |
| 2499 |
| 2500 // Clear device name |
| 2501 if (_recDeviceName) { |
| 2502 delete[] _recDeviceName; |
| 2503 _recDeviceName = NULL; |
| 2504 } |
| 2505 |
| 2506 _startRec = false; |
| 2507 _recording = true; |
| 2508 _recStartEvent.Set(); |
| 2509 |
| 2510 return true; |
| 2511 } |
| 2512 |
| 2513 if (_recording) { |
| 2514 // Read data and provide it to VoiceEngine |
| 2515 if (ReadRecordedData(_tempSampleData, _tempSampleDataSize) == -1) { |
| 2516 return true; |
| 2517 } |
| 2518 |
| 2519 _tempSampleData = NULL; |
| 2520 _tempSampleDataSize = 0; |
| 2521 |
| 2522 PaLock(); |
| 2523 while (true) { |
| 2524 // Ack the last thing we read |
| 2525 if (LATE(pa_stream_drop)(_recStream) != 0) { |
| 2526 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| 2527 " failed to drop, err=%d\n", |
| 2528 LATE(pa_context_errno)(_paContext)); |
| 2529 } |
| 2530 |
| 2531 if (LATE(pa_stream_readable_size)(_recStream) <= 0) { |
| 2532 // Then that was all the data |
| 2533 break; |
| 2534 } |
| 2535 |
| 2536 // Else more data. |
| 2537 const void* sampleData; |
| 2538 size_t sampleDataSize; |
| 2539 |
| 2540 if (LATE(pa_stream_peek)(_recStream, &sampleData, &sampleDataSize) != 0) { |
| 2541 _recError = 1; // triggers callback from module process thread |
2321 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | 2542 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
2322 " pa_stream_set_buffer_attr()"); | 2543 " RECORD_ERROR message posted, error = %d", |
2323 return; | 2544 LATE(pa_context_errno)(_paContext)); |
2324 } | 2545 break; |
2325 | 2546 } |
2326 // Don't need to wait for this to complete. | 2547 |
2327 LATE(pa_operation_unref)(op); | 2548 _sndCardRecDelay = (uint32_t)(LatencyUsecs(_recStream) / 1000); |
2328 | 2549 |
2329 // Save the new latency in case we underflow again. | 2550 // Drop lock for sigslot dispatch, which could take a while. |
2330 _configuredLatencyPlay = newLatency; | 2551 PaUnLock(); |
2331 } | 2552 // Read data and provide it to VoiceEngine |
2332 | 2553 if (ReadRecordedData(sampleData, sampleDataSize) == -1) { |
2333 void AudioDeviceLinuxPulse::EnableReadCallback() | |
2334 { | |
2335 LATE(pa_stream_set_read_callback)(_recStream, | |
2336 &PaStreamReadCallback, | |
2337 this); | |
2338 } | |
2339 | |
2340 void AudioDeviceLinuxPulse::DisableReadCallback() | |
2341 { | |
2342 LATE(pa_stream_set_read_callback)(_recStream, NULL, NULL); | |
2343 } | |
2344 | |
2345 void AudioDeviceLinuxPulse::PaStreamReadCallback(pa_stream */*unused1*/, | |
2346 size_t /*unused2*/, | |
2347 void *pThis) | |
2348 { | |
2349 static_cast<AudioDeviceLinuxPulse*> (pThis)-> | |
2350 PaStreamReadCallbackHandler(); | |
2351 } | |
2352 | |
2353 void AudioDeviceLinuxPulse::PaStreamReadCallbackHandler() | |
2354 { | |
2355 // We get the data pointer and size now in order to save one Lock/Unlock | |
2356 // in the worker thread. | |
2357 if (LATE(pa_stream_peek)(_recStream, | |
2358 &_tempSampleData, | |
2359 &_tempSampleDataSize) != 0) | |
2360 { | |
2361 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2362 " Can't read data!"); | |
2363 return; | |
2364 } | |
2365 | |
2366 // Since we consume the data asynchronously on a different thread, we have | |
2367 // to temporarily disable the read callback or else Pulse will call it | |
2368 // continuously until we consume the data. We re-enable it below. | |
2369 DisableReadCallback(); | |
2370 _timeEventRec.Set(); | |
2371 } | |
2372 | |
2373 void AudioDeviceLinuxPulse::PaStreamOverflowCallback(pa_stream */*unused*/, | |
2374 void *pThis) | |
2375 { | |
2376 static_cast<AudioDeviceLinuxPulse*> (pThis)-> | |
2377 PaStreamOverflowCallbackHandler(); | |
2378 } | |
2379 | |
2380 void AudioDeviceLinuxPulse::PaStreamOverflowCallbackHandler() | |
2381 { | |
2382 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
2383 " Recording overflow"); | |
2384 } | |
2385 | |
2386 int32_t AudioDeviceLinuxPulse::LatencyUsecs(pa_stream *stream) | |
2387 { | |
2388 if (!WEBRTC_PA_REPORT_LATENCY) | |
2389 { | |
2390 return 0; | |
2391 } | |
2392 | |
2393 if (!stream) | |
2394 { | |
2395 return 0; | |
2396 } | |
2397 | |
2398 pa_usec_t latency; | |
2399 int negative; | |
2400 if (LATE(pa_stream_get_latency)(stream, &latency, &negative) != 0) | |
2401 { | |
2402 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2403 " Can't query latency"); | |
2404 // We'd rather continue playout/capture with an incorrect delay than | |
2405 // stop it altogether, so return a valid value. | |
2406 return 0; | |
2407 } | |
2408 | |
2409 if (negative) | |
2410 { | |
2411 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2412 " warning: pa_stream_get_latency reported negative " | |
2413 "delay"); | |
2414 | |
2415 // The delay can be negative for monitoring streams if the captured | |
2416 // samples haven't been played yet. In such a case, "latency" | |
2417 // contains the magnitude, so we must negate it to get the real value. | |
2418 int32_t tmpLatency = (int32_t) -latency; | |
2419 if (tmpLatency < 0) | |
2420 { | |
2421 // Make sure that we don't use a negative delay. | |
2422 tmpLatency = 0; | |
2423 } | |
2424 | |
2425 return tmpLatency; | |
2426 } else | |
2427 { | |
2428 return (int32_t) latency; | |
2429 } | |
2430 } | |
2431 | |
2432 int32_t AudioDeviceLinuxPulse::ReadRecordedData( | |
2433 const void* bufferData, | |
2434 size_t bufferSize) EXCLUSIVE_LOCKS_REQUIRED(_critSect) | |
2435 { | |
2436 size_t size = bufferSize; | |
2437 uint32_t numRecSamples = _recordBufferSize / (2 * _recChannels); | |
2438 | |
2439 // Account for the peeked data and the used data. | |
2440 uint32_t recDelay = (uint32_t) ((LatencyUsecs(_recStream) | |
2441 / 1000) + 10 * ((size + _recordBufferUsed) / _recordBufferSize)); | |
2442 | |
2443 _sndCardRecDelay = recDelay; | |
2444 | |
2445 if (_playStream) | |
2446 { | |
2447 // Get the playout delay. | |
2448 _sndCardPlayDelay = (uint32_t) (LatencyUsecs(_playStream) / 1000); | |
2449 } | |
2450 | |
2451 if (_recordBufferUsed > 0) | |
2452 { | |
2453 // Have to copy to the buffer until it is full. | |
2454 size_t copy = _recordBufferSize - _recordBufferUsed; | |
2455 if (size < copy) | |
2456 { | |
2457 copy = size; | |
2458 } | |
2459 | |
2460 memcpy(&_recBuffer[_recordBufferUsed], bufferData, copy); | |
2461 _recordBufferUsed += copy; | |
2462 bufferData = static_cast<const char *> (bufferData) + copy; | |
2463 size -= copy; | |
2464 | |
2465 if (_recordBufferUsed != _recordBufferSize) | |
2466 { | |
2467 // Not enough data yet to pass to VoE. | |
2468 return 0; | |
2469 } | |
2470 | |
2471 // Provide data to VoiceEngine. | |
2472 if (ProcessRecordedData(_recBuffer, numRecSamples, recDelay) == -1) | |
2473 { | |
2474 // We have stopped recording. | |
2475 return -1; | |
2476 } | |
2477 | |
2478 _recordBufferUsed = 0; | |
2479 } | |
2480 | |
2481 // Now process full 10ms sample sets directly from the input. | |
2482 while (size >= _recordBufferSize) | |
2483 { | |
2484 // Provide data to VoiceEngine. | |
2485 if (ProcessRecordedData( | |
2486 static_cast<int8_t *> (const_cast<void *> (bufferData)), | |
2487 numRecSamples, recDelay) == -1) | |
2488 { | |
2489 // We have stopped recording. | |
2490 return -1; | |
2491 } | |
2492 | |
2493 bufferData = static_cast<const char *> (bufferData) + | |
2494 _recordBufferSize; | |
2495 size -= _recordBufferSize; | |
2496 | |
2497 // We have consumed 10ms of data. | |
2498 recDelay -= 10; | |
2499 } | |
2500 | |
2501 // Now save any leftovers for later. | |
2502 if (size > 0) | |
2503 { | |
2504 memcpy(_recBuffer, bufferData, size); | |
2505 _recordBufferUsed = size; | |
2506 } | |
2507 | |
2508 return 0; | |
2509 } | |
2510 | |
2511 int32_t AudioDeviceLinuxPulse::ProcessRecordedData( | |
2512 int8_t *bufferData, | |
2513 uint32_t bufferSizeInSamples, | |
2514 uint32_t recDelay) EXCLUSIVE_LOCKS_REQUIRED(_critSect) | |
2515 { | |
2516 uint32_t currentMicLevel(0); | |
2517 uint32_t newMicLevel(0); | |
2518 | |
2519 _ptrAudioBuffer->SetRecordedBuffer(bufferData, bufferSizeInSamples); | |
2520 | |
2521 if (AGC()) | |
2522 { | |
2523 // Store current mic level in the audio buffer if AGC is enabled | |
2524 if (MicrophoneVolume(currentMicLevel) == 0) | |
2525 { | |
2526 // This call does not affect the actual microphone volume | |
2527 _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); | |
2528 } | |
2529 } | |
2530 | |
2531 const uint32_t clockDrift(0); | |
2532 // TODO(andrew): this is a temporary hack, to avoid non-causal far- and | |
2533 // near-end signals at the AEC for PulseAudio. I think the system delay is | |
2534 // being correctly calculated here, but for legacy reasons we add +10 ms | |
2535 // to the value in the AEC. The real fix will be part of a larger | |
2536 // investigation into managing system delay in the AEC. | |
2537 if (recDelay > 10) | |
2538 recDelay -= 10; | |
2539 else | |
2540 recDelay = 0; | |
2541 _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, recDelay, clockDrift); | |
2542 _ptrAudioBuffer->SetTypingStatus(KeyPressed()); | |
2543 // Deliver recorded samples at specified sample rate, | |
2544 // mic level etc. to the observer using callback. | |
2545 UnLock(); | |
2546 _ptrAudioBuffer->DeliverRecordedData(); | |
2547 Lock(); | |
2548 | |
2549 // We have been unlocked - check the flag again. | |
2550 if (!_recording) | |
2551 { | |
2552 return -1; | |
2553 } | |
2554 | |
2555 if (AGC()) | |
2556 { | |
2557 newMicLevel = _ptrAudioBuffer->NewMicLevel(); | |
2558 if (newMicLevel != 0) | |
2559 { | |
2560 // The VQE will only deliver non-zero microphone levels when a | |
2561 // change is needed. | |
2562 // Set this new mic level (received from the observer as return | |
2563 // value in the callback). | |
2564 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, | |
2565 " AGC change of volume: old=%u => new=%u", | |
2566 currentMicLevel, newMicLevel); | |
2567 if (SetMicrophoneVolume(newMicLevel) == -1) | |
2568 { | |
2569 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, | |
2570 _id, | |
2571 " the required modification of the microphone " | |
2572 "volume failed"); | |
2573 } | |
2574 } | |
2575 } | |
2576 | |
2577 return 0; | |
2578 } | |
2579 | |
2580 bool AudioDeviceLinuxPulse::PlayThreadFunc(void* pThis) | |
2581 { | |
2582 return (static_cast<AudioDeviceLinuxPulse*> (pThis)->PlayThreadProcess()); | |
2583 } | |
2584 | |
2585 bool AudioDeviceLinuxPulse::RecThreadFunc(void* pThis) | |
2586 { | |
2587 return (static_cast<AudioDeviceLinuxPulse*> (pThis)->RecThreadProcess()); | |
2588 } | |
2589 | |
2590 bool AudioDeviceLinuxPulse::PlayThreadProcess() | |
2591 { | |
2592 switch (_timeEventPlay.Wait(1000)) | |
2593 { | |
2594 case kEventSignaled: | |
2595 break; | |
2596 case kEventError: | |
2597 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
2598 "EventWrapper::Wait() failed"); | |
2599 return true; | |
2600 case kEventTimeout: | |
2601 return true; | |
2602 } | |
2603 | |
2604 rtc::CritScope lock(&_critSect); | |
2605 | |
2606 if (_startPlay) | |
2607 { | |
2608 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, | |
2609 "_startPlay true, performing initial actions"); | |
2610 | |
2611 _startPlay = false; | |
2612 _playDeviceName = NULL; | |
2613 | |
2614 // Set if not default device | |
2615 if (_outputDeviceIndex > 0) | |
2616 { | |
2617 // Get the playout device name | |
2618 _playDeviceName = new char[kAdmMaxDeviceNameSize]; | |
2619 _deviceIndex = _outputDeviceIndex; | |
2620 PlayoutDevices(); | |
2621 } | |
2622 | |
2623 // Start muted only supported on 0.9.11 and up | |
2624 if (LATE(pa_context_get_protocol_version)(_paContext) | |
2625 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) | |
2626 { | |
2627 // Get the currently saved speaker mute status | |
2628 // and set the initial mute status accordingly | |
2629 bool enabled(false); | |
2630 _mixerManager.SpeakerMute(enabled); | |
2631 if (enabled) | |
2632 { | |
2633 _playStreamFlags |= PA_STREAM_START_MUTED; | |
2634 } | |
2635 } | |
2636 | |
2637 // Get the currently saved speaker volume | |
2638 uint32_t volume = 0; | |
2639 if (update_speaker_volume_at_startup_) | |
2640 _mixerManager.SpeakerVolume(volume); | |
2641 | |
2642 PaLock(); | |
2643 | |
2644 // NULL gives PA the choice of startup volume. | |
2645 pa_cvolume* ptr_cvolume = NULL; | |
2646 if (update_speaker_volume_at_startup_) { | |
2647 pa_cvolume cVolumes; | |
2648 ptr_cvolume = &cVolumes; | |
2649 | |
2650 // Set the same volume for all channels | |
2651 const pa_sample_spec *spec = | |
2652 LATE(pa_stream_get_sample_spec)(_playStream); | |
2653 LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); | |
2654 update_speaker_volume_at_startup_ = false; | |
2655 } | |
2656 | |
2657 // Connect the stream to a sink | |
2658 if (LATE(pa_stream_connect_playback)( | |
2659 _playStream, | |
2660 _playDeviceName, | |
2661 &_playBufferAttr, | |
2662 (pa_stream_flags_t) _playStreamFlags, | |
2663 ptr_cvolume, NULL) != PA_OK) | |
2664 { | |
2665 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2666 " failed to connect play stream, err=%d", | |
2667 LATE(pa_context_errno)(_paContext)); | |
2668 } | |
2669 | |
2670 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2671 " play stream connected"); | |
2672 | |
2673 // Wait for state change | |
2674 while (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_READY) | |
2675 { | |
2676 LATE(pa_threaded_mainloop_wait)(_paMainloop); | |
2677 } | |
2678 | |
2679 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2680 " play stream ready"); | |
2681 | |
2682 // We can now handle write callbacks | |
2683 EnableWriteCallback(); | |
2684 | |
2685 PaUnLock(); | |
2686 | |
2687 // Clear device name | |
2688 if (_playDeviceName) | |
2689 { | |
2690 delete [] _playDeviceName; | |
2691 _playDeviceName = NULL; | |
2692 } | |
2693 | |
2694 _playing = true; | |
2695 _playStartEvent.Set(); | |
2696 | |
2697 return true; | 2554 return true; |
2698 } | 2555 } |
2699 | 2556 PaLock(); |
2700 if (_playing) | 2557 |
2701 { | 2558 // Return to top of loop for the ack and the check for more data. |
2702 if (!_recording) | 2559 } |
2703 { | 2560 |
2704 // Update the playout delay | 2561 EnableReadCallback(); |
2705 _sndCardPlayDelay = (uint32_t) (LatencyUsecs(_playStream) | 2562 PaUnLock(); |
2706 / 1000); | 2563 |
2707 } | 2564 } // _recording |
2708 | 2565 |
2709 if (_playbackBufferUnused < _playbackBufferSize) | 2566 return true; |
2710 { | 2567 } |
2711 | 2568 |
2712 size_t write = _playbackBufferSize - _playbackBufferUnused; | 2569 bool AudioDeviceLinuxPulse::KeyPressed() const { |
2713 if (_tempBufferSpace < write) | |
2714 { | |
2715 write = _tempBufferSpace; | |
2716 } | |
2717 | |
2718 PaLock(); | |
2719 if (LATE(pa_stream_write)( | |
2720 _playStream, | |
2721 (void *) &_playBuffer[_playbackBufferUnused], | |
2722 write, NULL, (int64_t) 0, | |
2723 PA_SEEK_RELATIVE) != PA_OK) | |
2724 { | |
2725 _writeErrors++; | |
2726 if (_writeErrors > 10) | |
2727 { | |
2728 if (_playError == 1) | |
2729 { | |
2730 WEBRTC_TRACE(kTraceWarning, | |
2731 kTraceUtility, _id, | |
2732 " pending playout error exists"); | |
2733 } | |
2734 // Triggers callback from module process thread. | |
2735 _playError = 1; | |
2736 WEBRTC_TRACE( | |
2737 kTraceError, | |
2738 kTraceUtility, | |
2739 _id, | |
2740 " kPlayoutError message posted: " | |
2741 "_writeErrors=%u, error=%d", | |
2742 _writeErrors, | |
2743 LATE(pa_context_errno)(_paContext)); | |
2744 _writeErrors = 0; | |
2745 } | |
2746 } | |
2747 PaUnLock(); | |
2748 | |
2749 _playbackBufferUnused += write; | |
2750 _tempBufferSpace -= write; | |
2751 } | |
2752 | |
2753 uint32_t numPlaySamples = _playbackBufferSize / (2 * _playChannels); | |
2754 // Might have been reduced to zero by the above. | |
2755 if (_tempBufferSpace > 0) | |
2756 { | |
2757 // Ask for new PCM data to be played out using the | |
2758 // AudioDeviceBuffer ensure that this callback is executed | |
2759 // without taking the audio-thread lock. | |
2760 UnLock(); | |
2761 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2762 " requesting data"); | |
2763 uint32_t nSamples = | |
2764 _ptrAudioBuffer->RequestPlayoutData(numPlaySamples); | |
2765 Lock(); | |
2766 | |
2767 // We have been unlocked - check the flag again. | |
2768 if (!_playing) | |
2769 { | |
2770 return true; | |
2771 } | |
2772 | |
2773 nSamples = _ptrAudioBuffer->GetPlayoutData(_playBuffer); | |
2774 if (nSamples != numPlaySamples) | |
2775 { | |
2776 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, | |
2777 _id, " invalid number of output samples(%d)", | |
2778 nSamples); | |
2779 } | |
2780 | |
2781 size_t write = _playbackBufferSize; | |
2782 if (_tempBufferSpace < write) | |
2783 { | |
2784 write = _tempBufferSpace; | |
2785 } | |
2786 | |
2787 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2788 " will write"); | |
2789 PaLock(); | |
2790 if (LATE(pa_stream_write)(_playStream, (void *) &_playBuffer[0], | |
2791 write, NULL, (int64_t) 0, | |
2792 PA_SEEK_RELATIVE) != PA_OK) | |
2793 { | |
2794 _writeErrors++; | |
2795 if (_writeErrors > 10) | |
2796 { | |
2797 if (_playError == 1) | |
2798 { | |
2799 WEBRTC_TRACE(kTraceWarning, | |
2800 kTraceUtility, _id, | |
2801 " pending playout error exists"); | |
2802 } | |
2803 // Triggers callback from module process thread. | |
2804 _playError = 1; | |
2805 WEBRTC_TRACE( | |
2806 kTraceError, | |
2807 kTraceUtility, | |
2808 _id, | |
2809 " kPlayoutError message posted: " | |
2810 "_writeErrors=%u, error=%d", | |
2811 _writeErrors, | |
2812 LATE(pa_context_errno)(_paContext)); | |
2813 _writeErrors = 0; | |
2814 } | |
2815 } | |
2816 PaUnLock(); | |
2817 | |
2818 _playbackBufferUnused = write; | |
2819 } | |
2820 | |
2821 _tempBufferSpace = 0; | |
2822 PaLock(); | |
2823 EnableWriteCallback(); | |
2824 PaUnLock(); | |
2825 | |
2826 } // _playing | |
2827 | |
2828 return true; | |
2829 } | |
2830 | |
2831 bool AudioDeviceLinuxPulse::RecThreadProcess() | |
2832 { | |
2833 switch (_timeEventRec.Wait(1000)) | |
2834 { | |
2835 case kEventSignaled: | |
2836 break; | |
2837 case kEventError: | |
2838 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, | |
2839 "EventWrapper::Wait() failed"); | |
2840 return true; | |
2841 case kEventTimeout: | |
2842 return true; | |
2843 } | |
2844 | |
2845 rtc::CritScope lock(&_critSect); | |
2846 | |
2847 if (_startRec) | |
2848 { | |
2849 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, | |
2850 "_startRec true, performing initial actions"); | |
2851 | |
2852 _recDeviceName = NULL; | |
2853 | |
2854 // Set if not default device | |
2855 if (_inputDeviceIndex > 0) | |
2856 { | |
2857 // Get the recording device name | |
2858 _recDeviceName = new char[kAdmMaxDeviceNameSize]; | |
2859 _deviceIndex = _inputDeviceIndex; | |
2860 RecordingDevices(); | |
2861 } | |
2862 | |
2863 PaLock(); | |
2864 | |
2865 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2866 " connecting stream"); | |
2867 | |
2868 // Connect the stream to a source | |
2869 if (LATE(pa_stream_connect_record)(_recStream, | |
2870 _recDeviceName, | |
2871 &_recBufferAttr, | |
2872 (pa_stream_flags_t) _recStreamFlags) != PA_OK) | |
2873 { | |
2874 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, | |
2875 " failed to connect rec stream, err=%d", | |
2876 LATE(pa_context_errno)(_paContext)); | |
2877 } | |
2878 | |
2879 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2880 " connected"); | |
2881 | |
2882 // Wait for state change | |
2883 while (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_READY) | |
2884 { | |
2885 LATE(pa_threaded_mainloop_wait)(_paMainloop); | |
2886 } | |
2887 | |
2888 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, | |
2889 " done"); | |
2890 | |
2891 // We can now handle read callbacks | |
2892 EnableReadCallback(); | |
2893 | |
2894 PaUnLock(); | |
2895 | |
2896 // Clear device name | |
2897 if (_recDeviceName) | |
2898 { | |
2899 delete [] _recDeviceName; | |
2900 _recDeviceName = NULL; | |
2901 } | |
2902 | |
2903 _startRec = false; | |
2904 _recording = true; | |
2905 _recStartEvent.Set(); | |
2906 | |
2907 return true; | |
2908 } | |
2909 | |
2910 if (_recording) | |
2911 { | |
2912 // Read data and provide it to VoiceEngine | |
2913 if (ReadRecordedData(_tempSampleData, _tempSampleDataSize) == -1) | |
2914 { | |
2915 return true; | |
2916 } | |
2917 | |
2918 _tempSampleData = NULL; | |
2919 _tempSampleDataSize = 0; | |
2920 | |
2921 PaLock(); | |
2922 while (true) | |
2923 { | |
2924 // Ack the last thing we read | |
2925 if (LATE(pa_stream_drop)(_recStream) != 0) | |
2926 { | |
2927 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, | |
2928 _id, " failed to drop, err=%d\n", | |
2929 LATE(pa_context_errno)(_paContext)); | |
2930 } | |
2931 | |
2932 if (LATE(pa_stream_readable_size)(_recStream) <= 0) | |
2933 { | |
2934 // Then that was all the data | |
2935 break; | |
2936 } | |
2937 | |
2938 // Else more data. | |
2939 const void *sampleData; | |
2940 size_t sampleDataSize; | |
2941 | |
2942 if (LATE(pa_stream_peek)(_recStream, &sampleData, &sampleDataSize) | |
2943 != 0) | |
2944 { | |
2945 _recError = 1; // triggers callback from module process thread | |
2946 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, | |
2947 _id, " RECORD_ERROR message posted, error = %d", | |
2948 LATE(pa_context_errno)(_paContext)); | |
2949 break; | |
2950 } | |
2951 | |
2952 _sndCardRecDelay = (uint32_t) (LatencyUsecs(_recStream) | |
2953 / 1000); | |
2954 | |
2955 // Drop lock for sigslot dispatch, which could take a while. | |
2956 PaUnLock(); | |
2957 // Read data and provide it to VoiceEngine | |
2958 if (ReadRecordedData(sampleData, sampleDataSize) == -1) | |
2959 { | |
2960 return true; | |
2961 } | |
2962 PaLock(); | |
2963 | |
2964 // Return to top of loop for the ack and the check for more data. | |
2965 } | |
2966 | |
2967 EnableReadCallback(); | |
2968 PaUnLock(); | |
2969 | |
2970 } // _recording | |
2971 | |
2972 return true; | |
2973 } | |
2974 | |
2975 bool AudioDeviceLinuxPulse::KeyPressed() const{ | |
2976 | |
2977 char szKey[32]; | 2570 char szKey[32]; |
2978 unsigned int i = 0; | 2571 unsigned int i = 0; |
2979 char state = 0; | 2572 char state = 0; |
2980 | 2573 |
2981 if (!_XDisplay) | 2574 if (!_XDisplay) |
2982 return false; | 2575 return false; |
2983 | 2576 |
2984 // Check key map status | 2577 // Check key map status |
2985 XQueryKeymap(_XDisplay, szKey); | 2578 XQueryKeymap(_XDisplay, szKey); |
2986 | 2579 |
2987 // A bit change in keymap means a key is pressed | 2580 // A bit change in keymap means a key is pressed |
2988 for (i = 0; i < sizeof(szKey); i++) | 2581 for (i = 0; i < sizeof(szKey); i++) |
2989 state |= (szKey[i] ^ _oldKeyState[i]) & szKey[i]; | 2582 state |= (szKey[i] ^ _oldKeyState[i]) & szKey[i]; |
2990 | 2583 |
2991 // Save old state | 2584 // Save old state |
2992 memcpy((char*)_oldKeyState, (char*)szKey, sizeof(_oldKeyState)); | 2585 memcpy((char*)_oldKeyState, (char*)szKey, sizeof(_oldKeyState)); |
2993 return (state != 0); | 2586 return (state != 0); |
2994 } | 2587 } |
2995 } | 2588 } // namespace webrtc |
OLD | NEW |