Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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_
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
|
|
@ -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
|
||||
|
|
@ -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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue