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 <assert.h> 12 13 #include "webrtc/modules/audio_device/audio_device_config.h" 14 #include "webrtc/modules/audio_device/linux/audio_device_alsa_linux.h" 15 16 #include "webrtc/system_wrappers/include/event_wrapper.h" 17 #include "webrtc/system_wrappers/include/sleep.h" 18 #include "webrtc/system_wrappers/include/trace.h" 19 20 webrtc_adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable; 21 22 // Accesses ALSA functions through our late-binding symbol table instead of 23 // directly. This way we don't have to link to libasound, which means our binary 24 // will work on systems that don't have it. 25 #define LATE(sym) \ 26 LATESYM_GET(webrtc_adm_linux_alsa::AlsaSymbolTable, &AlsaSymbolTable, sym) 27 28 // Redefine these here to be able to do late-binding 29 #undef snd_ctl_card_info_alloca 30 #define snd_ctl_card_info_alloca(ptr) \ 31 do { *ptr = (snd_ctl_card_info_t *) \ 32 __builtin_alloca (LATE(snd_ctl_card_info_sizeof)()); \ 33 memset(*ptr, 0, LATE(snd_ctl_card_info_sizeof)()); } while (0) 34 35 #undef snd_pcm_info_alloca 36 #define snd_pcm_info_alloca(pInfo) \ 37 do { *pInfo = (snd_pcm_info_t *) \ 38 __builtin_alloca (LATE(snd_pcm_info_sizeof)()); \ 39 memset(*pInfo, 0, LATE(snd_pcm_info_sizeof)()); } while (0) 40 41 // snd_lib_error_handler_t 42 void WebrtcAlsaErrorHandler(const char *file, 43 int line, 44 const char *function, 45 int err, 46 const char *fmt,...){}; 47 48 namespace webrtc 49 { 50 static const unsigned int ALSA_PLAYOUT_FREQ = 48000; 51 static const unsigned int ALSA_PLAYOUT_CH = 2; 52 static const unsigned int ALSA_PLAYOUT_LATENCY = 40*1000; // in us 53 static const unsigned int ALSA_CAPTURE_FREQ = 48000; 54 static const unsigned int ALSA_CAPTURE_CH = 2; 55 static const unsigned int ALSA_CAPTURE_LATENCY = 40*1000; // in us 56 static const unsigned int ALSA_CAPTURE_WAIT_TIMEOUT = 5; // in ms 57 58 #define FUNC_GET_NUM_OF_DEVICE 0 59 #define FUNC_GET_DEVICE_NAME 1 60 #define FUNC_GET_DEVICE_NAME_FOR_AN_ENUM 2 61 62 AudioDeviceLinuxALSA::AudioDeviceLinuxALSA(const int32_t id) : 63 _ptrAudioBuffer(NULL), 64 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), 65 _id(id), 66 _mixerManager(id), 67 _inputDeviceIndex(0), 68 _outputDeviceIndex(0), 69 _inputDeviceIsSpecified(false), 70 _outputDeviceIsSpecified(false), 71 _handleRecord(NULL), 72 _handlePlayout(NULL), 73 _recordingBuffersizeInFrame(0), 74 _recordingPeriodSizeInFrame(0), 75 _playoutBufferSizeInFrame(0), 76 _playoutPeriodSizeInFrame(0), 77 _recordingBufferSizeIn10MS(0), 78 _playoutBufferSizeIn10MS(0), 79 _recordingFramesIn10MS(0), 80 _playoutFramesIn10MS(0), 81 _recordingFreq(ALSA_CAPTURE_FREQ), 82 _playoutFreq(ALSA_PLAYOUT_FREQ), 83 _recChannels(ALSA_CAPTURE_CH), 84 _playChannels(ALSA_PLAYOUT_CH), 85 _recordingBuffer(NULL), 86 _playoutBuffer(NULL), 87 _recordingFramesLeft(0), 88 _playoutFramesLeft(0), 89 _playBufType(AudioDeviceModule::kFixedBufferSize), 90 _initialized(false), 91 _recording(false), 92 _playing(false), 93 _recIsInitialized(false), 94 _playIsInitialized(false), 95 _AGC(false), 96 _recordingDelay(0), 97 _playoutDelay(0), 98 _playWarning(0), 99 _playError(0), 100 _recWarning(0), 101 _recError(0), 102 _playBufDelay(80), 103 _playBufDelayFixed(80) 104 { 105 memset(_oldKeyState, 0, sizeof(_oldKeyState)); 106 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, 107 "%s created", __FUNCTION__); 108 } 109 110 // ---------------------------------------------------------------------------- 111 // AudioDeviceLinuxALSA - dtor 112 // ---------------------------------------------------------------------------- 113 114 AudioDeviceLinuxALSA::~AudioDeviceLinuxALSA() 115 { 116 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, 117 "%s destroyed", __FUNCTION__); 118 119 Terminate(); 120 121 // Clean up the recording buffer and playout buffer. 122 if (_recordingBuffer) 123 { 124 delete [] _recordingBuffer; 125 _recordingBuffer = NULL; 126 } 127 if (_playoutBuffer) 128 { 129 delete [] _playoutBuffer; 130 _playoutBuffer = NULL; 131 } 132 delete &_critSect; 133 } 134 135 void AudioDeviceLinuxALSA::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) 136 { 137 138 CriticalSectionScoped lock(&_critSect); 139 140 _ptrAudioBuffer = audioBuffer; 141 142 // Inform the AudioBuffer about default settings for this implementation. 143 // Set all values to zero here since the actual settings will be done by 144 // InitPlayout and InitRecording later. 145 _ptrAudioBuffer->SetRecordingSampleRate(0); 146 _ptrAudioBuffer->SetPlayoutSampleRate(0); 147 _ptrAudioBuffer->SetRecordingChannels(0); 148 _ptrAudioBuffer->SetPlayoutChannels(0); 149 } 150 151 int32_t AudioDeviceLinuxALSA::ActiveAudioLayer( 152 AudioDeviceModule::AudioLayer& audioLayer) const 153 { 154 audioLayer = AudioDeviceModule::kLinuxAlsaAudio; 155 return 0; 156 } 157 158 int32_t AudioDeviceLinuxALSA::Init() 159 { 160 161 CriticalSectionScoped lock(&_critSect); 162 163 // Load libasound 164 if (!AlsaSymbolTable.Load()) 165 { 166 // Alsa is not installed on 167 // this system 168 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 169 " failed to load symbol table"); 170 return -1; 171 } 172 173 if (_initialized) 174 { 175 return 0; 176 } 177 #if defined(USE_X11) 178 //Get X display handle for typing detection 179 _XDisplay = XOpenDisplay(NULL); 180 if (!_XDisplay) 181 { 182 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 183 " failed to open X display, typing detection will not work"); 184 } 185 #endif 186 _playWarning = 0; 187 _playError = 0; 188 _recWarning = 0; 189 _recError = 0; 190 191 _initialized = true; 192 193 return 0; 194 } 195 196 int32_t AudioDeviceLinuxALSA::Terminate() 197 { 198 if (!_initialized) 199 { 200 return 0; 201 } 202 203 CriticalSectionScoped lock(&_critSect); 204 205 _mixerManager.Close(); 206 207 // RECORDING 208 if (_ptrThreadRec) 209 { 210 rtc::PlatformThread* tmpThread = _ptrThreadRec.release(); 211 _critSect.Leave(); 212 213 tmpThread->Stop(); 214 delete tmpThread; 215 216 _critSect.Enter(); 217 } 218 219 // PLAYOUT 220 if (_ptrThreadPlay) 221 { 222 rtc::PlatformThread* tmpThread = _ptrThreadPlay.release(); 223 _critSect.Leave(); 224 225 tmpThread->Stop(); 226 delete tmpThread; 227 228 _critSect.Enter(); 229 } 230 #if defined(USE_X11) 231 if (_XDisplay) 232 { 233 XCloseDisplay(_XDisplay); 234 _XDisplay = NULL; 235 } 236 #endif 237 _initialized = false; 238 _outputDeviceIsSpecified = false; 239 _inputDeviceIsSpecified = false; 240 241 return 0; 242 } 243 244 bool AudioDeviceLinuxALSA::Initialized() const 245 { 246 return (_initialized); 247 } 248 249 int32_t AudioDeviceLinuxALSA::InitSpeaker() 250 { 251 252 CriticalSectionScoped lock(&_critSect); 253 254 if (_playing) 255 { 256 return -1; 257 } 258 259 char devName[kAdmMaxDeviceNameSize] = {0}; 260 GetDevicesInfo(2, true, _outputDeviceIndex, devName, kAdmMaxDeviceNameSize); 261 return _mixerManager.OpenSpeaker(devName); 262 } 263 264 int32_t AudioDeviceLinuxALSA::InitMicrophone() 265 { 266 267 CriticalSectionScoped lock(&_critSect); 268 269 if (_recording) 270 { 271 return -1; 272 } 273 274 char devName[kAdmMaxDeviceNameSize] = {0}; 275 GetDevicesInfo(2, false, _inputDeviceIndex, devName, kAdmMaxDeviceNameSize); 276 return _mixerManager.OpenMicrophone(devName); 277 } 278 279 bool AudioDeviceLinuxALSA::SpeakerIsInitialized() const 280 { 281 return (_mixerManager.SpeakerIsInitialized()); 282 } 283 284 bool AudioDeviceLinuxALSA::MicrophoneIsInitialized() const 285 { 286 return (_mixerManager.MicrophoneIsInitialized()); 287 } 288 289 int32_t AudioDeviceLinuxALSA::SpeakerVolumeIsAvailable(bool& available) 290 { 291 292 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 293 294 // Make an attempt to open up the 295 // output mixer corresponding to the currently selected output device. 296 if (!wasInitialized && InitSpeaker() == -1) 297 { 298 // If we end up here it means that the selected speaker has no volume 299 // control. 300 available = false; 301 return 0; 302 } 303 304 // Given that InitSpeaker was successful, we know that a volume control 305 // exists 306 available = true; 307 308 // Close the initialized output mixer 309 if (!wasInitialized) 310 { 311 _mixerManager.CloseSpeaker(); 312 } 313 314 return 0; 315 } 316 317 int32_t AudioDeviceLinuxALSA::SetSpeakerVolume(uint32_t volume) 318 { 319 320 return (_mixerManager.SetSpeakerVolume(volume)); 321 } 322 323 int32_t AudioDeviceLinuxALSA::SpeakerVolume(uint32_t& volume) const 324 { 325 326 uint32_t level(0); 327 328 if (_mixerManager.SpeakerVolume(level) == -1) 329 { 330 return -1; 331 } 332 333 volume = level; 334 335 return 0; 336 } 337 338 339 int32_t AudioDeviceLinuxALSA::SetWaveOutVolume(uint16_t volumeLeft, 340 uint16_t volumeRight) 341 { 342 343 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 344 " API call not supported on this platform"); 345 return -1; 346 } 347 348 int32_t AudioDeviceLinuxALSA::WaveOutVolume( 349 uint16_t& /*volumeLeft*/, 350 uint16_t& /*volumeRight*/) const 351 { 352 353 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 354 " API call not supported on this platform"); 355 return -1; 356 } 357 358 int32_t AudioDeviceLinuxALSA::MaxSpeakerVolume( 359 uint32_t& maxVolume) const 360 { 361 362 uint32_t maxVol(0); 363 364 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) 365 { 366 return -1; 367 } 368 369 maxVolume = maxVol; 370 371 return 0; 372 } 373 374 int32_t AudioDeviceLinuxALSA::MinSpeakerVolume( 375 uint32_t& minVolume) const 376 { 377 378 uint32_t minVol(0); 379 380 if (_mixerManager.MinSpeakerVolume(minVol) == -1) 381 { 382 return -1; 383 } 384 385 minVolume = minVol; 386 387 return 0; 388 } 389 390 int32_t AudioDeviceLinuxALSA::SpeakerVolumeStepSize( 391 uint16_t& stepSize) const 392 { 393 394 uint16_t delta(0); 395 396 if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) 397 { 398 return -1; 399 } 400 401 stepSize = delta; 402 403 return 0; 404 } 405 406 int32_t AudioDeviceLinuxALSA::SpeakerMuteIsAvailable(bool& available) 407 { 408 409 bool isAvailable(false); 410 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 411 412 // Make an attempt to open up the 413 // output mixer corresponding to the currently selected output device. 414 // 415 if (!wasInitialized && InitSpeaker() == -1) 416 { 417 // If we end up here it means that the selected speaker has no volume 418 // control, hence it is safe to state that there is no mute control 419 // already at this stage. 420 available = false; 421 return 0; 422 } 423 424 // Check if the selected speaker has a mute control 425 _mixerManager.SpeakerMuteIsAvailable(isAvailable); 426 427 available = isAvailable; 428 429 // Close the initialized output mixer 430 if (!wasInitialized) 431 { 432 _mixerManager.CloseSpeaker(); 433 } 434 435 return 0; 436 } 437 438 int32_t AudioDeviceLinuxALSA::SetSpeakerMute(bool enable) 439 { 440 return (_mixerManager.SetSpeakerMute(enable)); 441 } 442 443 int32_t AudioDeviceLinuxALSA::SpeakerMute(bool& enabled) const 444 { 445 446 bool muted(0); 447 448 if (_mixerManager.SpeakerMute(muted) == -1) 449 { 450 return -1; 451 } 452 453 enabled = muted; 454 455 return 0; 456 } 457 458 int32_t AudioDeviceLinuxALSA::MicrophoneMuteIsAvailable(bool& available) 459 { 460 461 bool isAvailable(false); 462 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 463 464 // Make an attempt to open up the 465 // input mixer corresponding to the currently selected input device. 466 // 467 if (!wasInitialized && InitMicrophone() == -1) 468 { 469 // If we end up here it means that the selected microphone has no volume 470 // control, hence it is safe to state that there is no mute control 471 // already at this stage. 472 available = false; 473 return 0; 474 } 475 476 // Check if the selected microphone has a mute control 477 // 478 _mixerManager.MicrophoneMuteIsAvailable(isAvailable); 479 available = isAvailable; 480 481 // Close the initialized input mixer 482 // 483 if (!wasInitialized) 484 { 485 _mixerManager.CloseMicrophone(); 486 } 487 488 return 0; 489 } 490 491 int32_t AudioDeviceLinuxALSA::SetMicrophoneMute(bool enable) 492 { 493 return (_mixerManager.SetMicrophoneMute(enable)); 494 } 495 496 // ---------------------------------------------------------------------------- 497 // MicrophoneMute 498 // ---------------------------------------------------------------------------- 499 500 int32_t AudioDeviceLinuxALSA::MicrophoneMute(bool& enabled) const 501 { 502 503 bool muted(0); 504 505 if (_mixerManager.MicrophoneMute(muted) == -1) 506 { 507 return -1; 508 } 509 510 enabled = muted; 511 return 0; 512 } 513 514 int32_t AudioDeviceLinuxALSA::MicrophoneBoostIsAvailable(bool& available) 515 { 516 517 bool isAvailable(false); 518 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 519 520 // Enumerate all avaliable microphone and make an attempt to open up the 521 // input mixer corresponding to the currently selected input device. 522 // 523 if (!wasInitialized && InitMicrophone() == -1) 524 { 525 // If we end up here it means that the selected microphone has no volume 526 // control, hence it is safe to state that there is no boost control 527 // already at this stage. 528 available = false; 529 return 0; 530 } 531 532 // Check if the selected microphone has a boost control 533 _mixerManager.MicrophoneBoostIsAvailable(isAvailable); 534 available = isAvailable; 535 536 // Close the initialized input mixer 537 if (!wasInitialized) 538 { 539 _mixerManager.CloseMicrophone(); 540 } 541 542 return 0; 543 } 544 545 int32_t AudioDeviceLinuxALSA::SetMicrophoneBoost(bool enable) 546 { 547 548 return (_mixerManager.SetMicrophoneBoost(enable)); 549 } 550 551 int32_t AudioDeviceLinuxALSA::MicrophoneBoost(bool& enabled) const 552 { 553 554 bool onOff(0); 555 556 if (_mixerManager.MicrophoneBoost(onOff) == -1) 557 { 558 return -1; 559 } 560 561 enabled = onOff; 562 563 return 0; 564 } 565 566 int32_t AudioDeviceLinuxALSA::StereoRecordingIsAvailable(bool& available) 567 { 568 569 CriticalSectionScoped lock(&_critSect); 570 571 // If we already have initialized in stereo it's obviously available 572 if (_recIsInitialized && (2 == _recChannels)) 573 { 574 available = true; 575 return 0; 576 } 577 578 // Save rec states and the number of rec channels 579 bool recIsInitialized = _recIsInitialized; 580 bool recording = _recording; 581 int recChannels = _recChannels; 582 583 available = false; 584 585 // Stop/uninitialize recording if initialized (and possibly started) 586 if (_recIsInitialized) 587 { 588 StopRecording(); 589 } 590 591 // Try init in stereo; 592 _recChannels = 2; 593 if (InitRecording() == 0) 594 { 595 available = true; 596 } 597 598 // Stop/uninitialize recording 599 StopRecording(); 600 601 // Recover previous states 602 _recChannels = recChannels; 603 if (recIsInitialized) 604 { 605 InitRecording(); 606 } 607 if (recording) 608 { 609 StartRecording(); 610 } 611 612 return 0; 613 } 614 615 int32_t AudioDeviceLinuxALSA::SetStereoRecording(bool enable) 616 { 617 618 if (enable) 619 _recChannels = 2; 620 else 621 _recChannels = 1; 622 623 return 0; 624 } 625 626 int32_t AudioDeviceLinuxALSA::StereoRecording(bool& enabled) const 627 { 628 629 if (_recChannels == 2) 630 enabled = true; 631 else 632 enabled = false; 633 634 return 0; 635 } 636 637 int32_t AudioDeviceLinuxALSA::StereoPlayoutIsAvailable(bool& available) 638 { 639 640 CriticalSectionScoped lock(&_critSect); 641 642 // If we already have initialized in stereo it's obviously available 643 if (_playIsInitialized && (2 == _playChannels)) 644 { 645 available = true; 646 return 0; 647 } 648 649 // Save rec states and the number of rec channels 650 bool playIsInitialized = _playIsInitialized; 651 bool playing = _playing; 652 int playChannels = _playChannels; 653 654 available = false; 655 656 // Stop/uninitialize recording if initialized (and possibly started) 657 if (_playIsInitialized) 658 { 659 StopPlayout(); 660 } 661 662 // Try init in stereo; 663 _playChannels = 2; 664 if (InitPlayout() == 0) 665 { 666 available = true; 667 } 668 669 // Stop/uninitialize recording 670 StopPlayout(); 671 672 // Recover previous states 673 _playChannels = playChannels; 674 if (playIsInitialized) 675 { 676 InitPlayout(); 677 } 678 if (playing) 679 { 680 StartPlayout(); 681 } 682 683 return 0; 684 } 685 686 int32_t AudioDeviceLinuxALSA::SetStereoPlayout(bool enable) 687 { 688 689 if (enable) 690 _playChannels = 2; 691 else 692 _playChannels = 1; 693 694 return 0; 695 } 696 697 int32_t AudioDeviceLinuxALSA::StereoPlayout(bool& enabled) const 698 { 699 700 if (_playChannels == 2) 701 enabled = true; 702 else 703 enabled = false; 704 705 return 0; 706 } 707 708 int32_t AudioDeviceLinuxALSA::SetAGC(bool enable) 709 { 710 711 _AGC = enable; 712 713 return 0; 714 } 715 716 bool AudioDeviceLinuxALSA::AGC() const 717 { 718 719 return _AGC; 720 } 721 722 int32_t AudioDeviceLinuxALSA::MicrophoneVolumeIsAvailable(bool& available) 723 { 724 725 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 726 727 // Make an attempt to open up the 728 // input mixer corresponding to the currently selected output device. 729 if (!wasInitialized && InitMicrophone() == -1) 730 { 731 // If we end up here it means that the selected microphone has no volume 732 // control. 733 available = false; 734 return 0; 735 } 736 737 // Given that InitMicrophone was successful, we know that a volume control 738 // exists 739 available = true; 740 741 // Close the initialized input mixer 742 if (!wasInitialized) 743 { 744 _mixerManager.CloseMicrophone(); 745 } 746 747 return 0; 748 } 749 750 int32_t AudioDeviceLinuxALSA::SetMicrophoneVolume(uint32_t volume) 751 { 752 753 return (_mixerManager.SetMicrophoneVolume(volume)); 754 755 return 0; 756 } 757 758 int32_t AudioDeviceLinuxALSA::MicrophoneVolume(uint32_t& volume) const 759 { 760 761 uint32_t level(0); 762 763 if (_mixerManager.MicrophoneVolume(level) == -1) 764 { 765 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 766 " failed to retrive current microphone level"); 767 return -1; 768 } 769 770 volume = level; 771 772 return 0; 773 } 774 775 int32_t AudioDeviceLinuxALSA::MaxMicrophoneVolume( 776 uint32_t& maxVolume) const 777 { 778 779 uint32_t maxVol(0); 780 781 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) 782 { 783 return -1; 784 } 785 786 maxVolume = maxVol; 787 788 return 0; 789 } 790 791 int32_t AudioDeviceLinuxALSA::MinMicrophoneVolume( 792 uint32_t& minVolume) const 793 { 794 795 uint32_t minVol(0); 796 797 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) 798 { 799 return -1; 800 } 801 802 minVolume = minVol; 803 804 return 0; 805 } 806 807 int32_t AudioDeviceLinuxALSA::MicrophoneVolumeStepSize( 808 uint16_t& stepSize) const 809 { 810 811 uint16_t delta(0); 812 813 if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) 814 { 815 return -1; 816 } 817 818 stepSize = delta; 819 820 return 0; 821 } 822 823 int16_t AudioDeviceLinuxALSA::PlayoutDevices() 824 { 825 826 return (int16_t)GetDevicesInfo(0, true); 827 } 828 829 int32_t AudioDeviceLinuxALSA::SetPlayoutDevice(uint16_t index) 830 { 831 832 if (_playIsInitialized) 833 { 834 return -1; 835 } 836 837 uint32_t nDevices = GetDevicesInfo(0, true); 838 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 839 " number of availiable audio output devices is %u", nDevices); 840 841 if (index > (nDevices-1)) 842 { 843 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 844 " device index is out of range [0,%u]", (nDevices-1)); 845 return -1; 846 } 847 848 _outputDeviceIndex = index; 849 _outputDeviceIsSpecified = true; 850 851 return 0; 852 } 853 854 int32_t AudioDeviceLinuxALSA::SetPlayoutDevice( 855 AudioDeviceModule::WindowsDeviceType /*device*/) 856 { 857 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 858 "WindowsDeviceType not supported"); 859 return -1; 860 } 861 862 int32_t AudioDeviceLinuxALSA::PlayoutDeviceName( 863 uint16_t index, 864 char name[kAdmMaxDeviceNameSize], 865 char guid[kAdmMaxGuidSize]) 866 { 867 868 const uint16_t nDevices(PlayoutDevices()); 869 870 if ((index > (nDevices-1)) || (name == NULL)) 871 { 872 return -1; 873 } 874 875 memset(name, 0, kAdmMaxDeviceNameSize); 876 877 if (guid != NULL) 878 { 879 memset(guid, 0, kAdmMaxGuidSize); 880 } 881 882 return GetDevicesInfo(1, true, index, name, kAdmMaxDeviceNameSize); 883 } 884 885 int32_t AudioDeviceLinuxALSA::RecordingDeviceName( 886 uint16_t index, 887 char name[kAdmMaxDeviceNameSize], 888 char guid[kAdmMaxGuidSize]) 889 { 890 891 const uint16_t nDevices(RecordingDevices()); 892 893 if ((index > (nDevices-1)) || (name == NULL)) 894 { 895 return -1; 896 } 897 898 memset(name, 0, kAdmMaxDeviceNameSize); 899 900 if (guid != NULL) 901 { 902 memset(guid, 0, kAdmMaxGuidSize); 903 } 904 905 return GetDevicesInfo(1, false, index, name, kAdmMaxDeviceNameSize); 906 } 907 908 int16_t AudioDeviceLinuxALSA::RecordingDevices() 909 { 910 911 return (int16_t)GetDevicesInfo(0, false); 912 } 913 914 int32_t AudioDeviceLinuxALSA::SetRecordingDevice(uint16_t index) 915 { 916 917 if (_recIsInitialized) 918 { 919 return -1; 920 } 921 922 uint32_t nDevices = GetDevicesInfo(0, false); 923 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 924 " number of availiable audio input devices is %u", nDevices); 925 926 if (index > (nDevices-1)) 927 { 928 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 929 " device index is out of range [0,%u]", (nDevices-1)); 930 return -1; 931 } 932 933 _inputDeviceIndex = index; 934 _inputDeviceIsSpecified = true; 935 936 return 0; 937 } 938 939 // ---------------------------------------------------------------------------- 940 // SetRecordingDevice II (II) 941 // ---------------------------------------------------------------------------- 942 943 int32_t AudioDeviceLinuxALSA::SetRecordingDevice( 944 AudioDeviceModule::WindowsDeviceType /*device*/) 945 { 946 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 947 "WindowsDeviceType not supported"); 948 return -1; 949 } 950 951 int32_t AudioDeviceLinuxALSA::PlayoutIsAvailable(bool& available) 952 { 953 954 available = false; 955 956 // Try to initialize the playout side with mono 957 // Assumes that user set num channels after calling this function 958 _playChannels = 1; 959 int32_t res = InitPlayout(); 960 961 // Cancel effect of initialization 962 StopPlayout(); 963 964 if (res != -1) 965 { 966 available = true; 967 } 968 else 969 { 970 // It may be possible to play out in stereo 971 res = StereoPlayoutIsAvailable(available); 972 if (available) 973 { 974 // Then set channels to 2 so InitPlayout doesn't fail 975 _playChannels = 2; 976 } 977 } 978 979 return res; 980 } 981 982 int32_t AudioDeviceLinuxALSA::RecordingIsAvailable(bool& available) 983 { 984 985 available = false; 986 987 // Try to initialize the recording side with mono 988 // Assumes that user set num channels after calling this function 989 _recChannels = 1; 990 int32_t res = InitRecording(); 991 992 // Cancel effect of initialization 993 StopRecording(); 994 995 if (res != -1) 996 { 997 available = true; 998 } 999 else 1000 { 1001 // It may be possible to record in stereo 1002 res = StereoRecordingIsAvailable(available); 1003 if (available) 1004 { 1005 // Then set channels to 2 so InitPlayout doesn't fail 1006 _recChannels = 2; 1007 } 1008 } 1009 1010 return res; 1011 } 1012 1013 int32_t AudioDeviceLinuxALSA::InitPlayout() 1014 { 1015 1016 int errVal = 0; 1017 1018 CriticalSectionScoped lock(&_critSect); 1019 if (_playing) 1020 { 1021 return -1; 1022 } 1023 1024 if (!_outputDeviceIsSpecified) 1025 { 1026 return -1; 1027 } 1028 1029 if (_playIsInitialized) 1030 { 1031 return 0; 1032 } 1033 // Initialize the speaker (devices might have been added or removed) 1034 if (InitSpeaker() == -1) 1035 { 1036 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1037 " InitSpeaker() failed"); 1038 } 1039 1040 // Start by closing any existing wave-output devices 1041 // 1042 if (_handlePlayout != NULL) 1043 { 1044 LATE(snd_pcm_close)(_handlePlayout); 1045 _handlePlayout = NULL; 1046 _playIsInitialized = false; 1047 if (errVal < 0) 1048 { 1049 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1050 " Error closing current playout sound device, error:" 1051 " %s", LATE(snd_strerror)(errVal)); 1052 } 1053 } 1054 1055 // Open PCM device for playout 1056 char deviceName[kAdmMaxDeviceNameSize] = {0}; 1057 GetDevicesInfo(2, true, _outputDeviceIndex, deviceName, 1058 kAdmMaxDeviceNameSize); 1059 1060 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1061 " InitPlayout open (%s)", deviceName); 1062 1063 errVal = LATE(snd_pcm_open) 1064 (&_handlePlayout, 1065 deviceName, 1066 SND_PCM_STREAM_PLAYBACK, 1067 SND_PCM_NONBLOCK); 1068 1069 if (errVal == -EBUSY) // Device busy - try some more! 1070 { 1071 for (int i=0; i < 5; i++) 1072 { 1073 SleepMs(1000); 1074 errVal = LATE(snd_pcm_open) 1075 (&_handlePlayout, 1076 deviceName, 1077 SND_PCM_STREAM_PLAYBACK, 1078 SND_PCM_NONBLOCK); 1079 if (errVal == 0) 1080 { 1081 break; 1082 } 1083 } 1084 } 1085 if (errVal < 0) 1086 { 1087 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1088 " unable to open playback device: %s (%d)", 1089 LATE(snd_strerror)(errVal), 1090 errVal); 1091 _handlePlayout = NULL; 1092 return -1; 1093 } 1094 1095 _playoutFramesIn10MS = _playoutFreq/100; 1096 if ((errVal = LATE(snd_pcm_set_params)( _handlePlayout, 1097 #if defined(WEBRTC_ARCH_BIG_ENDIAN) 1098 SND_PCM_FORMAT_S16_BE, 1099 #else 1100 SND_PCM_FORMAT_S16_LE, //format 1101 #endif 1102 SND_PCM_ACCESS_RW_INTERLEAVED, //access 1103 _playChannels, //channels 1104 _playoutFreq, //rate 1105 1, //soft_resample 1106 ALSA_PLAYOUT_LATENCY //40*1000 //latency required overall latency in us 1107 )) < 0) 1108 { /* 0.5sec */ 1109 _playoutFramesIn10MS = 0; 1110 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1111 " unable to set playback device: %s (%d)", 1112 LATE(snd_strerror)(errVal), 1113 errVal); 1114 ErrorRecovery(errVal, _handlePlayout); 1115 errVal = LATE(snd_pcm_close)(_handlePlayout); 1116 _handlePlayout = NULL; 1117 return -1; 1118 } 1119 1120 errVal = LATE(snd_pcm_get_params)(_handlePlayout, 1121 &_playoutBufferSizeInFrame, &_playoutPeriodSizeInFrame); 1122 if (errVal < 0) 1123 { 1124 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1125 " snd_pcm_get_params %s", 1126 LATE(snd_strerror)(errVal), 1127 errVal); 1128 _playoutBufferSizeInFrame = 0; 1129 _playoutPeriodSizeInFrame = 0; 1130 } 1131 else { 1132 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1133 " playout snd_pcm_get_params " 1134 "buffer_size:%d period_size :%d", 1135 _playoutBufferSizeInFrame, _playoutPeriodSizeInFrame); 1136 } 1137 1138 if (_ptrAudioBuffer) 1139 { 1140 // Update webrtc audio buffer with the selected parameters 1141 _ptrAudioBuffer->SetPlayoutSampleRate(_playoutFreq); 1142 _ptrAudioBuffer->SetPlayoutChannels(_playChannels); 1143 } 1144 1145 // Set play buffer size 1146 _playoutBufferSizeIn10MS = LATE(snd_pcm_frames_to_bytes)( 1147 _handlePlayout, _playoutFramesIn10MS); 1148 1149 // Init varaibles used for play 1150 _playWarning = 0; 1151 _playError = 0; 1152 1153 if (_handlePlayout != NULL) 1154 { 1155 _playIsInitialized = true; 1156 return 0; 1157 } 1158 else 1159 { 1160 return -1; 1161 } 1162 1163 return 0; 1164 } 1165 1166 int32_t AudioDeviceLinuxALSA::InitRecording() 1167 { 1168 1169 int errVal = 0; 1170 1171 CriticalSectionScoped lock(&_critSect); 1172 1173 if (_recording) 1174 { 1175 return -1; 1176 } 1177 1178 if (!_inputDeviceIsSpecified) 1179 { 1180 return -1; 1181 } 1182 1183 if (_recIsInitialized) 1184 { 1185 return 0; 1186 } 1187 1188 // Initialize the microphone (devices might have been added or removed) 1189 if (InitMicrophone() == -1) 1190 { 1191 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1192 " InitMicrophone() failed"); 1193 } 1194 1195 // Start by closing any existing pcm-input devices 1196 // 1197 if (_handleRecord != NULL) 1198 { 1199 int errVal = LATE(snd_pcm_close)(_handleRecord); 1200 _handleRecord = NULL; 1201 _recIsInitialized = false; 1202 if (errVal < 0) 1203 { 1204 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1205 " Error closing current recording sound device," 1206 " error: %s", 1207 LATE(snd_strerror)(errVal)); 1208 } 1209 } 1210 1211 // Open PCM device for recording 1212 // The corresponding settings for playout are made after the record settings 1213 char deviceName[kAdmMaxDeviceNameSize] = {0}; 1214 GetDevicesInfo(2, false, _inputDeviceIndex, deviceName, 1215 kAdmMaxDeviceNameSize); 1216 1217 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1218 "InitRecording open (%s)", deviceName); 1219 errVal = LATE(snd_pcm_open) 1220 (&_handleRecord, 1221 deviceName, 1222 SND_PCM_STREAM_CAPTURE, 1223 SND_PCM_NONBLOCK); 1224 1225 // Available modes: 0 = blocking, SND_PCM_NONBLOCK, SND_PCM_ASYNC 1226 if (errVal == -EBUSY) // Device busy - try some more! 1227 { 1228 for (int i=0; i < 5; i++) 1229 { 1230 SleepMs(1000); 1231 errVal = LATE(snd_pcm_open) 1232 (&_handleRecord, 1233 deviceName, 1234 SND_PCM_STREAM_CAPTURE, 1235 SND_PCM_NONBLOCK); 1236 if (errVal == 0) 1237 { 1238 break; 1239 } 1240 } 1241 } 1242 if (errVal < 0) 1243 { 1244 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1245 " unable to open record device: %s", 1246 LATE(snd_strerror)(errVal)); 1247 _handleRecord = NULL; 1248 return -1; 1249 } 1250 1251 _recordingFramesIn10MS = _recordingFreq/100; 1252 if ((errVal = LATE(snd_pcm_set_params)(_handleRecord, 1253 #if defined(WEBRTC_ARCH_BIG_ENDIAN) 1254 SND_PCM_FORMAT_S16_BE, //format 1255 #else 1256 SND_PCM_FORMAT_S16_LE, //format 1257 #endif 1258 SND_PCM_ACCESS_RW_INTERLEAVED, //access 1259 _recChannels, //channels 1260 _recordingFreq, //rate 1261 1, //soft_resample 1262 ALSA_CAPTURE_LATENCY //latency in us 1263 )) < 0) 1264 { 1265 // Fall back to another mode then. 1266 if (_recChannels == 1) 1267 _recChannels = 2; 1268 else 1269 _recChannels = 1; 1270 1271 if ((errVal = LATE(snd_pcm_set_params)(_handleRecord, 1272 #if defined(WEBRTC_ARCH_BIG_ENDIAN) 1273 SND_PCM_FORMAT_S16_BE, //format 1274 #else 1275 SND_PCM_FORMAT_S16_LE, //format 1276 #endif 1277 SND_PCM_ACCESS_RW_INTERLEAVED, //access 1278 _recChannels, //channels 1279 _recordingFreq, //rate 1280 1, //soft_resample 1281 ALSA_CAPTURE_LATENCY //latency in us 1282 )) < 0) 1283 { 1284 _recordingFramesIn10MS = 0; 1285 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1286 " unable to set record settings: %s (%d)", 1287 LATE(snd_strerror)(errVal), errVal); 1288 ErrorRecovery(errVal, _handleRecord); 1289 errVal = LATE(snd_pcm_close)(_handleRecord); 1290 _handleRecord = NULL; 1291 return -1; 1292 } 1293 } 1294 1295 errVal = LATE(snd_pcm_get_params)(_handleRecord, 1296 &_recordingBuffersizeInFrame, &_recordingPeriodSizeInFrame); 1297 if (errVal < 0) 1298 { 1299 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1300 " snd_pcm_get_params %s", 1301 LATE(snd_strerror)(errVal), errVal); 1302 _recordingBuffersizeInFrame = 0; 1303 _recordingPeriodSizeInFrame = 0; 1304 } 1305 else { 1306 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1307 " capture snd_pcm_get_params " 1308 "buffer_size:%d period_size:%d", 1309 _recordingBuffersizeInFrame, _recordingPeriodSizeInFrame); 1310 } 1311 1312 if (_ptrAudioBuffer) 1313 { 1314 // Update webrtc audio buffer with the selected parameters 1315 _ptrAudioBuffer->SetRecordingSampleRate(_recordingFreq); 1316 _ptrAudioBuffer->SetRecordingChannels(_recChannels); 1317 } 1318 1319 // Set rec buffer size and create buffer 1320 _recordingBufferSizeIn10MS = LATE(snd_pcm_frames_to_bytes)( 1321 _handleRecord, _recordingFramesIn10MS); 1322 1323 if (_handleRecord != NULL) 1324 { 1325 // Mark recording side as initialized 1326 _recIsInitialized = true; 1327 return 0; 1328 } 1329 else 1330 { 1331 return -1; 1332 } 1333 1334 return 0; 1335 } 1336 1337 int32_t AudioDeviceLinuxALSA::StartRecording() 1338 { 1339 1340 if (!_recIsInitialized) 1341 { 1342 return -1; 1343 } 1344 1345 if (_recording) 1346 { 1347 return 0; 1348 } 1349 1350 _recording = true; 1351 1352 int errVal = 0; 1353 _recordingFramesLeft = _recordingFramesIn10MS; 1354 1355 // Make sure we only create the buffer once. 1356 if (!_recordingBuffer) 1357 _recordingBuffer = new int8_t[_recordingBufferSizeIn10MS]; 1358 if (!_recordingBuffer) 1359 { 1360 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 1361 " failed to alloc recording buffer"); 1362 _recording = false; 1363 return -1; 1364 } 1365 // RECORDING 1366 _ptrThreadRec.reset(new rtc::PlatformThread( 1367 RecThreadFunc, this, "webrtc_audio_module_capture_thread")); 1368 1369 _ptrThreadRec->Start(); 1370 _ptrThreadRec->SetPriority(rtc::kRealtimePriority); 1371 1372 errVal = LATE(snd_pcm_prepare)(_handleRecord); 1373 if (errVal < 0) 1374 { 1375 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1376 " capture snd_pcm_prepare failed (%s)\n", 1377 LATE(snd_strerror)(errVal)); 1378 // just log error 1379 // if snd_pcm_open fails will return -1 1380 } 1381 1382 errVal = LATE(snd_pcm_start)(_handleRecord); 1383 if (errVal < 0) 1384 { 1385 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1386 " capture snd_pcm_start err: %s", 1387 LATE(snd_strerror)(errVal)); 1388 errVal = LATE(snd_pcm_start)(_handleRecord); 1389 if (errVal < 0) 1390 { 1391 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1392 " capture snd_pcm_start 2nd try err: %s", 1393 LATE(snd_strerror)(errVal)); 1394 StopRecording(); 1395 return -1; 1396 } 1397 } 1398 1399 return 0; 1400 } 1401 1402 int32_t AudioDeviceLinuxALSA::StopRecording() 1403 { 1404 1405 { 1406 CriticalSectionScoped lock(&_critSect); 1407 1408 if (!_recIsInitialized) 1409 { 1410 return 0; 1411 } 1412 1413 if (_handleRecord == NULL) 1414 { 1415 return -1; 1416 } 1417 1418 // Make sure we don't start recording (it's asynchronous). 1419 _recIsInitialized = false; 1420 _recording = false; 1421 } 1422 1423 if (_ptrThreadRec) 1424 { 1425 _ptrThreadRec->Stop(); 1426 _ptrThreadRec.reset(); 1427 } 1428 1429 CriticalSectionScoped lock(&_critSect); 1430 _recordingFramesLeft = 0; 1431 if (_recordingBuffer) 1432 { 1433 delete [] _recordingBuffer; 1434 _recordingBuffer = NULL; 1435 } 1436 1437 // Stop and close pcm recording device. 1438 int errVal = LATE(snd_pcm_drop)(_handleRecord); 1439 if (errVal < 0) 1440 { 1441 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1442 " Error stop recording: %s", 1443 LATE(snd_strerror)(errVal)); 1444 return -1; 1445 } 1446 1447 errVal = LATE(snd_pcm_close)(_handleRecord); 1448 if (errVal < 0) 1449 { 1450 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1451 " Error closing record sound device, error: %s", 1452 LATE(snd_strerror)(errVal)); 1453 return -1; 1454 } 1455 1456 // Check if we have muted and unmute if so. 1457 bool muteEnabled = false; 1458 MicrophoneMute(muteEnabled); 1459 if (muteEnabled) 1460 { 1461 SetMicrophoneMute(false); 1462 } 1463 1464 // set the pcm input handle to NULL 1465 _handleRecord = NULL; 1466 return 0; 1467 } 1468 1469 bool AudioDeviceLinuxALSA::RecordingIsInitialized() const 1470 { 1471 return (_recIsInitialized); 1472 } 1473 1474 bool AudioDeviceLinuxALSA::Recording() const 1475 { 1476 return (_recording); 1477 } 1478 1479 bool AudioDeviceLinuxALSA::PlayoutIsInitialized() const 1480 { 1481 return (_playIsInitialized); 1482 } 1483 1484 int32_t AudioDeviceLinuxALSA::StartPlayout() 1485 { 1486 if (!_playIsInitialized) 1487 { 1488 return -1; 1489 } 1490 1491 if (_playing) 1492 { 1493 return 0; 1494 } 1495 1496 _playing = true; 1497 1498 _playoutFramesLeft = 0; 1499 if (!_playoutBuffer) 1500 _playoutBuffer = new int8_t[_playoutBufferSizeIn10MS]; 1501 if (!_playoutBuffer) 1502 { 1503 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1504 " failed to alloc playout buf"); 1505 _playing = false; 1506 return -1; 1507 } 1508 1509 // PLAYOUT 1510 _ptrThreadPlay.reset(new rtc::PlatformThread( 1511 PlayThreadFunc, this, "webrtc_audio_module_play_thread")); 1512 _ptrThreadPlay->Start(); 1513 _ptrThreadPlay->SetPriority(rtc::kRealtimePriority); 1514 1515 int errVal = LATE(snd_pcm_prepare)(_handlePlayout); 1516 if (errVal < 0) 1517 { 1518 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 1519 " playout snd_pcm_prepare failed (%s)\n", 1520 LATE(snd_strerror)(errVal)); 1521 // just log error 1522 // if snd_pcm_open fails will return -1 1523 } 1524 1525 return 0; 1526 } 1527 1528 int32_t AudioDeviceLinuxALSA::StopPlayout() 1529 { 1530 1531 { 1532 CriticalSectionScoped lock(&_critSect); 1533 1534 if (!_playIsInitialized) 1535 { 1536 return 0; 1537 } 1538 1539 if (_handlePlayout == NULL) 1540 { 1541 return -1; 1542 } 1543 1544 _playing = false; 1545 } 1546 1547 // stop playout thread first 1548 if (_ptrThreadPlay) 1549 { 1550 _ptrThreadPlay->Stop(); 1551 _ptrThreadPlay.reset(); 1552 } 1553 1554 CriticalSectionScoped lock(&_critSect); 1555 1556 _playoutFramesLeft = 0; 1557 delete [] _playoutBuffer; 1558 _playoutBuffer = NULL; 1559 1560 // stop and close pcm playout device 1561 int errVal = LATE(snd_pcm_drop)(_handlePlayout); 1562 if (errVal < 0) 1563 { 1564 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1565 " Error stop playing: %s", 1566 LATE(snd_strerror)(errVal)); 1567 } 1568 1569 errVal = LATE(snd_pcm_close)(_handlePlayout); 1570 if (errVal < 0) 1571 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1572 " Error closing playout sound device, error: %s", 1573 LATE(snd_strerror)(errVal)); 1574 1575 // set the pcm input handle to NULL 1576 _playIsInitialized = false; 1577 _handlePlayout = NULL; 1578 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1579 " handle_playout is now set to NULL"); 1580 1581 return 0; 1582 } 1583 1584 int32_t AudioDeviceLinuxALSA::PlayoutDelay(uint16_t& delayMS) const 1585 { 1586 delayMS = (uint16_t)_playoutDelay * 1000 / _playoutFreq; 1587 return 0; 1588 } 1589 1590 int32_t AudioDeviceLinuxALSA::RecordingDelay(uint16_t& delayMS) const 1591 { 1592 // Adding 10ms adjusted value to the record delay due to 10ms buffering. 1593 delayMS = (uint16_t)(10 + _recordingDelay * 1000 / _recordingFreq); 1594 return 0; 1595 } 1596 1597 bool AudioDeviceLinuxALSA::Playing() const 1598 { 1599 return (_playing); 1600 } 1601 // ---------------------------------------------------------------------------- 1602 // SetPlayoutBuffer 1603 // ---------------------------------------------------------------------------- 1604 1605 int32_t AudioDeviceLinuxALSA::SetPlayoutBuffer( 1606 const AudioDeviceModule::BufferType type, 1607 uint16_t sizeMS) 1608 { 1609 _playBufType = type; 1610 if (type == AudioDeviceModule::kFixedBufferSize) 1611 { 1612 _playBufDelayFixed = sizeMS; 1613 } 1614 return 0; 1615 } 1616 1617 int32_t AudioDeviceLinuxALSA::PlayoutBuffer( 1618 AudioDeviceModule::BufferType& type, 1619 uint16_t& sizeMS) const 1620 { 1621 type = _playBufType; 1622 if (type == AudioDeviceModule::kFixedBufferSize) 1623 { 1624 sizeMS = _playBufDelayFixed; 1625 } 1626 else 1627 { 1628 sizeMS = _playBufDelay; 1629 } 1630 1631 return 0; 1632 } 1633 1634 int32_t AudioDeviceLinuxALSA::CPULoad(uint16_t& load) const 1635 { 1636 1637 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1638 " API call not supported on this platform"); 1639 return -1; 1640 } 1641 1642 bool AudioDeviceLinuxALSA::PlayoutWarning() const 1643 { 1644 CriticalSectionScoped lock(&_critSect); 1645 return (_playWarning > 0); 1646 } 1647 1648 bool AudioDeviceLinuxALSA::PlayoutError() const 1649 { 1650 CriticalSectionScoped lock(&_critSect); 1651 return (_playError > 0); 1652 } 1653 1654 bool AudioDeviceLinuxALSA::RecordingWarning() const 1655 { 1656 CriticalSectionScoped lock(&_critSect); 1657 return (_recWarning > 0); 1658 } 1659 1660 bool AudioDeviceLinuxALSA::RecordingError() const 1661 { 1662 CriticalSectionScoped lock(&_critSect); 1663 return (_recError > 0); 1664 } 1665 1666 void AudioDeviceLinuxALSA::ClearPlayoutWarning() 1667 { 1668 CriticalSectionScoped lock(&_critSect); 1669 _playWarning = 0; 1670 } 1671 1672 void AudioDeviceLinuxALSA::ClearPlayoutError() 1673 { 1674 CriticalSectionScoped lock(&_critSect); 1675 _playError = 0; 1676 } 1677 1678 void AudioDeviceLinuxALSA::ClearRecordingWarning() 1679 { 1680 CriticalSectionScoped lock(&_critSect); 1681 _recWarning = 0; 1682 } 1683 1684 void AudioDeviceLinuxALSA::ClearRecordingError() 1685 { 1686 CriticalSectionScoped lock(&_critSect); 1687 _recError = 0; 1688 } 1689 1690 // ============================================================================ 1691 // Private Methods 1692 // ============================================================================ 1693 1694 int32_t AudioDeviceLinuxALSA::GetDevicesInfo( 1695 const int32_t function, 1696 const bool playback, 1697 const int32_t enumDeviceNo, 1698 char* enumDeviceName, 1699 const int32_t ednLen) const 1700 { 1701 1702 // Device enumeration based on libjingle implementation 1703 // by Tristan Schmelcher at Google Inc. 1704 1705 const char *type = playback ? "Output" : "Input"; 1706 // dmix and dsnoop are only for playback and capture, respectively, but ALSA 1707 // stupidly includes them in both lists. 1708 const char *ignorePrefix = playback ? "dsnoop:" : "dmix:" ; 1709 // (ALSA lists many more "devices" of questionable interest, but we show them 1710 // just in case the weird devices may actually be desirable for some 1711 // users/systems.) 1712 1713 int err; 1714 int enumCount(0); 1715 bool keepSearching(true); 1716 1717 // From Chromium issue 95797 1718 // Loop through the sound cards to get Alsa device hints. 1719 // Don't use snd_device_name_hint(-1,..) since there is a access violation 1720 // inside this ALSA API with libasound.so.2.0.0. 1721 int card = -1; 1722 while (!(LATE(snd_card_next)(&card)) && (card >= 0) && keepSearching) { 1723 void **hints; 1724 err = LATE(snd_device_name_hint)(card, "pcm", &hints); 1725 if (err != 0) 1726 { 1727 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1728 "GetDevicesInfo - device name hint error: %s", 1729 LATE(snd_strerror)(err)); 1730 return -1; 1731 } 1732 1733 enumCount++; // default is 0 1734 if ((function == FUNC_GET_DEVICE_NAME || 1735 function == FUNC_GET_DEVICE_NAME_FOR_AN_ENUM) && enumDeviceNo == 0) 1736 { 1737 strcpy(enumDeviceName, "default"); 1738 1739 err = LATE(snd_device_name_free_hint)(hints); 1740 if (err != 0) 1741 { 1742 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1743 "GetDevicesInfo - device name free hint error: %s", 1744 LATE(snd_strerror)(err)); 1745 } 1746 1747 return 0; 1748 } 1749 1750 for (void **list = hints; *list != NULL; ++list) 1751 { 1752 char *actualType = LATE(snd_device_name_get_hint)(*list, "IOID"); 1753 if (actualType) 1754 { // NULL means it's both. 1755 bool wrongType = (strcmp(actualType, type) != 0); 1756 free(actualType); 1757 if (wrongType) 1758 { 1759 // Wrong type of device (i.e., input vs. output). 1760 continue; 1761 } 1762 } 1763 1764 char *name = LATE(snd_device_name_get_hint)(*list, "NAME"); 1765 if (!name) 1766 { 1767 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1768 "Device has no name"); 1769 // Skip it. 1770 continue; 1771 } 1772 1773 // Now check if we actually want to show this device. 1774 if (strcmp(name, "default") != 0 && 1775 strcmp(name, "null") != 0 && 1776 strcmp(name, "pulse") != 0 && 1777 strncmp(name, ignorePrefix, strlen(ignorePrefix)) != 0) 1778 { 1779 // Yes, we do. 1780 char *desc = LATE(snd_device_name_get_hint)(*list, "DESC"); 1781 if (!desc) 1782 { 1783 // Virtual devices don't necessarily have descriptions. 1784 // Use their names instead. 1785 desc = name; 1786 } 1787 1788 if (FUNC_GET_NUM_OF_DEVICE == function) 1789 { 1790 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1791 " Enum device %d - %s", enumCount, name); 1792 1793 } 1794 if ((FUNC_GET_DEVICE_NAME == function) && 1795 (enumDeviceNo == enumCount)) 1796 { 1797 // We have found the enum device, copy the name to buffer. 1798 strncpy(enumDeviceName, desc, ednLen); 1799 enumDeviceName[ednLen-1] = '\0'; 1800 keepSearching = false; 1801 // Replace '\n' with '-'. 1802 char * pret = strchr(enumDeviceName, '\n'/*0xa*/); //LF 1803 if (pret) 1804 *pret = '-'; 1805 } 1806 if ((FUNC_GET_DEVICE_NAME_FOR_AN_ENUM == function) && 1807 (enumDeviceNo == enumCount)) 1808 { 1809 // We have found the enum device, copy the name to buffer. 1810 strncpy(enumDeviceName, name, ednLen); 1811 enumDeviceName[ednLen-1] = '\0'; 1812 keepSearching = false; 1813 } 1814 1815 if (keepSearching) 1816 ++enumCount; 1817 1818 if (desc != name) 1819 free(desc); 1820 } 1821 1822 free(name); 1823 1824 if (!keepSearching) 1825 break; 1826 } 1827 1828 err = LATE(snd_device_name_free_hint)(hints); 1829 if (err != 0) 1830 { 1831 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1832 "GetDevicesInfo - device name free hint error: %s", 1833 LATE(snd_strerror)(err)); 1834 // Continue and return true anyway, since we did get the whole list. 1835 } 1836 } 1837 1838 if (FUNC_GET_NUM_OF_DEVICE == function) 1839 { 1840 if (enumCount == 1) // only default? 1841 enumCount = 0; 1842 return enumCount; // Normal return point for function 0 1843 } 1844 1845 if (keepSearching) 1846 { 1847 // If we get here for function 1 and 2, we didn't find the specified 1848 // enum device. 1849 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1850 "GetDevicesInfo - Could not find device name or numbers"); 1851 return -1; 1852 } 1853 1854 return 0; 1855 } 1856 1857 int32_t AudioDeviceLinuxALSA::InputSanityCheckAfterUnlockedPeriod() const 1858 { 1859 if (_handleRecord == NULL) 1860 { 1861 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1862 " input state has been modified during unlocked period"); 1863 return -1; 1864 } 1865 return 0; 1866 } 1867 1868 int32_t AudioDeviceLinuxALSA::OutputSanityCheckAfterUnlockedPeriod() const 1869 { 1870 if (_handlePlayout == NULL) 1871 { 1872 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1873 " output state has been modified during unlocked period"); 1874 return -1; 1875 } 1876 return 0; 1877 } 1878 1879 int32_t AudioDeviceLinuxALSA::ErrorRecovery(int32_t error, 1880 snd_pcm_t* deviceHandle) 1881 { 1882 int st = LATE(snd_pcm_state)(deviceHandle); 1883 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1884 "Trying to recover from error: %s (%d) (state %d)", 1885 (LATE(snd_pcm_stream)(deviceHandle) == SND_PCM_STREAM_CAPTURE) ? 1886 "capture" : "playout", LATE(snd_strerror)(error), error, st); 1887 1888 // It is recommended to use snd_pcm_recover for all errors. If that function 1889 // cannot handle the error, the input error code will be returned, otherwise 1890 // 0 is returned. From snd_pcm_recover API doc: "This functions handles 1891 // -EINTR (4) (interrupted system call), -EPIPE (32) (playout overrun or 1892 // capture underrun) and -ESTRPIPE (86) (stream is suspended) error codes 1893 // trying to prepare given stream for next I/O." 1894 1895 /** Open */ 1896 // SND_PCM_STATE_OPEN = 0, 1897 /** Setup installed */ 1898 // SND_PCM_STATE_SETUP, 1899 /** Ready to start */ 1900 // SND_PCM_STATE_PREPARED, 1901 /** Running */ 1902 // SND_PCM_STATE_RUNNING, 1903 /** Stopped: underrun (playback) or overrun (capture) detected */ 1904 // SND_PCM_STATE_XRUN,= 4 1905 /** Draining: running (playback) or stopped (capture) */ 1906 // SND_PCM_STATE_DRAINING, 1907 /** Paused */ 1908 // SND_PCM_STATE_PAUSED, 1909 /** Hardware is suspended */ 1910 // SND_PCM_STATE_SUSPENDED, 1911 // ** Hardware is disconnected */ 1912 // SND_PCM_STATE_DISCONNECTED, 1913 // SND_PCM_STATE_LAST = SND_PCM_STATE_DISCONNECTED 1914 1915 // snd_pcm_recover isn't available in older alsa, e.g. on the FC4 machine 1916 // in Sthlm lab. 1917 1918 int res = LATE(snd_pcm_recover)(deviceHandle, error, 1); 1919 if (0 == res) 1920 { 1921 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1922 " Recovery - snd_pcm_recover OK"); 1923 1924 if ((error == -EPIPE || error == -ESTRPIPE) && // Buf underrun/overrun. 1925 _recording && 1926 LATE(snd_pcm_stream)(deviceHandle) == SND_PCM_STREAM_CAPTURE) 1927 { 1928 // For capture streams we also have to repeat the explicit start() 1929 // to get data flowing again. 1930 int err = LATE(snd_pcm_start)(deviceHandle); 1931 if (err != 0) 1932 { 1933 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1934 " Recovery - snd_pcm_start error: %u", err); 1935 return -1; 1936 } 1937 } 1938 1939 if ((error == -EPIPE || error == -ESTRPIPE) && // Buf underrun/overrun. 1940 _playing && 1941 LATE(snd_pcm_stream)(deviceHandle) == SND_PCM_STREAM_PLAYBACK) 1942 { 1943 // For capture streams we also have to repeat the explicit start() to get 1944 // data flowing again. 1945 int err = LATE(snd_pcm_start)(deviceHandle); 1946 if (err != 0) 1947 { 1948 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1949 " Recovery - snd_pcm_start error: %s", 1950 LATE(snd_strerror)(err)); 1951 return -1; 1952 } 1953 } 1954 1955 return -EPIPE == error ? 1 : 0; 1956 } 1957 else { 1958 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1959 " Unrecoverable alsa stream error: %d", res); 1960 } 1961 1962 return res; 1963 } 1964 1965 // ============================================================================ 1966 // Thread Methods 1967 // ============================================================================ 1968 1969 bool AudioDeviceLinuxALSA::PlayThreadFunc(void* pThis) 1970 { 1971 return (static_cast<AudioDeviceLinuxALSA*>(pThis)->PlayThreadProcess()); 1972 } 1973 1974 bool AudioDeviceLinuxALSA::RecThreadFunc(void* pThis) 1975 { 1976 return (static_cast<AudioDeviceLinuxALSA*>(pThis)->RecThreadProcess()); 1977 } 1978 1979 bool AudioDeviceLinuxALSA::PlayThreadProcess() 1980 { 1981 if(!_playing) 1982 return false; 1983 1984 int err; 1985 snd_pcm_sframes_t frames; 1986 snd_pcm_sframes_t avail_frames; 1987 1988 Lock(); 1989 //return a positive number of frames ready otherwise a negative error code 1990 avail_frames = LATE(snd_pcm_avail_update)(_handlePlayout); 1991 if (avail_frames < 0) 1992 { 1993 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1994 "playout snd_pcm_avail_update error: %s", 1995 LATE(snd_strerror)(avail_frames)); 1996 ErrorRecovery(avail_frames, _handlePlayout); 1997 UnLock(); 1998 return true; 1999 } 2000 else if (avail_frames == 0) 2001 { 2002 UnLock(); 2003 2004 //maximum tixe in milliseconds to wait, a negative value means infinity 2005 err = LATE(snd_pcm_wait)(_handlePlayout, 2); 2006 if (err == 0) 2007 { //timeout occured 2008 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, 2009 "playout snd_pcm_wait timeout"); 2010 } 2011 2012 return true; 2013 } 2014 2015 if (_playoutFramesLeft <= 0) 2016 { 2017 UnLock(); 2018 _ptrAudioBuffer->RequestPlayoutData(_playoutFramesIn10MS); 2019 Lock(); 2020 2021 _playoutFramesLeft = _ptrAudioBuffer->GetPlayoutData(_playoutBuffer); 2022 assert(_playoutFramesLeft == _playoutFramesIn10MS); 2023 } 2024 2025 if (static_cast<uint32_t>(avail_frames) > _playoutFramesLeft) 2026 avail_frames = _playoutFramesLeft; 2027 2028 int size = LATE(snd_pcm_frames_to_bytes)(_handlePlayout, 2029 _playoutFramesLeft); 2030 frames = LATE(snd_pcm_writei)( 2031 _handlePlayout, 2032 &_playoutBuffer[_playoutBufferSizeIn10MS - size], 2033 avail_frames); 2034 2035 if (frames < 0) 2036 { 2037 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, 2038 "playout snd_pcm_writei error: %s", 2039 LATE(snd_strerror)(frames)); 2040 _playoutFramesLeft = 0; 2041 ErrorRecovery(frames, _handlePlayout); 2042 UnLock(); 2043 return true; 2044 } 2045 else { 2046 assert(frames==avail_frames); 2047 _playoutFramesLeft -= frames; 2048 } 2049 2050 UnLock(); 2051 return true; 2052 } 2053 2054 bool AudioDeviceLinuxALSA::RecThreadProcess() 2055 { 2056 if (!_recording) 2057 return false; 2058 2059 int err; 2060 snd_pcm_sframes_t frames; 2061 snd_pcm_sframes_t avail_frames; 2062 int8_t buffer[_recordingBufferSizeIn10MS]; 2063 2064 Lock(); 2065 2066 //return a positive number of frames ready otherwise a negative error code 2067 avail_frames = LATE(snd_pcm_avail_update)(_handleRecord); 2068 if (avail_frames < 0) 2069 { 2070 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2071 "capture snd_pcm_avail_update error: %s", 2072 LATE(snd_strerror)(avail_frames)); 2073 ErrorRecovery(avail_frames, _handleRecord); 2074 UnLock(); 2075 return true; 2076 } 2077 else if (avail_frames == 0) 2078 { // no frame is available now 2079 UnLock(); 2080 2081 //maximum time in milliseconds to wait, a negative value means infinity 2082 err = LATE(snd_pcm_wait)(_handleRecord, 2083 ALSA_CAPTURE_WAIT_TIMEOUT); 2084 if (err == 0) //timeout occured 2085 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, 2086 "capture snd_pcm_wait timeout"); 2087 2088 return true; 2089 } 2090 2091 if (static_cast<uint32_t>(avail_frames) > _recordingFramesLeft) 2092 avail_frames = _recordingFramesLeft; 2093 2094 frames = LATE(snd_pcm_readi)(_handleRecord, 2095 buffer, avail_frames); // frames to be written 2096 if (frames < 0) 2097 { 2098 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2099 "capture snd_pcm_readi error: %s", 2100 LATE(snd_strerror)(frames)); 2101 ErrorRecovery(frames, _handleRecord); 2102 UnLock(); 2103 return true; 2104 } 2105 else if (frames > 0) 2106 { 2107 assert(frames == avail_frames); 2108 2109 int left_size = LATE(snd_pcm_frames_to_bytes)(_handleRecord, 2110 _recordingFramesLeft); 2111 int size = LATE(snd_pcm_frames_to_bytes)(_handleRecord, frames); 2112 2113 memcpy(&_recordingBuffer[_recordingBufferSizeIn10MS - left_size], 2114 buffer, size); 2115 _recordingFramesLeft -= frames; 2116 2117 if (!_recordingFramesLeft) 2118 { // buf is full 2119 _recordingFramesLeft = _recordingFramesIn10MS; 2120 2121 // store the recorded buffer (no action will be taken if the 2122 // #recorded samples is not a full buffer) 2123 _ptrAudioBuffer->SetRecordedBuffer(_recordingBuffer, 2124 _recordingFramesIn10MS); 2125 2126 uint32_t currentMicLevel = 0; 2127 uint32_t newMicLevel = 0; 2128 2129 if (AGC()) 2130 { 2131 // store current mic level in the audio buffer if AGC is enabled 2132 if (MicrophoneVolume(currentMicLevel) == 0) 2133 { 2134 if (currentMicLevel == 0xffffffff) 2135 currentMicLevel = 100; 2136 // this call does not affect the actual microphone volume 2137 _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); 2138 } 2139 } 2140 2141 // calculate delay 2142 _playoutDelay = 0; 2143 _recordingDelay = 0; 2144 if (_handlePlayout) 2145 { 2146 err = LATE(snd_pcm_delay)(_handlePlayout, 2147 &_playoutDelay); // returned delay in frames 2148 if (err < 0) 2149 { 2150 // TODO(xians): Shall we call ErrorRecovery() here? 2151 _playoutDelay = 0; 2152 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2153 "playout snd_pcm_delay: %s", 2154 LATE(snd_strerror)(err)); 2155 } 2156 } 2157 2158 err = LATE(snd_pcm_delay)(_handleRecord, 2159 &_recordingDelay); // returned delay in frames 2160 if (err < 0) 2161 { 2162 // TODO(xians): Shall we call ErrorRecovery() here? 2163 _recordingDelay = 0; 2164 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2165 "capture snd_pcm_delay: %s", 2166 LATE(snd_strerror)(err)); 2167 } 2168 2169 // TODO(xians): Shall we add 10ms buffer delay to the record delay? 2170 _ptrAudioBuffer->SetVQEData( 2171 _playoutDelay * 1000 / _playoutFreq, 2172 _recordingDelay * 1000 / _recordingFreq, 0); 2173 2174 _ptrAudioBuffer->SetTypingStatus(KeyPressed()); 2175 2176 // Deliver recorded samples at specified sample rate, mic level etc. 2177 // to the observer using callback. 2178 UnLock(); 2179 _ptrAudioBuffer->DeliverRecordedData(); 2180 Lock(); 2181 2182 if (AGC()) 2183 { 2184 newMicLevel = _ptrAudioBuffer->NewMicLevel(); 2185 if (newMicLevel != 0) 2186 { 2187 // The VQE will only deliver non-zero microphone levels when a 2188 // change is needed. Set this new mic level (received from the 2189 // observer as return value in the callback). 2190 if (SetMicrophoneVolume(newMicLevel) == -1) 2191 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2192 " the required modification of the " 2193 "microphone volume failed"); 2194 } 2195 } 2196 } 2197 } 2198 2199 UnLock(); 2200 return true; 2201 } 2202 2203 2204 bool AudioDeviceLinuxALSA::KeyPressed() const{ 2205 #if defined(USE_X11) 2206 char szKey[32]; 2207 unsigned int i = 0; 2208 char state = 0; 2209 2210 if (!_XDisplay) 2211 return false; 2212 2213 // Check key map status 2214 XQueryKeymap(_XDisplay, szKey); 2215 2216 // A bit change in keymap means a key is pressed 2217 for (i = 0; i < sizeof(szKey); i++) 2218 state |= (szKey[i] ^ _oldKeyState[i]) & szKey[i]; 2219 2220 // Save old state 2221 memcpy((char*)_oldKeyState, (char*)szKey, sizeof(_oldKeyState)); 2222 return (state != 0); 2223 #else 2224 return false; 2225 #endif 2226 } 2227 } // namespace webrtc 2228