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