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_pulse_linux.h" 16 17 #include "webrtc/system_wrappers/interface/event_wrapper.h" 18 #include "webrtc/system_wrappers/interface/thread_wrapper.h" 19 #include "webrtc/system_wrappers/interface/trace.h" 20 21 webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; 22 23 // Accesses Pulse functions through our late-binding symbol table instead of 24 // directly. This way we don't have to link to libpulse, which means our binary 25 // will work on systems that don't have it. 26 #define LATE(sym) \ 27 LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym) 28 29 namespace webrtc 30 { 31 32 // ============================================================================ 33 // Static Methods 34 // ============================================================================ 35 36 AudioDeviceLinuxPulse::AudioDeviceLinuxPulse(const int32_t id) : 37 _ptrAudioBuffer(NULL), 38 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), 39 _timeEventRec(*EventWrapper::Create()), 40 _timeEventPlay(*EventWrapper::Create()), 41 _recStartEvent(*EventWrapper::Create()), 42 _playStartEvent(*EventWrapper::Create()), 43 _ptrThreadPlay(NULL), 44 _ptrThreadRec(NULL), 45 _recThreadID(0), 46 _playThreadID(0), 47 _id(id), 48 _mixerManager(id), 49 _inputDeviceIndex(0), 50 _outputDeviceIndex(0), 51 _inputDeviceIsSpecified(false), 52 _outputDeviceIsSpecified(false), 53 sample_rate_hz_(0), 54 _recChannels(1), 55 _playChannels(1), 56 _playBufType(AudioDeviceModule::kFixedBufferSize), 57 _initialized(false), 58 _recording(false), 59 _playing(false), 60 _recIsInitialized(false), 61 _playIsInitialized(false), 62 _startRec(false), 63 _stopRec(false), 64 _startPlay(false), 65 _stopPlay(false), 66 _AGC(false), 67 update_speaker_volume_at_startup_(false), 68 _playBufDelayFixed(20), 69 _sndCardPlayDelay(0), 70 _sndCardRecDelay(0), 71 _writeErrors(0), 72 _playWarning(0), 73 _playError(0), 74 _recWarning(0), 75 _recError(0), 76 _deviceIndex(-1), 77 _numPlayDevices(0), 78 _numRecDevices(0), 79 _playDeviceName(NULL), 80 _recDeviceName(NULL), 81 _playDisplayDeviceName(NULL), 82 _recDisplayDeviceName(NULL), 83 _playBuffer(NULL), 84 _playbackBufferSize(0), 85 _playbackBufferUnused(0), 86 _tempBufferSpace(0), 87 _recBuffer(NULL), 88 _recordBufferSize(0), 89 _recordBufferUsed(0), 90 _tempSampleData(NULL), 91 _tempSampleDataSize(0), 92 _configuredLatencyPlay(0), 93 _configuredLatencyRec(0), 94 _paDeviceIndex(-1), 95 _paStateChanged(false), 96 _paMainloop(NULL), 97 _paMainloopApi(NULL), 98 _paContext(NULL), 99 _recStream(NULL), 100 _playStream(NULL), 101 _recStreamFlags(0), 102 _playStreamFlags(0) 103 { 104 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, 105 "%s created", __FUNCTION__); 106 107 memset(_paServerVersion, 0, sizeof(_paServerVersion)); 108 memset(&_playBufferAttr, 0, sizeof(_playBufferAttr)); 109 memset(&_recBufferAttr, 0, sizeof(_recBufferAttr)); 110 memset(_oldKeyState, 0, sizeof(_oldKeyState)); 111 } 112 113 AudioDeviceLinuxPulse::~AudioDeviceLinuxPulse() 114 { 115 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, 116 "%s destroyed", __FUNCTION__); 117 118 Terminate(); 119 120 if (_recBuffer) 121 { 122 delete [] _recBuffer; 123 _recBuffer = NULL; 124 } 125 if (_playBuffer) 126 { 127 delete [] _playBuffer; 128 _playBuffer = NULL; 129 } 130 if (_playDeviceName) 131 { 132 delete [] _playDeviceName; 133 _playDeviceName = NULL; 134 } 135 if (_recDeviceName) 136 { 137 delete [] _recDeviceName; 138 _recDeviceName = NULL; 139 } 140 141 delete &_recStartEvent; 142 delete &_playStartEvent; 143 delete &_timeEventRec; 144 delete &_timeEventPlay; 145 delete &_critSect; 146 } 147 148 void AudioDeviceLinuxPulse::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) 149 { 150 151 CriticalSectionScoped lock(&_critSect); 152 153 _ptrAudioBuffer = audioBuffer; 154 155 // Inform the AudioBuffer about default settings for this implementation. 156 // Set all values to zero here since the actual settings will be done by 157 // InitPlayout and InitRecording later. 158 _ptrAudioBuffer->SetRecordingSampleRate(0); 159 _ptrAudioBuffer->SetPlayoutSampleRate(0); 160 _ptrAudioBuffer->SetRecordingChannels(0); 161 _ptrAudioBuffer->SetPlayoutChannels(0); 162 } 163 164 // ---------------------------------------------------------------------------- 165 // ActiveAudioLayer 166 // ---------------------------------------------------------------------------- 167 168 int32_t AudioDeviceLinuxPulse::ActiveAudioLayer( 169 AudioDeviceModule::AudioLayer& audioLayer) const 170 { 171 audioLayer = AudioDeviceModule::kLinuxPulseAudio; 172 return 0; 173 } 174 175 int32_t AudioDeviceLinuxPulse::Init() 176 { 177 178 CriticalSectionScoped lock(&_critSect); 179 180 if (_initialized) 181 { 182 return 0; 183 } 184 185 // Initialize PulseAudio 186 if (InitPulseAudio() < 0) 187 { 188 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 189 " failed to initialize PulseAudio"); 190 191 if (TerminatePulseAudio() < 0) 192 { 193 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 194 " failed to terminate PulseAudio"); 195 } 196 197 return -1; 198 } 199 200 _playWarning = 0; 201 _playError = 0; 202 _recWarning = 0; 203 _recError = 0; 204 205 //Get X display handle for typing detection 206 _XDisplay = XOpenDisplay(NULL); 207 if (!_XDisplay) 208 { 209 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 210 " failed to open X display, typing detection will not work"); 211 } 212 213 // RECORDING 214 const char* threadName = "webrtc_audio_module_rec_thread"; 215 _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, 216 kRealtimePriority, threadName); 217 if (_ptrThreadRec == NULL) 218 { 219 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 220 " failed to create the rec audio thread"); 221 return -1; 222 } 223 224 unsigned int threadID(0); 225 if (!_ptrThreadRec->Start(threadID)) 226 { 227 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 228 " failed to start the rec audio thread"); 229 230 delete _ptrThreadRec; 231 _ptrThreadRec = NULL; 232 return -1; 233 } 234 _recThreadID = threadID; 235 236 // PLAYOUT 237 threadName = "webrtc_audio_module_play_thread"; 238 _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, this, 239 kRealtimePriority, threadName); 240 if (_ptrThreadPlay == NULL) 241 { 242 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 243 " failed to create the play audio thread"); 244 return -1; 245 } 246 247 threadID = 0; 248 if (!_ptrThreadPlay->Start(threadID)) 249 { 250 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 251 " failed to start the play audio thread"); 252 253 delete _ptrThreadPlay; 254 _ptrThreadPlay = NULL; 255 return -1; 256 } 257 _playThreadID = threadID; 258 259 _initialized = true; 260 261 return 0; 262 } 263 264 int32_t AudioDeviceLinuxPulse::Terminate() 265 { 266 267 if (!_initialized) 268 { 269 return 0; 270 } 271 272 Lock(); 273 274 _mixerManager.Close(); 275 276 // RECORDING 277 if (_ptrThreadRec) 278 { 279 ThreadWrapper* tmpThread = _ptrThreadRec; 280 _ptrThreadRec = NULL; 281 UnLock(); 282 283 tmpThread->SetNotAlive(); 284 _timeEventRec.Set(); 285 if (tmpThread->Stop()) 286 { 287 delete tmpThread; 288 } else 289 { 290 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 291 " failed to close down the rec audio thread"); 292 } 293 // Lock again since we need to protect _ptrThreadPlay. 294 Lock(); 295 } 296 297 // PLAYOUT 298 if (_ptrThreadPlay) 299 { 300 ThreadWrapper* tmpThread = _ptrThreadPlay; 301 _ptrThreadPlay = NULL; 302 _critSect.Leave(); 303 304 tmpThread->SetNotAlive(); 305 _timeEventPlay.Set(); 306 if (tmpThread->Stop()) 307 { 308 delete tmpThread; 309 } else 310 { 311 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 312 " failed to close down the play audio thread"); 313 } 314 } else { 315 UnLock(); 316 } 317 318 // Terminate PulseAudio 319 if (TerminatePulseAudio() < 0) 320 { 321 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 322 " failed to terminate PulseAudio"); 323 return -1; 324 } 325 326 if (_XDisplay) 327 { 328 XCloseDisplay(_XDisplay); 329 _XDisplay = NULL; 330 } 331 332 _initialized = false; 333 _outputDeviceIsSpecified = false; 334 _inputDeviceIsSpecified = false; 335 336 return 0; 337 } 338 339 bool AudioDeviceLinuxPulse::Initialized() const 340 { 341 return (_initialized); 342 } 343 344 int32_t AudioDeviceLinuxPulse::InitSpeaker() 345 { 346 347 CriticalSectionScoped lock(&_critSect); 348 349 if (_playing) 350 { 351 return -1; 352 } 353 354 if (!_outputDeviceIsSpecified) 355 { 356 return -1; 357 } 358 359 // check if default device 360 if (_outputDeviceIndex == 0) 361 { 362 uint16_t deviceIndex = 0; 363 GetDefaultDeviceInfo(false, NULL, deviceIndex); 364 _paDeviceIndex = deviceIndex; 365 } else 366 { 367 // get the PA device index from 368 // the callback 369 _deviceIndex = _outputDeviceIndex; 370 371 // get playout devices 372 PlayoutDevices(); 373 } 374 375 // the callback has now set the _paDeviceIndex to 376 // the PulseAudio index of the device 377 if (_mixerManager.OpenSpeaker(_paDeviceIndex) == -1) 378 { 379 return -1; 380 } 381 382 // clear _deviceIndex 383 _deviceIndex = -1; 384 _paDeviceIndex = -1; 385 386 return 0; 387 } 388 389 int32_t AudioDeviceLinuxPulse::InitMicrophone() 390 { 391 392 CriticalSectionScoped lock(&_critSect); 393 394 if (_recording) 395 { 396 return -1; 397 } 398 399 if (!_inputDeviceIsSpecified) 400 { 401 return -1; 402 } 403 404 // Check if default device 405 if (_inputDeviceIndex == 0) 406 { 407 uint16_t deviceIndex = 0; 408 GetDefaultDeviceInfo(true, NULL, deviceIndex); 409 _paDeviceIndex = deviceIndex; 410 } else 411 { 412 // Get the PA device index from 413 // the callback 414 _deviceIndex = _inputDeviceIndex; 415 416 // get recording devices 417 RecordingDevices(); 418 } 419 420 // The callback has now set the _paDeviceIndex to 421 // the PulseAudio index of the device 422 if (_mixerManager.OpenMicrophone(_paDeviceIndex) == -1) 423 { 424 return -1; 425 } 426 427 // Clear _deviceIndex 428 _deviceIndex = -1; 429 _paDeviceIndex = -1; 430 431 return 0; 432 } 433 434 bool AudioDeviceLinuxPulse::SpeakerIsInitialized() const 435 { 436 return (_mixerManager.SpeakerIsInitialized()); 437 } 438 439 bool AudioDeviceLinuxPulse::MicrophoneIsInitialized() const 440 { 441 return (_mixerManager.MicrophoneIsInitialized()); 442 } 443 444 int32_t AudioDeviceLinuxPulse::SpeakerVolumeIsAvailable(bool& available) 445 { 446 447 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 448 449 // Make an attempt to open up the 450 // output mixer corresponding to the currently selected output device. 451 if (!wasInitialized && InitSpeaker() == -1) 452 { 453 // If we end up here it means that the selected speaker has no volume 454 // control. 455 available = false; 456 return 0; 457 } 458 459 // Given that InitSpeaker was successful, we know that a volume control exists 460 available = true; 461 462 // Close the initialized output mixer 463 if (!wasInitialized) 464 { 465 _mixerManager.CloseSpeaker(); 466 } 467 468 return 0; 469 } 470 471 int32_t AudioDeviceLinuxPulse::SetSpeakerVolume(uint32_t volume) 472 { 473 if (!_playing) { 474 // Only update the volume if it's been set while we weren't playing. 475 update_speaker_volume_at_startup_ = true; 476 } 477 return (_mixerManager.SetSpeakerVolume(volume)); 478 } 479 480 int32_t AudioDeviceLinuxPulse::SpeakerVolume(uint32_t& volume) const 481 { 482 483 uint32_t level(0); 484 485 if (_mixerManager.SpeakerVolume(level) == -1) 486 { 487 return -1; 488 } 489 490 volume = level; 491 492 return 0; 493 } 494 495 int32_t AudioDeviceLinuxPulse::SetWaveOutVolume( 496 uint16_t volumeLeft, 497 uint16_t volumeRight) 498 { 499 500 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 501 " API call not supported on this platform"); 502 return -1; 503 } 504 505 int32_t AudioDeviceLinuxPulse::WaveOutVolume( 506 uint16_t& /*volumeLeft*/, 507 uint16_t& /*volumeRight*/) const 508 { 509 510 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 511 " API call not supported on this platform"); 512 return -1; 513 } 514 515 int32_t AudioDeviceLinuxPulse::MaxSpeakerVolume( 516 uint32_t& maxVolume) const 517 { 518 519 uint32_t maxVol(0); 520 521 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) 522 { 523 return -1; 524 } 525 526 maxVolume = maxVol; 527 528 return 0; 529 } 530 531 int32_t AudioDeviceLinuxPulse::MinSpeakerVolume( 532 uint32_t& minVolume) const 533 { 534 535 uint32_t minVol(0); 536 537 if (_mixerManager.MinSpeakerVolume(minVol) == -1) 538 { 539 return -1; 540 } 541 542 minVolume = minVol; 543 544 return 0; 545 } 546 547 int32_t AudioDeviceLinuxPulse::SpeakerVolumeStepSize( 548 uint16_t& stepSize) const 549 { 550 551 uint16_t delta(0); 552 553 if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) 554 { 555 return -1; 556 } 557 558 stepSize = delta; 559 560 return 0; 561 } 562 563 int32_t AudioDeviceLinuxPulse::SpeakerMuteIsAvailable(bool& available) 564 { 565 566 bool isAvailable(false); 567 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 568 569 // Make an attempt to open up the 570 // output mixer corresponding to the currently selected output device. 571 // 572 if (!wasInitialized && InitSpeaker() == -1) 573 { 574 // If we end up here it means that the selected speaker has no volume 575 // control, hence it is safe to state that there is no mute control 576 // already at this stage. 577 available = false; 578 return 0; 579 } 580 581 // Check if the selected speaker has a mute control 582 _mixerManager.SpeakerMuteIsAvailable(isAvailable); 583 584 available = isAvailable; 585 586 // Close the initialized output mixer 587 if (!wasInitialized) 588 { 589 _mixerManager.CloseSpeaker(); 590 } 591 592 return 0; 593 } 594 595 int32_t AudioDeviceLinuxPulse::SetSpeakerMute(bool enable) 596 { 597 598 return (_mixerManager.SetSpeakerMute(enable)); 599 } 600 601 int32_t AudioDeviceLinuxPulse::SpeakerMute(bool& enabled) const 602 { 603 604 bool muted(0); 605 if (_mixerManager.SpeakerMute(muted) == -1) 606 { 607 return -1; 608 } 609 610 enabled = muted; 611 return 0; 612 } 613 614 int32_t AudioDeviceLinuxPulse::MicrophoneMuteIsAvailable(bool& available) 615 { 616 617 bool isAvailable(false); 618 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 619 620 // Make an attempt to open up the 621 // input mixer corresponding to the currently selected input device. 622 // 623 if (!wasInitialized && InitMicrophone() == -1) 624 { 625 // If we end up here it means that the selected microphone has no volume 626 // control, hence it is safe to state that there is no boost control 627 // already at this stage. 628 available = false; 629 return 0; 630 } 631 632 // Check if the selected microphone has a mute control 633 // 634 _mixerManager.MicrophoneMuteIsAvailable(isAvailable); 635 available = isAvailable; 636 637 // Close the initialized input mixer 638 // 639 if (!wasInitialized) 640 { 641 _mixerManager.CloseMicrophone(); 642 } 643 644 return 0; 645 } 646 647 int32_t AudioDeviceLinuxPulse::SetMicrophoneMute(bool enable) 648 { 649 650 return (_mixerManager.SetMicrophoneMute(enable)); 651 } 652 653 int32_t AudioDeviceLinuxPulse::MicrophoneMute(bool& enabled) const 654 { 655 656 bool muted(0); 657 if (_mixerManager.MicrophoneMute(muted) == -1) 658 { 659 return -1; 660 } 661 662 enabled = muted; 663 return 0; 664 } 665 666 int32_t AudioDeviceLinuxPulse::MicrophoneBoostIsAvailable(bool& available) 667 { 668 669 bool isAvailable(false); 670 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 671 672 // Enumerate all avaliable microphone and make an attempt to open up the 673 // input mixer corresponding to the currently selected input device. 674 // 675 if (!wasInitialized && InitMicrophone() == -1) 676 { 677 // If we end up here it means that the selected microphone has no volume 678 // control, hence it is safe to state that there is no boost control 679 // already at this stage. 680 available = false; 681 return 0; 682 } 683 684 // Check if the selected microphone has a boost control 685 _mixerManager.MicrophoneBoostIsAvailable(isAvailable); 686 available = isAvailable; 687 688 // Close the initialized input mixer 689 if (!wasInitialized) 690 { 691 _mixerManager.CloseMicrophone(); 692 } 693 694 return 0; 695 } 696 697 int32_t AudioDeviceLinuxPulse::SetMicrophoneBoost(bool enable) 698 { 699 700 return (_mixerManager.SetMicrophoneBoost(enable)); 701 } 702 703 int32_t AudioDeviceLinuxPulse::MicrophoneBoost(bool& enabled) const 704 { 705 706 bool onOff(0); 707 708 if (_mixerManager.MicrophoneBoost(onOff) == -1) 709 { 710 return -1; 711 } 712 713 enabled = onOff; 714 715 return 0; 716 } 717 718 int32_t AudioDeviceLinuxPulse::StereoRecordingIsAvailable(bool& available) 719 { 720 721 if (_recChannels == 2 && _recording) { 722 available = true; 723 return 0; 724 } 725 726 available = false; 727 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 728 int error = 0; 729 730 if (!wasInitialized && InitMicrophone() == -1) 731 { 732 // Cannot open the specified device 733 available = false; 734 return 0; 735 } 736 737 // Check if the selected microphone can record stereo. 738 bool isAvailable(false); 739 error = _mixerManager.StereoRecordingIsAvailable(isAvailable); 740 if (!error) 741 available = isAvailable; 742 743 // Close the initialized input mixer 744 if (!wasInitialized) 745 { 746 _mixerManager.CloseMicrophone(); 747 } 748 749 return error; 750 } 751 752 int32_t AudioDeviceLinuxPulse::SetStereoRecording(bool enable) 753 { 754 755 if (enable) 756 _recChannels = 2; 757 else 758 _recChannels = 1; 759 760 return 0; 761 } 762 763 int32_t AudioDeviceLinuxPulse::StereoRecording(bool& enabled) const 764 { 765 766 if (_recChannels == 2) 767 enabled = true; 768 else 769 enabled = false; 770 771 return 0; 772 } 773 774 int32_t AudioDeviceLinuxPulse::StereoPlayoutIsAvailable(bool& available) 775 { 776 777 if (_playChannels == 2 && _playing) { 778 available = true; 779 return 0; 780 } 781 782 available = false; 783 bool wasInitialized = _mixerManager.SpeakerIsInitialized(); 784 int error = 0; 785 786 if (!wasInitialized && InitSpeaker() == -1) 787 { 788 // Cannot open the specified device. 789 return -1; 790 } 791 792 // Check if the selected speaker can play stereo. 793 bool isAvailable(false); 794 error = _mixerManager.StereoPlayoutIsAvailable(isAvailable); 795 if (!error) 796 available = isAvailable; 797 798 // Close the initialized input mixer 799 if (!wasInitialized) 800 { 801 _mixerManager.CloseSpeaker(); 802 } 803 804 return error; 805 } 806 807 int32_t AudioDeviceLinuxPulse::SetStereoPlayout(bool enable) 808 { 809 810 if (enable) 811 _playChannels = 2; 812 else 813 _playChannels = 1; 814 815 return 0; 816 } 817 818 int32_t AudioDeviceLinuxPulse::StereoPlayout(bool& enabled) const 819 { 820 821 if (_playChannels == 2) 822 enabled = true; 823 else 824 enabled = false; 825 826 return 0; 827 } 828 829 int32_t AudioDeviceLinuxPulse::SetAGC(bool enable) 830 { 831 832 _AGC = enable; 833 834 return 0; 835 } 836 837 bool AudioDeviceLinuxPulse::AGC() const 838 { 839 840 return _AGC; 841 } 842 843 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeIsAvailable( 844 bool& available) 845 { 846 847 bool wasInitialized = _mixerManager.MicrophoneIsInitialized(); 848 849 // Make an attempt to open up the 850 // input mixer corresponding to the currently selected output device. 851 if (!wasInitialized && InitMicrophone() == -1) 852 { 853 // If we end up here it means that the selected microphone has no volume 854 // control. 855 available = false; 856 return 0; 857 } 858 859 // Given that InitMicrophone was successful, we know that a volume control 860 // exists 861 available = true; 862 863 // Close the initialized input mixer 864 if (!wasInitialized) 865 { 866 _mixerManager.CloseMicrophone(); 867 } 868 869 return 0; 870 } 871 872 int32_t AudioDeviceLinuxPulse::SetMicrophoneVolume(uint32_t volume) 873 { 874 875 return (_mixerManager.SetMicrophoneVolume(volume)); 876 } 877 878 int32_t AudioDeviceLinuxPulse::MicrophoneVolume( 879 uint32_t& volume) const 880 { 881 882 uint32_t level(0); 883 884 if (_mixerManager.MicrophoneVolume(level) == -1) 885 { 886 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 887 " failed to retrive current microphone level"); 888 return -1; 889 } 890 891 volume = level; 892 893 return 0; 894 } 895 896 int32_t AudioDeviceLinuxPulse::MaxMicrophoneVolume( 897 uint32_t& maxVolume) const 898 { 899 900 uint32_t maxVol(0); 901 902 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) 903 { 904 return -1; 905 } 906 907 maxVolume = maxVol; 908 909 return 0; 910 } 911 912 int32_t AudioDeviceLinuxPulse::MinMicrophoneVolume( 913 uint32_t& minVolume) const 914 { 915 916 uint32_t minVol(0); 917 918 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) 919 { 920 return -1; 921 } 922 923 minVolume = minVol; 924 925 return 0; 926 } 927 928 int32_t AudioDeviceLinuxPulse::MicrophoneVolumeStepSize( 929 uint16_t& stepSize) const 930 { 931 932 uint16_t delta(0); 933 934 if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) 935 { 936 return -1; 937 } 938 939 stepSize = delta; 940 941 return 0; 942 } 943 944 int16_t AudioDeviceLinuxPulse::PlayoutDevices() 945 { 946 947 PaLock(); 948 949 pa_operation* paOperation = NULL; 950 _numPlayDevices = 1; // init to 1 to account for "default" 951 952 // get the whole list of devices and update _numPlayDevices 953 paOperation = LATE(pa_context_get_sink_info_list)(_paContext, 954 PaSinkInfoCallback, 955 this); 956 957 WaitForOperationCompletion(paOperation); 958 959 PaUnLock(); 960 961 return _numPlayDevices; 962 } 963 964 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice(uint16_t index) 965 { 966 967 if (_playIsInitialized) 968 { 969 return -1; 970 } 971 972 const uint16_t nDevices = PlayoutDevices(); 973 974 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 975 " number of availiable output devices is %u", nDevices); 976 977 if (index > (nDevices - 1)) 978 { 979 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 980 " device index is out of range [0,%u]", (nDevices - 1)); 981 return -1; 982 } 983 984 _outputDeviceIndex = index; 985 _outputDeviceIsSpecified = true; 986 987 return 0; 988 } 989 990 int32_t AudioDeviceLinuxPulse::SetPlayoutDevice( 991 AudioDeviceModule::WindowsDeviceType /*device*/) 992 { 993 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 994 "WindowsDeviceType not supported"); 995 return -1; 996 } 997 998 int32_t AudioDeviceLinuxPulse::PlayoutDeviceName( 999 uint16_t index, 1000 char name[kAdmMaxDeviceNameSize], 1001 char guid[kAdmMaxGuidSize]) 1002 { 1003 1004 const uint16_t nDevices = PlayoutDevices(); 1005 1006 if ((index > (nDevices - 1)) || (name == NULL)) 1007 { 1008 return -1; 1009 } 1010 1011 memset(name, 0, kAdmMaxDeviceNameSize); 1012 1013 if (guid != NULL) 1014 { 1015 memset(guid, 0, kAdmMaxGuidSize); 1016 } 1017 1018 // Check if default device 1019 if (index == 0) 1020 { 1021 uint16_t deviceIndex = 0; 1022 return GetDefaultDeviceInfo(false, name, deviceIndex); 1023 } 1024 1025 // Tell the callback that we want 1026 // The name for this device 1027 _playDisplayDeviceName = name; 1028 _deviceIndex = index; 1029 1030 // get playout devices 1031 PlayoutDevices(); 1032 1033 // clear device name and index 1034 _playDisplayDeviceName = NULL; 1035 _deviceIndex = -1; 1036 1037 return 0; 1038 } 1039 1040 int32_t AudioDeviceLinuxPulse::RecordingDeviceName( 1041 uint16_t index, 1042 char name[kAdmMaxDeviceNameSize], 1043 char guid[kAdmMaxGuidSize]) 1044 { 1045 1046 const uint16_t nDevices(RecordingDevices()); 1047 1048 if ((index > (nDevices - 1)) || (name == NULL)) 1049 { 1050 return -1; 1051 } 1052 1053 memset(name, 0, kAdmMaxDeviceNameSize); 1054 1055 if (guid != NULL) 1056 { 1057 memset(guid, 0, kAdmMaxGuidSize); 1058 } 1059 1060 // Check if default device 1061 if (index == 0) 1062 { 1063 uint16_t deviceIndex = 0; 1064 return GetDefaultDeviceInfo(true, name, deviceIndex); 1065 } 1066 1067 // Tell the callback that we want 1068 // the name for this device 1069 _recDisplayDeviceName = name; 1070 _deviceIndex = index; 1071 1072 // Get recording devices 1073 RecordingDevices(); 1074 1075 // Clear device name and index 1076 _recDisplayDeviceName = NULL; 1077 _deviceIndex = -1; 1078 1079 return 0; 1080 } 1081 1082 int16_t AudioDeviceLinuxPulse::RecordingDevices() 1083 { 1084 1085 PaLock(); 1086 1087 pa_operation* paOperation = NULL; 1088 _numRecDevices = 1; // Init to 1 to account for "default" 1089 1090 // Get the whole list of devices and update _numRecDevices 1091 paOperation = LATE(pa_context_get_source_info_list)(_paContext, 1092 PaSourceInfoCallback, 1093 this); 1094 1095 WaitForOperationCompletion(paOperation); 1096 1097 PaUnLock(); 1098 1099 return _numRecDevices; 1100 } 1101 1102 int32_t AudioDeviceLinuxPulse::SetRecordingDevice(uint16_t index) 1103 { 1104 1105 if (_recIsInitialized) 1106 { 1107 return -1; 1108 } 1109 1110 const uint16_t nDevices(RecordingDevices()); 1111 1112 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1113 " number of availiable input devices is %u", nDevices); 1114 1115 if (index > (nDevices - 1)) 1116 { 1117 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1118 " device index is out of range [0,%u]", (nDevices - 1)); 1119 return -1; 1120 } 1121 1122 _inputDeviceIndex = index; 1123 _inputDeviceIsSpecified = true; 1124 1125 return 0; 1126 } 1127 1128 int32_t AudioDeviceLinuxPulse::SetRecordingDevice( 1129 AudioDeviceModule::WindowsDeviceType /*device*/) 1130 { 1131 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1132 "WindowsDeviceType not supported"); 1133 return -1; 1134 } 1135 1136 int32_t AudioDeviceLinuxPulse::PlayoutIsAvailable(bool& available) 1137 { 1138 1139 available = false; 1140 1141 // Try to initialize the playout side 1142 int32_t res = InitPlayout(); 1143 1144 // Cancel effect of initialization 1145 StopPlayout(); 1146 1147 if (res != -1) 1148 { 1149 available = true; 1150 } 1151 1152 return res; 1153 } 1154 1155 int32_t AudioDeviceLinuxPulse::RecordingIsAvailable(bool& available) 1156 { 1157 1158 available = false; 1159 1160 // Try to initialize the playout side 1161 int32_t res = InitRecording(); 1162 1163 // Cancel effect of initialization 1164 StopRecording(); 1165 1166 if (res != -1) 1167 { 1168 available = true; 1169 } 1170 1171 return res; 1172 } 1173 1174 int32_t AudioDeviceLinuxPulse::InitPlayout() 1175 { 1176 1177 CriticalSectionScoped lock(&_critSect); 1178 1179 if (_playing) 1180 { 1181 return -1; 1182 } 1183 1184 if (!_outputDeviceIsSpecified) 1185 { 1186 return -1; 1187 } 1188 1189 if (_playIsInitialized) 1190 { 1191 return 0; 1192 } 1193 1194 // Initialize the speaker (devices might have been added or removed) 1195 if (InitSpeaker() == -1) 1196 { 1197 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1198 " InitSpeaker() failed"); 1199 } 1200 1201 // Set the play sample specification 1202 pa_sample_spec playSampleSpec; 1203 playSampleSpec.channels = _playChannels; 1204 playSampleSpec.format = PA_SAMPLE_S16LE; 1205 playSampleSpec.rate = sample_rate_hz_; 1206 1207 // Create a new play stream 1208 _playStream = LATE(pa_stream_new)(_paContext, "playStream", 1209 &playSampleSpec, NULL); 1210 1211 if (!_playStream) 1212 { 1213 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1214 " failed to create play stream, err=%d", 1215 LATE(pa_context_errno)(_paContext)); 1216 return -1; 1217 } 1218 1219 // Provide the playStream to the mixer 1220 _mixerManager.SetPlayStream(_playStream); 1221 1222 if (_ptrAudioBuffer) 1223 { 1224 // Update audio buffer with the selected parameters 1225 _ptrAudioBuffer->SetPlayoutSampleRate(sample_rate_hz_); 1226 _ptrAudioBuffer->SetPlayoutChannels((uint8_t) _playChannels); 1227 } 1228 1229 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1230 " stream state %d\n", LATE(pa_stream_get_state)(_playStream)); 1231 1232 // Set stream flags 1233 _playStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE 1234 | PA_STREAM_INTERPOLATE_TIMING); 1235 1236 if (_configuredLatencyPlay != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) 1237 { 1238 // If configuring a specific latency then we want to specify 1239 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters 1240 // automatically to reach that target latency. However, that flag doesn't 1241 // exist in Ubuntu 8.04 and many people still use that, so we have to check 1242 // the protocol version of libpulse. 1243 if (LATE(pa_context_get_protocol_version)(_paContext) 1244 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) 1245 { 1246 _playStreamFlags |= PA_STREAM_ADJUST_LATENCY; 1247 } 1248 1249 const pa_sample_spec *spec = 1250 LATE(pa_stream_get_sample_spec)(_playStream); 1251 if (!spec) 1252 { 1253 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1254 " pa_stream_get_sample_spec()"); 1255 return -1; 1256 } 1257 1258 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); 1259 uint32_t latency = bytesPerSec 1260 * WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS / WEBRTC_PA_MSECS_PER_SEC; 1261 1262 // Set the play buffer attributes 1263 _playBufferAttr.maxlength = latency; // num bytes stored in the buffer 1264 _playBufferAttr.tlength = latency; // target fill level of play buffer 1265 // minimum free num bytes before server request more data 1266 _playBufferAttr.minreq = latency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; 1267 _playBufferAttr.prebuf = _playBufferAttr.tlength 1268 - _playBufferAttr.minreq; // prebuffer tlength before starting playout 1269 1270 _configuredLatencyPlay = latency; 1271 } 1272 1273 // num samples in bytes * num channels 1274 _playbackBufferSize = sample_rate_hz_ / 100 * 2 * _playChannels; 1275 _playbackBufferUnused = _playbackBufferSize; 1276 _playBuffer = new int8_t[_playbackBufferSize]; 1277 1278 // Enable underflow callback 1279 LATE(pa_stream_set_underflow_callback)(_playStream, 1280 PaStreamUnderflowCallback, this); 1281 1282 // Set the state callback function for the stream 1283 LATE(pa_stream_set_state_callback)(_playStream, PaStreamStateCallback, this); 1284 1285 // Mark playout side as initialized 1286 _playIsInitialized = true; 1287 _sndCardPlayDelay = 0; 1288 _sndCardRecDelay = 0; 1289 1290 return 0; 1291 } 1292 1293 int32_t AudioDeviceLinuxPulse::InitRecording() 1294 { 1295 1296 CriticalSectionScoped lock(&_critSect); 1297 1298 if (_recording) 1299 { 1300 return -1; 1301 } 1302 1303 if (!_inputDeviceIsSpecified) 1304 { 1305 return -1; 1306 } 1307 1308 if (_recIsInitialized) 1309 { 1310 return 0; 1311 } 1312 1313 // Initialize the microphone (devices might have been added or removed) 1314 if (InitMicrophone() == -1) 1315 { 1316 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1317 " InitMicrophone() failed"); 1318 } 1319 1320 // Set the rec sample specification 1321 pa_sample_spec recSampleSpec; 1322 recSampleSpec.channels = _recChannels; 1323 recSampleSpec.format = PA_SAMPLE_S16LE; 1324 recSampleSpec.rate = sample_rate_hz_; 1325 1326 // Create a new rec stream 1327 _recStream = LATE(pa_stream_new)(_paContext, "recStream", &recSampleSpec, 1328 NULL); 1329 if (!_recStream) 1330 { 1331 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1332 " failed to create rec stream, err=%d", 1333 LATE(pa_context_errno)(_paContext)); 1334 return -1; 1335 } 1336 1337 // Provide the recStream to the mixer 1338 _mixerManager.SetRecStream(_recStream); 1339 1340 if (_ptrAudioBuffer) 1341 { 1342 // Update audio buffer with the selected parameters 1343 _ptrAudioBuffer->SetRecordingSampleRate(sample_rate_hz_); 1344 _ptrAudioBuffer->SetRecordingChannels((uint8_t) _recChannels); 1345 } 1346 1347 if (_configuredLatencyRec != WEBRTC_PA_NO_LATENCY_REQUIREMENTS) 1348 { 1349 _recStreamFlags = (pa_stream_flags_t) (PA_STREAM_AUTO_TIMING_UPDATE 1350 | PA_STREAM_INTERPOLATE_TIMING); 1351 1352 // If configuring a specific latency then we want to specify 1353 // PA_STREAM_ADJUST_LATENCY to make the server adjust parameters 1354 // automatically to reach that target latency. However, that flag doesn't 1355 // exist in Ubuntu 8.04 and many people still use that, so we have to check 1356 // the protocol version of libpulse. 1357 if (LATE(pa_context_get_protocol_version)(_paContext) 1358 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) 1359 { 1360 _recStreamFlags |= PA_STREAM_ADJUST_LATENCY; 1361 } 1362 1363 const pa_sample_spec *spec = 1364 LATE(pa_stream_get_sample_spec)(_recStream); 1365 if (!spec) 1366 { 1367 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1368 " pa_stream_get_sample_spec(rec)"); 1369 return -1; 1370 } 1371 1372 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); 1373 uint32_t latency = bytesPerSec 1374 * WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS / WEBRTC_PA_MSECS_PER_SEC; 1375 1376 // Set the rec buffer attributes 1377 // Note: fragsize specifies a maximum transfer size, not a minimum, so 1378 // it is not possible to force a high latency setting, only a low one. 1379 _recBufferAttr.fragsize = latency; // size of fragment 1380 _recBufferAttr.maxlength = latency + bytesPerSec 1381 * WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS / WEBRTC_PA_MSECS_PER_SEC; 1382 1383 _configuredLatencyRec = latency; 1384 } 1385 1386 _recordBufferSize = sample_rate_hz_ / 100 * 2 * _recChannels; 1387 _recordBufferUsed = 0; 1388 _recBuffer = new int8_t[_recordBufferSize]; 1389 1390 // Enable overflow callback 1391 LATE(pa_stream_set_overflow_callback)(_recStream, PaStreamOverflowCallback, 1392 this); 1393 1394 // Set the state callback function for the stream 1395 LATE(pa_stream_set_state_callback)(_recStream, PaStreamStateCallback, this); 1396 1397 // Mark recording side as initialized 1398 _recIsInitialized = true; 1399 1400 return 0; 1401 } 1402 1403 int32_t AudioDeviceLinuxPulse::StartRecording() 1404 { 1405 1406 if (!_recIsInitialized) 1407 { 1408 return -1; 1409 } 1410 1411 if (_recording) 1412 { 1413 return 0; 1414 } 1415 1416 // set state to ensure that the recording starts from the audio thread 1417 _startRec = true; 1418 1419 // the audio thread will signal when recording has started 1420 _timeEventRec.Set(); 1421 if (kEventTimeout == _recStartEvent.Wait(10000)) 1422 { 1423 { 1424 CriticalSectionScoped lock(&_critSect); 1425 _startRec = false; 1426 } 1427 StopRecording(); 1428 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1429 " failed to activate recording"); 1430 return -1; 1431 } 1432 1433 { 1434 CriticalSectionScoped lock(&_critSect); 1435 if (_recording) 1436 { 1437 // the recording state is set by the audio thread after recording has started 1438 } else 1439 { 1440 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1441 " failed to activate recording"); 1442 return -1; 1443 } 1444 } 1445 1446 return 0; 1447 } 1448 1449 int32_t AudioDeviceLinuxPulse::StopRecording() 1450 { 1451 1452 CriticalSectionScoped lock(&_critSect); 1453 1454 if (!_recIsInitialized) 1455 { 1456 return 0; 1457 } 1458 1459 if (_recStream == NULL) 1460 { 1461 return -1; 1462 } 1463 1464 _recIsInitialized = false; 1465 _recording = false; 1466 1467 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1468 " stopping recording"); 1469 1470 // Stop Recording 1471 PaLock(); 1472 1473 DisableReadCallback(); 1474 LATE(pa_stream_set_overflow_callback)(_recStream, NULL, NULL); 1475 1476 // Unset this here so that we don't get a TERMINATED callback 1477 LATE(pa_stream_set_state_callback)(_recStream, NULL, NULL); 1478 1479 if (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_UNCONNECTED) 1480 { 1481 // Disconnect the stream 1482 if (LATE(pa_stream_disconnect)(_recStream) != PA_OK) 1483 { 1484 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1485 " failed to disconnect rec stream, err=%d\n", 1486 LATE(pa_context_errno)(_paContext)); 1487 PaUnLock(); 1488 return -1; 1489 } 1490 1491 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1492 " disconnected recording"); 1493 } 1494 1495 LATE(pa_stream_unref)(_recStream); 1496 _recStream = NULL; 1497 1498 PaUnLock(); 1499 1500 // Provide the recStream to the mixer 1501 _mixerManager.SetRecStream(_recStream); 1502 1503 if (_recBuffer) 1504 { 1505 delete [] _recBuffer; 1506 _recBuffer = NULL; 1507 } 1508 1509 return 0; 1510 } 1511 1512 bool AudioDeviceLinuxPulse::RecordingIsInitialized() const 1513 { 1514 return (_recIsInitialized); 1515 } 1516 1517 bool AudioDeviceLinuxPulse::Recording() const 1518 { 1519 CriticalSectionScoped lock(&_critSect); 1520 return (_recording); 1521 } 1522 1523 bool AudioDeviceLinuxPulse::PlayoutIsInitialized() const 1524 { 1525 return (_playIsInitialized); 1526 } 1527 1528 int32_t AudioDeviceLinuxPulse::StartPlayout() 1529 { 1530 if (!_playIsInitialized) 1531 { 1532 return -1; 1533 } 1534 1535 if (_playing) 1536 { 1537 return 0; 1538 } 1539 1540 // set state to ensure that playout starts from the audio thread 1541 _startPlay = true; 1542 1543 // Both |_startPlay| and |_playing| needs protction since they are also 1544 // accessed on the playout thread. 1545 1546 // the audio thread will signal when playout has started 1547 _timeEventPlay.Set(); 1548 if (kEventTimeout == _playStartEvent.Wait(10000)) 1549 { 1550 { 1551 CriticalSectionScoped lock(&_critSect); 1552 _startPlay = false; 1553 } 1554 StopPlayout(); 1555 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1556 " failed to activate playout"); 1557 return -1; 1558 } 1559 1560 { 1561 CriticalSectionScoped lock(&_critSect); 1562 if (_playing) 1563 { 1564 // the playing state is set by the audio thread after playout has started 1565 } else 1566 { 1567 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1568 " failed to activate playing"); 1569 return -1; 1570 } 1571 } 1572 1573 return 0; 1574 } 1575 1576 int32_t AudioDeviceLinuxPulse::StopPlayout() 1577 { 1578 1579 CriticalSectionScoped lock(&_critSect); 1580 1581 if (!_playIsInitialized) 1582 { 1583 return 0; 1584 } 1585 1586 if (_playStream == NULL) 1587 { 1588 return -1; 1589 } 1590 1591 _playIsInitialized = false; 1592 _playing = false; 1593 _sndCardPlayDelay = 0; 1594 _sndCardRecDelay = 0; 1595 1596 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1597 " stopping playback"); 1598 1599 // Stop Playout 1600 PaLock(); 1601 1602 DisableWriteCallback(); 1603 LATE(pa_stream_set_underflow_callback)(_playStream, NULL, NULL); 1604 1605 // Unset this here so that we don't get a TERMINATED callback 1606 LATE(pa_stream_set_state_callback)(_playStream, NULL, NULL); 1607 1608 if (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_UNCONNECTED) 1609 { 1610 // Disconnect the stream 1611 if (LATE(pa_stream_disconnect)(_playStream) != PA_OK) 1612 { 1613 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1614 " failed to disconnect play stream, err=%d", 1615 LATE(pa_context_errno)(_paContext)); 1616 PaUnLock(); 1617 return -1; 1618 } 1619 1620 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1621 " disconnected playback"); 1622 } 1623 1624 LATE(pa_stream_unref)(_playStream); 1625 _playStream = NULL; 1626 1627 PaUnLock(); 1628 1629 // Provide the playStream to the mixer 1630 _mixerManager.SetPlayStream(_playStream); 1631 1632 if (_playBuffer) 1633 { 1634 delete [] _playBuffer; 1635 _playBuffer = NULL; 1636 } 1637 1638 return 0; 1639 } 1640 1641 int32_t AudioDeviceLinuxPulse::PlayoutDelay(uint16_t& delayMS) const 1642 { 1643 CriticalSectionScoped lock(&_critSect); 1644 delayMS = (uint16_t) _sndCardPlayDelay; 1645 return 0; 1646 } 1647 1648 int32_t AudioDeviceLinuxPulse::RecordingDelay(uint16_t& delayMS) const 1649 { 1650 CriticalSectionScoped lock(&_critSect); 1651 delayMS = (uint16_t) _sndCardRecDelay; 1652 return 0; 1653 } 1654 1655 bool AudioDeviceLinuxPulse::Playing() const 1656 { 1657 CriticalSectionScoped lock(&_critSect); 1658 return (_playing); 1659 } 1660 1661 int32_t AudioDeviceLinuxPulse::SetPlayoutBuffer( 1662 const AudioDeviceModule::BufferType type, 1663 uint16_t sizeMS) 1664 { 1665 1666 if (type != AudioDeviceModule::kFixedBufferSize) 1667 { 1668 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1669 " Adaptive buffer size not supported on this platform"); 1670 return -1; 1671 } 1672 1673 _playBufType = type; 1674 _playBufDelayFixed = sizeMS; 1675 1676 return 0; 1677 } 1678 1679 int32_t AudioDeviceLinuxPulse::PlayoutBuffer( 1680 AudioDeviceModule::BufferType& type, 1681 uint16_t& sizeMS) const 1682 { 1683 1684 type = _playBufType; 1685 sizeMS = _playBufDelayFixed; 1686 1687 return 0; 1688 } 1689 1690 int32_t AudioDeviceLinuxPulse::CPULoad(uint16_t& /*load*/) const 1691 { 1692 1693 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1694 " API call not supported on this platform"); 1695 return -1; 1696 } 1697 1698 bool AudioDeviceLinuxPulse::PlayoutWarning() const 1699 { 1700 CriticalSectionScoped lock(&_critSect); 1701 return (_playWarning > 0); 1702 } 1703 1704 bool AudioDeviceLinuxPulse::PlayoutError() const 1705 { 1706 CriticalSectionScoped lock(&_critSect); 1707 return (_playError > 0); 1708 } 1709 1710 bool AudioDeviceLinuxPulse::RecordingWarning() const 1711 { 1712 CriticalSectionScoped lock(&_critSect); 1713 return (_recWarning > 0); 1714 } 1715 1716 bool AudioDeviceLinuxPulse::RecordingError() const 1717 { 1718 CriticalSectionScoped lock(&_critSect); 1719 return (_recError > 0); 1720 } 1721 1722 void AudioDeviceLinuxPulse::ClearPlayoutWarning() 1723 { 1724 CriticalSectionScoped lock(&_critSect); 1725 _playWarning = 0; 1726 } 1727 1728 void AudioDeviceLinuxPulse::ClearPlayoutError() 1729 { 1730 CriticalSectionScoped lock(&_critSect); 1731 _playError = 0; 1732 } 1733 1734 void AudioDeviceLinuxPulse::ClearRecordingWarning() 1735 { 1736 CriticalSectionScoped lock(&_critSect); 1737 _recWarning = 0; 1738 } 1739 1740 void AudioDeviceLinuxPulse::ClearRecordingError() 1741 { 1742 CriticalSectionScoped lock(&_critSect); 1743 _recError = 0; 1744 } 1745 1746 // ============================================================================ 1747 // Private Methods 1748 // ============================================================================ 1749 1750 void AudioDeviceLinuxPulse::PaContextStateCallback(pa_context *c, void *pThis) 1751 { 1752 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaContextStateCallbackHandler( 1753 c); 1754 } 1755 1756 // ---------------------------------------------------------------------------- 1757 // PaSinkInfoCallback 1758 // ---------------------------------------------------------------------------- 1759 1760 void AudioDeviceLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, 1761 const pa_sink_info *i, int eol, 1762 void *pThis) 1763 { 1764 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaSinkInfoCallbackHandler( 1765 i, eol); 1766 } 1767 1768 void AudioDeviceLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, 1769 const pa_source_info *i, 1770 int eol, void *pThis) 1771 { 1772 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaSourceInfoCallbackHandler( 1773 i, eol); 1774 } 1775 1776 void AudioDeviceLinuxPulse::PaServerInfoCallback(pa_context */*c*/, 1777 const pa_server_info *i, 1778 void *pThis) 1779 { 1780 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaServerInfoCallbackHandler(i); 1781 } 1782 1783 void AudioDeviceLinuxPulse::PaStreamStateCallback(pa_stream *p, void *pThis) 1784 { 1785 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamStateCallbackHandler(p); 1786 } 1787 1788 void AudioDeviceLinuxPulse::PaContextStateCallbackHandler(pa_context *c) 1789 { 1790 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1791 " context state cb"); 1792 1793 pa_context_state_t state = LATE(pa_context_get_state)(c); 1794 switch (state) 1795 { 1796 case PA_CONTEXT_UNCONNECTED: 1797 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1798 " unconnected"); 1799 break; 1800 case PA_CONTEXT_CONNECTING: 1801 case PA_CONTEXT_AUTHORIZING: 1802 case PA_CONTEXT_SETTING_NAME: 1803 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1804 " no state"); 1805 break; 1806 case PA_CONTEXT_FAILED: 1807 case PA_CONTEXT_TERMINATED: 1808 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1809 " failed"); 1810 _paStateChanged = true; 1811 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1812 break; 1813 case PA_CONTEXT_READY: 1814 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1815 " ready"); 1816 _paStateChanged = true; 1817 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1818 break; 1819 } 1820 } 1821 1822 void AudioDeviceLinuxPulse::PaSinkInfoCallbackHandler(const pa_sink_info *i, 1823 int eol) 1824 { 1825 if (eol) 1826 { 1827 // Signal that we are done 1828 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1829 return; 1830 } 1831 1832 if (_numPlayDevices == _deviceIndex) 1833 { 1834 // Convert the device index to the one of the sink 1835 _paDeviceIndex = i->index; 1836 1837 if (_playDeviceName) 1838 { 1839 // Copy the sink name 1840 strncpy(_playDeviceName, i->name, kAdmMaxDeviceNameSize); 1841 _playDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1842 } 1843 if (_playDisplayDeviceName) 1844 { 1845 // Copy the sink display name 1846 strncpy(_playDisplayDeviceName, i->description, 1847 kAdmMaxDeviceNameSize); 1848 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1849 } 1850 } 1851 1852 _numPlayDevices++; 1853 } 1854 1855 void AudioDeviceLinuxPulse::PaSourceInfoCallbackHandler( 1856 const pa_source_info *i, 1857 int eol) 1858 { 1859 if (eol) 1860 { 1861 // Signal that we are done 1862 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1863 return; 1864 } 1865 1866 // We don't want to list output devices 1867 if (i->monitor_of_sink == PA_INVALID_INDEX) 1868 { 1869 if (_numRecDevices == _deviceIndex) 1870 { 1871 // Convert the device index to the one of the source 1872 _paDeviceIndex = i->index; 1873 1874 if (_recDeviceName) 1875 { 1876 // copy the source name 1877 strncpy(_recDeviceName, i->name, kAdmMaxDeviceNameSize); 1878 _recDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1879 } 1880 if (_recDisplayDeviceName) 1881 { 1882 // Copy the source display name 1883 strncpy(_recDisplayDeviceName, i->description, 1884 kAdmMaxDeviceNameSize); 1885 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1886 } 1887 } 1888 1889 _numRecDevices++; 1890 } 1891 } 1892 1893 void AudioDeviceLinuxPulse::PaServerInfoCallbackHandler(const pa_server_info *i) 1894 { 1895 // Use PA native sampling rate 1896 sample_rate_hz_ = i->sample_spec.rate; 1897 1898 // Copy the PA server version 1899 strncpy(_paServerVersion, i->server_version, 31); 1900 _paServerVersion[31] = '\0'; 1901 1902 if (_recDisplayDeviceName) 1903 { 1904 // Copy the source name 1905 strncpy(_recDisplayDeviceName, i->default_source_name, 1906 kAdmMaxDeviceNameSize); 1907 _recDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1908 } 1909 1910 if (_playDisplayDeviceName) 1911 { 1912 // Copy the sink name 1913 strncpy(_playDisplayDeviceName, i->default_sink_name, 1914 kAdmMaxDeviceNameSize); 1915 _playDisplayDeviceName[kAdmMaxDeviceNameSize - 1] = '\0'; 1916 } 1917 1918 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1919 } 1920 1921 void AudioDeviceLinuxPulse::PaStreamStateCallbackHandler(pa_stream *p) 1922 { 1923 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1924 " stream state cb"); 1925 1926 pa_stream_state_t state = LATE(pa_stream_get_state)(p); 1927 switch (state) 1928 { 1929 case PA_STREAM_UNCONNECTED: 1930 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1931 " unconnected"); 1932 break; 1933 case PA_STREAM_CREATING: 1934 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1935 " creating"); 1936 break; 1937 case PA_STREAM_FAILED: 1938 case PA_STREAM_TERMINATED: 1939 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1940 " failed"); 1941 break; 1942 case PA_STREAM_READY: 1943 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1944 " ready"); 1945 break; 1946 } 1947 1948 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1949 } 1950 1951 int32_t AudioDeviceLinuxPulse::CheckPulseAudioVersion() 1952 { 1953 PaLock(); 1954 1955 pa_operation* paOperation = NULL; 1956 1957 // get the server info and update deviceName 1958 paOperation = LATE(pa_context_get_server_info)(_paContext, 1959 PaServerInfoCallback, this); 1960 1961 WaitForOperationCompletion(paOperation); 1962 1963 PaUnLock(); 1964 1965 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, 1966 " checking PulseAudio version: %s", _paServerVersion); 1967 1968 return 0; 1969 } 1970 1971 int32_t AudioDeviceLinuxPulse::InitSamplingFrequency() 1972 { 1973 PaLock(); 1974 1975 pa_operation* paOperation = NULL; 1976 1977 // Get the server info and update sample_rate_hz_ 1978 paOperation = LATE(pa_context_get_server_info)(_paContext, 1979 PaServerInfoCallback, this); 1980 1981 WaitForOperationCompletion(paOperation); 1982 1983 PaUnLock(); 1984 1985 return 0; 1986 } 1987 1988 int32_t AudioDeviceLinuxPulse::GetDefaultDeviceInfo(bool recDevice, 1989 char* name, 1990 uint16_t& index) 1991 { 1992 char tmpName[kAdmMaxDeviceNameSize] = {0}; 1993 // subtract length of "default: " 1994 uint16_t nameLen = kAdmMaxDeviceNameSize - 9; 1995 char* pName = NULL; 1996 1997 if (name) 1998 { 1999 // Add "default: " 2000 strcpy(name, "default: "); 2001 pName = &name[9]; 2002 } 2003 2004 // Tell the callback that we want 2005 // the name for this device 2006 if (recDevice) 2007 { 2008 _recDisplayDeviceName = tmpName; 2009 } else 2010 { 2011 _playDisplayDeviceName = tmpName; 2012 } 2013 2014 // Set members 2015 _paDeviceIndex = -1; 2016 _deviceIndex = 0; 2017 _numPlayDevices = 0; 2018 _numRecDevices = 0; 2019 2020 PaLock(); 2021 2022 pa_operation* paOperation = NULL; 2023 2024 // Get the server info and update deviceName 2025 paOperation = LATE(pa_context_get_server_info)(_paContext, 2026 PaServerInfoCallback, this); 2027 2028 WaitForOperationCompletion(paOperation); 2029 2030 // Get the device index 2031 if (recDevice) 2032 { 2033 paOperation 2034 = LATE(pa_context_get_source_info_by_name)(_paContext, 2035 (char *) tmpName, 2036 PaSourceInfoCallback, 2037 this); 2038 } else 2039 { 2040 paOperation 2041 = LATE(pa_context_get_sink_info_by_name)(_paContext, 2042 (char *) tmpName, 2043 PaSinkInfoCallback, this); 2044 } 2045 2046 WaitForOperationCompletion(paOperation); 2047 2048 PaUnLock(); 2049 2050 // Set the index 2051 index = _paDeviceIndex; 2052 2053 if (name) 2054 { 2055 // Copy to name string 2056 strncpy(pName, tmpName, nameLen); 2057 } 2058 2059 // Clear members 2060 _playDisplayDeviceName = NULL; 2061 _recDisplayDeviceName = NULL; 2062 _paDeviceIndex = -1; 2063 _deviceIndex = -1; 2064 _numPlayDevices = 0; 2065 _numRecDevices = 0; 2066 2067 return 0; 2068 } 2069 2070 int32_t AudioDeviceLinuxPulse::InitPulseAudio() 2071 { 2072 int retVal = 0; 2073 2074 // Load libpulse 2075 if (!PaSymbolTable.Load()) 2076 { 2077 // Most likely the Pulse library and sound server are not installed on 2078 // this system 2079 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2080 " failed to load symbol table"); 2081 return -1; 2082 } 2083 2084 // Create a mainloop API and connection to the default server 2085 // the mainloop is the internal asynchronous API event loop 2086 if (_paMainloop) { 2087 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2088 " PA mainloop has already existed"); 2089 return -1; 2090 } 2091 _paMainloop = LATE(pa_threaded_mainloop_new)(); 2092 if (!_paMainloop) 2093 { 2094 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2095 " could not create mainloop"); 2096 return -1; 2097 } 2098 2099 // Start the threaded main loop 2100 retVal = LATE(pa_threaded_mainloop_start)(_paMainloop); 2101 if (retVal != PA_OK) 2102 { 2103 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2104 " failed to start main loop, error=%d", retVal); 2105 return -1; 2106 } 2107 2108 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2109 " mainloop running!"); 2110 2111 PaLock(); 2112 2113 _paMainloopApi = LATE(pa_threaded_mainloop_get_api)(_paMainloop); 2114 if (!_paMainloopApi) 2115 { 2116 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2117 " could not create mainloop API"); 2118 PaUnLock(); 2119 return -1; 2120 } 2121 2122 // Create a new PulseAudio context 2123 if (_paContext){ 2124 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2125 " PA context has already existed"); 2126 PaUnLock(); 2127 return -1; 2128 } 2129 _paContext = LATE(pa_context_new)(_paMainloopApi, "WEBRTC VoiceEngine"); 2130 2131 if (!_paContext) 2132 { 2133 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2134 " could not create context"); 2135 PaUnLock(); 2136 return -1; 2137 } 2138 2139 // Set state callback function 2140 LATE(pa_context_set_state_callback)(_paContext, PaContextStateCallback, 2141 this); 2142 2143 // Connect the context to a server (default) 2144 _paStateChanged = false; 2145 retVal = LATE(pa_context_connect)(_paContext, NULL, PA_CONTEXT_NOAUTOSPAWN, 2146 NULL); 2147 2148 if (retVal != PA_OK) 2149 { 2150 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2151 " failed to connect context, error=%d", retVal); 2152 PaUnLock(); 2153 return -1; 2154 } 2155 2156 // Wait for state change 2157 while (!_paStateChanged) 2158 { 2159 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2160 } 2161 2162 // Now check to see what final state we reached. 2163 pa_context_state_t state = LATE(pa_context_get_state)(_paContext); 2164 2165 if (state != PA_CONTEXT_READY) 2166 { 2167 if (state == PA_CONTEXT_FAILED) 2168 { 2169 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2170 " failed to connect to PulseAudio sound server"); 2171 } else if (state == PA_CONTEXT_TERMINATED) 2172 { 2173 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2174 " PulseAudio connection terminated early"); 2175 } else 2176 { 2177 // Shouldn't happen, because we only signal on one of those three 2178 // states 2179 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2180 " unknown problem connecting to PulseAudio"); 2181 } 2182 PaUnLock(); 2183 return -1; 2184 } 2185 2186 PaUnLock(); 2187 2188 // Give the objects to the mixer manager 2189 _mixerManager.SetPulseAudioObjects(_paMainloop, _paContext); 2190 2191 // Check the version 2192 if (CheckPulseAudioVersion() < 0) 2193 { 2194 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2195 " PulseAudio version %s not supported", _paServerVersion); 2196 return -1; 2197 } 2198 2199 // Initialize sampling frequency 2200 if (InitSamplingFrequency() < 0 || sample_rate_hz_ == 0) 2201 { 2202 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2203 " failed to initialize sampling frequency, set to %d Hz", 2204 sample_rate_hz_); 2205 return -1; 2206 } 2207 2208 return 0; 2209 } 2210 2211 int32_t AudioDeviceLinuxPulse::TerminatePulseAudio() 2212 { 2213 // Do nothing if the instance doesn't exist 2214 // likely PaSymbolTable.Load() fails 2215 if (!_paMainloop) { 2216 return 0; 2217 } 2218 2219 PaLock(); 2220 2221 // Disconnect the context 2222 if (_paContext) 2223 { 2224 LATE(pa_context_disconnect)(_paContext); 2225 } 2226 2227 // Unreference the context 2228 if (_paContext) 2229 { 2230 LATE(pa_context_unref)(_paContext); 2231 } 2232 2233 PaUnLock(); 2234 _paContext = NULL; 2235 2236 // Stop the threaded main loop 2237 if (_paMainloop) 2238 { 2239 LATE(pa_threaded_mainloop_stop)(_paMainloop); 2240 } 2241 2242 // Free the mainloop 2243 if (_paMainloop) 2244 { 2245 LATE(pa_threaded_mainloop_free)(_paMainloop); 2246 } 2247 2248 _paMainloop = NULL; 2249 2250 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2251 " PulseAudio terminated"); 2252 2253 return 0; 2254 } 2255 2256 void AudioDeviceLinuxPulse::PaLock() 2257 { 2258 LATE(pa_threaded_mainloop_lock)(_paMainloop); 2259 } 2260 2261 void AudioDeviceLinuxPulse::PaUnLock() 2262 { 2263 LATE(pa_threaded_mainloop_unlock)(_paMainloop); 2264 } 2265 2266 void AudioDeviceLinuxPulse::WaitForOperationCompletion( 2267 pa_operation* paOperation) const 2268 { 2269 if (!paOperation) 2270 { 2271 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2272 "paOperation NULL in WaitForOperationCompletion"); 2273 return; 2274 } 2275 2276 while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) 2277 { 2278 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2279 } 2280 2281 LATE(pa_operation_unref)(paOperation); 2282 } 2283 2284 // ============================================================================ 2285 // Thread Methods 2286 // ============================================================================ 2287 2288 void AudioDeviceLinuxPulse::EnableWriteCallback() 2289 { 2290 if (LATE(pa_stream_get_state)(_playStream) == PA_STREAM_READY) 2291 { 2292 // May already have available space. Must check. 2293 _tempBufferSpace = LATE(pa_stream_writable_size)(_playStream); 2294 if (_tempBufferSpace > 0) 2295 { 2296 // Yup, there is already space available, so if we register a write 2297 // callback then it will not receive any event. So dispatch one ourself 2298 // instead 2299 _timeEventPlay.Set(); 2300 return; 2301 } 2302 } 2303 2304 LATE(pa_stream_set_write_callback)(_playStream, &PaStreamWriteCallback, 2305 this); 2306 } 2307 2308 void AudioDeviceLinuxPulse::DisableWriteCallback() 2309 { 2310 LATE(pa_stream_set_write_callback)(_playStream, NULL, NULL); 2311 } 2312 2313 void AudioDeviceLinuxPulse::PaStreamWriteCallback(pa_stream */*unused*/, 2314 size_t buffer_space, 2315 void *pThis) 2316 { 2317 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamWriteCallbackHandler( 2318 buffer_space); 2319 } 2320 2321 void AudioDeviceLinuxPulse::PaStreamWriteCallbackHandler(size_t bufferSpace) 2322 { 2323 _tempBufferSpace = bufferSpace; 2324 2325 // Since we write the data asynchronously on a different thread, we have 2326 // to temporarily disable the write callback or else Pulse will call it 2327 // continuously until we write the data. We re-enable it below. 2328 DisableWriteCallback(); 2329 _timeEventPlay.Set(); 2330 } 2331 2332 void AudioDeviceLinuxPulse::PaStreamUnderflowCallback(pa_stream */*unused*/, 2333 void *pThis) 2334 { 2335 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamUnderflowCallbackHandler(); 2336 } 2337 2338 void AudioDeviceLinuxPulse::PaStreamUnderflowCallbackHandler() 2339 { 2340 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2341 " Playout underflow"); 2342 2343 if (_configuredLatencyPlay == WEBRTC_PA_NO_LATENCY_REQUIREMENTS) 2344 { 2345 // We didn't configure a pa_buffer_attr before, so switching to one now 2346 // would be questionable. 2347 return; 2348 } 2349 2350 // Otherwise reconfigure the stream with a higher target latency. 2351 2352 const pa_sample_spec *spec = LATE(pa_stream_get_sample_spec)(_playStream); 2353 if (!spec) 2354 { 2355 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2356 " pa_stream_get_sample_spec()"); 2357 return; 2358 } 2359 2360 size_t bytesPerSec = LATE(pa_bytes_per_second)(spec); 2361 uint32_t newLatency = _configuredLatencyPlay + bytesPerSec 2362 * WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS / WEBRTC_PA_MSECS_PER_SEC; 2363 2364 // Set the play buffer attributes 2365 _playBufferAttr.maxlength = newLatency; 2366 _playBufferAttr.tlength = newLatency; 2367 _playBufferAttr.minreq = newLatency / WEBRTC_PA_PLAYBACK_REQUEST_FACTOR; 2368 _playBufferAttr.prebuf = _playBufferAttr.tlength - _playBufferAttr.minreq; 2369 2370 pa_operation *op = LATE(pa_stream_set_buffer_attr)(_playStream, 2371 &_playBufferAttr, NULL, 2372 NULL); 2373 if (!op) 2374 { 2375 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2376 " pa_stream_set_buffer_attr()"); 2377 return; 2378 } 2379 2380 // Don't need to wait for this to complete. 2381 LATE(pa_operation_unref)(op); 2382 2383 // Save the new latency in case we underflow again. 2384 _configuredLatencyPlay = newLatency; 2385 } 2386 2387 void AudioDeviceLinuxPulse::EnableReadCallback() 2388 { 2389 LATE(pa_stream_set_read_callback)(_recStream, &PaStreamReadCallback, this); 2390 } 2391 2392 void AudioDeviceLinuxPulse::DisableReadCallback() 2393 { 2394 LATE(pa_stream_set_read_callback)(_recStream, NULL, NULL); 2395 } 2396 2397 void AudioDeviceLinuxPulse::PaStreamReadCallback(pa_stream */*unused1*/, 2398 size_t /*unused2*/, 2399 void *pThis) 2400 { 2401 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamReadCallbackHandler(); 2402 } 2403 2404 void AudioDeviceLinuxPulse::PaStreamReadCallbackHandler() 2405 { 2406 // We get the data pointer and size now in order to save one Lock/Unlock 2407 // in the worker thread 2408 if (LATE(pa_stream_peek)(_recStream, &_tempSampleData, &_tempSampleDataSize) 2409 != 0) 2410 { 2411 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2412 " Can't read data!"); 2413 return; 2414 } 2415 2416 // Since we consume the data asynchronously on a different thread, we have 2417 // to temporarily disable the read callback or else Pulse will call it 2418 // continuously until we consume the data. We re-enable it below 2419 DisableReadCallback(); 2420 _timeEventRec.Set(); 2421 } 2422 2423 void AudioDeviceLinuxPulse::PaStreamOverflowCallback(pa_stream */*unused*/, 2424 void *pThis) 2425 { 2426 static_cast<AudioDeviceLinuxPulse*> (pThis)->PaStreamOverflowCallbackHandler(); 2427 } 2428 2429 void AudioDeviceLinuxPulse::PaStreamOverflowCallbackHandler() 2430 { 2431 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2432 " Recording overflow"); 2433 } 2434 2435 int32_t AudioDeviceLinuxPulse::LatencyUsecs(pa_stream *stream) 2436 { 2437 if (!WEBRTC_PA_REPORT_LATENCY) 2438 { 2439 return 0; 2440 } 2441 2442 if (!stream) 2443 { 2444 return 0; 2445 } 2446 2447 pa_usec_t latency; 2448 int negative; 2449 if (LATE(pa_stream_get_latency)(stream, &latency, &negative) != 0) 2450 { 2451 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2452 " Can't query latency"); 2453 // We'd rather continue playout/capture with an incorrect delay than stop 2454 // it altogether, so return a valid value. 2455 return 0; 2456 } 2457 2458 if (negative) 2459 { 2460 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2461 " warning: pa_stream_get_latency reported negative delay"); 2462 2463 // The delay can be negative for monitoring streams if the captured 2464 // samples haven't been played yet. In such a case, "latency" contains the 2465 // magnitude, so we must negate it to get the real value. 2466 int32_t tmpLatency = (int32_t) -latency; 2467 if (tmpLatency < 0) 2468 { 2469 // Make sure that we don't use a negative delay 2470 tmpLatency = 0; 2471 } 2472 2473 return tmpLatency; 2474 } else 2475 { 2476 return (int32_t) latency; 2477 } 2478 } 2479 2480 int32_t AudioDeviceLinuxPulse::ReadRecordedData(const void* bufferData, 2481 size_t bufferSize) 2482 { 2483 size_t size = bufferSize; 2484 uint32_t numRecSamples = _recordBufferSize / (2 * _recChannels); 2485 2486 // Account for the peeked data and the used data 2487 uint32_t recDelay = (uint32_t) ((LatencyUsecs(_recStream) 2488 / 1000) + 10 * ((size + _recordBufferUsed) / _recordBufferSize)); 2489 2490 _sndCardRecDelay = recDelay; 2491 2492 if (_playStream) 2493 { 2494 // Get the playout delay 2495 _sndCardPlayDelay = (uint32_t) (LatencyUsecs(_playStream) / 1000); 2496 } 2497 2498 if (_recordBufferUsed > 0) 2499 { 2500 // Have to copy to the buffer until it is full 2501 size_t copy = _recordBufferSize - _recordBufferUsed; 2502 if (size < copy) 2503 { 2504 copy = size; 2505 } 2506 2507 memcpy(&_recBuffer[_recordBufferUsed], bufferData, copy); 2508 _recordBufferUsed += copy; 2509 bufferData = static_cast<const char *> (bufferData) + copy; 2510 size -= copy; 2511 2512 if (_recordBufferUsed != _recordBufferSize) 2513 { 2514 // Not enough data yet to pass to VoE 2515 return 0; 2516 } 2517 2518 // Provide data to VoiceEngine 2519 if (ProcessRecordedData(_recBuffer, numRecSamples, recDelay) == -1) 2520 { 2521 // We have stopped recording 2522 return -1; 2523 } 2524 2525 _recordBufferUsed = 0; 2526 } 2527 2528 // Now process full 10ms sample sets directly from the input 2529 while (size >= _recordBufferSize) 2530 { 2531 // Provide data to VoiceEngine 2532 if (ProcessRecordedData( 2533 static_cast<int8_t *> (const_cast<void *> (bufferData)), 2534 numRecSamples, recDelay) == -1) 2535 { 2536 // We have stopped recording 2537 return -1; 2538 } 2539 2540 bufferData = static_cast<const char *> (bufferData) + _recordBufferSize; 2541 size -= _recordBufferSize; 2542 2543 // We have consumed 10ms of data 2544 recDelay -= 10; 2545 } 2546 2547 // Now save any leftovers for later. 2548 if (size > 0) 2549 { 2550 memcpy(_recBuffer, bufferData, size); 2551 _recordBufferUsed = size; 2552 } 2553 2554 return 0; 2555 } 2556 2557 int32_t AudioDeviceLinuxPulse::ProcessRecordedData( 2558 int8_t *bufferData, 2559 uint32_t bufferSizeInSamples, 2560 uint32_t recDelay) EXCLUSIVE_LOCKS_REQUIRED(_critSect) 2561 { 2562 uint32_t currentMicLevel(0); 2563 uint32_t newMicLevel(0); 2564 2565 _ptrAudioBuffer->SetRecordedBuffer(bufferData, bufferSizeInSamples); 2566 2567 if (AGC()) 2568 { 2569 // Store current mic level in the audio buffer if AGC is enabled 2570 if (MicrophoneVolume(currentMicLevel) == 0) 2571 { 2572 // This call does not affect the actual microphone volume 2573 _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel); 2574 } 2575 } 2576 2577 const uint32_t clockDrift(0); 2578 // TODO(andrew): this is a temporary hack, to avoid non-causal far- and 2579 // near-end signals at the AEC for PulseAudio. I think the system delay is 2580 // being correctly calculated here, but for legacy reasons we add +10 ms to 2581 // the value in the AEC. The real fix will be part of a larger investigation 2582 // into managing system delay in the AEC. 2583 if (recDelay > 10) 2584 recDelay -= 10; 2585 else 2586 recDelay = 0; 2587 _ptrAudioBuffer->SetVQEData(_sndCardPlayDelay, recDelay, clockDrift); 2588 _ptrAudioBuffer->SetTypingStatus(KeyPressed()); 2589 // Deliver recorded samples at specified sample rate, 2590 // mic level etc. to the observer using callback 2591 UnLock(); 2592 _ptrAudioBuffer->DeliverRecordedData(); 2593 Lock(); 2594 2595 // We have been unlocked - check the flag again 2596 if (!_recording) 2597 { 2598 return -1; 2599 } 2600 2601 if (AGC()) 2602 { 2603 newMicLevel = _ptrAudioBuffer->NewMicLevel(); 2604 if (newMicLevel != 0) 2605 { 2606 // The VQE will only deliver non-zero microphone levels when a 2607 // change is needed. 2608 // Set this new mic level (received from the observer as return 2609 // value in the callback). 2610 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id, 2611 " AGC change of volume: old=%u => new=%u", 2612 currentMicLevel, newMicLevel); 2613 if (SetMicrophoneVolume(newMicLevel) == -1) 2614 { 2615 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, 2616 _id, 2617 " the required modification of the microphone " 2618 "volume failed"); 2619 } 2620 } 2621 } 2622 2623 return 0; 2624 } 2625 2626 bool AudioDeviceLinuxPulse::PlayThreadFunc(void* pThis) 2627 { 2628 return (static_cast<AudioDeviceLinuxPulse*> (pThis)->PlayThreadProcess()); 2629 } 2630 2631 bool AudioDeviceLinuxPulse::RecThreadFunc(void* pThis) 2632 { 2633 return (static_cast<AudioDeviceLinuxPulse*> (pThis)->RecThreadProcess()); 2634 } 2635 2636 bool AudioDeviceLinuxPulse::PlayThreadProcess() 2637 { 2638 switch (_timeEventPlay.Wait(1000)) 2639 { 2640 case kEventSignaled: 2641 _timeEventPlay.Reset(); 2642 break; 2643 case kEventError: 2644 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2645 "EventWrapper::Wait() failed"); 2646 return true; 2647 case kEventTimeout: 2648 return true; 2649 } 2650 2651 Lock(); 2652 2653 if (_startPlay) 2654 { 2655 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 2656 "_startPlay true, performing initial actions"); 2657 2658 _startPlay = false; 2659 _playDeviceName = NULL; 2660 2661 // Set if not default device 2662 if (_outputDeviceIndex > 0) 2663 { 2664 // Get the playout device name 2665 _playDeviceName = new char[kAdmMaxDeviceNameSize]; 2666 _deviceIndex = _outputDeviceIndex; 2667 PlayoutDevices(); 2668 } 2669 2670 // Start muted only supported on 0.9.11 and up 2671 if (LATE(pa_context_get_protocol_version)(_paContext) 2672 >= WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION) 2673 { 2674 // Get the currently saved speaker mute status 2675 // and set the initial mute status accordingly 2676 bool enabled(false); 2677 _mixerManager.SpeakerMute(enabled); 2678 if (enabled) 2679 { 2680 _playStreamFlags |= PA_STREAM_START_MUTED; 2681 } 2682 } 2683 2684 // Get the currently saved speaker volume 2685 uint32_t volume = 0; 2686 if (update_speaker_volume_at_startup_) 2687 _mixerManager.SpeakerVolume(volume); 2688 2689 PaLock(); 2690 2691 // NULL gives PA the choice of startup volume. 2692 pa_cvolume* ptr_cvolume = NULL; 2693 if (update_speaker_volume_at_startup_) { 2694 pa_cvolume cVolumes; 2695 ptr_cvolume = &cVolumes; 2696 2697 // Set the same volume for all channels 2698 const pa_sample_spec *spec = 2699 LATE(pa_stream_get_sample_spec)(_playStream); 2700 LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); 2701 update_speaker_volume_at_startup_ = false; 2702 } 2703 2704 // Connect the stream to a sink 2705 if (LATE(pa_stream_connect_playback)( 2706 _playStream, 2707 _playDeviceName, 2708 &_playBufferAttr, 2709 (pa_stream_flags_t) _playStreamFlags, 2710 ptr_cvolume, NULL) != PA_OK) 2711 { 2712 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2713 " failed to connect play stream, err=%d", 2714 LATE(pa_context_errno)(_paContext)); 2715 } 2716 2717 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2718 " play stream connected"); 2719 2720 // Wait for state change 2721 while (LATE(pa_stream_get_state)(_playStream) != PA_STREAM_READY) 2722 { 2723 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2724 } 2725 2726 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2727 " play stream ready"); 2728 2729 // We can now handle write callbacks 2730 EnableWriteCallback(); 2731 2732 PaUnLock(); 2733 2734 // Clear device name 2735 if (_playDeviceName) 2736 { 2737 delete [] _playDeviceName; 2738 _playDeviceName = NULL; 2739 } 2740 2741 _playing = true; 2742 _playStartEvent.Set(); 2743 2744 UnLock(); 2745 return true; 2746 } 2747 2748 if (_playing) 2749 { 2750 if (!_recording) 2751 { 2752 // Update the playout delay 2753 _sndCardPlayDelay = (uint32_t) (LatencyUsecs(_playStream) 2754 / 1000); 2755 } 2756 2757 if (_playbackBufferUnused < _playbackBufferSize) 2758 { 2759 2760 size_t write = _playbackBufferSize - _playbackBufferUnused; 2761 if (_tempBufferSpace < write) 2762 { 2763 write = _tempBufferSpace; 2764 } 2765 2766 PaLock(); 2767 if (LATE(pa_stream_write)( 2768 _playStream, 2769 (void *) &_playBuffer[_playbackBufferUnused], 2770 write, NULL, (int64_t) 0, 2771 PA_SEEK_RELATIVE) != PA_OK) 2772 { 2773 _writeErrors++; 2774 if (_writeErrors > 10) 2775 { 2776 if (_playError == 1) 2777 { 2778 WEBRTC_TRACE(kTraceWarning, 2779 kTraceUtility, _id, 2780 " pending playout error exists"); 2781 } 2782 _playError = 1; // Triggers callback from module process thread 2783 WEBRTC_TRACE( 2784 kTraceError, 2785 kTraceUtility, 2786 _id, 2787 " kPlayoutError message posted: " 2788 "_writeErrors=%u, error=%d", 2789 _writeErrors, 2790 LATE(pa_context_errno)(_paContext)); 2791 _writeErrors = 0; 2792 } 2793 } 2794 PaUnLock(); 2795 2796 _playbackBufferUnused += write; 2797 _tempBufferSpace -= write; 2798 } 2799 2800 uint32_t numPlaySamples = _playbackBufferSize / (2 * _playChannels); 2801 if (_tempBufferSpace > 0) // Might have been reduced to zero by the above 2802 { 2803 // Ask for new PCM data to be played out using the AudioDeviceBuffer 2804 // ensure that this callback is executed without taking the 2805 // audio-thread lock 2806 UnLock(); 2807 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2808 " requesting data"); 2809 uint32_t nSamples = 2810 _ptrAudioBuffer->RequestPlayoutData(numPlaySamples); 2811 Lock(); 2812 2813 // We have been unlocked - check the flag again 2814 if (!_playing) 2815 { 2816 UnLock(); 2817 return true; 2818 } 2819 2820 nSamples = _ptrAudioBuffer->GetPlayoutData(_playBuffer); 2821 if (nSamples != numPlaySamples) 2822 { 2823 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, 2824 _id, " invalid number of output samples(%d)", 2825 nSamples); 2826 } 2827 2828 size_t write = _playbackBufferSize; 2829 if (_tempBufferSpace < write) 2830 { 2831 write = _tempBufferSpace; 2832 } 2833 2834 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2835 " will write"); 2836 PaLock(); 2837 if (LATE(pa_stream_write)(_playStream, (void *) &_playBuffer[0], 2838 write, NULL, (int64_t) 0, 2839 PA_SEEK_RELATIVE) != PA_OK) 2840 { 2841 _writeErrors++; 2842 if (_writeErrors > 10) 2843 { 2844 if (_playError == 1) 2845 { 2846 WEBRTC_TRACE(kTraceWarning, 2847 kTraceUtility, _id, 2848 " pending playout error exists"); 2849 } 2850 _playError = 1; // triggers callback from module process thread 2851 WEBRTC_TRACE( 2852 kTraceError, 2853 kTraceUtility, 2854 _id, 2855 " kPlayoutError message posted: " 2856 "_writeErrors=%u, error=%d", 2857 _writeErrors, 2858 LATE(pa_context_errno)(_paContext)); 2859 _writeErrors = 0; 2860 } 2861 } 2862 PaUnLock(); 2863 2864 _playbackBufferUnused = write; 2865 } 2866 2867 _tempBufferSpace = 0; 2868 PaLock(); 2869 EnableWriteCallback(); 2870 PaUnLock(); 2871 2872 } // _playing 2873 2874 UnLock(); 2875 return true; 2876 } 2877 2878 bool AudioDeviceLinuxPulse::RecThreadProcess() 2879 { 2880 switch (_timeEventRec.Wait(1000)) 2881 { 2882 case kEventSignaled: 2883 _timeEventRec.Reset(); 2884 break; 2885 case kEventError: 2886 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 2887 "EventWrapper::Wait() failed"); 2888 return true; 2889 case kEventTimeout: 2890 return true; 2891 } 2892 2893 Lock(); 2894 2895 if (_startRec) 2896 { 2897 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 2898 "_startRec true, performing initial actions"); 2899 2900 _recDeviceName = NULL; 2901 2902 // Set if not default device 2903 if (_inputDeviceIndex > 0) 2904 { 2905 // Get the recording device name 2906 _recDeviceName = new char[kAdmMaxDeviceNameSize]; 2907 _deviceIndex = _inputDeviceIndex; 2908 RecordingDevices(); 2909 } 2910 2911 PaLock(); 2912 2913 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2914 " connecting stream"); 2915 2916 // Connect the stream to a source 2917 if (LATE(pa_stream_connect_record)(_recStream, _recDeviceName, 2918 &_recBufferAttr, 2919 (pa_stream_flags_t) _recStreamFlags) 2920 != PA_OK) 2921 { 2922 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 2923 " failed to connect rec stream, err=%d", 2924 LATE(pa_context_errno)(_paContext)); 2925 } 2926 2927 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2928 " connected"); 2929 2930 // Wait for state change 2931 while (LATE(pa_stream_get_state)(_recStream) != PA_STREAM_READY) 2932 { 2933 LATE(pa_threaded_mainloop_wait)(_paMainloop); 2934 } 2935 2936 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 2937 " done"); 2938 2939 // We can now handle read callbacks 2940 EnableReadCallback(); 2941 2942 PaUnLock(); 2943 2944 // Clear device name 2945 if (_recDeviceName) 2946 { 2947 delete [] _recDeviceName; 2948 _recDeviceName = NULL; 2949 } 2950 2951 _startRec = false; 2952 _recording = true; 2953 _recStartEvent.Set(); 2954 2955 UnLock(); 2956 return true; 2957 } 2958 2959 if (_recording) 2960 { 2961 // Read data and provide it to VoiceEngine 2962 if (ReadRecordedData(_tempSampleData, _tempSampleDataSize) == -1) 2963 { 2964 UnLock(); 2965 return true; 2966 } 2967 2968 _tempSampleData = NULL; 2969 _tempSampleDataSize = 0; 2970 2971 PaLock(); 2972 while (true) 2973 { 2974 // Ack the last thing we read 2975 if (LATE(pa_stream_drop)(_recStream) != 0) 2976 { 2977 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, 2978 _id, " failed to drop, err=%d\n", 2979 LATE(pa_context_errno)(_paContext)); 2980 } 2981 2982 if (LATE(pa_stream_readable_size)(_recStream) <= 0) 2983 { 2984 // Then that was all the data 2985 break; 2986 } 2987 2988 // Else more data. 2989 const void *sampleData; 2990 size_t sampleDataSize; 2991 2992 if (LATE(pa_stream_peek)(_recStream, &sampleData, &sampleDataSize) 2993 != 0) 2994 { 2995 _recError = 1; // triggers callback from module process thread 2996 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, 2997 _id, " RECORD_ERROR message posted, error = %d", 2998 LATE(pa_context_errno)(_paContext)); 2999 break; 3000 } 3001 3002 _sndCardRecDelay = (uint32_t) (LatencyUsecs(_recStream) 3003 / 1000); 3004 3005 // Drop lock for sigslot dispatch, which could take a while. 3006 PaUnLock(); 3007 // Read data and provide it to VoiceEngine 3008 if (ReadRecordedData(sampleData, sampleDataSize) == -1) 3009 { 3010 UnLock(); 3011 return true; 3012 } 3013 PaLock(); 3014 3015 // Return to top of loop for the ack and the check for more data. 3016 } 3017 3018 EnableReadCallback(); 3019 PaUnLock(); 3020 3021 } // _recording 3022 3023 UnLock(); 3024 return true; 3025 } 3026 3027 bool AudioDeviceLinuxPulse::KeyPressed() const{ 3028 3029 char szKey[32]; 3030 unsigned int i = 0; 3031 char state = 0; 3032 3033 if (!_XDisplay) 3034 return false; 3035 3036 // Check key map status 3037 XQueryKeymap(_XDisplay, szKey); 3038 3039 // A bit change in keymap means a key is pressed 3040 for (i = 0; i < sizeof(szKey); i++) 3041 state |= (szKey[i] ^ _oldKeyState[i]) & szKey[i]; 3042 3043 // Save old state 3044 memcpy((char*)_oldKeyState, (char*)szKey, sizeof(_oldKeyState)); 3045 return (state != 0); 3046 } 3047 } 3048