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