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