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