Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,40 @@
/*
* libjingle
* Copyright 2004--2010, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "modules/audio_device/linux/alsasymboltable_linux.h"
namespace webrtc {
namespace adm_linux_alsa {
LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(AlsaSymbolTable, "libasound.so.2")
#define X(sym) LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(AlsaSymbolTable, sym)
ALSA_SYMBOLS_LIST
#undef X
LATE_BINDING_SYMBOL_TABLE_DEFINE_END(AlsaSymbolTable)
} // namespace adm_linux_alsa
} // namespace webrtc

View file

@ -0,0 +1,148 @@
/*
* libjingle
* Copyright 2004--2010, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef AUDIO_DEVICE_ALSASYMBOLTABLE_LINUX_H_
#define AUDIO_DEVICE_ALSASYMBOLTABLE_LINUX_H_
#include "modules/audio_device/linux/latebindingsymboltable_linux.h"
namespace webrtc {
namespace adm_linux_alsa {
// The ALSA symbols we need, as an X-Macro list.
// This list must contain precisely every libasound function that is used in
// alsasoundsystem.cc.
#define ALSA_SYMBOLS_LIST \
X(snd_device_name_free_hint) \
X(snd_device_name_get_hint) \
X(snd_device_name_hint) \
X(snd_pcm_avail_update) \
X(snd_pcm_close) \
X(snd_pcm_delay) \
X(snd_pcm_drop) \
X(snd_pcm_open) \
X(snd_pcm_prepare) \
X(snd_pcm_readi) \
X(snd_pcm_recover) \
X(snd_pcm_resume) \
X(snd_pcm_reset) \
X(snd_pcm_state) \
X(snd_pcm_set_params) \
X(snd_pcm_get_params) \
X(snd_pcm_start) \
X(snd_pcm_stream) \
X(snd_pcm_frames_to_bytes) \
X(snd_pcm_bytes_to_frames) \
X(snd_pcm_wait) \
X(snd_pcm_writei) \
X(snd_pcm_info_get_class) \
X(snd_pcm_info_get_subdevices_avail) \
X(snd_pcm_info_get_subdevice_name) \
X(snd_pcm_info_set_subdevice) \
X(snd_pcm_info_get_id) \
X(snd_pcm_info_set_device) \
X(snd_pcm_info_set_stream) \
X(snd_pcm_info_get_name) \
X(snd_pcm_info_get_subdevices_count) \
X(snd_pcm_info_sizeof) \
X(snd_pcm_hw_params) \
X(snd_pcm_hw_params_malloc) \
X(snd_pcm_hw_params_free) \
X(snd_pcm_hw_params_any) \
X(snd_pcm_hw_params_set_access) \
X(snd_pcm_hw_params_set_format) \
X(snd_pcm_hw_params_set_channels) \
X(snd_pcm_hw_params_set_rate_near) \
X(snd_pcm_hw_params_set_buffer_size_near) \
X(snd_card_next) \
X(snd_card_get_name) \
X(snd_config_update) \
X(snd_config_copy) \
X(snd_config_get_id) \
X(snd_ctl_open) \
X(snd_ctl_close) \
X(snd_ctl_card_info) \
X(snd_ctl_card_info_sizeof) \
X(snd_ctl_card_info_get_id) \
X(snd_ctl_card_info_get_name) \
X(snd_ctl_pcm_next_device) \
X(snd_ctl_pcm_info) \
X(snd_mixer_load) \
X(snd_mixer_free) \
X(snd_mixer_detach) \
X(snd_mixer_close) \
X(snd_mixer_open) \
X(snd_mixer_attach) \
X(snd_mixer_first_elem) \
X(snd_mixer_elem_next) \
X(snd_mixer_selem_get_name) \
X(snd_mixer_selem_is_active) \
X(snd_mixer_selem_register) \
X(snd_mixer_selem_set_playback_volume_all) \
X(snd_mixer_selem_get_playback_volume) \
X(snd_mixer_selem_has_playback_volume) \
X(snd_mixer_selem_get_playback_volume_range) \
X(snd_mixer_selem_has_playback_switch) \
X(snd_mixer_selem_get_playback_switch) \
X(snd_mixer_selem_set_playback_switch_all) \
X(snd_mixer_selem_has_capture_switch) \
X(snd_mixer_selem_get_capture_switch) \
X(snd_mixer_selem_set_capture_switch_all) \
X(snd_mixer_selem_has_capture_volume) \
X(snd_mixer_selem_set_capture_volume_all) \
X(snd_mixer_selem_get_capture_volume) \
X(snd_mixer_selem_get_capture_volume_range) \
X(snd_dlopen) \
X(snd_dlclose) \
X(snd_config) \
X(snd_config_search) \
X(snd_config_get_string) \
X(snd_config_search_definition) \
X(snd_config_get_type) \
X(snd_config_delete) \
X(snd_config_iterator_entry) \
X(snd_config_iterator_first) \
X(snd_config_iterator_next) \
X(snd_config_iterator_end) \
X(snd_config_delete_compound_members) \
X(snd_config_get_integer) \
X(snd_config_get_bool) \
X(snd_dlsym) \
X(snd_strerror) \
X(snd_lib_error) \
X(snd_lib_error_set_handler)
LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(AlsaSymbolTable)
#define X(sym) LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(AlsaSymbolTable, sym)
ALSA_SYMBOLS_LIST
#undef X
LATE_BINDING_SYMBOL_TABLE_DECLARE_END(AlsaSymbolTable)
} // namespace adm_linux_alsa
} // namespace webrtc
#endif // AUDIO_DEVICE_ALSASYMBOLTABLE_LINUX_H_

View file

@ -0,0 +1,208 @@
/*
* 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.
*/
#ifndef AUDIO_DEVICE_AUDIO_DEVICE_ALSA_LINUX_H_
#define AUDIO_DEVICE_AUDIO_DEVICE_ALSA_LINUX_H_
#include <memory>
#include "modules/audio_device/audio_device_generic.h"
#include "modules/audio_device/linux/audio_mixer_manager_alsa_linux.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/synchronization/mutex.h"
#if defined(WEBRTC_USE_X11)
#include <X11/Xlib.h>
#endif
#include <alsa/asoundlib.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
typedef webrtc::adm_linux_alsa::AlsaSymbolTable WebRTCAlsaSymbolTable;
WebRTCAlsaSymbolTable* GetAlsaSymbolTable();
namespace webrtc {
class AudioDeviceLinuxALSA : public AudioDeviceGeneric {
public:
AudioDeviceLinuxALSA();
virtual ~AudioDeviceLinuxALSA();
// Retrieve the currently utilized audio layer
int32_t ActiveAudioLayer(
AudioDeviceModule::AudioLayer& audioLayer) const override;
// Main initializaton and termination
InitStatus Init() RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t Terminate() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Initialized() const override;
// Device enumeration
int16_t PlayoutDevices() override;
int16_t RecordingDevices() override;
int32_t PlayoutDeviceName(uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) override;
int32_t RecordingDeviceName(uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) override;
// Device selection
int32_t SetPlayoutDevice(uint16_t index) override;
int32_t SetPlayoutDevice(
AudioDeviceModule::WindowsDeviceType device) override;
int32_t SetRecordingDevice(uint16_t index) override;
int32_t SetRecordingDevice(
AudioDeviceModule::WindowsDeviceType device) override;
// Audio transport initialization
int32_t PlayoutIsAvailable(bool& available) override;
int32_t InitPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
bool PlayoutIsInitialized() const override;
int32_t RecordingIsAvailable(bool& available) override;
int32_t InitRecording() RTC_LOCKS_EXCLUDED(mutex_) override;
bool RecordingIsInitialized() const override;
// Audio transport control
int32_t StartPlayout() override;
int32_t StopPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Playing() const override;
int32_t StartRecording() override;
int32_t StopRecording() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Recording() const override;
// Audio mixer initialization
int32_t InitSpeaker() RTC_LOCKS_EXCLUDED(mutex_) override;
bool SpeakerIsInitialized() const override;
int32_t InitMicrophone() RTC_LOCKS_EXCLUDED(mutex_) override;
bool MicrophoneIsInitialized() const override;
// Speaker volume controls
int32_t SpeakerVolumeIsAvailable(bool& available) override;
int32_t SetSpeakerVolume(uint32_t volume) override;
int32_t SpeakerVolume(uint32_t& volume) const override;
int32_t MaxSpeakerVolume(uint32_t& maxVolume) const override;
int32_t MinSpeakerVolume(uint32_t& minVolume) const override;
// Microphone volume controls
int32_t MicrophoneVolumeIsAvailable(bool& available) override;
int32_t SetMicrophoneVolume(uint32_t volume) override;
int32_t MicrophoneVolume(uint32_t& volume) const override;
int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const override;
int32_t MinMicrophoneVolume(uint32_t& minVolume) const override;
// Speaker mute control
int32_t SpeakerMuteIsAvailable(bool& available) override;
int32_t SetSpeakerMute(bool enable) override;
int32_t SpeakerMute(bool& enabled) const override;
// Microphone mute control
int32_t MicrophoneMuteIsAvailable(bool& available) override;
int32_t SetMicrophoneMute(bool enable) override;
int32_t MicrophoneMute(bool& enabled) const override;
// Stereo support
int32_t StereoPlayoutIsAvailable(bool& available)
RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t SetStereoPlayout(bool enable) override;
int32_t StereoPlayout(bool& enabled) const override;
int32_t StereoRecordingIsAvailable(bool& available)
RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t SetStereoRecording(bool enable) override;
int32_t StereoRecording(bool& enabled) const override;
// Delay information and control
int32_t PlayoutDelay(uint16_t& delayMS) const override;
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer)
RTC_LOCKS_EXCLUDED(mutex_) override;
private:
int32_t InitRecordingLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t StopRecordingLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t StopPlayoutLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t InitPlayoutLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t InitSpeakerLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t InitMicrophoneLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t GetDevicesInfo(int32_t function,
bool playback,
int32_t enumDeviceNo = 0,
char* enumDeviceName = NULL,
int32_t ednLen = 0) const;
int32_t ErrorRecovery(int32_t error, snd_pcm_t* deviceHandle);
bool KeyPressed() const;
void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION(mutex_) { mutex_.Lock(); }
void UnLock() RTC_UNLOCK_FUNCTION(mutex_) { mutex_.Unlock(); }
inline int32_t InputSanityCheckAfterUnlockedPeriod() const;
inline int32_t OutputSanityCheckAfterUnlockedPeriod() const;
static void RecThreadFunc(void*);
static void PlayThreadFunc(void*);
bool RecThreadProcess();
bool PlayThreadProcess();
AudioDeviceBuffer* _ptrAudioBuffer;
Mutex mutex_;
rtc::PlatformThread _ptrThreadRec;
rtc::PlatformThread _ptrThreadPlay;
AudioMixerManagerLinuxALSA _mixerManager;
uint16_t _inputDeviceIndex;
uint16_t _outputDeviceIndex;
bool _inputDeviceIsSpecified;
bool _outputDeviceIsSpecified;
snd_pcm_t* _handleRecord;
snd_pcm_t* _handlePlayout;
snd_pcm_uframes_t _recordingBuffersizeInFrame;
snd_pcm_uframes_t _recordingPeriodSizeInFrame;
snd_pcm_uframes_t _playoutBufferSizeInFrame;
snd_pcm_uframes_t _playoutPeriodSizeInFrame;
ssize_t _recordingBufferSizeIn10MS;
ssize_t _playoutBufferSizeIn10MS;
uint32_t _recordingFramesIn10MS;
uint32_t _playoutFramesIn10MS;
uint32_t _recordingFreq;
uint32_t _playoutFreq;
uint8_t _recChannels;
uint8_t _playChannels;
int8_t* _recordingBuffer; // in byte
int8_t* _playoutBuffer; // in byte
uint32_t _recordingFramesLeft;
uint32_t _playoutFramesLeft;
bool _initialized;
bool _recording;
bool _playing;
bool _recIsInitialized;
bool _playIsInitialized;
snd_pcm_sframes_t _recordingDelay;
snd_pcm_sframes_t _playoutDelay;
char _oldKeyState[32];
#if defined(WEBRTC_USE_X11)
Display* _XDisplay;
#endif
};
} // namespace webrtc
#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_DEVICE_ALSA_LINUX_H_

View file

@ -0,0 +1,349 @@
/*
* 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.
*/
#ifndef AUDIO_DEVICE_AUDIO_DEVICE_PULSE_LINUX_H_
#define AUDIO_DEVICE_AUDIO_DEVICE_PULSE_LINUX_H_
#include <memory>
#include "api/sequence_checker.h"
#include "modules/audio_device/audio_device_buffer.h"
#include "modules/audio_device/audio_device_generic.h"
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_device/include/audio_device_defines.h"
#include "modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
#include "modules/audio_device/linux/pulseaudiosymboltable_linux.h"
#include "rtc_base/event.h"
#include "rtc_base/platform_thread.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
#if defined(WEBRTC_USE_X11)
#include <X11/Xlib.h>
#endif
#include <pulse/pulseaudio.h>
#include <stddef.h>
#include <stdint.h>
// We define this flag if it's missing from our headers, because we want to be
// able to compile against old headers but still use PA_STREAM_ADJUST_LATENCY
// if run against a recent version of the library.
#ifndef PA_STREAM_ADJUST_LATENCY
#define PA_STREAM_ADJUST_LATENCY 0x2000U
#endif
#ifndef PA_STREAM_START_MUTED
#define PA_STREAM_START_MUTED 0x1000U
#endif
// Set this constant to 0 to disable latency reading
const uint32_t WEBRTC_PA_REPORT_LATENCY = 1;
// Constants from implementation by Tristan Schmelcher [tschmelcher@google.com]
// First PulseAudio protocol version that supports PA_STREAM_ADJUST_LATENCY.
const uint32_t WEBRTC_PA_ADJUST_LATENCY_PROTOCOL_VERSION = 13;
// Some timing constants for optimal operation. See
// https://tango.0pointer.de/pipermail/pulseaudio-discuss/2008-January/001170.html
// for a good explanation of some of the factors that go into this.
// Playback.
// For playback, there is a round-trip delay to fill the server-side playback
// buffer, so setting too low of a latency is a buffer underflow risk. We will
// automatically increase the latency if a buffer underflow does occur, but we
// also enforce a sane minimum at start-up time. Anything lower would be
// virtually guaranteed to underflow at least once, so there's no point in
// allowing lower latencies.
const uint32_t WEBRTC_PA_PLAYBACK_LATENCY_MINIMUM_MSECS = 20;
// Every time a playback stream underflows, we will reconfigure it with target
// latency that is greater by this amount.
const uint32_t WEBRTC_PA_PLAYBACK_LATENCY_INCREMENT_MSECS = 20;
// We also need to configure a suitable request size. Too small and we'd burn
// CPU from the overhead of transfering small amounts of data at once. Too large
// and the amount of data remaining in the buffer right before refilling it
// would be a buffer underflow risk. We set it to half of the buffer size.
const uint32_t WEBRTC_PA_PLAYBACK_REQUEST_FACTOR = 2;
// Capture.
// For capture, low latency is not a buffer overflow risk, but it makes us burn
// CPU from the overhead of transfering small amounts of data at once, so we set
// a recommended value that we use for the kLowLatency constant (but if the user
// explicitly requests something lower then we will honour it).
// 1ms takes about 6-7% CPU. 5ms takes about 5%. 10ms takes about 4.x%.
const uint32_t WEBRTC_PA_LOW_CAPTURE_LATENCY_MSECS = 10;
// There is a round-trip delay to ack the data to the server, so the
// server-side buffer needs extra space to prevent buffer overflow. 20ms is
// sufficient, but there is no penalty to making it bigger, so we make it huge.
// (750ms is libpulse's default value for the _total_ buffer size in the
// kNoLatencyRequirements case.)
const uint32_t WEBRTC_PA_CAPTURE_BUFFER_EXTRA_MSECS = 750;
const uint32_t WEBRTC_PA_MSECS_PER_SEC = 1000;
// Init _configuredLatencyRec/Play to this value to disable latency requirements
const int32_t WEBRTC_PA_NO_LATENCY_REQUIREMENTS = -1;
// Set this const to 1 to account for peeked and used data in latency
// calculation
const uint32_t WEBRTC_PA_CAPTURE_BUFFER_LATENCY_ADJUSTMENT = 0;
typedef webrtc::adm_linux_pulse::PulseAudioSymbolTable WebRTCPulseSymbolTable;
WebRTCPulseSymbolTable* GetPulseSymbolTable();
namespace webrtc {
class AudioDeviceLinuxPulse : public AudioDeviceGeneric {
public:
AudioDeviceLinuxPulse();
virtual ~AudioDeviceLinuxPulse();
// Retrieve the currently utilized audio layer
int32_t ActiveAudioLayer(
AudioDeviceModule::AudioLayer& audioLayer) const override;
// Main initializaton and termination
InitStatus Init() override;
int32_t Terminate() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Initialized() const override;
// Device enumeration
int16_t PlayoutDevices() override;
int16_t RecordingDevices() override;
int32_t PlayoutDeviceName(uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) override;
int32_t RecordingDeviceName(uint16_t index,
char name[kAdmMaxDeviceNameSize],
char guid[kAdmMaxGuidSize]) override;
// Device selection
int32_t SetPlayoutDevice(uint16_t index) override;
int32_t SetPlayoutDevice(
AudioDeviceModule::WindowsDeviceType device) override;
int32_t SetRecordingDevice(uint16_t index) override;
int32_t SetRecordingDevice(
AudioDeviceModule::WindowsDeviceType device) override;
// Audio transport initialization
int32_t PlayoutIsAvailable(bool& available) override;
int32_t InitPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
bool PlayoutIsInitialized() const override;
int32_t RecordingIsAvailable(bool& available) override;
int32_t InitRecording() override;
bool RecordingIsInitialized() const override;
// Audio transport control
int32_t StartPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t StopPlayout() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Playing() const override;
int32_t StartRecording() RTC_LOCKS_EXCLUDED(mutex_) override;
int32_t StopRecording() RTC_LOCKS_EXCLUDED(mutex_) override;
bool Recording() const override;
// Audio mixer initialization
int32_t InitSpeaker() override;
bool SpeakerIsInitialized() const override;
int32_t InitMicrophone() override;
bool MicrophoneIsInitialized() const override;
// Speaker volume controls
int32_t SpeakerVolumeIsAvailable(bool& available) override;
int32_t SetSpeakerVolume(uint32_t volume) override;
int32_t SpeakerVolume(uint32_t& volume) const override;
int32_t MaxSpeakerVolume(uint32_t& maxVolume) const override;
int32_t MinSpeakerVolume(uint32_t& minVolume) const override;
// Microphone volume controls
int32_t MicrophoneVolumeIsAvailable(bool& available) override;
int32_t SetMicrophoneVolume(uint32_t volume) override;
int32_t MicrophoneVolume(uint32_t& volume) const override;
int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const override;
int32_t MinMicrophoneVolume(uint32_t& minVolume) const override;
// Speaker mute control
int32_t SpeakerMuteIsAvailable(bool& available) override;
int32_t SetSpeakerMute(bool enable) override;
int32_t SpeakerMute(bool& enabled) const override;
// Microphone mute control
int32_t MicrophoneMuteIsAvailable(bool& available) override;
int32_t SetMicrophoneMute(bool enable) override;
int32_t MicrophoneMute(bool& enabled) const override;
// Stereo support
int32_t StereoPlayoutIsAvailable(bool& available) override;
int32_t SetStereoPlayout(bool enable) override;
int32_t StereoPlayout(bool& enabled) const override;
int32_t StereoRecordingIsAvailable(bool& available) override;
int32_t SetStereoRecording(bool enable) override;
int32_t StereoRecording(bool& enabled) const override;
// Delay information and control
int32_t PlayoutDelay(uint16_t& delayMS) const
RTC_LOCKS_EXCLUDED(mutex_) override;
void AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) override;
private:
void Lock() RTC_EXCLUSIVE_LOCK_FUNCTION(mutex_) { mutex_.Lock(); }
void UnLock() RTC_UNLOCK_FUNCTION(mutex_) { mutex_.Unlock(); }
void WaitForOperationCompletion(pa_operation* paOperation) const;
void WaitForSuccess(pa_operation* paOperation) const;
bool KeyPressed() const;
static void PaContextStateCallback(pa_context* c, void* pThis);
static void PaSinkInfoCallback(pa_context* c,
const pa_sink_info* i,
int eol,
void* pThis);
static void PaSourceInfoCallback(pa_context* c,
const pa_source_info* i,
int eol,
void* pThis);
static void PaServerInfoCallback(pa_context* c,
const pa_server_info* i,
void* pThis);
static void PaStreamStateCallback(pa_stream* p, void* pThis);
void PaContextStateCallbackHandler(pa_context* c);
void PaSinkInfoCallbackHandler(const pa_sink_info* i, int eol);
void PaSourceInfoCallbackHandler(const pa_source_info* i, int eol);
void PaServerInfoCallbackHandler(const pa_server_info* i);
void PaStreamStateCallbackHandler(pa_stream* p);
void EnableWriteCallback();
void DisableWriteCallback();
static void PaStreamWriteCallback(pa_stream* unused,
size_t buffer_space,
void* pThis);
void PaStreamWriteCallbackHandler(size_t buffer_space);
static void PaStreamUnderflowCallback(pa_stream* unused, void* pThis);
void PaStreamUnderflowCallbackHandler();
void EnableReadCallback();
void DisableReadCallback();
static void PaStreamReadCallback(pa_stream* unused1,
size_t unused2,
void* pThis);
void PaStreamReadCallbackHandler();
static void PaStreamOverflowCallback(pa_stream* unused, void* pThis);
void PaStreamOverflowCallbackHandler();
int32_t LatencyUsecs(pa_stream* stream);
int32_t ReadRecordedData(const void* bufferData, size_t bufferSize);
int32_t ProcessRecordedData(int8_t* bufferData,
uint32_t bufferSizeInSamples,
uint32_t recDelay);
int32_t CheckPulseAudioVersion();
int32_t InitSamplingFrequency();
int32_t GetDefaultDeviceInfo(bool recDevice, char* name, uint16_t& index);
int32_t InitPulseAudio();
int32_t TerminatePulseAudio();
void PaLock();
void PaUnLock();
static void RecThreadFunc(void*);
static void PlayThreadFunc(void*);
bool RecThreadProcess() RTC_LOCKS_EXCLUDED(mutex_);
bool PlayThreadProcess() RTC_LOCKS_EXCLUDED(mutex_);
AudioDeviceBuffer* _ptrAudioBuffer;
mutable Mutex mutex_;
rtc::Event _timeEventRec;
rtc::Event _timeEventPlay;
rtc::Event _recStartEvent;
rtc::Event _playStartEvent;
rtc::PlatformThread _ptrThreadPlay;
rtc::PlatformThread _ptrThreadRec;
AudioMixerManagerLinuxPulse _mixerManager;
uint16_t _inputDeviceIndex;
uint16_t _outputDeviceIndex;
bool _inputDeviceIsSpecified;
bool _outputDeviceIsSpecified;
int sample_rate_hz_;
uint8_t _recChannels;
uint8_t _playChannels;
// Stores thread ID in constructor.
// We can then use RTC_DCHECK_RUN_ON(&worker_thread_checker_) to ensure that
// other methods are called from the same thread.
// Currently only does RTC_DCHECK(thread_checker_.IsCurrent()).
SequenceChecker thread_checker_;
bool _initialized;
bool _recording;
bool _playing;
bool _recIsInitialized;
bool _playIsInitialized;
bool _startRec;
bool _startPlay;
bool update_speaker_volume_at_startup_;
bool quit_ RTC_GUARDED_BY(&mutex_);
uint32_t _sndCardPlayDelay RTC_GUARDED_BY(&mutex_);
int32_t _writeErrors;
uint16_t _deviceIndex;
int16_t _numPlayDevices;
int16_t _numRecDevices;
char* _playDeviceName;
char* _recDeviceName;
char* _playDisplayDeviceName;
char* _recDisplayDeviceName;
char _paServerVersion[32];
int8_t* _playBuffer;
size_t _playbackBufferSize;
size_t _playbackBufferUnused;
size_t _tempBufferSpace;
int8_t* _recBuffer;
size_t _recordBufferSize;
size_t _recordBufferUsed;
const void* _tempSampleData;
size_t _tempSampleDataSize;
int32_t _configuredLatencyPlay;
int32_t _configuredLatencyRec;
// PulseAudio
uint16_t _paDeviceIndex;
bool _paStateChanged;
pa_threaded_mainloop* _paMainloop;
pa_mainloop_api* _paMainloopApi;
pa_context* _paContext;
pa_stream* _recStream;
pa_stream* _playStream;
uint32_t _recStreamFlags;
uint32_t _playStreamFlags;
pa_buffer_attr _playBufferAttr;
pa_buffer_attr _recBufferAttr;
char _oldKeyState[32];
#if defined(WEBRTC_USE_X11)
Display* _XDisplay;
#endif
};
} // namespace webrtc
#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_DEVICE_PULSE_LINUX_H_

View file

@ -0,0 +1,979 @@
/*
* 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 "modules/audio_device/linux/audio_mixer_manager_alsa_linux.h"
#include "modules/audio_device/linux/audio_device_alsa_linux.h"
#include "rtc_base/logging.h"
// Accesses ALSA functions through our late-binding symbol table instead of
// directly. This way we don't have to link to libasound, which means our binary
// will work on systems that don't have it.
#define LATE(sym) \
LATESYM_GET(webrtc::adm_linux_alsa::AlsaSymbolTable, GetAlsaSymbolTable(), \
sym)
namespace webrtc {
AudioMixerManagerLinuxALSA::AudioMixerManagerLinuxALSA()
: _outputMixerHandle(NULL),
_inputMixerHandle(NULL),
_outputMixerElement(NULL),
_inputMixerElement(NULL) {
RTC_DLOG(LS_INFO) << __FUNCTION__ << " created";
memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize);
memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize);
}
AudioMixerManagerLinuxALSA::~AudioMixerManagerLinuxALSA() {
RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed";
Close();
}
// ============================================================================
// PUBLIC METHODS
// ============================================================================
int32_t AudioMixerManagerLinuxALSA::Close() {
RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
MutexLock lock(&mutex_);
CloseSpeakerLocked();
CloseMicrophoneLocked();
return 0;
}
int32_t AudioMixerManagerLinuxALSA::CloseSpeaker() {
MutexLock lock(&mutex_);
return CloseSpeakerLocked();
}
int32_t AudioMixerManagerLinuxALSA::CloseSpeakerLocked() {
RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
int errVal = 0;
if (_outputMixerHandle != NULL) {
RTC_LOG(LS_VERBOSE) << "Closing playout mixer";
LATE(snd_mixer_free)(_outputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error freeing playout mixer: "
<< LATE(snd_strerror)(errVal);
}
errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error detaching playout mixer: "
<< LATE(snd_strerror)(errVal);
}
errVal = LATE(snd_mixer_close)(_outputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal="
<< errVal;
}
_outputMixerHandle = NULL;
_outputMixerElement = NULL;
}
memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::CloseMicrophone() {
MutexLock lock(&mutex_);
return CloseMicrophoneLocked();
}
int32_t AudioMixerManagerLinuxALSA::CloseMicrophoneLocked() {
RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
int errVal = 0;
if (_inputMixerHandle != NULL) {
RTC_LOG(LS_VERBOSE) << "Closing record mixer";
LATE(snd_mixer_free)(_inputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error freeing record mixer: "
<< LATE(snd_strerror)(errVal);
}
RTC_LOG(LS_VERBOSE) << "Closing record mixer 2";
errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error detaching record mixer: "
<< LATE(snd_strerror)(errVal);
}
RTC_LOG(LS_VERBOSE) << "Closing record mixer 3";
errVal = LATE(snd_mixer_close)(_inputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal="
<< errVal;
}
RTC_LOG(LS_VERBOSE) << "Closing record mixer 4";
_inputMixerHandle = NULL;
_inputMixerElement = NULL;
}
memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::OpenSpeaker(char* deviceName) {
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::OpenSpeaker(name="
<< deviceName << ")";
MutexLock lock(&mutex_);
int errVal = 0;
// Close any existing output mixer handle
//
if (_outputMixerHandle != NULL) {
RTC_LOG(LS_VERBOSE) << "Closing playout mixer";
LATE(snd_mixer_free)(_outputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error freeing playout mixer: "
<< LATE(snd_strerror)(errVal);
}
errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error detaching playout mixer: "
<< LATE(snd_strerror)(errVal);
}
errVal = LATE(snd_mixer_close)(_outputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal="
<< errVal;
}
}
_outputMixerHandle = NULL;
_outputMixerElement = NULL;
errVal = LATE(snd_mixer_open)(&_outputMixerHandle, 0);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "snd_mixer_open(&_outputMixerHandle, 0) - error";
return -1;
}
char controlName[kAdmMaxDeviceNameSize] = {0};
GetControlName(controlName, deviceName);
RTC_LOG(LS_VERBOSE) << "snd_mixer_attach(_outputMixerHandle, " << controlName
<< ")";
errVal = LATE(snd_mixer_attach)(_outputMixerHandle, controlName);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "snd_mixer_attach(_outputMixerHandle, " << controlName
<< ") error: " << LATE(snd_strerror)(errVal);
_outputMixerHandle = NULL;
return -1;
}
strcpy(_outputMixerStr, controlName);
errVal = LATE(snd_mixer_selem_register)(_outputMixerHandle, NULL, NULL);
if (errVal < 0) {
RTC_LOG(LS_ERROR)
<< "snd_mixer_selem_register(_outputMixerHandle, NULL, NULL), "
"error: "
<< LATE(snd_strerror)(errVal);
_outputMixerHandle = NULL;
return -1;
}
// Load and find the proper mixer element
if (LoadSpeakerMixerElement() < 0) {
return -1;
}
if (_outputMixerHandle != NULL) {
RTC_LOG(LS_VERBOSE) << "the output mixer device is now open ("
<< _outputMixerHandle << ")";
}
return 0;
}
int32_t AudioMixerManagerLinuxALSA::OpenMicrophone(char* deviceName) {
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::OpenMicrophone(name="
<< deviceName << ")";
MutexLock lock(&mutex_);
int errVal = 0;
// Close any existing input mixer handle
//
if (_inputMixerHandle != NULL) {
RTC_LOG(LS_VERBOSE) << "Closing record mixer";
LATE(snd_mixer_free)(_inputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error freeing record mixer: "
<< LATE(snd_strerror)(errVal);
}
RTC_LOG(LS_VERBOSE) << "Closing record mixer";
errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error detaching record mixer: "
<< LATE(snd_strerror)(errVal);
}
RTC_LOG(LS_VERBOSE) << "Closing record mixer";
errVal = LATE(snd_mixer_close)(_inputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal="
<< errVal;
}
RTC_LOG(LS_VERBOSE) << "Closing record mixer";
}
_inputMixerHandle = NULL;
_inputMixerElement = NULL;
errVal = LATE(snd_mixer_open)(&_inputMixerHandle, 0);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "snd_mixer_open(&_inputMixerHandle, 0) - error";
return -1;
}
char controlName[kAdmMaxDeviceNameSize] = {0};
GetControlName(controlName, deviceName);
RTC_LOG(LS_VERBOSE) << "snd_mixer_attach(_inputMixerHandle, " << controlName
<< ")";
errVal = LATE(snd_mixer_attach)(_inputMixerHandle, controlName);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "snd_mixer_attach(_inputMixerHandle, " << controlName
<< ") error: " << LATE(snd_strerror)(errVal);
_inputMixerHandle = NULL;
return -1;
}
strcpy(_inputMixerStr, controlName);
errVal = LATE(snd_mixer_selem_register)(_inputMixerHandle, NULL, NULL);
if (errVal < 0) {
RTC_LOG(LS_ERROR)
<< "snd_mixer_selem_register(_inputMixerHandle, NULL, NULL), "
"error: "
<< LATE(snd_strerror)(errVal);
_inputMixerHandle = NULL;
return -1;
}
// Load and find the proper mixer element
if (LoadMicMixerElement() < 0) {
return -1;
}
if (_inputMixerHandle != NULL) {
RTC_LOG(LS_VERBOSE) << "the input mixer device is now open ("
<< _inputMixerHandle << ")";
}
return 0;
}
bool AudioMixerManagerLinuxALSA::SpeakerIsInitialized() const {
RTC_DLOG(LS_INFO) << __FUNCTION__;
return (_outputMixerHandle != NULL);
}
bool AudioMixerManagerLinuxALSA::MicrophoneIsInitialized() const {
RTC_DLOG(LS_INFO) << __FUNCTION__;
return (_inputMixerHandle != NULL);
}
int32_t AudioMixerManagerLinuxALSA::SetSpeakerVolume(uint32_t volume) {
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetSpeakerVolume(volume="
<< volume << ")";
MutexLock lock(&mutex_);
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
int errVal = LATE(snd_mixer_selem_set_playback_volume_all)(
_outputMixerElement, volume);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error changing master volume: "
<< LATE(snd_strerror)(errVal);
return -1;
}
return (0);
}
int32_t AudioMixerManagerLinuxALSA::SpeakerVolume(uint32_t& volume) const {
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
long int vol(0);
int errVal = LATE(snd_mixer_selem_get_playback_volume)(
_outputMixerElement, (snd_mixer_selem_channel_id_t)0, &vol);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error getting outputvolume: "
<< LATE(snd_strerror)(errVal);
return -1;
}
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SpeakerVolume() => vol="
<< vol;
volume = static_cast<uint32_t>(vol);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::MaxSpeakerVolume(
uint32_t& maxVolume) const {
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avilable output mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = LATE(snd_mixer_selem_get_playback_volume_range)(
_outputMixerElement, &minVol, &maxVol);
RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (maxVol <= minVol) {
RTC_LOG(LS_ERROR) << "Error getting get_playback_volume_range: "
<< LATE(snd_strerror)(errVal);
}
maxVolume = static_cast<uint32_t>(maxVol);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::MinSpeakerVolume(
uint32_t& minVolume) const {
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = LATE(snd_mixer_selem_get_playback_volume_range)(
_outputMixerElement, &minVol, &maxVol);
RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (maxVol <= minVol) {
RTC_LOG(LS_ERROR) << "Error getting get_playback_volume_range: "
<< LATE(snd_strerror)(errVal);
}
minVolume = static_cast<uint32_t>(minVol);
return 0;
}
// TL: Have done testnig with these but they don't seem reliable and
// they were therefore not added
/*
// ----------------------------------------------------------------------------
// SetMaxSpeakerVolume
// ----------------------------------------------------------------------------
int32_t AudioMixerManagerLinuxALSA::SetMaxSpeakerVolume(
uint32_t maxVolume)
{
if (_outputMixerElement == NULL)
{
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = snd_mixer_selem_get_playback_volume_range(
_outputMixerElement, &minVol, &maxVol);
if ((maxVol <= minVol) || (errVal != 0))
{
RTC_LOG(LS_WARNING) << "Error getting playback volume range: "
<< snd_strerror(errVal);
}
maxVol = maxVolume;
errVal = snd_mixer_selem_set_playback_volume_range(
_outputMixerElement, minVol, maxVol);
RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (errVal != 0)
{
RTC_LOG(LS_ERROR) << "Error setting playback volume range: "
<< snd_strerror(errVal);
return -1;
}
return 0;
}
// ----------------------------------------------------------------------------
// SetMinSpeakerVolume
// ----------------------------------------------------------------------------
int32_t AudioMixerManagerLinuxALSA::SetMinSpeakerVolume(
uint32_t minVolume)
{
if (_outputMixerElement == NULL)
{
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = snd_mixer_selem_get_playback_volume_range(
_outputMixerElement, &minVol, &maxVol);
if ((maxVol <= minVol) || (errVal != 0))
{
RTC_LOG(LS_WARNING) << "Error getting playback volume range: "
<< snd_strerror(errVal);
}
minVol = minVolume;
errVal = snd_mixer_selem_set_playback_volume_range(
_outputMixerElement, minVol, maxVol);
RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (errVal != 0)
{
RTC_LOG(LS_ERROR) << "Error setting playback volume range: "
<< snd_strerror(errVal);
return -1;
}
return 0;
}
*/
int32_t AudioMixerManagerLinuxALSA::SpeakerVolumeIsAvailable(bool& available) {
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
available = LATE(snd_mixer_selem_has_playback_volume)(_outputMixerElement);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::SpeakerMuteIsAvailable(bool& available) {
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
available = LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::SetSpeakerMute(bool enable) {
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetSpeakerMute(enable="
<< enable << ")";
MutexLock lock(&mutex_);
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
// Ensure that the selected speaker destination has a valid mute control.
bool available(false);
SpeakerMuteIsAvailable(available);
if (!available) {
RTC_LOG(LS_WARNING) << "it is not possible to mute the speaker";
return -1;
}
// Note value = 0 (off) means muted
int errVal = LATE(snd_mixer_selem_set_playback_switch_all)(
_outputMixerElement, !enable);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error setting playback switch: "
<< LATE(snd_strerror)(errVal);
return -1;
}
return (0);
}
int32_t AudioMixerManagerLinuxALSA::SpeakerMute(bool& enabled) const {
if (_outputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable output mixer exists";
return -1;
}
// Ensure that the selected speaker destination has a valid mute control.
bool available =
LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement);
if (!available) {
RTC_LOG(LS_WARNING) << "it is not possible to mute the speaker";
return -1;
}
int value(false);
// Retrieve one boolean control value for a specified mute-control
//
int errVal = LATE(snd_mixer_selem_get_playback_switch)(
_outputMixerElement, (snd_mixer_selem_channel_id_t)0, &value);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error getting playback switch: "
<< LATE(snd_strerror)(errVal);
return -1;
}
// Note value = 0 (off) means muted
enabled = (bool)!value;
return 0;
}
int32_t AudioMixerManagerLinuxALSA::MicrophoneMuteIsAvailable(bool& available) {
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
available = LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::SetMicrophoneMute(bool enable) {
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetMicrophoneMute(enable="
<< enable << ")";
MutexLock lock(&mutex_);
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
// Ensure that the selected microphone destination has a valid mute control.
bool available(false);
MicrophoneMuteIsAvailable(available);
if (!available) {
RTC_LOG(LS_WARNING) << "it is not possible to mute the microphone";
return -1;
}
// Note value = 0 (off) means muted
int errVal =
LATE(snd_mixer_selem_set_capture_switch_all)(_inputMixerElement, !enable);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error setting capture switch: "
<< LATE(snd_strerror)(errVal);
return -1;
}
return (0);
}
int32_t AudioMixerManagerLinuxALSA::MicrophoneMute(bool& enabled) const {
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer exists";
return -1;
}
// Ensure that the selected microphone destination has a valid mute control.
bool available = LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement);
if (!available) {
RTC_LOG(LS_WARNING) << "it is not possible to mute the microphone";
return -1;
}
int value(false);
// Retrieve one boolean control value for a specified mute-control
//
int errVal = LATE(snd_mixer_selem_get_capture_switch)(
_inputMixerElement, (snd_mixer_selem_channel_id_t)0, &value);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error getting capture switch: "
<< LATE(snd_strerror)(errVal);
return -1;
}
// Note value = 0 (off) means muted
enabled = (bool)!value;
return 0;
}
int32_t AudioMixerManagerLinuxALSA::MicrophoneVolumeIsAvailable(
bool& available) {
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
available = LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::SetMicrophoneVolume(uint32_t volume) {
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxALSA::SetMicrophoneVolume(volume=" << volume
<< ")";
MutexLock lock(&mutex_);
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
int errVal =
LATE(snd_mixer_selem_set_capture_volume_all)(_inputMixerElement, volume);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error changing microphone volume: "
<< LATE(snd_strerror)(errVal);
return -1;
}
return (0);
}
// TL: Have done testnig with these but they don't seem reliable and
// they were therefore not added
/*
// ----------------------------------------------------------------------------
// SetMaxMicrophoneVolume
// ----------------------------------------------------------------------------
int32_t AudioMixerManagerLinuxALSA::SetMaxMicrophoneVolume(
uint32_t maxVolume)
{
if (_inputMixerElement == NULL)
{
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = snd_mixer_selem_get_capture_volume_range(_inputMixerElement,
&minVol, &maxVol);
if ((maxVol <= minVol) || (errVal != 0))
{
RTC_LOG(LS_WARNING) << "Error getting capture volume range: "
<< snd_strerror(errVal);
}
maxVol = (long int)maxVolume;
printf("min %d max %d", minVol, maxVol);
errVal = snd_mixer_selem_set_capture_volume_range(_inputMixerElement, minVol,
maxVol); RTC_LOG(LS_VERBOSE) << "Capture hardware volume range, min: " <<
minVol
<< ", max: " << maxVol;
if (errVal != 0)
{
RTC_LOG(LS_ERROR) << "Error setting capture volume range: "
<< snd_strerror(errVal);
return -1;
}
return 0;
}
// ----------------------------------------------------------------------------
// SetMinMicrophoneVolume
// ----------------------------------------------------------------------------
int32_t AudioMixerManagerLinuxALSA::SetMinMicrophoneVolume(
uint32_t minVolume)
{
if (_inputMixerElement == NULL)
{
RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = snd_mixer_selem_get_capture_volume_range(
_inputMixerElement, &minVol, &maxVol);
if (maxVol <= minVol)
{
//maxVol = 255;
RTC_LOG(LS_WARNING) << "Error getting capture volume range: "
<< snd_strerror(errVal);
}
printf("min %d max %d", minVol, maxVol);
minVol = (long int)minVolume;
errVal = snd_mixer_selem_set_capture_volume_range(
_inputMixerElement, minVol, maxVol);
RTC_LOG(LS_VERBOSE) << "Capture hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (errVal != 0)
{
RTC_LOG(LS_ERROR) << "Error setting capture volume range: "
<< snd_strerror(errVal);
return -1;
}
return 0;
}
*/
int32_t AudioMixerManagerLinuxALSA::MicrophoneVolume(uint32_t& volume) const {
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
long int vol(0);
int errVal = LATE(snd_mixer_selem_get_capture_volume)(
_inputMixerElement, (snd_mixer_selem_channel_id_t)0, &vol);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "Error getting inputvolume: "
<< LATE(snd_strerror)(errVal);
return -1;
}
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxALSA::MicrophoneVolume() => vol=" << vol;
volume = static_cast<uint32_t>(vol);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::MaxMicrophoneVolume(
uint32_t& maxVolume) const {
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
// check if we have mic volume at all
if (!LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement)) {
RTC_LOG(LS_ERROR) << "No microphone volume available";
return -1;
}
int errVal = LATE(snd_mixer_selem_get_capture_volume_range)(
_inputMixerElement, &minVol, &maxVol);
RTC_LOG(LS_VERBOSE) << "Microphone hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (maxVol <= minVol) {
RTC_LOG(LS_ERROR) << "Error getting microphone volume range: "
<< LATE(snd_strerror)(errVal);
}
maxVolume = static_cast<uint32_t>(maxVol);
return 0;
}
int32_t AudioMixerManagerLinuxALSA::MinMicrophoneVolume(
uint32_t& minVolume) const {
if (_inputMixerElement == NULL) {
RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists";
return -1;
}
long int minVol(0);
long int maxVol(0);
int errVal = LATE(snd_mixer_selem_get_capture_volume_range)(
_inputMixerElement, &minVol, &maxVol);
RTC_LOG(LS_VERBOSE) << "Microphone hardware volume range, min: " << minVol
<< ", max: " << maxVol;
if (maxVol <= minVol) {
RTC_LOG(LS_ERROR) << "Error getting microphone volume range: "
<< LATE(snd_strerror)(errVal);
}
minVolume = static_cast<uint32_t>(minVol);
return 0;
}
// ============================================================================
// Private Methods
// ============================================================================
int32_t AudioMixerManagerLinuxALSA::LoadMicMixerElement() const {
int errVal = LATE(snd_mixer_load)(_inputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "snd_mixer_load(_inputMixerHandle), error: "
<< LATE(snd_strerror)(errVal);
_inputMixerHandle = NULL;
return -1;
}
snd_mixer_elem_t* elem = NULL;
snd_mixer_elem_t* micElem = NULL;
unsigned mixerIdx = 0;
const char* selemName = NULL;
// Find and store handles to the right mixer elements
for (elem = LATE(snd_mixer_first_elem)(_inputMixerHandle); elem;
elem = LATE(snd_mixer_elem_next)(elem), mixerIdx++) {
if (LATE(snd_mixer_selem_is_active)(elem)) {
selemName = LATE(snd_mixer_selem_get_name)(elem);
if (strcmp(selemName, "Capture") == 0) // "Capture", "Mic"
{
_inputMixerElement = elem;
RTC_LOG(LS_VERBOSE) << "Capture element set";
} else if (strcmp(selemName, "Mic") == 0) {
micElem = elem;
RTC_LOG(LS_VERBOSE) << "Mic element found";
}
}
if (_inputMixerElement) {
// Use the first Capture element that is found
// The second one may not work
break;
}
}
if (_inputMixerElement == NULL) {
// We didn't find a Capture handle, use Mic.
if (micElem != NULL) {
_inputMixerElement = micElem;
RTC_LOG(LS_VERBOSE) << "Using Mic as capture volume.";
} else {
_inputMixerElement = NULL;
RTC_LOG(LS_ERROR) << "Could not find capture volume on the mixer.";
return -1;
}
}
return 0;
}
int32_t AudioMixerManagerLinuxALSA::LoadSpeakerMixerElement() const {
int errVal = LATE(snd_mixer_load)(_outputMixerHandle);
if (errVal < 0) {
RTC_LOG(LS_ERROR) << "snd_mixer_load(_outputMixerHandle), error: "
<< LATE(snd_strerror)(errVal);
_outputMixerHandle = NULL;
return -1;
}
snd_mixer_elem_t* elem = NULL;
snd_mixer_elem_t* masterElem = NULL;
snd_mixer_elem_t* speakerElem = NULL;
unsigned mixerIdx = 0;
const char* selemName = NULL;
// Find and store handles to the right mixer elements
for (elem = LATE(snd_mixer_first_elem)(_outputMixerHandle); elem;
elem = LATE(snd_mixer_elem_next)(elem), mixerIdx++) {
if (LATE(snd_mixer_selem_is_active)(elem)) {
selemName = LATE(snd_mixer_selem_get_name)(elem);
RTC_LOG(LS_VERBOSE) << "snd_mixer_selem_get_name " << mixerIdx << ": "
<< selemName << " =" << elem;
// "Master", "PCM", "Wave", "Master Mono", "PC Speaker", "PCM", "Wave"
if (strcmp(selemName, "PCM") == 0) {
_outputMixerElement = elem;
RTC_LOG(LS_VERBOSE) << "PCM element set";
} else if (strcmp(selemName, "Master") == 0) {
masterElem = elem;
RTC_LOG(LS_VERBOSE) << "Master element found";
} else if (strcmp(selemName, "Speaker") == 0) {
speakerElem = elem;
RTC_LOG(LS_VERBOSE) << "Speaker element found";
}
}
if (_outputMixerElement) {
// We have found the element we want
break;
}
}
// If we didn't find a PCM Handle, use Master or Speaker
if (_outputMixerElement == NULL) {
if (masterElem != NULL) {
_outputMixerElement = masterElem;
RTC_LOG(LS_VERBOSE) << "Using Master as output volume.";
} else if (speakerElem != NULL) {
_outputMixerElement = speakerElem;
RTC_LOG(LS_VERBOSE) << "Using Speaker as output volume.";
} else {
_outputMixerElement = NULL;
RTC_LOG(LS_ERROR) << "Could not find output volume in the mixer.";
return -1;
}
}
return 0;
}
void AudioMixerManagerLinuxALSA::GetControlName(char* controlName,
char* deviceName) const {
// Example
// deviceName: "front:CARD=Intel,DEV=0"
// controlName: "hw:CARD=Intel"
char* pos1 = strchr(deviceName, ':');
char* pos2 = strchr(deviceName, ',');
if (!pos2) {
// Can also be default:CARD=Intel
pos2 = &deviceName[strlen(deviceName)];
}
if (pos1 && pos2) {
strcpy(controlName, "hw");
int nChar = (int)(pos2 - pos1);
strncpy(&controlName[2], pos1, nChar);
controlName[2 + nChar] = '\0';
} else {
strcpy(controlName, deviceName);
}
}
} // namespace webrtc

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2011 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.
*/
#ifndef AUDIO_DEVICE_AUDIO_MIXER_MANAGER_ALSA_LINUX_H_
#define AUDIO_DEVICE_AUDIO_MIXER_MANAGER_ALSA_LINUX_H_
#include <alsa/asoundlib.h>
#include "modules/audio_device/include/audio_device.h"
#include "modules/audio_device/linux/alsasymboltable_linux.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
class AudioMixerManagerLinuxALSA {
public:
int32_t OpenSpeaker(char* deviceName) RTC_LOCKS_EXCLUDED(mutex_);
int32_t OpenMicrophone(char* deviceName) RTC_LOCKS_EXCLUDED(mutex_);
int32_t SetSpeakerVolume(uint32_t volume) RTC_LOCKS_EXCLUDED(mutex_);
int32_t SpeakerVolume(uint32_t& volume) const;
int32_t MaxSpeakerVolume(uint32_t& maxVolume) const;
int32_t MinSpeakerVolume(uint32_t& minVolume) const;
int32_t SpeakerVolumeIsAvailable(bool& available);
int32_t SpeakerMuteIsAvailable(bool& available);
int32_t SetSpeakerMute(bool enable) RTC_LOCKS_EXCLUDED(mutex_);
int32_t SpeakerMute(bool& enabled) const;
int32_t MicrophoneMuteIsAvailable(bool& available);
int32_t SetMicrophoneMute(bool enable) RTC_LOCKS_EXCLUDED(mutex_);
int32_t MicrophoneMute(bool& enabled) const;
int32_t MicrophoneVolumeIsAvailable(bool& available);
int32_t SetMicrophoneVolume(uint32_t volume) RTC_LOCKS_EXCLUDED(mutex_);
int32_t MicrophoneVolume(uint32_t& volume) const;
int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const;
int32_t MinMicrophoneVolume(uint32_t& minVolume) const;
int32_t Close() RTC_LOCKS_EXCLUDED(mutex_);
int32_t CloseSpeaker() RTC_LOCKS_EXCLUDED(mutex_);
int32_t CloseMicrophone() RTC_LOCKS_EXCLUDED(mutex_);
bool SpeakerIsInitialized() const;
bool MicrophoneIsInitialized() const;
public:
AudioMixerManagerLinuxALSA();
~AudioMixerManagerLinuxALSA();
private:
int32_t CloseSpeakerLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t CloseMicrophoneLocked() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
int32_t LoadMicMixerElement() const;
int32_t LoadSpeakerMixerElement() const;
void GetControlName(char* controlName, char* deviceName) const;
private:
Mutex mutex_;
mutable snd_mixer_t* _outputMixerHandle;
char _outputMixerStr[kAdmMaxDeviceNameSize];
mutable snd_mixer_t* _inputMixerHandle;
char _inputMixerStr[kAdmMaxDeviceNameSize];
mutable snd_mixer_elem_t* _outputMixerElement;
mutable snd_mixer_elem_t* _inputMixerElement;
};
} // namespace webrtc
#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_MIXER_MANAGER_ALSA_LINUX_H_

View file

@ -0,0 +1,844 @@
/*
* 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 "modules/audio_device/linux/audio_mixer_manager_pulse_linux.h"
#include <stddef.h>
#include "modules/audio_device/linux/audio_device_pulse_linux.h"
#include "modules/audio_device/linux/latebindingsymboltable_linux.h"
#include "modules/audio_device/linux/pulseaudiosymboltable_linux.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
// 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, \
GetPulseSymbolTable(), 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) {
RTC_DLOG(LS_INFO) << __FUNCTION__ << " created";
}
AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse() {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed";
Close();
}
// ===========================================================================
// PUBLIC METHODS
// ===========================================================================
int32_t AudioMixerManagerLinuxPulse::SetPulseAudioObjects(
pa_threaded_mainloop* mainloop,
pa_context* context) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
if (!mainloop || !context) {
RTC_LOG(LS_ERROR) << "could not set PulseAudio objects for mixer";
return -1;
}
_paMainloop = mainloop;
_paContext = context;
_paObjectsSet = true;
RTC_LOG(LS_VERBOSE) << "the PulseAudio objects for the mixer has been set";
return 0;
}
int32_t AudioMixerManagerLinuxPulse::Close() {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
CloseSpeaker();
CloseMicrophone();
_paMainloop = NULL;
_paContext = NULL;
_paObjectsSet = false;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::CloseSpeaker() {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
// Reset the index to -1
_paOutputDeviceIndex = -1;
_paPlayStream = NULL;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::CloseMicrophone() {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(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_.IsCurrent());
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)";
_paPlayStream = playStream;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetRecStream(recStream)";
_paRecStream = recStream;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::OpenSpeaker(uint16_t deviceIndex) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex="
<< deviceIndex << ")";
// No point in opening the speaker
// if PA objects have not been set
if (!_paObjectsSet) {
RTC_LOG(LS_ERROR) << "PulseAudio objects has not been set";
return -1;
}
// Set the index for the PulseAudio
// output device to control
_paOutputDeviceIndex = deviceIndex;
RTC_LOG(LS_VERBOSE) << "the output mixer device is now open";
return 0;
}
int32_t AudioMixerManagerLinuxPulse::OpenMicrophone(uint16_t deviceIndex) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex="
<< deviceIndex << ")";
// No point in opening the microphone
// if PA objects have not been set
if (!_paObjectsSet) {
RTC_LOG(LS_ERROR) << "PulseAudio objects have not been set";
return -1;
}
// Set the index for the PulseAudio
// input device to control
_paInputDeviceIndex = deviceIndex;
RTC_LOG(LS_VERBOSE) << "the input mixer device is now open";
return 0;
}
bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(LS_INFO) << __FUNCTION__;
return (_paOutputDeviceIndex != -1);
}
bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_DLOG(LS_INFO) << __FUNCTION__;
return (_paInputDeviceIndex != -1);
}
int32_t AudioMixerManagerLinuxPulse::SetSpeakerVolume(uint32_t volume) {
RTC_DCHECK(thread_checker_.IsCurrent());
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume="
<< volume << ")";
if (_paOutputDeviceIndex == -1) {
RTC_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) {
RTC_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) {
RTC_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) {
RTC_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;
}
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SpeakerVolume() => vol="
<< volume;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::MaxSpeakerVolume(
uint32_t& maxVolume) const {
if (_paOutputDeviceIndex == -1) {
RTC_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) {
RTC_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_.IsCurrent());
if (_paOutputDeviceIndex == -1) {
RTC_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_.IsCurrent());
if (_paOutputDeviceIndex == -1) {
RTC_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_.IsCurrent());
RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable="
<< enable << ")";
if (_paOutputDeviceIndex == -1) {
RTC_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) {
RTC_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) {
RTC_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;
}
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::SpeakerMute() => enabled=" << enabled;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available) {
RTC_DCHECK(thread_checker_.IsCurrent());
if (_paOutputDeviceIndex == -1) {
RTC_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_.IsCurrent());
if (_paInputDeviceIndex == -1) {
RTC_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);
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()"
" => available="
<< available;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable(
bool& available) {
RTC_DCHECK(thread_checker_.IsCurrent());
if (_paInputDeviceIndex == -1) {
RTC_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_.IsCurrent());
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=" << enable
<< ")";
if (_paInputDeviceIndex == -1) {
RTC_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) {
RTC_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_.IsCurrent());
if (_paInputDeviceIndex == -1) {
RTC_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);
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::MicrophoneMute() => enabled=" << enabled;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable(
bool& available) {
RTC_DCHECK(thread_checker_.IsCurrent());
if (_paInputDeviceIndex == -1) {
RTC_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) {
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=" << volume
<< ")";
if (_paInputDeviceIndex == -1) {
RTC_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) {
RTC_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) {
RTC_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);
}
RTC_LOG(LS_VERBOSE)
<< "AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=" << volume;
return 0;
}
int32_t AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(
uint32_t& maxVolume) const {
if (_paInputDeviceIndex == -1) {
RTC_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) {
RTC_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) {
RTC_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;
}
} // namespace webrtc

View file

@ -0,0 +1,114 @@
/*
* 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.
*/
#ifndef AUDIO_DEVICE_AUDIO_MIXER_MANAGER_PULSE_LINUX_H_
#define AUDIO_DEVICE_AUDIO_MIXER_MANAGER_PULSE_LINUX_H_
#include <pulse/pulseaudio.h>
#include <stdint.h>
#include "api/sequence_checker.h"
#ifndef UINT32_MAX
#define UINT32_MAX ((uint32_t)-1)
#endif
namespace webrtc {
class AudioMixerManagerLinuxPulse {
public:
int32_t SetPlayStream(pa_stream* playStream);
int32_t SetRecStream(pa_stream* recStream);
int32_t OpenSpeaker(uint16_t deviceIndex);
int32_t OpenMicrophone(uint16_t deviceIndex);
int32_t SetSpeakerVolume(uint32_t volume);
int32_t SpeakerVolume(uint32_t& volume) const;
int32_t MaxSpeakerVolume(uint32_t& maxVolume) const;
int32_t MinSpeakerVolume(uint32_t& minVolume) const;
int32_t SpeakerVolumeIsAvailable(bool& available);
int32_t SpeakerMuteIsAvailable(bool& available);
int32_t SetSpeakerMute(bool enable);
int32_t StereoPlayoutIsAvailable(bool& available);
int32_t StereoRecordingIsAvailable(bool& available);
int32_t SpeakerMute(bool& enabled) const;
int32_t MicrophoneMuteIsAvailable(bool& available);
int32_t SetMicrophoneMute(bool enable);
int32_t MicrophoneMute(bool& enabled) const;
int32_t MicrophoneVolumeIsAvailable(bool& available);
int32_t SetMicrophoneVolume(uint32_t volume);
int32_t MicrophoneVolume(uint32_t& volume) const;
int32_t MaxMicrophoneVolume(uint32_t& maxVolume) const;
int32_t MinMicrophoneVolume(uint32_t& minVolume) const;
int32_t SetPulseAudioObjects(pa_threaded_mainloop* mainloop,
pa_context* context);
int32_t Close();
int32_t CloseSpeaker();
int32_t CloseMicrophone();
bool SpeakerIsInitialized() const;
bool MicrophoneIsInitialized() const;
public:
AudioMixerManagerLinuxPulse();
~AudioMixerManagerLinuxPulse();
private:
static void PaSinkInfoCallback(pa_context* c,
const pa_sink_info* i,
int eol,
void* pThis);
static void PaSinkInputInfoCallback(pa_context* c,
const pa_sink_input_info* i,
int eol,
void* pThis);
static void PaSourceInfoCallback(pa_context* c,
const pa_source_info* i,
int eol,
void* pThis);
static void PaSetVolumeCallback(pa_context* /*c*/,
int success,
void* /*pThis*/);
void PaSinkInfoCallbackHandler(const pa_sink_info* i, int eol);
void PaSinkInputInfoCallbackHandler(const pa_sink_input_info* i, int eol);
void PaSourceInfoCallbackHandler(const pa_source_info* i, int eol);
void WaitForOperationCompletion(pa_operation* paOperation) const;
bool GetSinkInputInfo() const;
bool GetSinkInfoByIndex(int device_index) const;
bool GetSourceInfoByIndex(int device_index) const;
private:
int16_t _paOutputDeviceIndex;
int16_t _paInputDeviceIndex;
pa_stream* _paPlayStream;
pa_stream* _paRecStream;
pa_threaded_mainloop* _paMainloop;
pa_context* _paContext;
mutable uint32_t _paVolume;
mutable uint32_t _paMute;
mutable uint32_t _paVolSteps;
bool _paSpeakerMute;
mutable uint32_t _paSpeakerVolume;
mutable uint8_t _paChannels;
bool _paObjectsSet;
// Stores thread ID in constructor.
// We can then use RTC_DCHECK_RUN_ON(&worker_thread_checker_) to ensure that
// other methods are called from the same thread.
// Currently only does RTC_DCHECK(thread_checker_.IsCurrent()).
SequenceChecker thread_checker_;
};
} // namespace webrtc
#endif // MODULES_AUDIO_DEVICE_MAIN_SOURCE_LINUX_AUDIO_MIXER_MANAGER_PULSE_LINUX_H_

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2010 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 "modules/audio_device/linux/latebindingsymboltable_linux.h"
#include "absl/strings/string_view.h"
#include "rtc_base/logging.h"
#ifdef WEBRTC_LINUX
#include <dlfcn.h>
#endif
namespace webrtc {
namespace adm_linux {
inline static const char* GetDllError() {
#ifdef WEBRTC_LINUX
char* err = dlerror();
if (err) {
return err;
} else {
return "No error";
}
#else
#error Not implemented
#endif
}
DllHandle InternalLoadDll(absl::string_view dll_name) {
#ifdef WEBRTC_LINUX
DllHandle handle = dlopen(std::string(dll_name).c_str(), RTLD_NOW);
#else
#error Not implemented
#endif
if (handle == kInvalidDllHandle) {
RTC_LOG(LS_WARNING) << "Can't load " << dll_name << " : " << GetDllError();
}
return handle;
}
void InternalUnloadDll(DllHandle handle) {
#ifdef WEBRTC_LINUX
// TODO(pbos): Remove this dlclose() exclusion when leaks and suppressions from
// here are gone (or AddressSanitizer can display them properly).
//
// Skip dlclose() on AddressSanitizer as leaks including this module in the
// stack trace gets displayed as <unknown module> instead of the actual library
// -> it can not be suppressed.
// https://code.google.com/p/address-sanitizer/issues/detail?id=89
#if !defined(ADDRESS_SANITIZER)
if (dlclose(handle) != 0) {
RTC_LOG(LS_ERROR) << GetDllError();
}
#endif // !defined(ADDRESS_SANITIZER)
#else
#error Not implemented
#endif
}
static bool LoadSymbol(DllHandle handle,
absl::string_view symbol_name,
void** symbol) {
#ifdef WEBRTC_LINUX
*symbol = dlsym(handle, std::string(symbol_name).c_str());
char* err = dlerror();
if (err) {
RTC_LOG(LS_ERROR) << "Error loading symbol " << symbol_name << " : " << err;
return false;
} else if (!*symbol) {
RTC_LOG(LS_ERROR) << "Symbol " << symbol_name << " is NULL";
return false;
}
return true;
#else
#error Not implemented
#endif
}
// This routine MUST assign SOME value for every symbol, even if that value is
// NULL, or else some symbols may be left with uninitialized data that the
// caller may later interpret as a valid address.
bool InternalLoadSymbols(DllHandle handle,
int num_symbols,
const char* const symbol_names[],
void* symbols[]) {
#ifdef WEBRTC_LINUX
// Clear any old errors.
dlerror();
#endif
for (int i = 0; i < num_symbols; ++i) {
if (!LoadSymbol(handle, symbol_names[i], &symbols[i])) {
return false;
}
}
return true;
}
} // namespace adm_linux
} // namespace webrtc

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2010 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.
*/
#ifndef AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_LINUX_H_
#define AUDIO_DEVICE_LATEBINDINGSYMBOLTABLE_LINUX_H_
#include <stddef.h> // for NULL
#include <string.h>
#include "absl/strings/string_view.h"
#include "rtc_base/checks.h"
// This file provides macros for creating "symbol table" classes to simplify the
// dynamic loading of symbols from DLLs. Currently the implementation only
// supports Linux and pure C symbols.
// See talk/sound/pulseaudiosymboltable.(h|cc) for an example.
namespace webrtc {
namespace adm_linux {
#ifdef WEBRTC_LINUX
typedef void* DllHandle;
const DllHandle kInvalidDllHandle = NULL;
#else
#error Not implemented
#endif
// These are helpers for use only by the class below.
DllHandle InternalLoadDll(absl::string_view);
void InternalUnloadDll(DllHandle handle);
bool InternalLoadSymbols(DllHandle handle,
int num_symbols,
const char* const symbol_names[],
void* symbols[]);
template <int SYMBOL_TABLE_SIZE,
const char kDllName[],
const char* const kSymbolNames[]>
class LateBindingSymbolTable {
public:
LateBindingSymbolTable()
: handle_(kInvalidDllHandle), undefined_symbols_(false) {
memset(symbols_, 0, sizeof(symbols_));
}
~LateBindingSymbolTable() { Unload(); }
LateBindingSymbolTable(const LateBindingSymbolTable&) = delete;
LateBindingSymbolTable& operator=(LateBindingSymbolTable&) = delete;
static int NumSymbols() { return SYMBOL_TABLE_SIZE; }
// We do not use this, but we offer it for theoretical convenience.
static const char* GetSymbolName(int index) {
RTC_DCHECK_LT(index, NumSymbols());
return kSymbolNames[index];
}
bool IsLoaded() const { return handle_ != kInvalidDllHandle; }
// Loads the DLL and the symbol table. Returns true iff the DLL and symbol
// table loaded successfully.
bool Load() {
if (IsLoaded()) {
return true;
}
if (undefined_symbols_) {
// We do not attempt to load again because repeated attempts are not
// likely to succeed and DLL loading is costly.
return false;
}
handle_ = InternalLoadDll(kDllName);
if (!IsLoaded()) {
return false;
}
if (!InternalLoadSymbols(handle_, NumSymbols(), kSymbolNames, symbols_)) {
undefined_symbols_ = true;
Unload();
return false;
}
return true;
}
void Unload() {
if (!IsLoaded()) {
return;
}
InternalUnloadDll(handle_);
handle_ = kInvalidDllHandle;
memset(symbols_, 0, sizeof(symbols_));
}
// Retrieves the given symbol. NOTE: Recommended to use LATESYM_GET below
// instead of this.
void* GetSymbol(int index) const {
RTC_DCHECK(IsLoaded());
RTC_DCHECK_LT(index, NumSymbols());
return symbols_[index];
}
private:
DllHandle handle_;
bool undefined_symbols_;
void* symbols_[SYMBOL_TABLE_SIZE];
};
// This macro must be invoked in a header to declare a symbol table class.
#define LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(ClassName) enum {
// This macro must be invoked in the header declaration once for each symbol
// (recommended to use an X-Macro to avoid duplication).
// This macro defines an enum with names built from the symbols, which
// essentially creates a hash table in the compiler from symbol names to their
// indices in the symbol table class.
#define LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(ClassName, sym) \
ClassName##_SYMBOL_TABLE_INDEX_##sym,
// This macro completes the header declaration.
#define LATE_BINDING_SYMBOL_TABLE_DECLARE_END(ClassName) \
ClassName##_SYMBOL_TABLE_SIZE \
} \
; \
\
extern const char ClassName##_kDllName[]; \
extern const char* const \
ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE]; \
\
typedef ::webrtc::adm_linux::LateBindingSymbolTable< \
ClassName##_SYMBOL_TABLE_SIZE, ClassName##_kDllName, \
ClassName##_kSymbolNames> \
ClassName;
// This macro must be invoked in a .cc file to define a previously-declared
// symbol table class.
#define LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(ClassName, dllName) \
const char ClassName##_kDllName[] = dllName; \
const char* const ClassName##_kSymbolNames[ClassName##_SYMBOL_TABLE_SIZE] = {
// This macro must be invoked in the .cc definition once for each symbol
// (recommended to use an X-Macro to avoid duplication).
// This would have to use the mangled name if we were to ever support C++
// symbols.
#define LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(ClassName, sym) #sym,
#define LATE_BINDING_SYMBOL_TABLE_DEFINE_END(ClassName) \
} \
;
// Index of a given symbol in the given symbol table class.
#define LATESYM_INDEXOF(ClassName, sym) (ClassName##_SYMBOL_TABLE_INDEX_##sym)
// Returns a reference to the given late-binded symbol, with the correct type.
#define LATESYM_GET(ClassName, inst, sym) \
(*reinterpret_cast<__typeof__(&sym)>( \
(inst)->GetSymbol(LATESYM_INDEXOF(ClassName, sym))))
} // namespace adm_linux
} // namespace webrtc
#endif // ADM_LATEBINDINGSYMBOLTABLE_LINUX_H_

View file

@ -0,0 +1,41 @@
/*
* libjingle
* Copyright 2004--2010, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "modules/audio_device/linux/pulseaudiosymboltable_linux.h"
namespace webrtc {
namespace adm_linux_pulse {
LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGIN(PulseAudioSymbolTable, "libpulse.so.0")
#define X(sym) \
LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY(PulseAudioSymbolTable, sym)
PULSE_AUDIO_SYMBOLS_LIST
#undef X
LATE_BINDING_SYMBOL_TABLE_DEFINE_END(PulseAudioSymbolTable)
} // namespace adm_linux_pulse
} // namespace webrtc

View file

@ -0,0 +1,106 @@
/*
* libjingle
* Copyright 2004--2010, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef AUDIO_DEVICE_PULSEAUDIOSYMBOLTABLE_LINUX_H_
#define AUDIO_DEVICE_PULSEAUDIOSYMBOLTABLE_LINUX_H_
#include "modules/audio_device/linux/latebindingsymboltable_linux.h"
namespace webrtc {
namespace adm_linux_pulse {
// The PulseAudio symbols we need, as an X-Macro list.
// This list must contain precisely every libpulse function that is used in
// the ADM LINUX PULSE Device and Mixer classes
#define PULSE_AUDIO_SYMBOLS_LIST \
X(pa_bytes_per_second) \
X(pa_context_connect) \
X(pa_context_disconnect) \
X(pa_context_errno) \
X(pa_context_get_protocol_version) \
X(pa_context_get_server_info) \
X(pa_context_get_sink_info_list) \
X(pa_context_get_sink_info_by_index) \
X(pa_context_get_sink_info_by_name) \
X(pa_context_get_sink_input_info) \
X(pa_context_get_source_info_by_index) \
X(pa_context_get_source_info_by_name) \
X(pa_context_get_source_info_list) \
X(pa_context_get_state) \
X(pa_context_new) \
X(pa_context_set_sink_input_volume) \
X(pa_context_set_sink_input_mute) \
X(pa_context_set_source_volume_by_index) \
X(pa_context_set_source_mute_by_index) \
X(pa_context_set_state_callback) \
X(pa_context_unref) \
X(pa_cvolume_set) \
X(pa_operation_get_state) \
X(pa_operation_unref) \
X(pa_stream_connect_playback) \
X(pa_stream_connect_record) \
X(pa_stream_disconnect) \
X(pa_stream_drop) \
X(pa_stream_get_device_index) \
X(pa_stream_get_index) \
X(pa_stream_get_latency) \
X(pa_stream_get_sample_spec) \
X(pa_stream_get_state) \
X(pa_stream_new) \
X(pa_stream_peek) \
X(pa_stream_readable_size) \
X(pa_stream_set_buffer_attr) \
X(pa_stream_set_overflow_callback) \
X(pa_stream_set_read_callback) \
X(pa_stream_set_state_callback) \
X(pa_stream_set_underflow_callback) \
X(pa_stream_set_write_callback) \
X(pa_stream_unref) \
X(pa_stream_writable_size) \
X(pa_stream_write) \
X(pa_strerror) \
X(pa_threaded_mainloop_free) \
X(pa_threaded_mainloop_get_api) \
X(pa_threaded_mainloop_lock) \
X(pa_threaded_mainloop_new) \
X(pa_threaded_mainloop_signal) \
X(pa_threaded_mainloop_start) \
X(pa_threaded_mainloop_stop) \
X(pa_threaded_mainloop_unlock) \
X(pa_threaded_mainloop_wait)
LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGIN(PulseAudioSymbolTable)
#define X(sym) \
LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY(PulseAudioSymbolTable, sym)
PULSE_AUDIO_SYMBOLS_LIST
#undef X
LATE_BINDING_SYMBOL_TABLE_DECLARE_END(PulseAudioSymbolTable)
} // namespace adm_linux_pulse
} // namespace webrtc
#endif // AUDIO_DEVICE_PULSEAUDIOSYMBOLTABLE_LINUX_H_