mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-15 14:50:39 +01:00

In https://webrtc-review.googlesource.com/c/src/+/1560 we moved WebRTC from src/webrtc to src/ (in order to preserve an healthy git history). This CL takes care of fixing header guards, #include paths, etc... NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true TBR=tommi@webrtc.org Bug: chromium:611808 Change-Id: Iea91618212bee0af16aa3f05071eab8f93706578 Reviewed-on: https://webrtc-review.googlesource.com/1561 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Henrik Kjellander <kjellander@webrtc.org> Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#19846}
977 lines
26 KiB
C++
977 lines
26 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/logging.h"
|
|
|
|
extern webrtc::adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable;
|
|
|
|
// Accesses Pulse functions through our late-binding symbol table instead of
|
|
// directly. This way we don't have to link to libpulse, which means our
|
|
// binary will work on systems that don't have it.
|
|
#define LATE(sym) \
|
|
LATESYM_GET(webrtc::adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, \
|
|
sym)
|
|
|
|
namespace webrtc
|
|
{
|
|
|
|
class AutoPulseLock {
|
|
public:
|
|
explicit AutoPulseLock(pa_threaded_mainloop* pa_mainloop)
|
|
: pa_mainloop_(pa_mainloop) {
|
|
LATE(pa_threaded_mainloop_lock)(pa_mainloop_);
|
|
}
|
|
|
|
~AutoPulseLock() {
|
|
LATE(pa_threaded_mainloop_unlock)(pa_mainloop_);
|
|
}
|
|
|
|
private:
|
|
pa_threaded_mainloop* const pa_mainloop_;
|
|
};
|
|
|
|
AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse() :
|
|
_paOutputDeviceIndex(-1),
|
|
_paInputDeviceIndex(-1),
|
|
_paPlayStream(NULL),
|
|
_paRecStream(NULL),
|
|
_paMainloop(NULL),
|
|
_paContext(NULL),
|
|
_paVolume(0),
|
|
_paMute(0),
|
|
_paVolSteps(0),
|
|
_paSpeakerMute(false),
|
|
_paSpeakerVolume(PA_VOLUME_NORM),
|
|
_paChannels(0),
|
|
_paObjectsSet(false)
|
|
{
|
|
LOG(LS_INFO) << __FUNCTION__ << " created";
|
|
}
|
|
|
|
AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse()
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_INFO) << __FUNCTION__ << " destroyed";
|
|
|
|
Close();
|
|
}
|
|
|
|
// ===========================================================================
|
|
// PUBLIC METHODS
|
|
// ===========================================================================
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects(
|
|
pa_threaded_mainloop* mainloop,
|
|
pa_context* context)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << __FUNCTION__;
|
|
|
|
if (!mainloop || !context)
|
|
{
|
|
LOG(LS_ERROR) << "could not set PulseAudio objects for mixer";
|
|
return -1;
|
|
}
|
|
|
|
_paMainloop = mainloop;
|
|
_paContext = context;
|
|
_paObjectsSet = true;
|
|
|
|
LOG(LS_VERBOSE) << "the PulseAudio objects for the mixer has been set";
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::Close()
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << __FUNCTION__;
|
|
|
|
CloseSpeaker();
|
|
CloseMicrophone();
|
|
|
|
_paMainloop = NULL;
|
|
_paContext = NULL;
|
|
_paObjectsSet = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::CloseSpeaker()
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << __FUNCTION__;
|
|
|
|
// Reset the index to -1
|
|
_paOutputDeviceIndex = -1;
|
|
_paPlayStream = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::CloseMicrophone()
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << __FUNCTION__;
|
|
|
|
// Reset the index to -1
|
|
_paInputDeviceIndex = -1;
|
|
_paRecStream = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)";
|
|
|
|
_paPlayStream = playStream;
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetRecStream(recStream)";
|
|
|
|
_paRecStream = recStream;
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::OpenSpeaker(
|
|
uint16_t deviceIndex)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex="
|
|
<< deviceIndex << ")";
|
|
|
|
// No point in opening the speaker
|
|
// if PA objects have not been set
|
|
if (!_paObjectsSet)
|
|
{
|
|
LOG(LS_ERROR) << "PulseAudio objects has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Set the index for the PulseAudio
|
|
// output device to control
|
|
_paOutputDeviceIndex = deviceIndex;
|
|
|
|
LOG(LS_VERBOSE) << "the output mixer device is now open";
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::OpenMicrophone(
|
|
uint16_t deviceIndex)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE)
|
|
<< "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex="
|
|
<< deviceIndex << ")";
|
|
|
|
// No point in opening the microphone
|
|
// if PA objects have not been set
|
|
if (!_paObjectsSet)
|
|
{
|
|
LOG(LS_ERROR) << "PulseAudio objects have not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Set the index for the PulseAudio
|
|
// input device to control
|
|
_paInputDeviceIndex = deviceIndex;
|
|
|
|
LOG(LS_VERBOSE) << "the input mixer device is now open";
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_INFO) << __FUNCTION__;
|
|
|
|
return (_paOutputDeviceIndex != -1);
|
|
}
|
|
|
|
bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_INFO) << __FUNCTION__;
|
|
|
|
return (_paInputDeviceIndex != -1);
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume(
|
|
uint32_t volume)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume="
|
|
<< volume << ")";
|
|
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
bool setFailed(false);
|
|
|
|
if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
// We can only really set the volume if we have a connected stream
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
|
|
// Get the number of channels from the sample specification
|
|
const pa_sample_spec *spec =
|
|
LATE(pa_stream_get_sample_spec)(_paPlayStream);
|
|
if (!spec)
|
|
{
|
|
LOG(LS_ERROR) << "could not get sample specification";
|
|
return -1;
|
|
}
|
|
|
|
// Set the same volume for all channels
|
|
pa_cvolume cVolumes;
|
|
LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume);
|
|
|
|
pa_operation* paOperation = NULL;
|
|
paOperation = LATE(pa_context_set_sink_input_volume)(
|
|
_paContext,
|
|
LATE(pa_stream_get_index)(_paPlayStream),
|
|
&cVolumes,
|
|
PaSetVolumeCallback, NULL);
|
|
if (!paOperation)
|
|
{
|
|
setFailed = true;
|
|
}
|
|
|
|
// Don't need to wait for the completion
|
|
LATE(pa_operation_unref)(paOperation);
|
|
} else
|
|
{
|
|
// We have not created a stream or it's not connected to the sink
|
|
// Save the volume to be set at connection
|
|
_paSpeakerVolume = volume;
|
|
}
|
|
|
|
if (setFailed)
|
|
{
|
|
LOG(LS_WARNING) << "could not set speaker volume, error="
|
|
<< LATE(pa_context_errno)(_paContext);
|
|
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::SpeakerVolume(uint32_t& volume) const
|
|
{
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
// We can only get the volume if we have a connected stream
|
|
if (!GetSinkInputInfo())
|
|
return -1;
|
|
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
volume = static_cast<uint32_t> (_paVolume);
|
|
} else
|
|
{
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
volume = _paSpeakerVolume;
|
|
}
|
|
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SpeakerVolume() => vol="
|
|
<< volume;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::MaxSpeakerVolume(uint32_t& maxVolume) const
|
|
{
|
|
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// PA_VOLUME_NORM corresponds to 100% (0db)
|
|
// but PA allows up to 150 db amplification
|
|
maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::MinSpeakerVolume(uint32_t& minVolume) const
|
|
{
|
|
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Always available in Pulse Audio
|
|
available = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Always available in Pulse Audio
|
|
available = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable="
|
|
<< enable << ")";
|
|
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
bool setFailed(false);
|
|
|
|
if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
// We can only really mute if we have a connected stream
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
|
|
pa_operation* paOperation = NULL;
|
|
paOperation = LATE(pa_context_set_sink_input_mute)(
|
|
_paContext,
|
|
LATE(pa_stream_get_index)(_paPlayStream),
|
|
(int) enable,
|
|
PaSetVolumeCallback,
|
|
NULL);
|
|
if (!paOperation)
|
|
{
|
|
setFailed = true;
|
|
}
|
|
|
|
// Don't need to wait for the completion
|
|
LATE(pa_operation_unref)(paOperation);
|
|
} else
|
|
{
|
|
// We have not created a stream or it's not connected to the sink
|
|
// Save the mute status to be set at connection
|
|
_paSpeakerMute = enable;
|
|
}
|
|
|
|
if (setFailed)
|
|
{
|
|
LOG(LS_WARNING) << "could not mute speaker, error="
|
|
<< LATE(pa_context_errno)(_paContext);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const
|
|
{
|
|
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
// We can only get the mute status if we have a connected stream
|
|
if (!GetSinkInputInfo())
|
|
return -1;
|
|
|
|
enabled = static_cast<bool> (_paMute);
|
|
} else
|
|
{
|
|
enabled = _paSpeakerMute;
|
|
}
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SpeakerMute() => enabled="
|
|
<< enabled;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paOutputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "output device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
uint32_t deviceIndex = (uint32_t) _paOutputDeviceIndex;
|
|
|
|
{
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
|
|
// Get the actual stream device index if we have a connected stream
|
|
// The device used by the stream can be changed
|
|
// during the call
|
|
if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream);
|
|
}
|
|
}
|
|
|
|
if (!GetSinkInfoByIndex(deviceIndex))
|
|
return -1;
|
|
|
|
available = static_cast<bool> (_paChannels == 2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(bool& available)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
|
|
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
|
|
// Get the actual stream device index if we have a connected stream
|
|
// The device used by the stream can be changed
|
|
// during the call
|
|
if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
|
|
}
|
|
|
|
pa_operation* paOperation = NULL;
|
|
|
|
// Get info for this source
|
|
// We want to know if the actual device can record in stereo
|
|
paOperation = LATE(pa_context_get_source_info_by_index)(
|
|
_paContext, deviceIndex,
|
|
PaSourceInfoCallback,
|
|
(void*) this);
|
|
|
|
WaitForOperationCompletion(paOperation);
|
|
|
|
available = static_cast<bool> (_paChannels == 2);
|
|
|
|
LOG(LS_VERBOSE)
|
|
<< "AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()"
|
|
<< " => available=" << available;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable(
|
|
bool& available)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Always available in Pulse Audio
|
|
available = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable="
|
|
<< enable << ")";
|
|
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
bool setFailed(false);
|
|
pa_operation* paOperation = NULL;
|
|
|
|
uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
|
|
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
|
|
// Get the actual stream device index if we have a connected stream
|
|
// The device used by the stream can be changed
|
|
// during the call
|
|
if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
|
|
}
|
|
|
|
// Set mute switch for the source
|
|
paOperation = LATE(pa_context_set_source_mute_by_index)(
|
|
_paContext, deviceIndex,
|
|
enable,
|
|
PaSetVolumeCallback, NULL);
|
|
|
|
if (!paOperation)
|
|
{
|
|
setFailed = true;
|
|
}
|
|
|
|
// Don't need to wait for this to complete.
|
|
LATE(pa_operation_unref)(paOperation);
|
|
|
|
if (setFailed)
|
|
{
|
|
LOG(LS_WARNING) << "could not mute microphone, error="
|
|
<< LATE(pa_context_errno)(_paContext);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
|
|
|
|
{
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
// Get the actual stream device index if we have a connected stream
|
|
// The device used by the stream can be changed
|
|
// during the call
|
|
if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
|
|
}
|
|
}
|
|
|
|
if (!GetSourceInfoByIndex(deviceIndex))
|
|
return -1;
|
|
|
|
enabled = static_cast<bool> (_paMute);
|
|
|
|
LOG(LS_VERBOSE)
|
|
<< "AudioMixerManagerLinuxPulse::MicrophoneMute() => enabled="
|
|
<< enabled;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable(
|
|
bool& available)
|
|
{
|
|
RTC_DCHECK(thread_checker_.CalledOnValidThread());
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Always available in Pulse Audio
|
|
available = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::SetMicrophoneVolume(uint32_t volume)
|
|
{
|
|
LOG(LS_VERBOSE)
|
|
<< "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=" << volume
|
|
<< ")";
|
|
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// Unlike output streams, input streams have no concept of a stream
|
|
// volume, only a device volume. So we have to change the volume of the
|
|
// device itself.
|
|
|
|
// The device may have a different number of channels than the stream and
|
|
// their mapping may be different, so we don't want to use the channel
|
|
// count from our sample spec. We could use PA_CHANNELS_MAX to cover our
|
|
// bases, and the server allows that even if the device's channel count
|
|
// is lower, but some buggy PA clients don't like that (the pavucontrol
|
|
// on Hardy dies in an assert if the channel count is different). So
|
|
// instead we look up the actual number of channels that the device has.
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
|
|
|
|
// Get the actual stream device index if we have a connected stream
|
|
// The device used by the stream can be changed
|
|
// during the call
|
|
if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
|
|
}
|
|
|
|
bool setFailed(false);
|
|
pa_operation* paOperation = NULL;
|
|
|
|
// Get the number of channels for this source
|
|
paOperation
|
|
= LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex,
|
|
PaSourceInfoCallback,
|
|
(void*) this);
|
|
|
|
WaitForOperationCompletion(paOperation);
|
|
|
|
uint8_t channels = _paChannels;
|
|
pa_cvolume cVolumes;
|
|
LATE(pa_cvolume_set)(&cVolumes, channels, volume);
|
|
|
|
// Set the volume for the source
|
|
paOperation
|
|
= LATE(pa_context_set_source_volume_by_index)(_paContext, deviceIndex,
|
|
&cVolumes,
|
|
PaSetVolumeCallback,
|
|
NULL);
|
|
|
|
if (!paOperation)
|
|
{
|
|
setFailed = true;
|
|
}
|
|
|
|
// Don't need to wait for this to complete.
|
|
LATE(pa_operation_unref)(paOperation);
|
|
|
|
if (setFailed)
|
|
{
|
|
LOG(LS_WARNING) << "could not set microphone volume, error="
|
|
<< LATE(pa_context_errno)(_paContext);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::MicrophoneVolume(uint32_t& volume) const
|
|
{
|
|
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex;
|
|
|
|
{
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
// Get the actual stream device index if we have a connected stream.
|
|
// The device used by the stream can be changed during the call.
|
|
if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream)
|
|
!= PA_STREAM_UNCONNECTED))
|
|
{
|
|
deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream);
|
|
}
|
|
}
|
|
|
|
if (!GetSourceInfoByIndex(deviceIndex))
|
|
return -1;
|
|
|
|
{
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
volume = static_cast<uint32_t> (_paVolume);
|
|
}
|
|
|
|
LOG(LS_VERBOSE)
|
|
<< "AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol="
|
|
<< volume;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(uint32_t& maxVolume) const
|
|
{
|
|
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
// PA_VOLUME_NORM corresponds to 100% (0db)
|
|
// PA allows up to 150 db amplification (PA_VOLUME_MAX)
|
|
// but that doesn't work well for all sound cards
|
|
maxVolume = static_cast<uint32_t> (PA_VOLUME_NORM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t
|
|
AudioMixerManagerLinuxPulse::MinMicrophoneVolume(uint32_t& minVolume) const
|
|
{
|
|
|
|
if (_paInputDeviceIndex == -1)
|
|
{
|
|
LOG(LS_WARNING) << "input device index has not been set";
|
|
return -1;
|
|
}
|
|
|
|
minVolume = static_cast<uint32_t> (PA_VOLUME_MUTED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ===========================================================================
|
|
// Private Methods
|
|
// ===========================================================================
|
|
|
|
void
|
|
AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context */*c*/,
|
|
const pa_sink_info *i,
|
|
int eol,
|
|
void *pThis)
|
|
{
|
|
static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
|
|
PaSinkInfoCallbackHandler(i, eol);
|
|
}
|
|
|
|
void
|
|
AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback(
|
|
pa_context */*c*/,
|
|
const pa_sink_input_info *i,
|
|
int eol,
|
|
void *pThis)
|
|
{
|
|
static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
|
|
PaSinkInputInfoCallbackHandler(i, eol);
|
|
}
|
|
|
|
|
|
void
|
|
AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context */*c*/,
|
|
const pa_source_info *i,
|
|
int eol,
|
|
void *pThis)
|
|
{
|
|
static_cast<AudioMixerManagerLinuxPulse*> (pThis)->
|
|
PaSourceInfoCallbackHandler(i, eol);
|
|
}
|
|
|
|
void
|
|
AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context * c,
|
|
int success,
|
|
void */*pThis*/)
|
|
{
|
|
if (!success)
|
|
{
|
|
LOG(LS_ERROR) << "failed to set volume";
|
|
}
|
|
}
|
|
|
|
void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler(
|
|
const pa_sink_info *i,
|
|
int eol)
|
|
{
|
|
if (eol)
|
|
{
|
|
// Signal that we are done
|
|
LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
|
|
return;
|
|
}
|
|
|
|
_paChannels = i->channel_map.channels; // Get number of channels
|
|
pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
|
|
for (int j = 0; j < _paChannels; ++j)
|
|
{
|
|
if (paVolume < i->volume.values[j])
|
|
{
|
|
paVolume = i->volume.values[j];
|
|
}
|
|
}
|
|
_paVolume = paVolume; // get the max volume for any channel
|
|
_paMute = i->mute; // get mute status
|
|
|
|
// supported since PA 0.9.15
|
|
//_paVolSteps = i->n_volume_steps; // get the number of volume steps
|
|
// default value is PA_VOLUME_NORM+1
|
|
_paVolSteps = PA_VOLUME_NORM + 1;
|
|
}
|
|
|
|
void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler(
|
|
const pa_sink_input_info *i,
|
|
int eol)
|
|
{
|
|
if (eol)
|
|
{
|
|
// Signal that we are done
|
|
LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
|
|
return;
|
|
}
|
|
|
|
_paChannels = i->channel_map.channels; // Get number of channels
|
|
pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
|
|
for (int j = 0; j < _paChannels; ++j)
|
|
{
|
|
if (paVolume < i->volume.values[j])
|
|
{
|
|
paVolume = i->volume.values[j];
|
|
}
|
|
}
|
|
_paVolume = paVolume; // Get the max volume for any channel
|
|
_paMute = i->mute; // Get mute status
|
|
}
|
|
|
|
void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler(
|
|
const pa_source_info *i,
|
|
int eol)
|
|
{
|
|
if (eol)
|
|
{
|
|
// Signal that we are done
|
|
LATE(pa_threaded_mainloop_signal)(_paMainloop, 0);
|
|
return;
|
|
}
|
|
|
|
_paChannels = i->channel_map.channels; // Get number of channels
|
|
pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value.
|
|
for (int j = 0; j < _paChannels; ++j)
|
|
{
|
|
if (paVolume < i->volume.values[j])
|
|
{
|
|
paVolume = i->volume.values[j];
|
|
}
|
|
}
|
|
_paVolume = paVolume; // Get the max volume for any channel
|
|
_paMute = i->mute; // Get mute status
|
|
|
|
// supported since PA 0.9.15
|
|
//_paVolSteps = i->n_volume_steps; // Get the number of volume steps
|
|
// default value is PA_VOLUME_NORM+1
|
|
_paVolSteps = PA_VOLUME_NORM + 1;
|
|
}
|
|
|
|
void AudioMixerManagerLinuxPulse::WaitForOperationCompletion(
|
|
pa_operation* paOperation) const
|
|
{
|
|
while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING)
|
|
{
|
|
LATE(pa_threaded_mainloop_wait)(_paMainloop);
|
|
}
|
|
|
|
LATE(pa_operation_unref)(paOperation);
|
|
}
|
|
|
|
bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const {
|
|
pa_operation* paOperation = NULL;
|
|
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
// Get info for this stream (sink input).
|
|
paOperation = LATE(pa_context_get_sink_input_info)(
|
|
_paContext,
|
|
LATE(pa_stream_get_index)(_paPlayStream),
|
|
PaSinkInputInfoCallback,
|
|
(void*) this);
|
|
|
|
WaitForOperationCompletion(paOperation);
|
|
return true;
|
|
}
|
|
|
|
bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex(
|
|
int device_index) const {
|
|
pa_operation* paOperation = NULL;
|
|
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
paOperation = LATE(pa_context_get_sink_info_by_index)(_paContext,
|
|
device_index, PaSinkInfoCallback, (void*) this);
|
|
|
|
WaitForOperationCompletion(paOperation);
|
|
return true;
|
|
}
|
|
|
|
bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex(
|
|
int device_index) const {
|
|
pa_operation* paOperation = NULL;
|
|
|
|
AutoPulseLock auto_lock(_paMainloop);
|
|
paOperation = LATE(pa_context_get_source_info_by_index)(
|
|
_paContext, device_index, PaSourceInfoCallback, (void*) this);
|
|
|
|
WaitForOperationCompletion(paOperation);
|
|
return true;
|
|
}
|
|
|
|
}
|