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/linux/audio_mixer_manager_pulse_linux.h" 14 #include "webrtc/system_wrappers/interface/trace.h" 15 16 extern webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; 17 18 // Accesses Pulse functions through our late-binding symbol table instead of 19 // directly. This way we don't have to link to libpulse, which means our binary 20 // will work on systems that don't have it. 21 #define LATE(sym) \ 22 LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym) 23 24 namespace webrtc 25 { 26 27 enum { kMaxRetryOnFailure = 2 }; 28 29 AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse(const int32_t id) : 30 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), 31 _id(id), 32 _paOutputDeviceIndex(-1), 33 _paInputDeviceIndex(-1), 34 _paPlayStream(NULL), 35 _paRecStream(NULL), 36 _paMainloop(NULL), 37 _paContext(NULL), 38 _paVolume(0), 39 _paMute(0), 40 _paVolSteps(0), 41 _paSpeakerMute(false), 42 _paSpeakerVolume(PA_VOLUME_NORM), 43 _paChannels(0), 44 _paObjectsSet(false), 45 _callbackValues(false) 46 { 47 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, 48 "%s constructed", __FUNCTION__); 49 } 50 51 AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse() 52 { 53 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, 54 "%s destructed", __FUNCTION__); 55 56 Close(); 57 58 delete &_critSect; 59 } 60 61 // ============================================================================ 62 // PUBLIC METHODS 63 // ============================================================================ 64 65 int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects( 66 pa_threaded_mainloop* mainloop, 67 pa_context* context) 68 { 69 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", 70 __FUNCTION__); 71 72 CriticalSectionScoped lock(&_critSect); 73 74 if (!mainloop || !context) 75 { 76 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 77 " could not set PulseAudio objects for mixer"); 78 return -1; 79 } 80 81 _paMainloop = mainloop; 82 _paContext = context; 83 _paObjectsSet = true; 84 85 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 86 " the PulseAudio objects for the mixer has been set"); 87 88 return 0; 89 } 90 91 int32_t AudioMixerManagerLinuxPulse::Close() 92 { 93 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", 94 __FUNCTION__); 95 96 CriticalSectionScoped lock(&_critSect); 97 98 CloseSpeaker(); 99 CloseMicrophone(); 100 101 _paMainloop = NULL; 102 _paContext = NULL; 103 _paObjectsSet = false; 104 105 return 0; 106 107 } 108 109 int32_t AudioMixerManagerLinuxPulse::CloseSpeaker() 110 { 111 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", 112 __FUNCTION__); 113 114 CriticalSectionScoped lock(&_critSect); 115 116 // Reset the index to -1 117 _paOutputDeviceIndex = -1; 118 _paPlayStream = NULL; 119 120 return 0; 121 } 122 123 int32_t AudioMixerManagerLinuxPulse::CloseMicrophone() 124 { 125 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", 126 __FUNCTION__); 127 128 CriticalSectionScoped lock(&_critSect); 129 130 // Reset the index to -1 131 _paInputDeviceIndex = -1; 132 _paRecStream = NULL; 133 134 return 0; 135 } 136 137 int32_t AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream) 138 { 139 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 140 "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)"); 141 142 CriticalSectionScoped lock(&_critSect); 143 _paPlayStream = playStream; 144 return 0; 145 } 146 147 int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream) 148 { 149 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 150 "AudioMixerManagerLinuxPulse::SetRecStream(recStream)"); 151 152 CriticalSectionScoped lock(&_critSect); 153 _paRecStream = recStream; 154 return 0; 155 } 156 157 int32_t AudioMixerManagerLinuxPulse::OpenSpeaker( 158 uint16_t deviceIndex) 159 { 160 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 161 "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex=%d)", 162 deviceIndex); 163 164 CriticalSectionScoped lock(&_critSect); 165 166 // No point in opening the speaker 167 // if PA objects have not been set 168 if (!_paObjectsSet) 169 { 170 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 171 " PulseAudio objects has not been set"); 172 return -1; 173 } 174 175 // Set the index for the PulseAudio 176 // output device to control 177 _paOutputDeviceIndex = deviceIndex; 178 179 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 180 " the output mixer device is now open"); 181 182 return 0; 183 } 184 185 int32_t AudioMixerManagerLinuxPulse::OpenMicrophone( 186 uint16_t deviceIndex) 187 { 188 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 189 "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex=%d)", 190 deviceIndex); 191 192 CriticalSectionScoped lock(&_critSect); 193 194 // No point in opening the microphone 195 // if PA objects have not been set 196 if (!_paObjectsSet) 197 { 198 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 199 " PulseAudio objects have not been set"); 200 return -1; 201 } 202 203 // Set the index for the PulseAudio 204 // input device to control 205 _paInputDeviceIndex = deviceIndex; 206 207 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 208 " the input mixer device is now open"); 209 210 return 0; 211 } 212 213 bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const 214 { 215 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", 216 __FUNCTION__); 217 218 return (_paOutputDeviceIndex != -1); 219 } 220 221 bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const 222 { 223 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", 224 __FUNCTION__); 225 226 return (_paInputDeviceIndex != -1); 227 } 228 229 int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume( 230 uint32_t volume) 231 { 232 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 233 "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume=%u)", 234 volume); 235 236 CriticalSectionScoped lock(&_critSect); 237 238 if (_paOutputDeviceIndex == -1) 239 { 240 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 241 " output device index has not been set"); 242 return -1; 243 } 244 245 bool setFailed(false); 246 247 if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) 248 != PA_STREAM_UNCONNECTED)) 249 { 250 // We can only really set the volume if we have a connected stream 251 PaLock(); 252 253 // Get the number of channels from the sample specification 254 const pa_sample_spec *spec = 255 LATE(pa_stream_get_sample_spec)(_paPlayStream); 256 if (!spec) 257 { 258 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 259 " could not get sample specification"); 260 PaUnLock(); 261 return -1; 262 } 263 264 // Set the same volume for all channels 265 pa_cvolume cVolumes; 266 LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); 267 268 pa_operation* paOperation = NULL; 269 paOperation = LATE(pa_context_set_sink_input_volume)( 270 _paContext, 271 LATE(pa_stream_get_index)(_paPlayStream), 272 &cVolumes, 273 PaSetVolumeCallback, NULL); 274 if (!paOperation) 275 { 276 setFailed = true; 277 } 278 279 // Don't need to wait for the completion 280 LATE(pa_operation_unref)(paOperation); 281 282 PaUnLock(); 283 } else 284 { 285 // We have not created a stream or it's not connected to the sink 286 // Save the volume to be set at connection 287 _paSpeakerVolume = volume; 288 } 289 290 if (setFailed) 291 { 292 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 293 " could not set speaker volume, error%d", 294 LATE(pa_context_errno)(_paContext)); 295 296 return -1; 297 } 298 299 return 0; 300 } 301 302 int32_t 303 AudioMixerManagerLinuxPulse::SpeakerVolume(uint32_t& volume) const 304 { 305 306 if (_paOutputDeviceIndex == -1) 307 { 308 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 309 " output device index has not been set"); 310 return -1; 311 } 312 313 if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) 314 != PA_STREAM_UNCONNECTED)) 315 { 316 // We can only get the volume if we have a connected stream 317 if (!GetSinkInputInfo()) 318 return -1; 319 320 volume = static_cast<uint32_t> (_paVolume); 321 ResetCallbackVariables(); 322 } else 323 { 324 volume = _paSpeakerVolume; 325 } 326 327 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 328 " AudioMixerManagerLinuxPulse::SpeakerVolume() => vol=%i", 329 volume); 330 331 return 0; 332 } 333 334 int32_t 335 AudioMixerManagerLinuxPulse::MaxSpeakerVolume(uint32_t& maxVolume) const 336 { 337 338 if (_paOutputDeviceIndex == -1) 339 { 340 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 341 " output device index has not been set"); 342 return -1; 343 } 344 345 // PA_VOLUME_NORM corresponds to 100% (0db) 346 // but PA allows up to 150 db amplification 347 maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM); 348 349 return 0; 350 } 351 352 int32_t 353 AudioMixerManagerLinuxPulse::MinSpeakerVolume(uint32_t& minVolume) const 354 { 355 356 if (_paOutputDeviceIndex == -1) 357 { 358 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 359 " output device index has not been set"); 360 return -1; 361 } 362 363 minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED); 364 365 return 0; 366 } 367 368 int32_t 369 AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize(uint16_t& stepSize) const 370 { 371 372 if (_paOutputDeviceIndex == -1) 373 { 374 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 375 " output device index has not been set"); 376 return -1; 377 } 378 379 // The sink input (stream) will always have step size = 1 380 // There are PA_VOLUME_NORM+1 steps 381 stepSize = 1; 382 383 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 384 " AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize() => " 385 "size=%i, stepSize"); 386 387 // Reset members modified by callback 388 ResetCallbackVariables(); 389 390 return 0; 391 } 392 393 int32_t 394 AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available) 395 { 396 if (_paOutputDeviceIndex == -1) 397 { 398 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 399 " output device index has not been set"); 400 return -1; 401 } 402 403 // Always available in Pulse Audio 404 available = true; 405 406 return 0; 407 } 408 409 int32_t 410 AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available) 411 { 412 if (_paOutputDeviceIndex == -1) 413 { 414 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 415 " output device index has not been set"); 416 return -1; 417 } 418 419 // Always available in Pulse Audio 420 available = true; 421 422 return 0; 423 } 424 425 int32_t AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable) 426 { 427 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 428 "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable=%u)", 429 enable); 430 431 CriticalSectionScoped lock(&_critSect); 432 433 if (_paOutputDeviceIndex == -1) 434 { 435 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 436 " output device index has not been set"); 437 return -1; 438 } 439 440 bool setFailed(false); 441 442 if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) 443 != PA_STREAM_UNCONNECTED)) 444 { 445 // We can only really mute if we have a connected stream 446 PaLock(); 447 448 pa_operation* paOperation = NULL; 449 paOperation = LATE(pa_context_set_sink_input_mute)( 450 _paContext, 451 LATE(pa_stream_get_index)(_paPlayStream), 452 (int) enable, 453 PaSetVolumeCallback, 454 NULL); 455 if (!paOperation) 456 { 457 setFailed = true; 458 } 459 460 // Don't need to wait for the completion 461 LATE(pa_operation_unref)(paOperation); 462 463 PaUnLock(); 464 } else 465 { 466 // We have not created a stream or it's not connected to the sink 467 // Save the mute status to be set at connection 468 _paSpeakerMute = enable; 469 } 470 471 if (setFailed) 472 { 473 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 474 " could not mute speaker, error%d", 475 LATE(pa_context_errno)(_paContext)); 476 return -1; 477 } 478 479 return 0; 480 } 481 482 int32_t AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const 483 { 484 485 if (_paOutputDeviceIndex == -1) 486 { 487 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 488 " output device index has not been set"); 489 return -1; 490 } 491 492 if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) 493 != PA_STREAM_UNCONNECTED)) 494 { 495 // We can only get the mute status if we have a connected stream 496 if (!GetSinkInputInfo()) 497 return -1; 498 499 enabled = static_cast<bool> (_paMute); 500 ResetCallbackVariables(); 501 } else 502 { 503 enabled = _paSpeakerMute; 504 } 505 506 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 507 " AudioMixerManagerLinuxPulse::SpeakerMute() => " 508 "enabled=%i, enabled"); 509 510 return 0; 511 } 512 513 int32_t 514 AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available) 515 { 516 if (_paOutputDeviceIndex == -1) 517 { 518 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 519 " output device index has not been set"); 520 return -1; 521 } 522 523 uint32_t deviceIndex = (uint32_t) _paOutputDeviceIndex; 524 525 PaLock(); 526 527 // Get the actual stream device index if we have a connected stream 528 // The device used by the stream can be changed 529 // during the call 530 if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) 531 != PA_STREAM_UNCONNECTED)) 532 { 533 deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream); 534 } 535 536 PaUnLock(); 537 538 if (!GetSinkInfoByIndex(deviceIndex)) 539 return -1; 540 541 available = static_cast<bool> (_paChannels == 2); 542 543 // Reset members modified by callback 544 ResetCallbackVariables(); 545 546 return 0; 547 } 548 549 int32_t 550 AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(bool& available) 551 { 552 if (_paInputDeviceIndex == -1) 553 { 554 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 555 " input device index has not been set"); 556 return -1; 557 } 558 559 uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; 560 561 PaLock(); 562 563 // Get the actual stream device index if we have a connected stream 564 // The device used by the stream can be changed 565 // during the call 566 if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) 567 != PA_STREAM_UNCONNECTED)) 568 { 569 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); 570 } 571 572 pa_operation* paOperation = NULL; 573 ResetCallbackVariables(); 574 575 // Get info for this source 576 // We want to know if the actual device can record in stereo 577 paOperation = LATE(pa_context_get_source_info_by_index)( 578 _paContext, deviceIndex, 579 PaSourceInfoCallback, 580 (void*) this); 581 582 WaitForOperationCompletion(paOperation); 583 PaUnLock(); 584 585 if (!_callbackValues) 586 { 587 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 588 "Error getting number of input channels: %d", 589 LATE(pa_context_errno)(_paContext)); 590 return -1; 591 } 592 593 available = static_cast<bool> (_paChannels == 2); 594 595 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 596 " AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()" 597 " => available=%i, available"); 598 599 // Reset members modified by callback 600 ResetCallbackVariables(); 601 602 return 0; 603 } 604 605 int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable( 606 bool& available) 607 { 608 if (_paInputDeviceIndex == -1) 609 { 610 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 611 " input device index has not been set"); 612 return -1; 613 } 614 615 // Always available in Pulse Audio 616 available = true; 617 618 return 0; 619 } 620 621 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable) 622 { 623 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 624 "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=%u)", 625 enable); 626 627 CriticalSectionScoped lock(&_critSect); 628 629 if (_paInputDeviceIndex == -1) 630 { 631 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 632 " input device index has not been set"); 633 return -1; 634 } 635 636 bool setFailed(false); 637 pa_operation* paOperation = NULL; 638 ResetCallbackVariables(); 639 640 uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; 641 642 PaLock(); 643 644 // Get the actual stream device index if we have a connected stream 645 // The device used by the stream can be changed 646 // during the call 647 if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) 648 != PA_STREAM_UNCONNECTED)) 649 { 650 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); 651 } 652 653 // Set mute switch for the source 654 paOperation = LATE(pa_context_set_source_mute_by_index)( 655 _paContext, deviceIndex, 656 enable, 657 PaSetVolumeCallback, NULL); 658 659 if (!paOperation) 660 { 661 setFailed = true; 662 } 663 664 // Don't need to wait for this to complete. 665 LATE(pa_operation_unref)(paOperation); 666 667 PaUnLock(); 668 669 // Reset variables altered by callback 670 ResetCallbackVariables(); 671 672 if (setFailed) 673 { 674 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 675 " could not mute microphone, error%d", 676 LATE(pa_context_errno)(_paContext)); 677 return -1; 678 } 679 680 return 0; 681 } 682 683 int32_t AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const 684 { 685 686 if (_paInputDeviceIndex == -1) 687 { 688 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 689 " input device index has not been set"); 690 return -1; 691 } 692 693 uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; 694 695 PaLock(); 696 697 // Get the actual stream device index if we have a connected stream 698 // The device used by the stream can be changed 699 // during the call 700 if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) 701 != PA_STREAM_UNCONNECTED)) 702 { 703 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); 704 } 705 706 PaUnLock(); 707 708 if (!GetSourceInfoByIndex(deviceIndex)) 709 return -1; 710 711 enabled = static_cast<bool> (_paMute); 712 713 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 714 " AudioMixerManagerLinuxPulse::MicrophoneMute() =>" 715 " enabled=%i, enabled"); 716 717 // Reset members modified by callback 718 ResetCallbackVariables(); 719 720 return 0; 721 } 722 723 int32_t 724 AudioMixerManagerLinuxPulse::MicrophoneBoostIsAvailable(bool& available) 725 { 726 if (_paInputDeviceIndex == -1) 727 { 728 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 729 " input device index has not been set"); 730 return -1; 731 } 732 733 // Always unavailable in Pulse Audio 734 // Could make it possible to use PA_VOLUME_MAX 735 // but that gives bad audio with some sound cards 736 available = false; 737 738 return 0; 739 } 740 741 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneBoost(bool enable) 742 { 743 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 744 "AudioMixerManagerLinuxPulse::SetMicrophoneBoost(enable=%u)", 745 enable); 746 747 CriticalSectionScoped lock(&_critSect); 748 749 if (_paInputDeviceIndex == -1) 750 { 751 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 752 " input device index has not been set"); 753 return -1; 754 } 755 756 // Ensure that the selected microphone destination has a valid boost control 757 bool available(false); 758 MicrophoneBoostIsAvailable(available); 759 if (!available) 760 { 761 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 762 " it is not possible to enable microphone boost"); 763 return -1; 764 } 765 766 // It is assumed that the call above fails! 767 768 return 0; 769 } 770 771 int32_t AudioMixerManagerLinuxPulse::MicrophoneBoost(bool& enabled) const 772 { 773 774 if (_paInputDeviceIndex == -1) 775 { 776 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 777 " input device index has not been set"); 778 return -1; 779 } 780 781 // Microphone boost cannot be enabled on this platform! 782 enabled = false; 783 784 return 0; 785 } 786 787 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable( 788 bool& available) 789 { 790 if (_paInputDeviceIndex == -1) 791 { 792 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 793 " input device index has not been set"); 794 return -1; 795 } 796 797 // Always available in Pulse Audio 798 available = true; 799 800 return 0; 801 } 802 803 int32_t 804 AudioMixerManagerLinuxPulse::SetMicrophoneVolume(uint32_t volume) 805 { 806 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 807 "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=%u)", 808 volume); 809 810 CriticalSectionScoped lock(&_critSect); 811 812 if (_paInputDeviceIndex == -1) 813 { 814 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 815 " input device index has not been set"); 816 return -1; 817 } 818 819 // Unlike output streams, input streams have no concept of a stream volume, 820 // only a device volume. So we have to change the volume of the device 821 // itself. 822 823 // The device may have a different number of channels than the stream and 824 // their mapping may be different, so we don't want to use the channel count 825 // from our sample spec. We could use PA_CHANNELS_MAX to cover our bases, 826 // and the server allows that even if the device's channel count is lower, 827 // but some buggy PA clients don't like that (the pavucontrol on Hardy dies 828 // in an assert if the channel count is different). So instead we look up 829 // the actual number of channels that the device has. 830 831 uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; 832 833 PaLock(); 834 835 // Get the actual stream device index if we have a connected stream 836 // The device used by the stream can be changed 837 // during the call 838 if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) 839 != PA_STREAM_UNCONNECTED)) 840 { 841 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); 842 } 843 844 bool setFailed(false); 845 pa_operation* paOperation = NULL; 846 ResetCallbackVariables(); 847 848 // Get the number of channels for this source 849 paOperation 850 = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, 851 PaSourceInfoCallback, 852 (void*) this); 853 854 WaitForOperationCompletion(paOperation); 855 856 if (!_callbackValues) 857 { 858 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 859 "Error getting input channels: %d", 860 LATE(pa_context_errno)(_paContext)); 861 PaUnLock(); 862 return -1; 863 } 864 865 uint8_t channels = _paChannels; 866 ResetCallbackVariables(); 867 868 pa_cvolume cVolumes; 869 LATE(pa_cvolume_set)(&cVolumes, channels, volume); 870 871 // Set the volume for the source 872 paOperation 873 = LATE(pa_context_set_source_volume_by_index)(_paContext, deviceIndex, 874 &cVolumes, 875 PaSetVolumeCallback, NULL); 876 877 if (!paOperation) 878 { 879 setFailed = true; 880 } 881 882 // Don't need to wait for this to complete. 883 LATE(pa_operation_unref)(paOperation); 884 885 PaUnLock(); 886 887 // Reset variables altered by callback 888 ResetCallbackVariables(); 889 890 if (setFailed) 891 { 892 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 893 " could not set microphone volume, error%d", 894 LATE(pa_context_errno)(_paContext)); 895 return -1; 896 } 897 898 return 0; 899 } 900 901 int32_t 902 AudioMixerManagerLinuxPulse::MicrophoneVolume(uint32_t& volume) const 903 { 904 905 if (_paInputDeviceIndex == -1) 906 { 907 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 908 " input device index has not been set"); 909 return -1; 910 } 911 912 uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; 913 914 PaLock(); 915 916 // Get the actual stream device index if we have a connected stream 917 // The device used by the stream can be changed 918 // during the call 919 if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) 920 != PA_STREAM_UNCONNECTED)) 921 { 922 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); 923 } 924 925 PaUnLock(); 926 927 if (!GetSourceInfoByIndex(deviceIndex)) 928 return -1; 929 930 volume = static_cast<uint32_t> (_paVolume); 931 932 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 933 " AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=%i, volume"); 934 935 // Reset members modified by callback 936 ResetCallbackVariables(); 937 938 return 0; 939 } 940 941 int32_t 942 AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(uint32_t& maxVolume) const 943 { 944 945 if (_paInputDeviceIndex == -1) 946 { 947 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 948 " input device index has not been set"); 949 return -1; 950 } 951 952 // PA_VOLUME_NORM corresponds to 100% (0db) 953 // PA allows up to 150 db amplification (PA_VOLUME_MAX) 954 // but that doesn't work well for all sound cards 955 maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM); 956 957 return 0; 958 } 959 960 int32_t 961 AudioMixerManagerLinuxPulse::MinMicrophoneVolume(uint32_t& minVolume) const 962 { 963 964 if (_paInputDeviceIndex == -1) 965 { 966 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 967 " input device index has not been set"); 968 return -1; 969 } 970 971 minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED); 972 973 return 0; 974 } 975 976 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize( 977 uint16_t& stepSize) const 978 { 979 980 if (_paInputDeviceIndex == -1) 981 { 982 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 983 " input device index has not been set"); 984 return -1; 985 } 986 987 uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; 988 989 PaLock(); 990 991 // Get the actual stream device index if we have a connected stream 992 // The device used by the stream can be changed 993 // during the call 994 if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) 995 != PA_STREAM_UNCONNECTED)) 996 { 997 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); 998 } 999 1000 pa_operation* paOperation = NULL; 1001 ResetCallbackVariables(); 1002 1003 // Get info for this source 1004 paOperation 1005 = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, 1006 PaSourceInfoCallback, 1007 (void*) this); 1008 1009 WaitForOperationCompletion(paOperation); 1010 1011 PaUnLock(); 1012 1013 if (!_callbackValues) 1014 { 1015 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1016 "Error getting step size: %d", 1017 LATE(pa_context_errno)(_paContext)); 1018 return -1; 1019 } 1020 1021 stepSize = static_cast<uint16_t> ((PA_VOLUME_NORM + 1) / _paVolSteps); 1022 1023 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1024 " AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize()" 1025 " => size=%i, stepSize"); 1026 1027 // Reset members modified by callback 1028 ResetCallbackVariables(); 1029 1030 return 0; 1031 } 1032 1033 // ============================================================================ 1034 // Private Methods 1035 // ============================================================================ 1036 1037 void AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, 1038 const pa_sink_info *i, 1039 int eol, void *pThis) 1040 { 1041 static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> PaSinkInfoCallbackHandler( 1042 i, eol); 1043 } 1044 1045 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback( 1046 pa_context */*c*/, 1047 const pa_sink_input_info *i, 1048 int eol, void *pThis) 1049 { 1050 static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> 1051 PaSinkInputInfoCallbackHandler(i, eol); 1052 } 1053 1054 1055 void AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, 1056 const pa_source_info *i, 1057 int eol, void *pThis) 1058 { 1059 static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> 1060 PaSourceInfoCallbackHandler(i, eol); 1061 } 1062 1063 void AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context * c, 1064 int success, void */*pThis*/) 1065 { 1066 if (!success) 1067 { 1068 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, 1069 " failed to set volume"); 1070 } 1071 } 1072 1073 void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler( 1074 const pa_sink_info *i, 1075 int eol) 1076 { 1077 if (eol) 1078 { 1079 // Signal that we are done 1080 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1081 return; 1082 } 1083 1084 _callbackValues = true; 1085 _paChannels = i->channel_map.channels; // Get number of channels 1086 pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. 1087 for (int j = 0; j < _paChannels; ++j) 1088 { 1089 if (paVolume < i->volume.values[j]) 1090 { 1091 paVolume = i->volume.values[j]; 1092 } 1093 } 1094 _paVolume = paVolume; // get the max volume for any channel 1095 _paMute = i->mute; // get mute status 1096 1097 // supported since PA 0.9.15 1098 //_paVolSteps = i->n_volume_steps; // get the number of volume steps 1099 // default value is PA_VOLUME_NORM+1 1100 _paVolSteps = PA_VOLUME_NORM + 1; 1101 } 1102 1103 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler( 1104 const pa_sink_input_info *i, 1105 int eol) 1106 { 1107 if (eol) 1108 { 1109 // Signal that we are done 1110 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1111 return; 1112 } 1113 1114 _callbackValues = true; 1115 _paChannels = i->channel_map.channels; // Get number of channels 1116 pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. 1117 for (int j = 0; j < _paChannels; ++j) 1118 { 1119 if (paVolume < i->volume.values[j]) 1120 { 1121 paVolume = i->volume.values[j]; 1122 } 1123 } 1124 _paVolume = paVolume; // Get the max volume for any channel 1125 _paMute = i->mute; // Get mute status 1126 } 1127 1128 void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler( 1129 const pa_source_info *i, 1130 int eol) 1131 { 1132 if (eol) 1133 { 1134 // Signal that we are done 1135 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); 1136 return; 1137 } 1138 1139 _callbackValues = true; 1140 _paChannels = i->channel_map.channels; // Get number of channels 1141 pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. 1142 for (int j = 0; j < _paChannels; ++j) 1143 { 1144 if (paVolume < i->volume.values[j]) 1145 { 1146 paVolume = i->volume.values[j]; 1147 } 1148 } 1149 _paVolume = paVolume; // Get the max volume for any channel 1150 _paMute = i->mute; // Get mute status 1151 1152 // supported since PA 0.9.15 1153 //_paVolSteps = i->n_volume_steps; // Get the number of volume steps 1154 // default value is PA_VOLUME_NORM+1 1155 _paVolSteps = PA_VOLUME_NORM + 1; 1156 } 1157 1158 void AudioMixerManagerLinuxPulse::ResetCallbackVariables() const 1159 { 1160 _paVolume = 0; 1161 _paMute = 0; 1162 _paVolSteps = 0; 1163 _paChannels = 0; 1164 _callbackValues = false; 1165 } 1166 1167 void AudioMixerManagerLinuxPulse::WaitForOperationCompletion( 1168 pa_operation* paOperation) const 1169 { 1170 while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) 1171 { 1172 LATE(pa_threaded_mainloop_wait)(_paMainloop); 1173 } 1174 1175 LATE(pa_operation_unref)(paOperation); 1176 } 1177 1178 void AudioMixerManagerLinuxPulse::PaLock() const 1179 { 1180 LATE(pa_threaded_mainloop_lock)(_paMainloop); 1181 } 1182 1183 void AudioMixerManagerLinuxPulse::PaUnLock() const 1184 { 1185 LATE(pa_threaded_mainloop_unlock)(_paMainloop); 1186 } 1187 1188 bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const { 1189 pa_operation* paOperation = NULL; 1190 ResetCallbackVariables(); 1191 1192 PaLock(); 1193 for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues; 1194 retries ++) { 1195 // Get info for this stream (sink input). 1196 paOperation = LATE(pa_context_get_sink_input_info)( 1197 _paContext, 1198 LATE(pa_stream_get_index)(_paPlayStream), 1199 PaSinkInputInfoCallback, 1200 (void*) this); 1201 1202 WaitForOperationCompletion(paOperation); 1203 } 1204 PaUnLock(); 1205 1206 if (!_callbackValues) { 1207 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1208 "GetSinkInputInfo failed to get volume info : %d", 1209 LATE(pa_context_errno)(_paContext)); 1210 return false; 1211 } 1212 1213 return true; 1214 } 1215 1216 bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex( 1217 int device_index) const { 1218 pa_operation* paOperation = NULL; 1219 ResetCallbackVariables(); 1220 1221 PaLock(); 1222 for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues; 1223 retries ++) { 1224 paOperation = LATE(pa_context_get_sink_info_by_index)(_paContext, 1225 device_index, PaSinkInfoCallback, (void*) this); 1226 1227 WaitForOperationCompletion(paOperation); 1228 } 1229 PaUnLock(); 1230 1231 if (!_callbackValues) { 1232 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1233 "GetSinkInfoByIndex failed to get volume info: %d", 1234 LATE(pa_context_errno)(_paContext)); 1235 return false; 1236 } 1237 1238 return true; 1239 } 1240 1241 bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex( 1242 int device_index) const { 1243 pa_operation* paOperation = NULL; 1244 ResetCallbackVariables(); 1245 1246 PaLock(); 1247 for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues; 1248 retries ++) { 1249 paOperation = LATE(pa_context_get_source_info_by_index)( 1250 _paContext, device_index, PaSourceInfoCallback, (void*) this); 1251 1252 WaitForOperationCompletion(paOperation); 1253 } 1254 1255 PaUnLock(); 1256 1257 if (!_callbackValues) { 1258 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1259 "GetSourceInfoByIndex error: %d", 1260 LATE(pa_context_errno)(_paContext)); 1261 return false; 1262 } 1263 1264 return true; 1265 } 1266 1267 } 1268