ADM(AudioDeviceModule)在 WebRTC 中主要用于音频数据的录制采集和音频数据的播放,这里是 WebRTC 的实时音视频系统与系统的音频硬件衔接的地方。WebRTC 为 Linux 平台实现了 ALSA 和 Pulse 等类型的 ADM AudioDeviceLinuxALSAAudioDeviceLinuxPulse,它们分别基于 Linux 系统提供的库 libasoundlibpulse 实现。

WebRTC 为做到 Linux ADM 实现的高度灵活性,实现了一套符号的延迟加载机制,即在 WebRTC 编译的时候,无需链接 libasoundlibpulse 库,在创建需要的 ADM 对象时,才通过动态链接加载机制加载需要的库及其中的符号。

这套机制在底层基于 Linux 平台的 libdl 提供的 dlopen()dlclose()dlsym()dlerror() 等接口实现动态链接库的动态加载,及符号的查找。更上一层,WebRTC 为每种 ADM 实现基于 libdl 提供的接口,封装一个用于符号延迟加载及访问的 wrapper 类。在 ADM 的具体实现中,借助于一个宏,以类似于普通的符号访问的方式,访问延迟加载的符号。

webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h 中的 libdl 接口的 wrapper 类 LateBindingSymbolTable 声明如下:

#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(const char dll_name[]);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(); }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) {assert(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 {assert(IsLoaded());assert(index < NumSymbols());return symbols_[index];}private:DllHandle handle_;bool undefined_symbols_;void* symbols_[SYMBOL_TABLE_SIZE];RTC_DISALLOW_COPY_AND_ASSIGN(LateBindingSymbolTable);
};

LateBindingSymbolTable 是一个模板类,其类定义中调用的几个用于动态链接库加载和符号查找的函数在 webrtc/modules/audio_device/linux/latebindingsymboltable_linux.cc 文件中的定义如下:

inline static const char* GetDllError() {
#ifdef WEBRTC_LINUXchar* err = dlerror();if (err) {return err;} else {return "No error";}
#else
#error Not implemented
#endif
}DllHandle InternalLoadDll(const char dll_name[]) {
#ifdef WEBRTC_LINUXDllHandle handle = dlopen(dll_name, RTLD_NOW);
#else
#error Not implemented
#endifif (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,const char* symbol_name,void** symbol) {
#ifdef WEBRTC_LINUX*symbol = dlsym(handle, symbol_name);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();
#endiffor (int i = 0; i < num_symbols; ++i) {if (!LoadSymbol(handle, symbol_names[i], &symbols[i])) {return false;}}return true;
}

符号表大小,动态链接库的名称,及符号表的定义是模板类 LateBindingSymbolTable 的模板参数,这几个模板参数都是数值参数,而不是类型参数。LateBindingSymbolTable 也可以不实现为模板类,比如,把符号表大小,动态链接库的名称,及符号表的定义作为构造函数的参数传入 LateBindingSymbolTable 类。但将动态链接库名称、符号表大小及符号表定义做成类的模板参数更方便对这些数据进行访问,同时也有利于为符号地址数组分配空间。

WebRTC 定义了几个宏,用于方便符号表的定义(webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h),具体的 LateBindingSymbolTable 类的特化,及符号的访问:

// 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) \}                                                     \;

LATE_BINDING_SYMBOL_TABLE_DECLARE_BEGINLATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRYLATE_BINDING_SYMBOL_TABLE_DECLARE_END 这几个宏,需要在头文件中调用,它们首先定义了一个 enum,用于得到每个符号在符号表中的索引。LATE_BINDING_SYMBOL_TABLE_DECLARE_END 宏除了给 enum 定义添加结束的 }; 之外,它还为 enum 增加了最后一个 item,用于得到符号表的大小。此外,LATE_BINDING_SYMBOL_TABLE_DECLARE_END 宏还声明了动态链接库名称,符号表,及具体 LateBindingSymbolTable 类的特化。

如 Pulse ADM 的 webrtc/modules/audio_device/linux/pulseaudiosymboltable_linux.h 头文件:

// 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)

这个头文件里,X 宏是对 LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY 的封装。webrtc/modules/audio_device/linux/pulseaudiosymboltable_linux.h 头文件中调用的各个宏都展开之后,得到如下内容:

enum {PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_bytes_per_second,                   \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_connect,                    \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_disconnect,                 \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_errno,                      \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_protocol_version,       \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_server_info,            \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_sink_info_list,         \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_sink_info_by_index,     \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_sink_info_by_name,      \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_sink_input_info,        \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_source_info_by_index,   \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_source_info_by_name,    \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_source_info_list,       \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_get_state,                  \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_new,                        \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_set_sink_input_volume,      \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_set_sink_input_mute,        \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_set_source_volume_by_index, \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_set_source_mute_by_index,   \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_set_state_callback,         \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_context_unref,                      \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_cvolume_set,                        \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_operation_get_state,                \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_operation_unref,                    \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_connect_playback,            \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_connect_record,              \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_disconnect,                  \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_drop,                        \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_get_device_index,            \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_get_index,                   \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_get_latency,                 \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_get_sample_spec,             \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_get_state,                   \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_new,                         \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_peek,                        \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_readable_size,               \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_set_buffer_attr,             \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_set_overflow_callback,       \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_set_read_callback,           \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_set_state_callback,          \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_set_underflow_callback,      \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_set_write_callback,          \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_unref,                       \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_writable_size,               \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_stream_write,                       \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_strerror,                           \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_free,             \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_get_api,          \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_lock,             \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_new,              \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_signal,           \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_start,            \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_stop,             \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_unlock,           \PulseAudioSymbolTable_SYMBOL_TABLE_INDEX_pa_threaded_mainloop_wait,PulseAudioSymbolTable_SYMBOL_TABLE_SIZE                                \}                                                            \;                                                            \\extern const char PulseAudioSymbolTable_kDllName[];                    \extern const char* const                                     \PulseAudioSymbolTable_kSymbolNames[PulseAudioSymbolTable_SYMBOL_TABLE_SIZE]; \\typedef ::webrtc::adm_linux::LateBindingSymbolTable<         \PulseAudioSymbolTable_SYMBOL_TABLE_SIZE, PulseAudioSymbolTable_kDllName,     \PulseAudioSymbolTable_kSymbolNames>                                \PulseAudioSymbolTable;

LATE_BINDING_SYMBOL_TABLE_DEFINE_BEGINLATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRYLATE_BINDING_SYMBOL_TABLE_DEFINE_END 宏,需要在 C++ 源文件中调用,用于定义动态链接库的名称,及符号表。如在 webrtc/modules/audio_device/linux/pulseaudiosymboltable_linux.cc 文件中:

#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

在这个源文件中,X 宏是对 LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY 宏的封装。这些宏展开之后得到如下内容:

const char PulseAudioSymbolTable_kDllName[] = "libpulse.so.0";                     \const char* const PulseAudioSymbolTable_kSymbolNames[PulseAudioSymbolTable_SYMBOL_TABLE_SIZE] = {
"pa_bytes_per_second",                   \"pa_context_connect",                    \"pa_context_disconnect",                 \"pa_context_errno",                      \"pa_context_get_protocol_version",       \"pa_context_get_server_info",            \"pa_context_get_sink_info_list",         \"pa_context_get_sink_info_by_index",     \"pa_context_get_sink_info_by_name",      \"pa_context_get_sink_input_info",        \"pa_context_get_source_info_by_index",   \"pa_context_get_source_info_by_name",    \"pa_context_get_source_info_list",       \"pa_context_get_state",                  \"pa_context_new",                        \"pa_context_set_sink_input_volume",      \"pa_context_set_sink_input_mute",        \"pa_context_set_source_volume_by_index", \"pa_context_set_source_mute_by_index",   \"pa_context_set_state_callback",         \"pa_context_unref",                      \"pa_cvolume_set",                        \"pa_operation_get_state",                \"pa_operation_unref",                    \"pa_stream_connect_playback",            \"pa_stream_connect_record",              \"pa_stream_disconnect",                  \"pa_stream_drop",                        \"pa_stream_get_device_index",            \"pa_stream_get_index",                   \"pa_stream_get_latency",                 \"pa_stream_get_sample_spec",             \"pa_stream_get_state",                   \"pa_stream_new",                         \"pa_stream_peek",                        \"pa_stream_readable_size",               \"pa_stream_set_buffer_attr",             \"pa_stream_set_overflow_callback",       \"pa_stream_set_read_callback",           \"pa_stream_set_state_callback",          \"pa_stream_set_underflow_callback",      \"pa_stream_set_write_callback",          \"pa_stream_unref",                       \"pa_stream_writable_size",               \"pa_stream_write",                       \"pa_strerror",                           \"pa_threaded_mainloop_free",             \"pa_threaded_mainloop_get_api",          \"pa_threaded_mainloop_lock",             \"pa_threaded_mainloop_new",              \"pa_threaded_mainloop_signal",           \"pa_threaded_mainloop_start",            \"pa_threaded_mainloop_stop",             \"pa_threaded_mainloop_unlock",           \"pa_threaded_mainloop_wait",
}                                                     \;

WebRTC 定义了几个宏(webrtc/modules/audio_device/linux/latebindingsymboltable_linux.h),用于方便对符号的调用:

// 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))))

需要访问动态加载的库的符号的组件,需要包含符号声明的头文件,如 AudioDeviceLinuxPulse 类需要调用 Pulse 库的函数,则在头文件webrtc/modules/audio_device/linux/audio_device_pulse_linux.h 中包含了 pulse/pulseaudio.h 头文件。上面看到的 LATESYM_GET 宏,通过符号的声明及编译器的 __typeof__ 扩展得到一个符号的类型,以方便对于函数的调用

LATESYM_GET 宏用于借助具体的 LateBindingSymbolTable 类的对象实例,访问某个函数,LATESYM_INDEXOF 宏用于获得某个符号在符号表中的索引。对动态加载的库的符号的调用,需要配合在源文件中定义的另一个宏来实现,如 webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc 文件中:

webrtc::adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable;// Accesses Pulse functions through our late-binding symbol table instead of
// directly. This way we don't have to link to libpulse, which means our binary
// will work on systems that don't have it.
#define LATE(sym)                                                             \LATESYM_GET(webrtc::adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, \sym)

AudioDeviceLinuxPulse 类的 RecordingDevices() 函数调用 Pulse 库的 pa_context_get_source_info_list() 函数:

int16_t AudioDeviceLinuxPulse::RecordingDevices() {PaLock();pa_operation* paOperation = NULL;_numRecDevices = 1;  // Init to 1 to account for "default"// Get the whole list of devices and update _numRecDevicespaOperation = LATE(pa_context_get_source_info_list)(_paContext, PaSourceInfoCallback, this);WaitForOperationCompletion(paOperation);PaUnLock();return _numRecDevices;
}

总结一下,WebRTC Linux ADM 实现中的符号延迟加载机制由如下这些部分组成:

  • libdl 的模板 wrapper 类
  • 辅助定义具体的 libdl wrapper 类的宏,定义方便访问符号的 enum 的宏,用于声明符号表和动态链接库名称的宏
  • 用于定义动态链接库名称和符号表的宏。
  • 用于访问特定符号的宏。

WebRTC 实现的这套东西存在一些问题:

  1. 重复逻辑没有完全消除。为了访问延迟加载的符号,重复定义了用于访问符号的宏。如在 webrtc/modules/audio_device/linux/audio_device_alsa_linux.ccwebrtc/modules/audio_device/linux/audio_device_pulse_linux.ccwebrtc/modules/audio_device/linux/audio_mixer_manager_alsa_linux.ccwebrtc/modules/audio_device/linux/audio_mixer_manager_pulse_linux.cc 这几个文件中重复定义了 LATE 宏。

  2. 创建具体的 AudioDeviceGeneric 类型对象时,没有判断系统是否真的能够支持。即在 webrtc/modules/audio_device/audio_device_impl.cc 文件中,仅仅根据编译时的宏开关和传入的 audio_layer 参数来决定创建的具体 AudioDeviceGeneric 类型对象,而没有判断系统是否能够支持。

对于上面的第 2 个问题,可以创建一个 AudioManager 类,用于判断系统是否支持,如 AudioManager 的声明如下:

#ifndef MODULES_AUDIO_DEVICE_LINUX_AUDIO_MANAGER_H_
#define MODULES_AUDIO_DEVICE_LINUX_AUDIO_MANAGER_H_namespace webrtc {class AudioManager {
public:AudioManager();virtual ~AudioManager();bool IsALSAAudioSupported();bool IsPulseAudioSupported();
};}  // namespace webrtc#endif /* MODULES_AUDIO_DEVICE_LINUX_AUDIO_MANAGER_H_ */

AudioManager 类的定义如下:

#include "audio_manager.h"#if defined(LINUX_ALSA)
#include "modules/audio_device/linux/alsasymboltable_linux.h"
#endif#if defined(LINUX_PULSE)
#include "modules/audio_device/linux/pulseaudiosymboltable_linux.h"
#endif#if defined(LINUX_ALSA)
extern webrtc::adm_linux_alsa::AlsaSymbolTable AlsaSymbolTable;
#endif#if defined(LINUX_PULSE)
extern webrtc::adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable;
#endifnamespace webrtc {AudioManager::AudioManager() {}AudioManager::~AudioManager() {}bool AudioManager::IsALSAAudioSupported() {
#if defined(LINUX_ALSA)return AlsaSymbolTable.Load();
#elsereturn false;
#endif
}bool AudioManager::IsPulseAudioSupported() {
#if defined(LINUX_PULSE)return PaSymbolTable.Load();
#elsereturn false;
#endif
}}  // namespace webrtc

webrtc/modules/audio_device/audio_device_impl.cc 中的相关逻辑则可以修改为如下这样:

#elif defined(WEBRTC_LINUX)std::unique_ptr<AudioManager> audio_manager(new AudioManager());
#if !defined(LINUX_PULSE)// Build flag 'rtc_include_pulse_audio' is set to false. In this mode:// - kPlatformDefaultAudio => ALSA, and// - kLinuxAlsaAudio => ALSA, and// - kLinuxPulseAudio => Invalid selection.RTC_LOG(WARNING) << "PulseAudio is disabled using build flag.";if ((audio_layer == kLinuxAlsaAudio) ||(audio_layer == kPlatformDefaultAudio)) {audio_device_.reset(new AudioDeviceLinuxALSA());RTC_LOG(INFO) << "Linux ALSA APIs will be utilized.";}
#else// Build flag 'rtc_include_pulse_audio' is set to true (default). In this// mode:// - kPlatformDefaultAudio => PulseAudio, and// - kLinuxPulseAudio => PulseAudio, and// - kLinuxAlsaAudio => ALSA (supported but not default).RTC_LOG(INFO) << "PulseAudio support is enabled.";if (((audio_layer == kLinuxPulseAudio) ||(audio_layer == kPlatformDefaultAudio)) && audio_manager->IsPulseAudioSupported()) {// Linux PulseAudio implementation is default.audio_device_.reset(new AudioDeviceLinuxPulse());RTC_LOG(INFO) << "Linux PulseAudio APIs will be utilized";} else if ((audio_layer == kLinuxAlsaAudio) ||(audio_layer == kPlatformDefaultAudio)) {audio_device_.reset(new AudioDeviceLinuxALSA());RTC_LOG(WARNING) << "Linux ALSA APIs will be utilized.";}
#endif  // #if !defined(LINUX_PULSE)
#endif  // #if defined(WEBRTC_LINUX)

不过这种做法也有一些问题,即在判断系统是否支持时,就提前加载了对应的系统库。

WebRTC Linux ADM 实现中的符号延迟加载机制相关推荐

  1. linux去掉文件中数值符号,去掉linux文本文件中的 ^M 符号

    在linux下,不可避免的会用VIM打开一些windows下编辑过的文本文件.我们会发现文件的每行结尾都会有一个^M符号,这是因为 DOS下的编辑器和Linux编辑器对文件行末的回车符处理不一致, 对 ...

  2. Linux的nm查看动态和静态库中的符号

    功能 列出.o .a .so中的符号信息,包括诸如符号的值,符号类型及符号名称等.所谓符号,通常指定义出的函数,全局变量等等. 使用 nm [option(s)] [file(s)] 有用的optio ...

  3. linux不能识别 符号,在linux中文件中^M符号的问题以及中文识别问题

    ^M符号的问题 在Windows系统下编辑的文件,换行符回车的格式为'\r\n',在linux系统下,回车的格式为'\n',在Windows下编辑的文本文件在上传至linux服务器时,回车'\r\n' ...

  4. 【Linux命令】nm 查看动态库和静态库中的符号

    功能 列出.o .a .so中的符号信息,包括诸如符号的值,符号类型及符号名称等.所谓符号,通常指定义出的函数,全局变量等等. 使用 nm [option(s)] [file(s)] 有用的optio ...

  5. linux文件中出现 m,在linux中文件中^M符号的问题以及中文识别问题

    ^M符号的问题 在Windows系统下编辑的文件,换行符回车的格式为'\r\n',在linux系统下,回车的格式为'\n',在Windows下编辑的文本文件在上传至linux服务器时,回车'\r\n' ...

  6. 在 Linux shell 字符串中,百分比符号是什么?

    当你正在学习如何完全使用 Linux shell 时,你可以能会觉得自己能够对字符串进行操作. 记住,今天q&一个文章的超级用户对读者的一个奇怪问题有答案. 今天,回答会话的问题来自超级用户的 ...

  7. linux中$符号有什么作用,linux中的“$”符号表示什么

    linux中的"$"符号表示什么 发布时间:2020-07-03 09:43:09 来源:亿速云 阅读:124 作者:Leah linux中的"$"符号表示什么 ...

  8. Linux下C++中可使用的3种Hook方法

    Hook即钩子,截获API调用的技术,是将执行流程重定向到你自己的代码,类似于hack.如使程序运行时调用你自己实现的malloc函数代替调用系统库中的malloc函数.这里介绍下Linux下C++中 ...

  9. linux命令行 正则,在Linux命令行中使用正则表达式

    上一节我们介绍过grep命令在搜索文本信息时的基本用法,其实,grep命令还可以配合正则表达式执行更加复杂的搜索操作. 本节我们将对如何在Linux命令行中使用正则表达式进行介绍. 所谓正则表达式,就 ...

最新文章

  1. 不用asp.net MVC,用WebForm照样可以实现MVC(请看最后一句话)
  2. oracle的al32utf8,oracle字符集修改(AL32UTF8-UTF8)
  3. mysql双主数据一致性_MySQL双主一致性架构优化 | 架构师之路-阿里云开发者社区...
  4. Silver Cow Party(POJ-3268)
  5. win7升级到win10不能上网解决方法
  6. golang |问题代码报go并发死锁
  7. 程序猿都应学习的语言:看 25 张图学 UML
  8. 细数微软 Teams 的 14 宗“罪”!
  9. CodeForces - 468A 24 Game
  10. Atitit 获取多媒体mp3 mp4 m4a元数据 G:\桌面安装\paidashi\bin\ffprobe.exe ffprobe -i 1.flv -print_format json -sh
  11. pads layout 无法将dxf文件导入进来的2D Line转换成Board Outline
  12. 74cms搭建 2021.03.13
  13. Make sure that the latest NVIDIA driver is installed and running.
  14. C#批量发送短信操作
  15. 阿里张建锋:互联网主要解决三大问题
  16. GPS nmealib学习笔记 .
  17. Python爬取微信公众号文章、点赞数
  18. R-CNN+SPP-NET+Fast-R-CNN+Faster-R-CNN+YOLO+SSD阅读笔记
  19. Python 命名关键字形参
  20. 数字形态学滤波matlab,数字形态学滤波器与智能车路径记忆

热门文章

  1. 异常-异常捕获的完整语法
  2. Nginx_虚拟主机配置讲解
  3. ExecutorService- Future - Java多线程编程
  4. Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结
  5. IDEA同时使用maven和gradle
  6. Java线程阻塞原语-LockSupport
  7. 应用指定显示到副屏_LED灯杆屏广泛应用逐渐成为智慧城市信息化的重要窗口
  8. 如何在 CSS 中设置组件在浏览器屏幕水平垂直居中
  9. Redis持久化方式的选择
  10. POJ3070 矩阵快速幂模板