xref: /aosp_15_r20/external/webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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