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 16extern 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 24namespace webrtc 25{ 26 27enum { kMaxRetryOnFailure = 2 }; 28 29AudioMixerManagerLinuxPulse::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 51AudioMixerManagerLinuxPulse::~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 65int32_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 91int32_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 109int32_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 123int32_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 137int32_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 147int32_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 157int32_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 185int32_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 213bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const 214{ 215 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", 216 __FUNCTION__); 217 218 return (_paOutputDeviceIndex != -1); 219} 220 221bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const 222{ 223 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", 224 __FUNCTION__); 225 226 return (_paInputDeviceIndex != -1); 227} 228 229int32_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 302int32_t 303AudioMixerManagerLinuxPulse::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 334int32_t 335AudioMixerManagerLinuxPulse::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 352int32_t 353AudioMixerManagerLinuxPulse::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 368int32_t 369AudioMixerManagerLinuxPulse::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 393int32_t 394AudioMixerManagerLinuxPulse::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 409int32_t 410AudioMixerManagerLinuxPulse::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 425int32_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 482int32_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 513int32_t 514AudioMixerManagerLinuxPulse::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 549int32_t 550AudioMixerManagerLinuxPulse::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 605int32_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 621int32_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 683int32_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 723int32_t 724AudioMixerManagerLinuxPulse::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 741int32_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 771int32_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 787int32_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 803int32_t 804AudioMixerManagerLinuxPulse::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 901int32_t 902AudioMixerManagerLinuxPulse::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 941int32_t 942AudioMixerManagerLinuxPulse::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 960int32_t 961AudioMixerManagerLinuxPulse::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 976int32_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 1037void 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 1045void 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 1055void 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 1063void 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 1073void 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 1103void 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 1128void 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 1158void AudioMixerManagerLinuxPulse::ResetCallbackVariables() const 1159{ 1160 _paVolume = 0; 1161 _paMute = 0; 1162 _paVolSteps = 0; 1163 _paChannels = 0; 1164 _callbackValues = false; 1165} 1166 1167void 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 1178void AudioMixerManagerLinuxPulse::PaLock() const 1179{ 1180 LATE(pa_threaded_mainloop_lock)(_paMainloop); 1181} 1182 1183void AudioMixerManagerLinuxPulse::PaUnLock() const 1184{ 1185 LATE(pa_threaded_mainloop_unlock)(_paMainloop); 1186} 1187 1188bool 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 1216bool 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 1241bool 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