1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "modules/audio_device/linux/audio_device_alsa_linux.h"
12
13
14 #include "modules/audio_device/audio_device_config.h"
15 #include "rtc_base/logging.h"
16 #include "rtc_base/system/arch.h"
17 #include "system_wrappers/include/sleep.h"
18
GetAlsaSymbolTable()19 WebRTCAlsaSymbolTable* GetAlsaSymbolTable() {
20 static WebRTCAlsaSymbolTable* alsa_symbol_table = new WebRTCAlsaSymbolTable();
21 return alsa_symbol_table;
22 }
23
24 // Accesses ALSA functions through our late-binding symbol table instead of
25 // directly. This way we don't have to link to libasound, which means our binary
26 // will work on systems that don't have it.
27 #define LATE(sym) \
28 LATESYM_GET(webrtc::adm_linux_alsa::AlsaSymbolTable, GetAlsaSymbolTable(), \
29 sym)
30
31 // Redefine these here to be able to do late-binding
32 #undef snd_ctl_card_info_alloca
33 #define snd_ctl_card_info_alloca(ptr) \
34 do { \
35 *ptr = (snd_ctl_card_info_t*)__builtin_alloca( \
36 LATE(snd_ctl_card_info_sizeof)()); \
37 memset(*ptr, 0, LATE(snd_ctl_card_info_sizeof)()); \
38 } while (0)
39
40 #undef snd_pcm_info_alloca
41 #define snd_pcm_info_alloca(pInfo) \
42 do { \
43 *pInfo = (snd_pcm_info_t*)__builtin_alloca(LATE(snd_pcm_info_sizeof)()); \
44 memset(*pInfo, 0, LATE(snd_pcm_info_sizeof)()); \
45 } while (0)
46
47 // snd_lib_error_handler_t
WebrtcAlsaErrorHandler(const char * file,int line,const char * function,int err,const char * fmt,...)48 void WebrtcAlsaErrorHandler(const char* file,
49 int line,
50 const char* function,
51 int err,
52 const char* fmt,
53 ...) {}
54
55 namespace webrtc {
56 static const unsigned int ALSA_PLAYOUT_FREQ = 48000;
57 static const unsigned int ALSA_PLAYOUT_CH = 2;
58 static const unsigned int ALSA_PLAYOUT_LATENCY = 40 * 1000; // in us
59 static const unsigned int ALSA_CAPTURE_FREQ = 48000;
60 static const unsigned int ALSA_CAPTURE_CH = 2;
61 static const unsigned int ALSA_CAPTURE_LATENCY = 40 * 1000; // in us
62 static const unsigned int ALSA_CAPTURE_WAIT_TIMEOUT = 5; // in ms
63
64 #define FUNC_GET_NUM_OF_DEVICE 0
65 #define FUNC_GET_DEVICE_NAME 1
66 #define FUNC_GET_DEVICE_NAME_FOR_AN_ENUM 2
67
AudioDeviceLinuxALSA()68 AudioDeviceLinuxALSA::AudioDeviceLinuxALSA()
69 : _ptrAudioBuffer(NULL),
70 _inputDeviceIndex(0),
71 _outputDeviceIndex(0),
72 _inputDeviceIsSpecified(false),
73 _outputDeviceIsSpecified(false),
74 _handleRecord(NULL),
75 _handlePlayout(NULL),
76 _recordingBuffersizeInFrame(0),
77 _recordingPeriodSizeInFrame(0),
78 _playoutBufferSizeInFrame(0),
79 _playoutPeriodSizeInFrame(0),
80 _recordingBufferSizeIn10MS(0),
81 _playoutBufferSizeIn10MS(0),
82 _recordingFramesIn10MS(0),
83 _playoutFramesIn10MS(0),
84 _recordingFreq(ALSA_CAPTURE_FREQ),
85 _playoutFreq(ALSA_PLAYOUT_FREQ),
86 _recChannels(ALSA_CAPTURE_CH),
87 _playChannels(ALSA_PLAYOUT_CH),
88 _recordingBuffer(NULL),
89 _playoutBuffer(NULL),
90 _recordingFramesLeft(0),
91 _playoutFramesLeft(0),
92 _initialized(false),
93 _recording(false),
94 _playing(false),
95 _recIsInitialized(false),
96 _playIsInitialized(false),
97 _recordingDelay(0),
98 _playoutDelay(0) {
99 memset(_oldKeyState, 0, sizeof(_oldKeyState));
100 RTC_DLOG(LS_INFO) << __FUNCTION__ << " created";
101 }
102
103 // ----------------------------------------------------------------------------
104 // AudioDeviceLinuxALSA - dtor
105 // ----------------------------------------------------------------------------
106
~AudioDeviceLinuxALSA()107 AudioDeviceLinuxALSA::~AudioDeviceLinuxALSA() {
108 RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed";
109
110 Terminate();
111
112 // Clean up the recording buffer and playout buffer.
113 if (_recordingBuffer) {
114 delete[] _recordingBuffer;
115 _recordingBuffer = NULL;
116 }
117 if (_playoutBuffer) {
118 delete[] _playoutBuffer;
119 _playoutBuffer = NULL;
120 }
121 }
122
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)123 void AudioDeviceLinuxALSA::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
124 MutexLock lock(&mutex_);
125
126 _ptrAudioBuffer = audioBuffer;
127
128 // Inform the AudioBuffer about default settings for this implementation.
129 // Set all values to zero here since the actual settings will be done by
130 // InitPlayout and InitRecording later.
131 _ptrAudioBuffer->SetRecordingSampleRate(0);
132 _ptrAudioBuffer->SetPlayoutSampleRate(0);
133 _ptrAudioBuffer->SetRecordingChannels(0);
134 _ptrAudioBuffer->SetPlayoutChannels(0);
135 }
136
ActiveAudioLayer(AudioDeviceModule::AudioLayer & audioLayer) const137 int32_t AudioDeviceLinuxALSA::ActiveAudioLayer(
138 AudioDeviceModule::AudioLayer& audioLayer) const {
139 audioLayer = AudioDeviceModule::kLinuxAlsaAudio;
140 return 0;
141 }
142
Init()143 AudioDeviceGeneric::InitStatus AudioDeviceLinuxALSA::Init() {
144 MutexLock lock(&mutex_);
145
146 // Load libasound
147 if (!GetAlsaSymbolTable()->Load()) {
148 // Alsa is not installed on this system
149 RTC_LOG(LS_ERROR) << "failed to load symbol table";
150 return InitStatus::OTHER_ERROR;
151 }
152
153 if (_initialized) {
154 return InitStatus::OK;
155 }
156 #if defined(WEBRTC_USE_X11)
157 // Get X display handle for typing detection
158 _XDisplay = XOpenDisplay(NULL);
159 if (!_XDisplay) {
160 RTC_LOG(LS_WARNING)
161 << "failed to open X display, typing detection will not work";
162 }
163 #endif
164
165 _initialized = true;
166
167 return InitStatus::OK;
168 }
169
Terminate()170 int32_t AudioDeviceLinuxALSA::Terminate() {
171 if (!_initialized) {
172 return 0;
173 }
174
175 MutexLock lock(&mutex_);
176
177 _mixerManager.Close();
178
179 // RECORDING
180 mutex_.Unlock();
181 _ptrThreadRec.Finalize();
182
183 // PLAYOUT
184 _ptrThreadPlay.Finalize();
185 mutex_.Lock();
186
187 #if defined(WEBRTC_USE_X11)
188 if (_XDisplay) {
189 XCloseDisplay(_XDisplay);
190 _XDisplay = NULL;
191 }
192 #endif
193 _initialized = false;
194 _outputDeviceIsSpecified = false;
195 _inputDeviceIsSpecified = false;
196
197 return 0;
198 }
199
Initialized() const200 bool AudioDeviceLinuxALSA::Initialized() const {
201 return (_initialized);
202 }
203
InitSpeaker()204 int32_t AudioDeviceLinuxALSA::InitSpeaker() {
205 MutexLock lock(&mutex_);
206 return InitSpeakerLocked();
207 }
208
InitSpeakerLocked()209 int32_t AudioDeviceLinuxALSA::InitSpeakerLocked() {
210 if (_playing) {
211 return -1;
212 }
213
214 char devName[kAdmMaxDeviceNameSize] = {0};
215 GetDevicesInfo(2, true, _outputDeviceIndex, devName, kAdmMaxDeviceNameSize);
216 return _mixerManager.OpenSpeaker(devName);
217 }
218
InitMicrophone()219 int32_t AudioDeviceLinuxALSA::InitMicrophone() {
220 MutexLock lock(&mutex_);
221 return InitMicrophoneLocked();
222 }
223
InitMicrophoneLocked()224 int32_t AudioDeviceLinuxALSA::InitMicrophoneLocked() {
225 if (_recording) {
226 return -1;
227 }
228
229 char devName[kAdmMaxDeviceNameSize] = {0};
230 GetDevicesInfo(2, false, _inputDeviceIndex, devName, kAdmMaxDeviceNameSize);
231 return _mixerManager.OpenMicrophone(devName);
232 }
233
SpeakerIsInitialized() const234 bool AudioDeviceLinuxALSA::SpeakerIsInitialized() const {
235 return (_mixerManager.SpeakerIsInitialized());
236 }
237
MicrophoneIsInitialized() const238 bool AudioDeviceLinuxALSA::MicrophoneIsInitialized() const {
239 return (_mixerManager.MicrophoneIsInitialized());
240 }
241
SpeakerVolumeIsAvailable(bool & available)242 int32_t AudioDeviceLinuxALSA::SpeakerVolumeIsAvailable(bool& available) {
243 bool wasInitialized = _mixerManager.SpeakerIsInitialized();
244
245 // Make an attempt to open up the
246 // output mixer corresponding to the currently selected output device.
247 if (!wasInitialized && InitSpeaker() == -1) {
248 // If we end up here it means that the selected speaker has no volume
249 // control.
250 available = false;
251 return 0;
252 }
253
254 // Given that InitSpeaker was successful, we know that a volume control
255 // exists
256 available = true;
257
258 // Close the initialized output mixer
259 if (!wasInitialized) {
260 _mixerManager.CloseSpeaker();
261 }
262
263 return 0;
264 }
265
SetSpeakerVolume(uint32_t volume)266 int32_t AudioDeviceLinuxALSA::SetSpeakerVolume(uint32_t volume) {
267 return (_mixerManager.SetSpeakerVolume(volume));
268 }
269
SpeakerVolume(uint32_t & volume) const270 int32_t AudioDeviceLinuxALSA::SpeakerVolume(uint32_t& volume) const {
271 uint32_t level(0);
272
273 if (_mixerManager.SpeakerVolume(level) == -1) {
274 return -1;
275 }
276
277 volume = level;
278
279 return 0;
280 }
281
MaxSpeakerVolume(uint32_t & maxVolume) const282 int32_t AudioDeviceLinuxALSA::MaxSpeakerVolume(uint32_t& maxVolume) const {
283 uint32_t maxVol(0);
284
285 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) {
286 return -1;
287 }
288
289 maxVolume = maxVol;
290
291 return 0;
292 }
293
MinSpeakerVolume(uint32_t & minVolume) const294 int32_t AudioDeviceLinuxALSA::MinSpeakerVolume(uint32_t& minVolume) const {
295 uint32_t minVol(0);
296
297 if (_mixerManager.MinSpeakerVolume(minVol) == -1) {
298 return -1;
299 }
300
301 minVolume = minVol;
302
303 return 0;
304 }
305
SpeakerMuteIsAvailable(bool & available)306 int32_t AudioDeviceLinuxALSA::SpeakerMuteIsAvailable(bool& available) {
307 bool isAvailable(false);
308 bool wasInitialized = _mixerManager.SpeakerIsInitialized();
309
310 // Make an attempt to open up the
311 // output mixer corresponding to the currently selected output device.
312 //
313 if (!wasInitialized && InitSpeaker() == -1) {
314 // If we end up here it means that the selected speaker has no volume
315 // control, hence it is safe to state that there is no mute control
316 // already at this stage.
317 available = false;
318 return 0;
319 }
320
321 // Check if the selected speaker has a mute control
322 _mixerManager.SpeakerMuteIsAvailable(isAvailable);
323
324 available = isAvailable;
325
326 // Close the initialized output mixer
327 if (!wasInitialized) {
328 _mixerManager.CloseSpeaker();
329 }
330
331 return 0;
332 }
333
SetSpeakerMute(bool enable)334 int32_t AudioDeviceLinuxALSA::SetSpeakerMute(bool enable) {
335 return (_mixerManager.SetSpeakerMute(enable));
336 }
337
SpeakerMute(bool & enabled) const338 int32_t AudioDeviceLinuxALSA::SpeakerMute(bool& enabled) const {
339 bool muted(0);
340
341 if (_mixerManager.SpeakerMute(muted) == -1) {
342 return -1;
343 }
344
345 enabled = muted;
346
347 return 0;
348 }
349
MicrophoneMuteIsAvailable(bool & available)350 int32_t AudioDeviceLinuxALSA::MicrophoneMuteIsAvailable(bool& available) {
351 bool isAvailable(false);
352 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
353
354 // Make an attempt to open up the
355 // input mixer corresponding to the currently selected input device.
356 //
357 if (!wasInitialized && InitMicrophone() == -1) {
358 // If we end up here it means that the selected microphone has no volume
359 // control, hence it is safe to state that there is no mute control
360 // already at this stage.
361 available = false;
362 return 0;
363 }
364
365 // Check if the selected microphone has a mute control
366 //
367 _mixerManager.MicrophoneMuteIsAvailable(isAvailable);
368 available = isAvailable;
369
370 // Close the initialized input mixer
371 //
372 if (!wasInitialized) {
373 _mixerManager.CloseMicrophone();
374 }
375
376 return 0;
377 }
378
SetMicrophoneMute(bool enable)379 int32_t AudioDeviceLinuxALSA::SetMicrophoneMute(bool enable) {
380 return (_mixerManager.SetMicrophoneMute(enable));
381 }
382
383 // ----------------------------------------------------------------------------
384 // MicrophoneMute
385 // ----------------------------------------------------------------------------
386
MicrophoneMute(bool & enabled) const387 int32_t AudioDeviceLinuxALSA::MicrophoneMute(bool& enabled) const {
388 bool muted(0);
389
390 if (_mixerManager.MicrophoneMute(muted) == -1) {
391 return -1;
392 }
393
394 enabled = muted;
395 return 0;
396 }
397
StereoRecordingIsAvailable(bool & available)398 int32_t AudioDeviceLinuxALSA::StereoRecordingIsAvailable(bool& available) {
399 MutexLock lock(&mutex_);
400
401 // If we already have initialized in stereo it's obviously available
402 if (_recIsInitialized && (2 == _recChannels)) {
403 available = true;
404 return 0;
405 }
406
407 // Save rec states and the number of rec channels
408 bool recIsInitialized = _recIsInitialized;
409 bool recording = _recording;
410 int recChannels = _recChannels;
411
412 available = false;
413
414 // Stop/uninitialize recording if initialized (and possibly started)
415 if (_recIsInitialized) {
416 StopRecordingLocked();
417 }
418
419 // Try init in stereo;
420 _recChannels = 2;
421 if (InitRecordingLocked() == 0) {
422 available = true;
423 }
424
425 // Stop/uninitialize recording
426 StopRecordingLocked();
427
428 // Recover previous states
429 _recChannels = recChannels;
430 if (recIsInitialized) {
431 InitRecordingLocked();
432 }
433 if (recording) {
434 StartRecording();
435 }
436
437 return 0;
438 }
439
SetStereoRecording(bool enable)440 int32_t AudioDeviceLinuxALSA::SetStereoRecording(bool enable) {
441 if (enable)
442 _recChannels = 2;
443 else
444 _recChannels = 1;
445
446 return 0;
447 }
448
StereoRecording(bool & enabled) const449 int32_t AudioDeviceLinuxALSA::StereoRecording(bool& enabled) const {
450 if (_recChannels == 2)
451 enabled = true;
452 else
453 enabled = false;
454
455 return 0;
456 }
457
StereoPlayoutIsAvailable(bool & available)458 int32_t AudioDeviceLinuxALSA::StereoPlayoutIsAvailable(bool& available) {
459 MutexLock lock(&mutex_);
460
461 // If we already have initialized in stereo it's obviously available
462 if (_playIsInitialized && (2 == _playChannels)) {
463 available = true;
464 return 0;
465 }
466
467 // Save rec states and the number of rec channels
468 bool playIsInitialized = _playIsInitialized;
469 bool playing = _playing;
470 int playChannels = _playChannels;
471
472 available = false;
473
474 // Stop/uninitialize recording if initialized (and possibly started)
475 if (_playIsInitialized) {
476 StopPlayoutLocked();
477 }
478
479 // Try init in stereo;
480 _playChannels = 2;
481 if (InitPlayoutLocked() == 0) {
482 available = true;
483 }
484
485 // Stop/uninitialize recording
486 StopPlayoutLocked();
487
488 // Recover previous states
489 _playChannels = playChannels;
490 if (playIsInitialized) {
491 InitPlayoutLocked();
492 }
493 if (playing) {
494 StartPlayout();
495 }
496
497 return 0;
498 }
499
SetStereoPlayout(bool enable)500 int32_t AudioDeviceLinuxALSA::SetStereoPlayout(bool enable) {
501 if (enable)
502 _playChannels = 2;
503 else
504 _playChannels = 1;
505
506 return 0;
507 }
508
StereoPlayout(bool & enabled) const509 int32_t AudioDeviceLinuxALSA::StereoPlayout(bool& enabled) const {
510 if (_playChannels == 2)
511 enabled = true;
512 else
513 enabled = false;
514
515 return 0;
516 }
517
MicrophoneVolumeIsAvailable(bool & available)518 int32_t AudioDeviceLinuxALSA::MicrophoneVolumeIsAvailable(bool& available) {
519 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
520
521 // Make an attempt to open up the
522 // input mixer corresponding to the currently selected output device.
523 if (!wasInitialized && InitMicrophone() == -1) {
524 // If we end up here it means that the selected microphone has no volume
525 // control.
526 available = false;
527 return 0;
528 }
529
530 // Given that InitMicrophone was successful, we know that a volume control
531 // exists
532 available = true;
533
534 // Close the initialized input mixer
535 if (!wasInitialized) {
536 _mixerManager.CloseMicrophone();
537 }
538
539 return 0;
540 }
541
SetMicrophoneVolume(uint32_t volume)542 int32_t AudioDeviceLinuxALSA::SetMicrophoneVolume(uint32_t volume) {
543 return (_mixerManager.SetMicrophoneVolume(volume));
544 }
545
MicrophoneVolume(uint32_t & volume) const546 int32_t AudioDeviceLinuxALSA::MicrophoneVolume(uint32_t& volume) const {
547 uint32_t level(0);
548
549 if (_mixerManager.MicrophoneVolume(level) == -1) {
550 RTC_LOG(LS_WARNING) << "failed to retrive current microphone level";
551 return -1;
552 }
553
554 volume = level;
555
556 return 0;
557 }
558
MaxMicrophoneVolume(uint32_t & maxVolume) const559 int32_t AudioDeviceLinuxALSA::MaxMicrophoneVolume(uint32_t& maxVolume) const {
560 uint32_t maxVol(0);
561
562 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) {
563 return -1;
564 }
565
566 maxVolume = maxVol;
567
568 return 0;
569 }
570
MinMicrophoneVolume(uint32_t & minVolume) const571 int32_t AudioDeviceLinuxALSA::MinMicrophoneVolume(uint32_t& minVolume) const {
572 uint32_t minVol(0);
573
574 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) {
575 return -1;
576 }
577
578 minVolume = minVol;
579
580 return 0;
581 }
582
PlayoutDevices()583 int16_t AudioDeviceLinuxALSA::PlayoutDevices() {
584 return (int16_t)GetDevicesInfo(0, true);
585 }
586
SetPlayoutDevice(uint16_t index)587 int32_t AudioDeviceLinuxALSA::SetPlayoutDevice(uint16_t index) {
588 if (_playIsInitialized) {
589 return -1;
590 }
591
592 uint32_t nDevices = GetDevicesInfo(0, true);
593 RTC_LOG(LS_VERBOSE) << "number of available audio output devices is "
594 << nDevices;
595
596 if (index > (nDevices - 1)) {
597 RTC_LOG(LS_ERROR) << "device index is out of range [0," << (nDevices - 1)
598 << "]";
599 return -1;
600 }
601
602 _outputDeviceIndex = index;
603 _outputDeviceIsSpecified = true;
604
605 return 0;
606 }
607
SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType)608 int32_t AudioDeviceLinuxALSA::SetPlayoutDevice(
609 AudioDeviceModule::WindowsDeviceType /*device*/) {
610 RTC_LOG(LS_ERROR) << "WindowsDeviceType not supported";
611 return -1;
612 }
613
PlayoutDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])614 int32_t AudioDeviceLinuxALSA::PlayoutDeviceName(
615 uint16_t index,
616 char name[kAdmMaxDeviceNameSize],
617 char guid[kAdmMaxGuidSize]) {
618 const uint16_t nDevices(PlayoutDevices());
619
620 if ((index > (nDevices - 1)) || (name == NULL)) {
621 return -1;
622 }
623
624 memset(name, 0, kAdmMaxDeviceNameSize);
625
626 if (guid != NULL) {
627 memset(guid, 0, kAdmMaxGuidSize);
628 }
629
630 return GetDevicesInfo(1, true, index, name, kAdmMaxDeviceNameSize);
631 }
632
RecordingDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])633 int32_t AudioDeviceLinuxALSA::RecordingDeviceName(
634 uint16_t index,
635 char name[kAdmMaxDeviceNameSize],
636 char guid[kAdmMaxGuidSize]) {
637 const uint16_t nDevices(RecordingDevices());
638
639 if ((index > (nDevices - 1)) || (name == NULL)) {
640 return -1;
641 }
642
643 memset(name, 0, kAdmMaxDeviceNameSize);
644
645 if (guid != NULL) {
646 memset(guid, 0, kAdmMaxGuidSize);
647 }
648
649 return GetDevicesInfo(1, false, index, name, kAdmMaxDeviceNameSize);
650 }
651
RecordingDevices()652 int16_t AudioDeviceLinuxALSA::RecordingDevices() {
653 return (int16_t)GetDevicesInfo(0, false);
654 }
655
SetRecordingDevice(uint16_t index)656 int32_t AudioDeviceLinuxALSA::SetRecordingDevice(uint16_t index) {
657 if (_recIsInitialized) {
658 return -1;
659 }
660
661 uint32_t nDevices = GetDevicesInfo(0, false);
662 RTC_LOG(LS_VERBOSE) << "number of availiable audio input devices is "
663 << nDevices;
664
665 if (index > (nDevices - 1)) {
666 RTC_LOG(LS_ERROR) << "device index is out of range [0," << (nDevices - 1)
667 << "]";
668 return -1;
669 }
670
671 _inputDeviceIndex = index;
672 _inputDeviceIsSpecified = true;
673
674 return 0;
675 }
676
677 // ----------------------------------------------------------------------------
678 // SetRecordingDevice II (II)
679 // ----------------------------------------------------------------------------
680
SetRecordingDevice(AudioDeviceModule::WindowsDeviceType)681 int32_t AudioDeviceLinuxALSA::SetRecordingDevice(
682 AudioDeviceModule::WindowsDeviceType /*device*/) {
683 RTC_LOG(LS_ERROR) << "WindowsDeviceType not supported";
684 return -1;
685 }
686
PlayoutIsAvailable(bool & available)687 int32_t AudioDeviceLinuxALSA::PlayoutIsAvailable(bool& available) {
688 available = false;
689
690 // Try to initialize the playout side with mono
691 // Assumes that user set num channels after calling this function
692 _playChannels = 1;
693 int32_t res = InitPlayout();
694
695 // Cancel effect of initialization
696 StopPlayout();
697
698 if (res != -1) {
699 available = true;
700 } else {
701 // It may be possible to play out in stereo
702 res = StereoPlayoutIsAvailable(available);
703 if (available) {
704 // Then set channels to 2 so InitPlayout doesn't fail
705 _playChannels = 2;
706 }
707 }
708
709 return res;
710 }
711
RecordingIsAvailable(bool & available)712 int32_t AudioDeviceLinuxALSA::RecordingIsAvailable(bool& available) {
713 available = false;
714
715 // Try to initialize the recording side with mono
716 // Assumes that user set num channels after calling this function
717 _recChannels = 1;
718 int32_t res = InitRecording();
719
720 // Cancel effect of initialization
721 StopRecording();
722
723 if (res != -1) {
724 available = true;
725 } else {
726 // It may be possible to record in stereo
727 res = StereoRecordingIsAvailable(available);
728 if (available) {
729 // Then set channels to 2 so InitPlayout doesn't fail
730 _recChannels = 2;
731 }
732 }
733
734 return res;
735 }
736
InitPlayout()737 int32_t AudioDeviceLinuxALSA::InitPlayout() {
738 MutexLock lock(&mutex_);
739 return InitPlayoutLocked();
740 }
741
InitPlayoutLocked()742 int32_t AudioDeviceLinuxALSA::InitPlayoutLocked() {
743 int errVal = 0;
744
745 if (_playing) {
746 return -1;
747 }
748
749 if (!_outputDeviceIsSpecified) {
750 return -1;
751 }
752
753 if (_playIsInitialized) {
754 return 0;
755 }
756 // Initialize the speaker (devices might have been added or removed)
757 if (InitSpeakerLocked() == -1) {
758 RTC_LOG(LS_WARNING) << "InitSpeaker() failed";
759 }
760
761 // Start by closing any existing wave-output devices
762 //
763 if (_handlePlayout != NULL) {
764 LATE(snd_pcm_close)(_handlePlayout);
765 _handlePlayout = NULL;
766 _playIsInitialized = false;
767 if (errVal < 0) {
768 RTC_LOG(LS_ERROR) << "Error closing current playout sound device, error: "
769 << LATE(snd_strerror)(errVal);
770 }
771 }
772
773 // Open PCM device for playout
774 char deviceName[kAdmMaxDeviceNameSize] = {0};
775 GetDevicesInfo(2, true, _outputDeviceIndex, deviceName,
776 kAdmMaxDeviceNameSize);
777
778 RTC_LOG(LS_VERBOSE) << "InitPlayout open (" << deviceName << ")";
779
780 errVal = LATE(snd_pcm_open)(&_handlePlayout, deviceName,
781 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
782
783 if (errVal == -EBUSY) // Device busy - try some more!
784 {
785 for (int i = 0; i < 5; i++) {
786 SleepMs(1000);
787 errVal = LATE(snd_pcm_open)(&_handlePlayout, deviceName,
788 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
789 if (errVal == 0) {
790 break;
791 }
792 }
793 }
794 if (errVal < 0) {
795 RTC_LOG(LS_ERROR) << "unable to open playback device: "
796 << LATE(snd_strerror)(errVal) << " (" << errVal << ")";
797 _handlePlayout = NULL;
798 return -1;
799 }
800
801 _playoutFramesIn10MS = _playoutFreq / 100;
802 if ((errVal = LATE(snd_pcm_set_params)(
803 _handlePlayout,
804 #if defined(WEBRTC_ARCH_BIG_ENDIAN)
805 SND_PCM_FORMAT_S16_BE,
806 #else
807 SND_PCM_FORMAT_S16_LE, // format
808 #endif
809 SND_PCM_ACCESS_RW_INTERLEAVED, // access
810 _playChannels, // channels
811 _playoutFreq, // rate
812 1, // soft_resample
813 ALSA_PLAYOUT_LATENCY // 40*1000 //latency required overall latency
814 // in us
815 )) < 0) { /* 0.5sec */
816 _playoutFramesIn10MS = 0;
817 RTC_LOG(LS_ERROR) << "unable to set playback device: "
818 << LATE(snd_strerror)(errVal) << " (" << errVal << ")";
819 ErrorRecovery(errVal, _handlePlayout);
820 errVal = LATE(snd_pcm_close)(_handlePlayout);
821 _handlePlayout = NULL;
822 return -1;
823 }
824
825 errVal = LATE(snd_pcm_get_params)(_handlePlayout, &_playoutBufferSizeInFrame,
826 &_playoutPeriodSizeInFrame);
827 if (errVal < 0) {
828 RTC_LOG(LS_ERROR) << "snd_pcm_get_params: " << LATE(snd_strerror)(errVal)
829 << " (" << errVal << ")";
830 _playoutBufferSizeInFrame = 0;
831 _playoutPeriodSizeInFrame = 0;
832 } else {
833 RTC_LOG(LS_VERBOSE) << "playout snd_pcm_get_params buffer_size:"
834 << _playoutBufferSizeInFrame
835 << " period_size :" << _playoutPeriodSizeInFrame;
836 }
837
838 if (_ptrAudioBuffer) {
839 // Update webrtc audio buffer with the selected parameters
840 _ptrAudioBuffer->SetPlayoutSampleRate(_playoutFreq);
841 _ptrAudioBuffer->SetPlayoutChannels(_playChannels);
842 }
843
844 // Set play buffer size
845 _playoutBufferSizeIn10MS =
846 LATE(snd_pcm_frames_to_bytes)(_handlePlayout, _playoutFramesIn10MS);
847
848 // Init varaibles used for play
849
850 if (_handlePlayout != NULL) {
851 _playIsInitialized = true;
852 return 0;
853 } else {
854 return -1;
855 }
856 }
857
InitRecording()858 int32_t AudioDeviceLinuxALSA::InitRecording() {
859 MutexLock lock(&mutex_);
860 return InitRecordingLocked();
861 }
862
InitRecordingLocked()863 int32_t AudioDeviceLinuxALSA::InitRecordingLocked() {
864 int errVal = 0;
865
866 if (_recording) {
867 return -1;
868 }
869
870 if (!_inputDeviceIsSpecified) {
871 return -1;
872 }
873
874 if (_recIsInitialized) {
875 return 0;
876 }
877
878 // Initialize the microphone (devices might have been added or removed)
879 if (InitMicrophoneLocked() == -1) {
880 RTC_LOG(LS_WARNING) << "InitMicrophone() failed";
881 }
882
883 // Start by closing any existing pcm-input devices
884 //
885 if (_handleRecord != NULL) {
886 int errVal = LATE(snd_pcm_close)(_handleRecord);
887 _handleRecord = NULL;
888 _recIsInitialized = false;
889 if (errVal < 0) {
890 RTC_LOG(LS_ERROR)
891 << "Error closing current recording sound device, error: "
892 << LATE(snd_strerror)(errVal);
893 }
894 }
895
896 // Open PCM device for recording
897 // The corresponding settings for playout are made after the record settings
898 char deviceName[kAdmMaxDeviceNameSize] = {0};
899 GetDevicesInfo(2, false, _inputDeviceIndex, deviceName,
900 kAdmMaxDeviceNameSize);
901
902 RTC_LOG(LS_VERBOSE) << "InitRecording open (" << deviceName << ")";
903 errVal = LATE(snd_pcm_open)(&_handleRecord, deviceName,
904 SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
905
906 // Available modes: 0 = blocking, SND_PCM_NONBLOCK, SND_PCM_ASYNC
907 if (errVal == -EBUSY) // Device busy - try some more!
908 {
909 for (int i = 0; i < 5; i++) {
910 SleepMs(1000);
911 errVal = LATE(snd_pcm_open)(&_handleRecord, deviceName,
912 SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
913 if (errVal == 0) {
914 break;
915 }
916 }
917 }
918 if (errVal < 0) {
919 RTC_LOG(LS_ERROR) << "unable to open record device: "
920 << LATE(snd_strerror)(errVal);
921 _handleRecord = NULL;
922 return -1;
923 }
924
925 _recordingFramesIn10MS = _recordingFreq / 100;
926 if ((errVal =
927 LATE(snd_pcm_set_params)(_handleRecord,
928 #if defined(WEBRTC_ARCH_BIG_ENDIAN)
929 SND_PCM_FORMAT_S16_BE, // format
930 #else
931 SND_PCM_FORMAT_S16_LE, // format
932 #endif
933 SND_PCM_ACCESS_RW_INTERLEAVED, // access
934 _recChannels, // channels
935 _recordingFreq, // rate
936 1, // soft_resample
937 ALSA_CAPTURE_LATENCY // latency in us
938 )) < 0) {
939 // Fall back to another mode then.
940 if (_recChannels == 1)
941 _recChannels = 2;
942 else
943 _recChannels = 1;
944
945 if ((errVal =
946 LATE(snd_pcm_set_params)(_handleRecord,
947 #if defined(WEBRTC_ARCH_BIG_ENDIAN)
948 SND_PCM_FORMAT_S16_BE, // format
949 #else
950 SND_PCM_FORMAT_S16_LE, // format
951 #endif
952 SND_PCM_ACCESS_RW_INTERLEAVED, // access
953 _recChannels, // channels
954 _recordingFreq, // rate
955 1, // soft_resample
956 ALSA_CAPTURE_LATENCY // latency in us
957 )) < 0) {
958 _recordingFramesIn10MS = 0;
959 RTC_LOG(LS_ERROR) << "unable to set record settings: "
960 << LATE(snd_strerror)(errVal) << " (" << errVal << ")";
961 ErrorRecovery(errVal, _handleRecord);
962 errVal = LATE(snd_pcm_close)(_handleRecord);
963 _handleRecord = NULL;
964 return -1;
965 }
966 }
967
968 errVal = LATE(snd_pcm_get_params)(_handleRecord, &_recordingBuffersizeInFrame,
969 &_recordingPeriodSizeInFrame);
970 if (errVal < 0) {
971 RTC_LOG(LS_ERROR) << "snd_pcm_get_params " << LATE(snd_strerror)(errVal)
972 << " (" << errVal << ")";
973 _recordingBuffersizeInFrame = 0;
974 _recordingPeriodSizeInFrame = 0;
975 } else {
976 RTC_LOG(LS_VERBOSE) << "capture snd_pcm_get_params, buffer_size:"
977 << _recordingBuffersizeInFrame
978 << ", period_size:" << _recordingPeriodSizeInFrame;
979 }
980
981 if (_ptrAudioBuffer) {
982 // Update webrtc audio buffer with the selected parameters
983 _ptrAudioBuffer->SetRecordingSampleRate(_recordingFreq);
984 _ptrAudioBuffer->SetRecordingChannels(_recChannels);
985 }
986
987 // Set rec buffer size and create buffer
988 _recordingBufferSizeIn10MS =
989 LATE(snd_pcm_frames_to_bytes)(_handleRecord, _recordingFramesIn10MS);
990
991 if (_handleRecord != NULL) {
992 // Mark recording side as initialized
993 _recIsInitialized = true;
994 return 0;
995 } else {
996 return -1;
997 }
998 }
999
StartRecording()1000 int32_t AudioDeviceLinuxALSA::StartRecording() {
1001 if (!_recIsInitialized) {
1002 return -1;
1003 }
1004
1005 if (_recording) {
1006 return 0;
1007 }
1008
1009 _recording = true;
1010
1011 int errVal = 0;
1012 _recordingFramesLeft = _recordingFramesIn10MS;
1013
1014 // Make sure we only create the buffer once.
1015 if (!_recordingBuffer)
1016 _recordingBuffer = new int8_t[_recordingBufferSizeIn10MS];
1017 if (!_recordingBuffer) {
1018 RTC_LOG(LS_ERROR) << "failed to alloc recording buffer";
1019 _recording = false;
1020 return -1;
1021 }
1022 // RECORDING
1023 _ptrThreadRec = rtc::PlatformThread::SpawnJoinable(
1024 [this] {
1025 while (RecThreadProcess()) {
1026 }
1027 },
1028 "webrtc_audio_module_capture_thread",
1029 rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kRealtime));
1030
1031 errVal = LATE(snd_pcm_prepare)(_handleRecord);
1032 if (errVal < 0) {
1033 RTC_LOG(LS_ERROR) << "capture snd_pcm_prepare failed ("
1034 << LATE(snd_strerror)(errVal) << ")\n";
1035 // just log error
1036 // if snd_pcm_open fails will return -1
1037 }
1038
1039 errVal = LATE(snd_pcm_start)(_handleRecord);
1040 if (errVal < 0) {
1041 RTC_LOG(LS_ERROR) << "capture snd_pcm_start err: "
1042 << LATE(snd_strerror)(errVal);
1043 errVal = LATE(snd_pcm_start)(_handleRecord);
1044 if (errVal < 0) {
1045 RTC_LOG(LS_ERROR) << "capture snd_pcm_start 2nd try err: "
1046 << LATE(snd_strerror)(errVal);
1047 StopRecording();
1048 return -1;
1049 }
1050 }
1051
1052 return 0;
1053 }
1054
StopRecording()1055 int32_t AudioDeviceLinuxALSA::StopRecording() {
1056 MutexLock lock(&mutex_);
1057 return StopRecordingLocked();
1058 }
1059
StopRecordingLocked()1060 int32_t AudioDeviceLinuxALSA::StopRecordingLocked() {
1061 if (!_recIsInitialized) {
1062 return 0;
1063 }
1064
1065 if (_handleRecord == NULL) {
1066 return -1;
1067 }
1068
1069 // Make sure we don't start recording (it's asynchronous).
1070 _recIsInitialized = false;
1071 _recording = false;
1072
1073 _ptrThreadRec.Finalize();
1074
1075 _recordingFramesLeft = 0;
1076 if (_recordingBuffer) {
1077 delete[] _recordingBuffer;
1078 _recordingBuffer = NULL;
1079 }
1080
1081 // Stop and close pcm recording device.
1082 int errVal = LATE(snd_pcm_drop)(_handleRecord);
1083 if (errVal < 0) {
1084 RTC_LOG(LS_ERROR) << "Error stop recording: " << LATE(snd_strerror)(errVal);
1085 return -1;
1086 }
1087
1088 errVal = LATE(snd_pcm_close)(_handleRecord);
1089 if (errVal < 0) {
1090 RTC_LOG(LS_ERROR) << "Error closing record sound device, error: "
1091 << LATE(snd_strerror)(errVal);
1092 return -1;
1093 }
1094
1095 // Check if we have muted and unmute if so.
1096 bool muteEnabled = false;
1097 MicrophoneMute(muteEnabled);
1098 if (muteEnabled) {
1099 SetMicrophoneMute(false);
1100 }
1101
1102 // set the pcm input handle to NULL
1103 _handleRecord = NULL;
1104 return 0;
1105 }
1106
RecordingIsInitialized() const1107 bool AudioDeviceLinuxALSA::RecordingIsInitialized() const {
1108 return (_recIsInitialized);
1109 }
1110
Recording() const1111 bool AudioDeviceLinuxALSA::Recording() const {
1112 return (_recording);
1113 }
1114
PlayoutIsInitialized() const1115 bool AudioDeviceLinuxALSA::PlayoutIsInitialized() const {
1116 return (_playIsInitialized);
1117 }
1118
StartPlayout()1119 int32_t AudioDeviceLinuxALSA::StartPlayout() {
1120 if (!_playIsInitialized) {
1121 return -1;
1122 }
1123
1124 if (_playing) {
1125 return 0;
1126 }
1127
1128 _playing = true;
1129
1130 _playoutFramesLeft = 0;
1131 if (!_playoutBuffer)
1132 _playoutBuffer = new int8_t[_playoutBufferSizeIn10MS];
1133 if (!_playoutBuffer) {
1134 RTC_LOG(LS_ERROR) << "failed to alloc playout buf";
1135 _playing = false;
1136 return -1;
1137 }
1138
1139 // PLAYOUT
1140 _ptrThreadPlay = rtc::PlatformThread::SpawnJoinable(
1141 [this] {
1142 while (PlayThreadProcess()) {
1143 }
1144 },
1145 "webrtc_audio_module_play_thread",
1146 rtc::ThreadAttributes().SetPriority(rtc::ThreadPriority::kRealtime));
1147
1148 int errVal = LATE(snd_pcm_prepare)(_handlePlayout);
1149 if (errVal < 0) {
1150 RTC_LOG(LS_ERROR) << "playout snd_pcm_prepare failed ("
1151 << LATE(snd_strerror)(errVal) << ")\n";
1152 // just log error
1153 // if snd_pcm_open fails will return -1
1154 }
1155
1156 return 0;
1157 }
1158
StopPlayout()1159 int32_t AudioDeviceLinuxALSA::StopPlayout() {
1160 MutexLock lock(&mutex_);
1161 return StopPlayoutLocked();
1162 }
1163
StopPlayoutLocked()1164 int32_t AudioDeviceLinuxALSA::StopPlayoutLocked() {
1165 if (!_playIsInitialized) {
1166 return 0;
1167 }
1168
1169 if (_handlePlayout == NULL) {
1170 return -1;
1171 }
1172
1173 _playing = false;
1174
1175 // stop playout thread first
1176 _ptrThreadPlay.Finalize();
1177
1178 _playoutFramesLeft = 0;
1179 delete[] _playoutBuffer;
1180 _playoutBuffer = NULL;
1181
1182 // stop and close pcm playout device
1183 int errVal = LATE(snd_pcm_drop)(_handlePlayout);
1184 if (errVal < 0) {
1185 RTC_LOG(LS_ERROR) << "Error stop playing: " << LATE(snd_strerror)(errVal);
1186 }
1187
1188 errVal = LATE(snd_pcm_close)(_handlePlayout);
1189 if (errVal < 0)
1190 RTC_LOG(LS_ERROR) << "Error closing playout sound device, error: "
1191 << LATE(snd_strerror)(errVal);
1192
1193 // set the pcm input handle to NULL
1194 _playIsInitialized = false;
1195 _handlePlayout = NULL;
1196 RTC_LOG(LS_VERBOSE) << "handle_playout is now set to NULL";
1197
1198 return 0;
1199 }
1200
PlayoutDelay(uint16_t & delayMS) const1201 int32_t AudioDeviceLinuxALSA::PlayoutDelay(uint16_t& delayMS) const {
1202 delayMS = (uint16_t)_playoutDelay * 1000 / _playoutFreq;
1203 return 0;
1204 }
1205
Playing() const1206 bool AudioDeviceLinuxALSA::Playing() const {
1207 return (_playing);
1208 }
1209
1210 // ============================================================================
1211 // Private Methods
1212 // ============================================================================
1213
GetDevicesInfo(const int32_t function,const bool playback,const int32_t enumDeviceNo,char * enumDeviceName,const int32_t ednLen) const1214 int32_t AudioDeviceLinuxALSA::GetDevicesInfo(const int32_t function,
1215 const bool playback,
1216 const int32_t enumDeviceNo,
1217 char* enumDeviceName,
1218 const int32_t ednLen) const {
1219 // Device enumeration based on libjingle implementation
1220 // by Tristan Schmelcher at Google Inc.
1221
1222 const char* type = playback ? "Output" : "Input";
1223 // dmix and dsnoop are only for playback and capture, respectively, but ALSA
1224 // stupidly includes them in both lists.
1225 const char* ignorePrefix = playback ? "dsnoop:" : "dmix:";
1226 // (ALSA lists many more "devices" of questionable interest, but we show them
1227 // just in case the weird devices may actually be desirable for some
1228 // users/systems.)
1229
1230 int err;
1231 int enumCount(0);
1232 bool keepSearching(true);
1233
1234 // From Chromium issue 95797
1235 // Loop through the sound cards to get Alsa device hints.
1236 // Don't use snd_device_name_hint(-1,..) since there is a access violation
1237 // inside this ALSA API with libasound.so.2.0.0.
1238 int card = -1;
1239 while (!(LATE(snd_card_next)(&card)) && (card >= 0) && keepSearching) {
1240 void** hints;
1241 err = LATE(snd_device_name_hint)(card, "pcm", &hints);
1242 if (err != 0) {
1243 RTC_LOG(LS_ERROR) << "GetDevicesInfo - device name hint error: "
1244 << LATE(snd_strerror)(err);
1245 return -1;
1246 }
1247
1248 enumCount++; // default is 0
1249 if ((function == FUNC_GET_DEVICE_NAME ||
1250 function == FUNC_GET_DEVICE_NAME_FOR_AN_ENUM) &&
1251 enumDeviceNo == 0) {
1252 strcpy(enumDeviceName, "default");
1253
1254 err = LATE(snd_device_name_free_hint)(hints);
1255 if (err != 0) {
1256 RTC_LOG(LS_ERROR) << "GetDevicesInfo - device name free hint error: "
1257 << LATE(snd_strerror)(err);
1258 }
1259
1260 return 0;
1261 }
1262
1263 for (void** list = hints; *list != NULL; ++list) {
1264 char* actualType = LATE(snd_device_name_get_hint)(*list, "IOID");
1265 if (actualType) { // NULL means it's both.
1266 bool wrongType = (strcmp(actualType, type) != 0);
1267 free(actualType);
1268 if (wrongType) {
1269 // Wrong type of device (i.e., input vs. output).
1270 continue;
1271 }
1272 }
1273
1274 char* name = LATE(snd_device_name_get_hint)(*list, "NAME");
1275 if (!name) {
1276 RTC_LOG(LS_ERROR) << "Device has no name";
1277 // Skip it.
1278 continue;
1279 }
1280
1281 // Now check if we actually want to show this device.
1282 if (strcmp(name, "default") != 0 && strcmp(name, "null") != 0 &&
1283 strcmp(name, "pulse") != 0 &&
1284 strncmp(name, ignorePrefix, strlen(ignorePrefix)) != 0) {
1285 // Yes, we do.
1286 char* desc = LATE(snd_device_name_get_hint)(*list, "DESC");
1287 if (!desc) {
1288 // Virtual devices don't necessarily have descriptions.
1289 // Use their names instead.
1290 desc = name;
1291 }
1292
1293 if (FUNC_GET_NUM_OF_DEVICE == function) {
1294 RTC_LOG(LS_VERBOSE) << "Enum device " << enumCount << " - " << name;
1295 }
1296 if ((FUNC_GET_DEVICE_NAME == function) && (enumDeviceNo == enumCount)) {
1297 // We have found the enum device, copy the name to buffer.
1298 strncpy(enumDeviceName, desc, ednLen);
1299 enumDeviceName[ednLen - 1] = '\0';
1300 keepSearching = false;
1301 // Replace '\n' with '-'.
1302 char* pret = strchr(enumDeviceName, '\n' /*0xa*/); // LF
1303 if (pret)
1304 *pret = '-';
1305 }
1306 if ((FUNC_GET_DEVICE_NAME_FOR_AN_ENUM == function) &&
1307 (enumDeviceNo == enumCount)) {
1308 // We have found the enum device, copy the name to buffer.
1309 strncpy(enumDeviceName, name, ednLen);
1310 enumDeviceName[ednLen - 1] = '\0';
1311 keepSearching = false;
1312 }
1313
1314 if (keepSearching)
1315 ++enumCount;
1316
1317 if (desc != name)
1318 free(desc);
1319 }
1320
1321 free(name);
1322
1323 if (!keepSearching)
1324 break;
1325 }
1326
1327 err = LATE(snd_device_name_free_hint)(hints);
1328 if (err != 0) {
1329 RTC_LOG(LS_ERROR) << "GetDevicesInfo - device name free hint error: "
1330 << LATE(snd_strerror)(err);
1331 // Continue and return true anyway, since we did get the whole list.
1332 }
1333 }
1334
1335 if (FUNC_GET_NUM_OF_DEVICE == function) {
1336 if (enumCount == 1) // only default?
1337 enumCount = 0;
1338 return enumCount; // Normal return point for function 0
1339 }
1340
1341 if (keepSearching) {
1342 // If we get here for function 1 and 2, we didn't find the specified
1343 // enum device.
1344 RTC_LOG(LS_ERROR)
1345 << "GetDevicesInfo - Could not find device name or numbers";
1346 return -1;
1347 }
1348
1349 return 0;
1350 }
1351
InputSanityCheckAfterUnlockedPeriod() const1352 int32_t AudioDeviceLinuxALSA::InputSanityCheckAfterUnlockedPeriod() const {
1353 if (_handleRecord == NULL) {
1354 RTC_LOG(LS_ERROR) << "input state has been modified during unlocked period";
1355 return -1;
1356 }
1357 return 0;
1358 }
1359
OutputSanityCheckAfterUnlockedPeriod() const1360 int32_t AudioDeviceLinuxALSA::OutputSanityCheckAfterUnlockedPeriod() const {
1361 if (_handlePlayout == NULL) {
1362 RTC_LOG(LS_ERROR)
1363 << "output state has been modified during unlocked period";
1364 return -1;
1365 }
1366 return 0;
1367 }
1368
ErrorRecovery(int32_t error,snd_pcm_t * deviceHandle)1369 int32_t AudioDeviceLinuxALSA::ErrorRecovery(int32_t error,
1370 snd_pcm_t* deviceHandle) {
1371 int st = LATE(snd_pcm_state)(deviceHandle);
1372 RTC_LOG(LS_VERBOSE) << "Trying to recover from "
1373 << ((LATE(snd_pcm_stream)(deviceHandle) ==
1374 SND_PCM_STREAM_CAPTURE)
1375 ? "capture"
1376 : "playout")
1377 << " error: " << LATE(snd_strerror)(error) << " ("
1378 << error << ") (state " << st << ")";
1379
1380 // It is recommended to use snd_pcm_recover for all errors. If that function
1381 // cannot handle the error, the input error code will be returned, otherwise
1382 // 0 is returned. From snd_pcm_recover API doc: "This functions handles
1383 // -EINTR (4) (interrupted system call), -EPIPE (32) (playout overrun or
1384 // capture underrun) and -ESTRPIPE (86) (stream is suspended) error codes
1385 // trying to prepare given stream for next I/O."
1386
1387 /** Open */
1388 // SND_PCM_STATE_OPEN = 0,
1389 /** Setup installed */
1390 // SND_PCM_STATE_SETUP,
1391 /** Ready to start */
1392 // SND_PCM_STATE_PREPARED,
1393 /** Running */
1394 // SND_PCM_STATE_RUNNING,
1395 /** Stopped: underrun (playback) or overrun (capture) detected */
1396 // SND_PCM_STATE_XRUN,= 4
1397 /** Draining: running (playback) or stopped (capture) */
1398 // SND_PCM_STATE_DRAINING,
1399 /** Paused */
1400 // SND_PCM_STATE_PAUSED,
1401 /** Hardware is suspended */
1402 // SND_PCM_STATE_SUSPENDED,
1403 // ** Hardware is disconnected */
1404 // SND_PCM_STATE_DISCONNECTED,
1405 // SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED
1406
1407 // snd_pcm_recover isn't available in older alsa, e.g. on the FC4 machine
1408 // in Sthlm lab.
1409
1410 int res = LATE(snd_pcm_recover)(deviceHandle, error, 1);
1411 if (0 == res) {
1412 RTC_LOG(LS_VERBOSE) << "Recovery - snd_pcm_recover OK";
1413
1414 if ((error == -EPIPE || error == -ESTRPIPE) && // Buf underrun/overrun.
1415 _recording &&
1416 LATE(snd_pcm_stream)(deviceHandle) == SND_PCM_STREAM_CAPTURE) {
1417 // For capture streams we also have to repeat the explicit start()
1418 // to get data flowing again.
1419 int err = LATE(snd_pcm_start)(deviceHandle);
1420 if (err != 0) {
1421 RTC_LOG(LS_ERROR) << "Recovery - snd_pcm_start error: " << err;
1422 return -1;
1423 }
1424 }
1425
1426 if ((error == -EPIPE || error == -ESTRPIPE) && // Buf underrun/overrun.
1427 _playing &&
1428 LATE(snd_pcm_stream)(deviceHandle) == SND_PCM_STREAM_PLAYBACK) {
1429 // For capture streams we also have to repeat the explicit start() to get
1430 // data flowing again.
1431 int err = LATE(snd_pcm_start)(deviceHandle);
1432 if (err != 0) {
1433 RTC_LOG(LS_ERROR) << "Recovery - snd_pcm_start error: "
1434 << LATE(snd_strerror)(err);
1435 return -1;
1436 }
1437 }
1438
1439 return -EPIPE == error ? 1 : 0;
1440 } else {
1441 RTC_LOG(LS_ERROR) << "Unrecoverable alsa stream error: " << res;
1442 }
1443
1444 return res;
1445 }
1446
1447 // ============================================================================
1448 // Thread Methods
1449 // ============================================================================
1450
PlayThreadProcess()1451 bool AudioDeviceLinuxALSA::PlayThreadProcess() {
1452 if (!_playing)
1453 return false;
1454
1455 int err;
1456 snd_pcm_sframes_t frames;
1457 snd_pcm_sframes_t avail_frames;
1458
1459 Lock();
1460 // return a positive number of frames ready otherwise a negative error code
1461 avail_frames = LATE(snd_pcm_avail_update)(_handlePlayout);
1462 if (avail_frames < 0) {
1463 RTC_LOG(LS_ERROR) << "playout snd_pcm_avail_update error: "
1464 << LATE(snd_strerror)(avail_frames);
1465 ErrorRecovery(avail_frames, _handlePlayout);
1466 UnLock();
1467 return true;
1468 } else if (avail_frames == 0) {
1469 UnLock();
1470
1471 // maximum tixe in milliseconds to wait, a negative value means infinity
1472 err = LATE(snd_pcm_wait)(_handlePlayout, 2);
1473 if (err == 0) { // timeout occured
1474 RTC_LOG(LS_VERBOSE) << "playout snd_pcm_wait timeout";
1475 }
1476
1477 return true;
1478 }
1479
1480 if (_playoutFramesLeft <= 0) {
1481 UnLock();
1482 _ptrAudioBuffer->RequestPlayoutData(_playoutFramesIn10MS);
1483 Lock();
1484
1485 _playoutFramesLeft = _ptrAudioBuffer->GetPlayoutData(_playoutBuffer);
1486 RTC_DCHECK_EQ(_playoutFramesLeft, _playoutFramesIn10MS);
1487 }
1488
1489 if (static_cast<uint32_t>(avail_frames) > _playoutFramesLeft)
1490 avail_frames = _playoutFramesLeft;
1491
1492 int size = LATE(snd_pcm_frames_to_bytes)(_handlePlayout, _playoutFramesLeft);
1493 frames = LATE(snd_pcm_writei)(
1494 _handlePlayout, &_playoutBuffer[_playoutBufferSizeIn10MS - size],
1495 avail_frames);
1496
1497 if (frames < 0) {
1498 RTC_LOG(LS_VERBOSE) << "playout snd_pcm_writei error: "
1499 << LATE(snd_strerror)(frames);
1500 _playoutFramesLeft = 0;
1501 ErrorRecovery(frames, _handlePlayout);
1502 UnLock();
1503 return true;
1504 } else {
1505 RTC_DCHECK_EQ(frames, avail_frames);
1506 _playoutFramesLeft -= frames;
1507 }
1508
1509 UnLock();
1510 return true;
1511 }
1512
RecThreadProcess()1513 bool AudioDeviceLinuxALSA::RecThreadProcess() {
1514 if (!_recording)
1515 return false;
1516
1517 int err;
1518 snd_pcm_sframes_t frames;
1519 snd_pcm_sframes_t avail_frames;
1520 int8_t buffer[_recordingBufferSizeIn10MS];
1521
1522 Lock();
1523
1524 // return a positive number of frames ready otherwise a negative error code
1525 avail_frames = LATE(snd_pcm_avail_update)(_handleRecord);
1526 if (avail_frames < 0) {
1527 RTC_LOG(LS_ERROR) << "capture snd_pcm_avail_update error: "
1528 << LATE(snd_strerror)(avail_frames);
1529 ErrorRecovery(avail_frames, _handleRecord);
1530 UnLock();
1531 return true;
1532 } else if (avail_frames == 0) { // no frame is available now
1533 UnLock();
1534
1535 // maximum time in milliseconds to wait, a negative value means infinity
1536 err = LATE(snd_pcm_wait)(_handleRecord, ALSA_CAPTURE_WAIT_TIMEOUT);
1537 if (err == 0) // timeout occured
1538 RTC_LOG(LS_VERBOSE) << "capture snd_pcm_wait timeout";
1539
1540 return true;
1541 }
1542
1543 if (static_cast<uint32_t>(avail_frames) > _recordingFramesLeft)
1544 avail_frames = _recordingFramesLeft;
1545
1546 frames = LATE(snd_pcm_readi)(_handleRecord, buffer,
1547 avail_frames); // frames to be written
1548 if (frames < 0) {
1549 RTC_LOG(LS_ERROR) << "capture snd_pcm_readi error: "
1550 << LATE(snd_strerror)(frames);
1551 ErrorRecovery(frames, _handleRecord);
1552 UnLock();
1553 return true;
1554 } else if (frames > 0) {
1555 RTC_DCHECK_EQ(frames, avail_frames);
1556
1557 int left_size =
1558 LATE(snd_pcm_frames_to_bytes)(_handleRecord, _recordingFramesLeft);
1559 int size = LATE(snd_pcm_frames_to_bytes)(_handleRecord, frames);
1560
1561 memcpy(&_recordingBuffer[_recordingBufferSizeIn10MS - left_size], buffer,
1562 size);
1563 _recordingFramesLeft -= frames;
1564
1565 if (!_recordingFramesLeft) { // buf is full
1566 _recordingFramesLeft = _recordingFramesIn10MS;
1567
1568 // store the recorded buffer (no action will be taken if the
1569 // #recorded samples is not a full buffer)
1570 _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer,
1571 _recordingFramesIn10MS);
1572
1573 // calculate delay
1574 _playoutDelay = 0;
1575 _recordingDelay = 0;
1576 if (_handlePlayout) {
1577 err = LATE(snd_pcm_delay)(_handlePlayout,
1578 &_playoutDelay); // returned delay in frames
1579 if (err < 0) {
1580 // TODO(xians): Shall we call ErrorRecovery() here?
1581 _playoutDelay = 0;
1582 RTC_LOG(LS_ERROR)
1583 << "playout snd_pcm_delay: " << LATE(snd_strerror)(err);
1584 }
1585 }
1586
1587 err = LATE(snd_pcm_delay)(_handleRecord,
1588 &_recordingDelay); // returned delay in frames
1589 if (err < 0) {
1590 // TODO(xians): Shall we call ErrorRecovery() here?
1591 _recordingDelay = 0;
1592 RTC_LOG(LS_ERROR) << "capture snd_pcm_delay: "
1593 << LATE(snd_strerror)(err);
1594 }
1595
1596 // TODO(xians): Shall we add 10ms buffer delay to the record delay?
1597 _ptrAudioBuffer->SetVQEData(_playoutDelay * 1000 / _playoutFreq,
1598 _recordingDelay * 1000 / _recordingFreq);
1599
1600 _ptrAudioBuffer->SetTypingStatus(KeyPressed());
1601
1602 // Deliver recorded samples at specified sample rate, mic level etc.
1603 // to the observer using callback.
1604 UnLock();
1605 _ptrAudioBuffer->DeliverRecordedData();
1606 Lock();
1607 }
1608 }
1609
1610 UnLock();
1611 return true;
1612 }
1613
KeyPressed() const1614 bool AudioDeviceLinuxALSA::KeyPressed() const {
1615 #if defined(WEBRTC_USE_X11)
1616 char szKey[32];
1617 unsigned int i = 0;
1618 char state = 0;
1619
1620 if (!_XDisplay)
1621 return false;
1622
1623 // Check key map status
1624 XQueryKeymap(_XDisplay, szKey);
1625
1626 // A bit change in keymap means a key is pressed
1627 for (i = 0; i < sizeof(szKey); i++)
1628 state |= (szKey[i] ^ _oldKeyState[i]) & szKey[i];
1629
1630 // Save old state
1631 memcpy((char*)_oldKeyState, (char*)szKey, sizeof(_oldKeyState));
1632 return (state != 0);
1633 #else
1634 return false;
1635 #endif
1636 }
1637 } // namespace webrtc
1638