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