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 "modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
12
13 #include <stddef.h>
14
15 #include "modules/audio_device/linux/audio_device_pulse_linux.h"
16 #include "modules/audio_device/linux/latebindingsymboltable_linux.h"
17 #include "modules/audio_device/linux/pulseaudiosymboltable_linux.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20
21 // Accesses Pulse functions through our late-binding symbol table instead of
22 // directly. This way we don't have to link to libpulse, which means our binary
23 // will work on systems that don't have it.
24 #define LATE(sym) \
25 LATESYM_GET(webrtc::adm_linux_pulse::PulseAudioSymbolTable, \
26 GetPulseSymbolTable(), sym)
27
28 namespace webrtc {
29
30 class AutoPulseLock {
31 public:
AutoPulseLock(pa_threaded_mainloop * pa_mainloop)32 explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
33 : pa_mainloop_(pa_mainloop) {
34 LATE(pa_threaded_mainloop_lock)(pa_mainloop_);
35 }
36
~AutoPulseLock()37 ~AutoPulseLock() { LATE(pa_threaded_mainloop_unlock)(pa_mainloop_); }
38
39 private:
40 pa_threaded_mainloop* const pa_mainloop_;
41 };
42
AudioMixerManagerLinuxPulse()43 AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse()
44 : _paOutputDeviceIndex(-1),
45 _paInputDeviceIndex(-1),
46 _paPlayStream(NULL),
47 _paRecStream(NULL),
48 _paMainloop(NULL),
49 _paContext(NULL),
50 _paVolume(0),
51 _paMute(0),
52 _paVolSteps(0),
53 _paSpeakerMute(false),
54 _paSpeakerVolume(PA_VOLUME_NORM),
55 _paChannels(0),
56 _paObjectsSet(false) {
57 RTC_LOG(LS_INFO) << __FUNCTION__ << " created";
58 }
59
~AudioMixerManagerLinuxPulse()60 AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse() {
61 RTC_DCHECK(thread_checker_.IsCurrent());
62 RTC_LOG(LS_INFO) << __FUNCTION__ << " destroyed";
63
64 Close();
65 }
66
67 // ===========================================================================
68 // PUBLIC METHODS
69 // ===========================================================================
70
SetPulseAudioObjects(pa_threaded_mainloop * mainloop,pa_context * context)71 int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects(
72 pa_threaded_mainloop* mainloop,
73 pa_context* context) {
74 RTC_DCHECK(thread_checker_.IsCurrent());
75 RTC_LOG(LS_VERBOSE) << __FUNCTION__;
76
77 if (!mainloop || !context) {
78 RTC_LOG(LS_ERROR) << "could not set PulseAudio objects for mixer";
79 return -1;
80 }
81
82 _paMainloop = mainloop;
83 _paContext = context;
84 _paObjectsSet = true;
85
86 RTC_LOG(LS_VERBOSE) << "the PulseAudio objects for the mixer has been set";
87
88 return 0;
89 }
90
Close()91 int32_t AudioMixerManagerLinuxPulse::Close() {
92 RTC_DCHECK(thread_checker_.IsCurrent());
93 RTC_LOG(LS_VERBOSE) << __FUNCTION__;
94
95 CloseSpeaker();
96 CloseMicrophone();
97
98 _paMainloop = NULL;
99 _paContext = NULL;
100 _paObjectsSet = false;
101
102 return 0;
103 }
104
CloseSpeaker()105 int32_t AudioMixerManagerLinuxPulse::CloseSpeaker() {
106 RTC_DCHECK(thread_checker_.IsCurrent());
107 RTC_LOG(LS_VERBOSE) << __FUNCTION__;
108
109 // Reset the index to -1
110 _paOutputDeviceIndex = -1;
111 _paPlayStream = NULL;
112
113 return 0;
114 }
115
CloseMicrophone()116 int32_t AudioMixerManagerLinuxPulse::CloseMicrophone() {
117 RTC_DCHECK(thread_checker_.IsCurrent());
118 RTC_LOG(LS_VERBOSE) << __FUNCTION__;
119
120 // Reset the index to -1
121 _paInputDeviceIndex = -1;
122 _paRecStream = NULL;
123
124 return 0;
125 }
126
SetPlayStream(pa_stream * playStream)127 int32_t AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream) {
128 RTC_DCHECK(thread_checker_.IsCurrent());
129 RTC_LOG(LS_VERBOSE)
130 << "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)";
131
132 _paPlayStream = playStream;
133 return 0;
134 }
135
SetRecStream(pa_stream * recStream)136 int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream) {
137 RTC_DCHECK(thread_checker_.IsCurrent());
138 RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetRecStream(recStream)";
139
140 _paRecStream = recStream;
141 return 0;
142 }
143
OpenSpeaker(uint16_t deviceIndex)144 int32_t AudioMixerManagerLinuxPulse::OpenSpeaker(uint16_t deviceIndex) {
145 RTC_DCHECK(thread_checker_.IsCurrent());
146 RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex="
147 << deviceIndex << ")";
148
149 // No point in opening the speaker
150 // if PA objects have not been set
151 if (!_paObjectsSet) {
152 RTC_LOG(LS_ERROR) << "PulseAudio objects has not been set";
153 return -1;
154 }
155
156 // Set the index for the PulseAudio
157 // output device to control
158 _paOutputDeviceIndex = deviceIndex;
159
160 RTC_LOG(LS_VERBOSE) << "the output mixer device is now open";
161
162 return 0;
163 }
164
OpenMicrophone(uint16_t deviceIndex)165 int32_t AudioMixerManagerLinuxPulse::OpenMicrophone(uint16_t deviceIndex) {
166 RTC_DCHECK(thread_checker_.IsCurrent());
167 RTC_LOG(LS_VERBOSE)
168 << "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex="
169 << deviceIndex << ")";
170
171 // No point in opening the microphone
172 // if PA objects have not been set
173 if (!_paObjectsSet) {
174 RTC_LOG(LS_ERROR) << "PulseAudio objects have not been set";
175 return -1;
176 }
177
178 // Set the index for the PulseAudio
179 // input device to control
180 _paInputDeviceIndex = deviceIndex;
181
182 RTC_LOG(LS_VERBOSE) << "the input mixer device is now open";
183
184 return 0;
185 }
186
SpeakerIsInitialized() const187 bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const {
188 RTC_DCHECK(thread_checker_.IsCurrent());
189 RTC_LOG(LS_INFO) << __FUNCTION__;
190
191 return (_paOutputDeviceIndex != -1);
192 }
193
MicrophoneIsInitialized() const194 bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const {
195 RTC_DCHECK(thread_checker_.IsCurrent());
196 RTC_LOG(LS_INFO) << __FUNCTION__;
197
198 return (_paInputDeviceIndex != -1);
199 }
200
SetSpeakerVolume(uint32_t volume)201 int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume(uint32_t volume) {
202 RTC_DCHECK(thread_checker_.IsCurrent());
203 RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume="
204 << volume << ")";
205
206 if (_paOutputDeviceIndex == -1) {
207 RTC_LOG(LS_WARNING) << "output device index has not been set";
208 return -1;
209 }
210
211 bool setFailed(false);
212
213 if (_paPlayStream &&
214 (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
215 // We can only really set the volume if we have a connected stream
216 AutoPulseLock auto_lock(_paMainloop);
217
218 // Get the number of channels from the sample specification
219 const pa_sample_spec* spec = LATE(pa_stream_get_sample_spec)(_paPlayStream);
220 if (!spec) {
221 RTC_LOG(LS_ERROR) << "could not get sample specification";
222 return -1;
223 }
224
225 // Set the same volume for all channels
226 pa_cvolume cVolumes;
227 LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume);
228
229 pa_operation* paOperation = NULL;
230 paOperation = LATE(pa_context_set_sink_input_volume)(
231 _paContext, LATE(pa_stream_get_index)(_paPlayStream), &cVolumes,
232 PaSetVolumeCallback, NULL);
233 if (!paOperation) {
234 setFailed = true;
235 }
236
237 // Don't need to wait for the completion
238 LATE(pa_operation_unref)(paOperation);
239 } else {
240 // We have not created a stream or it's not connected to the sink
241 // Save the volume to be set at connection
242 _paSpeakerVolume = volume;
243 }
244
245 if (setFailed) {
246 RTC_LOG(LS_WARNING) << "could not set speaker volume, error="
247 << LATE(pa_context_errno)(_paContext);
248
249 return -1;
250 }
251
252 return 0;
253 }
254
SpeakerVolume(uint32_t & volume) const255 int32_t AudioMixerManagerLinuxPulse::SpeakerVolume(uint32_t& volume) const {
256 if (_paOutputDeviceIndex == -1) {
257 RTC_LOG(LS_WARNING) << "output device index has not been set";
258 return -1;
259 }
260
261 if (_paPlayStream &&
262 (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
263 // We can only get the volume if we have a connected stream
264 if (!GetSinkInputInfo())
265 return -1;
266
267 AutoPulseLock auto_lock(_paMainloop);
268 volume = static_cast<uint32_t>(_paVolume);
269 } else {
270 AutoPulseLock auto_lock(_paMainloop);
271 volume = _paSpeakerVolume;
272 }
273
274 RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SpeakerVolume() => vol="
275 << volume;
276
277 return 0;
278 }
279
MaxSpeakerVolume(uint32_t & maxVolume) const280 int32_t AudioMixerManagerLinuxPulse::MaxSpeakerVolume(
281 uint32_t& maxVolume) const {
282 if (_paOutputDeviceIndex == -1) {
283 RTC_LOG(LS_WARNING) << "output device index has not been set";
284 return -1;
285 }
286
287 // PA_VOLUME_NORM corresponds to 100% (0db)
288 // but PA allows up to 150 db amplification
289 maxVolume = static_cast<uint32_t>(PA_VOLUME_NORM);
290
291 return 0;
292 }
293
MinSpeakerVolume(uint32_t & minVolume) const294 int32_t AudioMixerManagerLinuxPulse::MinSpeakerVolume(
295 uint32_t& minVolume) const {
296 if (_paOutputDeviceIndex == -1) {
297 RTC_LOG(LS_WARNING) << "output device index has not been set";
298 return -1;
299 }
300
301 minVolume = static_cast<uint32_t>(PA_VOLUME_MUTED);
302
303 return 0;
304 }
305
SpeakerVolumeIsAvailable(bool & available)306 int32_t AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available) {
307 RTC_DCHECK(thread_checker_.IsCurrent());
308 if (_paOutputDeviceIndex == -1) {
309 RTC_LOG(LS_WARNING) << "output device index has not been set";
310 return -1;
311 }
312
313 // Always available in Pulse Audio
314 available = true;
315
316 return 0;
317 }
318
SpeakerMuteIsAvailable(bool & available)319 int32_t AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available) {
320 RTC_DCHECK(thread_checker_.IsCurrent());
321 if (_paOutputDeviceIndex == -1) {
322 RTC_LOG(LS_WARNING) << "output device index has not been set";
323 return -1;
324 }
325
326 // Always available in Pulse Audio
327 available = true;
328
329 return 0;
330 }
331
SetSpeakerMute(bool enable)332 int32_t AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable) {
333 RTC_DCHECK(thread_checker_.IsCurrent());
334 RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable="
335 << enable << ")";
336
337 if (_paOutputDeviceIndex == -1) {
338 RTC_LOG(LS_WARNING) << "output device index has not been set";
339 return -1;
340 }
341
342 bool setFailed(false);
343
344 if (_paPlayStream &&
345 (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
346 // We can only really mute if we have a connected stream
347 AutoPulseLock auto_lock(_paMainloop);
348
349 pa_operation* paOperation = NULL;
350 paOperation = LATE(pa_context_set_sink_input_mute)(
351 _paContext, LATE(pa_stream_get_index)(_paPlayStream), (int)enable,
352 PaSetVolumeCallback, NULL);
353 if (!paOperation) {
354 setFailed = true;
355 }
356
357 // Don't need to wait for the completion
358 LATE(pa_operation_unref)(paOperation);
359 } else {
360 // We have not created a stream or it's not connected to the sink
361 // Save the mute status to be set at connection
362 _paSpeakerMute = enable;
363 }
364
365 if (setFailed) {
366 RTC_LOG(LS_WARNING) << "could not mute speaker, error="
367 << LATE(pa_context_errno)(_paContext);
368 return -1;
369 }
370
371 return 0;
372 }
373
SpeakerMute(bool & enabled) const374 int32_t AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const {
375 if (_paOutputDeviceIndex == -1) {
376 RTC_LOG(LS_WARNING) << "output device index has not been set";
377 return -1;
378 }
379
380 if (_paPlayStream &&
381 (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
382 // We can only get the mute status if we have a connected stream
383 if (!GetSinkInputInfo())
384 return -1;
385
386 enabled = static_cast<bool>(_paMute);
387 } else {
388 enabled = _paSpeakerMute;
389 }
390 RTC_LOG(LS_VERBOSE)
391 << "AudioMixerManagerLinuxPulse::SpeakerMute() => enabled=" << enabled;
392
393 return 0;
394 }
395
StereoPlayoutIsAvailable(bool & available)396 int32_t AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available) {
397 RTC_DCHECK(thread_checker_.IsCurrent());
398 if (_paOutputDeviceIndex == -1) {
399 RTC_LOG(LS_WARNING) << "output device index has not been set";
400 return -1;
401 }
402
403 uint32_t deviceIndex = (uint32_t)_paOutputDeviceIndex;
404
405 {
406 AutoPulseLock auto_lock(_paMainloop);
407
408 // Get the actual stream device index if we have a connected stream
409 // The device used by the stream can be changed
410 // during the call
411 if (_paPlayStream &&
412 (LATE(pa_stream_get_state)(_paPlayStream) != PA_STREAM_UNCONNECTED)) {
413 deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream);
414 }
415 }
416
417 if (!GetSinkInfoByIndex(deviceIndex))
418 return -1;
419
420 available = static_cast<bool>(_paChannels == 2);
421
422 return 0;
423 }
424
StereoRecordingIsAvailable(bool & available)425 int32_t AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(
426 bool& available) {
427 RTC_DCHECK(thread_checker_.IsCurrent());
428 if (_paInputDeviceIndex == -1) {
429 RTC_LOG(LS_WARNING) << "input device index has not been set";
430 return -1;
431 }
432
433 uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
434
435 AutoPulseLock auto_lock(_paMainloop);
436
437 // Get the actual stream device index if we have a connected stream
438 // The device used by the stream can be changed
439 // during the call
440 if (_paRecStream &&
441 (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
442 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
443 }
444
445 pa_operation* paOperation = NULL;
446
447 // Get info for this source
448 // We want to know if the actual device can record in stereo
449 paOperation = LATE(pa_context_get_source_info_by_index)(
450 _paContext, deviceIndex, PaSourceInfoCallback, (void*)this);
451
452 WaitForOperationCompletion(paOperation);
453
454 available = static_cast<bool>(_paChannels == 2);
455
456 RTC_LOG(LS_VERBOSE)
457 << "AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()"
458 " => available="
459 << available;
460
461 return 0;
462 }
463
MicrophoneMuteIsAvailable(bool & available)464 int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable(
465 bool& available) {
466 RTC_DCHECK(thread_checker_.IsCurrent());
467 if (_paInputDeviceIndex == -1) {
468 RTC_LOG(LS_WARNING) << "input device index has not been set";
469 return -1;
470 }
471
472 // Always available in Pulse Audio
473 available = true;
474
475 return 0;
476 }
477
SetMicrophoneMute(bool enable)478 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable) {
479 RTC_DCHECK(thread_checker_.IsCurrent());
480 RTC_LOG(LS_VERBOSE)
481 << "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=" << enable
482 << ")";
483
484 if (_paInputDeviceIndex == -1) {
485 RTC_LOG(LS_WARNING) << "input device index has not been set";
486 return -1;
487 }
488
489 bool setFailed(false);
490 pa_operation* paOperation = NULL;
491
492 uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
493
494 AutoPulseLock auto_lock(_paMainloop);
495
496 // Get the actual stream device index if we have a connected stream
497 // The device used by the stream can be changed
498 // during the call
499 if (_paRecStream &&
500 (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
501 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
502 }
503
504 // Set mute switch for the source
505 paOperation = LATE(pa_context_set_source_mute_by_index)(
506 _paContext, deviceIndex, enable, PaSetVolumeCallback, NULL);
507
508 if (!paOperation) {
509 setFailed = true;
510 }
511
512 // Don't need to wait for this to complete.
513 LATE(pa_operation_unref)(paOperation);
514
515 if (setFailed) {
516 RTC_LOG(LS_WARNING) << "could not mute microphone, error="
517 << LATE(pa_context_errno)(_paContext);
518 return -1;
519 }
520
521 return 0;
522 }
523
MicrophoneMute(bool & enabled) const524 int32_t AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const {
525 RTC_DCHECK(thread_checker_.IsCurrent());
526 if (_paInputDeviceIndex == -1) {
527 RTC_LOG(LS_WARNING) << "input device index has not been set";
528 return -1;
529 }
530
531 uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
532
533 {
534 AutoPulseLock auto_lock(_paMainloop);
535 // Get the actual stream device index if we have a connected stream
536 // The device used by the stream can be changed
537 // during the call
538 if (_paRecStream &&
539 (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
540 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
541 }
542 }
543
544 if (!GetSourceInfoByIndex(deviceIndex))
545 return -1;
546
547 enabled = static_cast<bool>(_paMute);
548
549 RTC_LOG(LS_VERBOSE)
550 << "AudioMixerManagerLinuxPulse::MicrophoneMute() => enabled=" << enabled;
551
552 return 0;
553 }
554
MicrophoneVolumeIsAvailable(bool & available)555 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable(
556 bool& available) {
557 RTC_DCHECK(thread_checker_.IsCurrent());
558 if (_paInputDeviceIndex == -1) {
559 RTC_LOG(LS_WARNING) << "input device index has not been set";
560 return -1;
561 }
562
563 // Always available in Pulse Audio
564 available = true;
565
566 return 0;
567 }
568
SetMicrophoneVolume(uint32_t volume)569 int32_t AudioMixerManagerLinuxPulse::SetMicrophoneVolume(uint32_t volume) {
570 RTC_LOG(LS_VERBOSE)
571 << "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=" << volume
572 << ")";
573
574 if (_paInputDeviceIndex == -1) {
575 RTC_LOG(LS_WARNING) << "input device index has not been set";
576 return -1;
577 }
578
579 // Unlike output streams, input streams have no concept of a stream
580 // volume, only a device volume. So we have to change the volume of the
581 // device itself.
582
583 // The device may have a different number of channels than the stream and
584 // their mapping may be different, so we don't want to use the channel
585 // count from our sample spec. We could use PA_CHANNELS_MAX to cover our
586 // bases, and the server allows that even if the device's channel count
587 // is lower, but some buggy PA clients don't like that (the pavucontrol
588 // on Hardy dies in an assert if the channel count is different). So
589 // instead we look up the actual number of channels that the device has.
590 AutoPulseLock auto_lock(_paMainloop);
591 uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
592
593 // Get the actual stream device index if we have a connected stream
594 // The device used by the stream can be changed
595 // during the call
596 if (_paRecStream &&
597 (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
598 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
599 }
600
601 bool setFailed(false);
602 pa_operation* paOperation = NULL;
603
604 // Get the number of channels for this source
605 paOperation = LATE(pa_context_get_source_info_by_index)(
606 _paContext, deviceIndex, PaSourceInfoCallback, (void*)this);
607
608 WaitForOperationCompletion(paOperation);
609
610 uint8_t channels = _paChannels;
611 pa_cvolume cVolumes;
612 LATE(pa_cvolume_set)(&cVolumes, channels, volume);
613
614 // Set the volume for the source
615 paOperation = LATE(pa_context_set_source_volume_by_index)(
616 _paContext, deviceIndex, &cVolumes, PaSetVolumeCallback, NULL);
617
618 if (!paOperation) {
619 setFailed = true;
620 }
621
622 // Don't need to wait for this to complete.
623 LATE(pa_operation_unref)(paOperation);
624
625 if (setFailed) {
626 RTC_LOG(LS_WARNING) << "could not set microphone volume, error="
627 << LATE(pa_context_errno)(_paContext);
628 return -1;
629 }
630
631 return 0;
632 }
633
MicrophoneVolume(uint32_t & volume) const634 int32_t AudioMixerManagerLinuxPulse::MicrophoneVolume(uint32_t& volume) const {
635 if (_paInputDeviceIndex == -1) {
636 RTC_LOG(LS_WARNING) << "input device index has not been set";
637 return -1;
638 }
639
640 uint32_t deviceIndex = (uint32_t)_paInputDeviceIndex;
641
642 {
643 AutoPulseLock auto_lock(_paMainloop);
644 // Get the actual stream device index if we have a connected stream.
645 // The device used by the stream can be changed during the call.
646 if (_paRecStream &&
647 (LATE(pa_stream_get_state)(_paRecStream) != PA_STREAM_UNCONNECTED)) {
648 deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
649 }
650 }
651
652 if (!GetSourceInfoByIndex(deviceIndex))
653 return -1;
654
655 {
656 AutoPulseLock auto_lock(_paMainloop);
657 volume = static_cast<uint32_t>(_paVolume);
658 }
659
660 RTC_LOG(LS_VERBOSE)
661 << "AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=" << volume;
662
663 return 0;
664 }
665
MaxMicrophoneVolume(uint32_t & maxVolume) const666 int32_t AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(
667 uint32_t& maxVolume) const {
668 if (_paInputDeviceIndex == -1) {
669 RTC_LOG(LS_WARNING) << "input device index has not been set";
670 return -1;
671 }
672
673 // PA_VOLUME_NORM corresponds to 100% (0db)
674 // PA allows up to 150 db amplification (PA_VOLUME_MAX)
675 // but that doesn't work well for all sound cards
676 maxVolume = static_cast<uint32_t>(PA_VOLUME_NORM);
677
678 return 0;
679 }
680
MinMicrophoneVolume(uint32_t & minVolume) const681 int32_t AudioMixerManagerLinuxPulse::MinMicrophoneVolume(
682 uint32_t& minVolume) const {
683 if (_paInputDeviceIndex == -1) {
684 RTC_LOG(LS_WARNING) << "input device index has not been set";
685 return -1;
686 }
687
688 minVolume = static_cast<uint32_t>(PA_VOLUME_MUTED);
689
690 return 0;
691 }
692
693 // ===========================================================================
694 // Private Methods
695 // ===========================================================================
696
PaSinkInfoCallback(pa_context *,const pa_sink_info * i,int eol,void * pThis)697 void AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context* /*c*/,
698 const pa_sink_info* i,
699 int eol,
700 void* pThis) {
701 static_cast<AudioMixerManagerLinuxPulse*>(pThis)->PaSinkInfoCallbackHandler(
702 i, eol);
703 }
704
PaSinkInputInfoCallback(pa_context *,const pa_sink_input_info * i,int eol,void * pThis)705 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback(
706 pa_context* /*c*/,
707 const pa_sink_input_info* i,
708 int eol,
709 void* pThis) {
710 static_cast<AudioMixerManagerLinuxPulse*>(pThis)
711 ->PaSinkInputInfoCallbackHandler(i, eol);
712 }
713
PaSourceInfoCallback(pa_context *,const pa_source_info * i,int eol,void * pThis)714 void AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context* /*c*/,
715 const pa_source_info* i,
716 int eol,
717 void* pThis) {
718 static_cast<AudioMixerManagerLinuxPulse*>(pThis)->PaSourceInfoCallbackHandler(
719 i, eol);
720 }
721
PaSetVolumeCallback(pa_context * c,int success,void *)722 void AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context* c,
723 int success,
724 void* /*pThis*/) {
725 if (!success) {
726 RTC_LOG(LS_ERROR) << "failed to set volume";
727 }
728 }
729
PaSinkInfoCallbackHandler(const pa_sink_info * i,int eol)730 void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler(
731 const pa_sink_info* i,
732 int eol) {
733 if (eol) {
734 // Signal that we are done
735 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
736 return;
737 }
738
739 _paChannels = i->channel_map.channels; // Get number of channels
740 pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
741 for (int j = 0; j < _paChannels; ++j) {
742 if (paVolume < i->volume.values[j]) {
743 paVolume = i->volume.values[j];
744 }
745 }
746 _paVolume = paVolume; // get the max volume for any channel
747 _paMute = i->mute; // get mute status
748
749 // supported since PA 0.9.15
750 //_paVolSteps = i->n_volume_steps; // get the number of volume steps
751 // default value is PA_VOLUME_NORM+1
752 _paVolSteps = PA_VOLUME_NORM + 1;
753 }
754
PaSinkInputInfoCallbackHandler(const pa_sink_input_info * i,int eol)755 void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler(
756 const pa_sink_input_info* i,
757 int eol) {
758 if (eol) {
759 // Signal that we are done
760 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
761 return;
762 }
763
764 _paChannels = i->channel_map.channels; // Get number of channels
765 pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
766 for (int j = 0; j < _paChannels; ++j) {
767 if (paVolume < i->volume.values[j]) {
768 paVolume = i->volume.values[j];
769 }
770 }
771 _paVolume = paVolume; // Get the max volume for any channel
772 _paMute = i->mute; // Get mute status
773 }
774
PaSourceInfoCallbackHandler(const pa_source_info * i,int eol)775 void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler(
776 const pa_source_info* i,
777 int eol) {
778 if (eol) {
779 // Signal that we are done
780 LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
781 return;
782 }
783
784 _paChannels = i->channel_map.channels; // Get number of channels
785 pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
786 for (int j = 0; j < _paChannels; ++j) {
787 if (paVolume < i->volume.values[j]) {
788 paVolume = i->volume.values[j];
789 }
790 }
791 _paVolume = paVolume; // Get the max volume for any channel
792 _paMute = i->mute; // Get mute status
793
794 // supported since PA 0.9.15
795 //_paVolSteps = i->n_volume_steps; // Get the number of volume steps
796 // default value is PA_VOLUME_NORM+1
797 _paVolSteps = PA_VOLUME_NORM + 1;
798 }
799
WaitForOperationCompletion(pa_operation * paOperation) const800 void AudioMixerManagerLinuxPulse::WaitForOperationCompletion(
801 pa_operation* paOperation) const {
802 while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) {
803 LATE(pa_threaded_mainloop_wait)(_paMainloop);
804 }
805
806 LATE(pa_operation_unref)(paOperation);
807 }
808
GetSinkInputInfo() const809 bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const {
810 pa_operation* paOperation = NULL;
811
812 AutoPulseLock auto_lock(_paMainloop);
813 // Get info for this stream (sink input).
814 paOperation = LATE(pa_context_get_sink_input_info)(
815 _paContext, LATE(pa_stream_get_index)(_paPlayStream),
816 PaSinkInputInfoCallback, (void*)this);
817
818 WaitForOperationCompletion(paOperation);
819 return true;
820 }
821
GetSinkInfoByIndex(int device_index) const822 bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex(int device_index) const {
823 pa_operation* paOperation = NULL;
824
825 AutoPulseLock auto_lock(_paMainloop);
826 paOperation = LATE(pa_context_get_sink_info_by_index)(
827 _paContext, device_index, PaSinkInfoCallback, (void*)this);
828
829 WaitForOperationCompletion(paOperation);
830 return true;
831 }
832
GetSourceInfoByIndex(int device_index) const833 bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex(int device_index) const {
834 pa_operation* paOperation = NULL;
835
836 AutoPulseLock auto_lock(_paMainloop);
837 paOperation = LATE(pa_context_get_source_info_by_index)(
838 _paContext, device_index, PaSourceInfoCallback, (void*)this);
839
840 WaitForOperationCompletion(paOperation);
841 return true;
842 }
843
844 } // namespace webrtc
845