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