WebRTC Linux ADM 实现中的符号延迟加载机制
ADM(AudioDeviceModule)在 WebRTC 中主要用于音頻數(shù)據(jù)的錄制采集和音頻數(shù)據(jù)的播放,這里是 WebRTC 的實時音視頻系統(tǒng)與系統(tǒng)的音頻硬件銜接的地方。WebRTC 為 Linux 平臺實現(xiàn)了 ALSA 和 Pulse 等類型的 ADM AudioDeviceLinuxALSA 和 AudioDeviceLinuxPulse,它們分別基于 Linux 系統(tǒng)提供的庫 libasound 和 libpulse 實現(xiàn)。
WebRTC 為做到 Linux ADM 實現(xiàn)的高度靈活性,實現(xiàn)了一套符號的延遲加載機制,即在 WebRTC 編譯的時候,無需鏈接 libasound 和 libpulse 庫,在創(chuàng)建需要的 ADM 對象時,才通過動態(tài)鏈接加載機制加載需要的庫及其中的符號。
這套機制在底層基于 Linux 平臺的 libdl 提供的 dlopen()、dlclose()、dlsym() 和 dlerror() 等接口實現(xiàn)動態(tài)鏈接庫的動態(tài)加載,及符號的查找。更上一層,WebRTC 為每種 ADM 實現(xiàn)基于 libdl 提供的接口,封裝一個用于符號延遲加載及訪問的 wrapper 類。在 ADM 的具體實現(xiàn)中,借助于一個宏,以類似于普通的符號訪問的方式,訪問延遲加載的符號。
在 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 是一個模板類,其類定義中調(diào)用的幾個用于動態(tài)鏈接庫加載和符號查找的函數(shù)在 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; }符號表大小,動態(tài)鏈接庫的名稱,及符號表的定義是模板類 LateBindingSymbolTable 的模板參數(shù),這幾個模板參數(shù)都是數(shù)值參數(shù),而不是類型參數(shù)。LateBindingSymbolTable 也可以不實現(xiàn)為模板類,比如,把符號表大小,動態(tài)鏈接庫的名稱,及符號表的定義作為構(gòu)造函數(shù)的參數(shù)傳入 LateBindingSymbolTable 類。但將動態(tài)鏈接庫名稱、符號表大小及符號表定義做成類的模板參數(shù)更方便對這些數(shù)據(jù)進行訪問,同時也有利于為符號地址數(shù)組分配空間。
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_BEGIN、LATE_BINDING_SYMBOL_TABLE_DECLARE_ENTRY 和 LATE_BINDING_SYMBOL_TABLE_DECLARE_END 這幾個宏,需要在頭文件中調(diào)用,它們首先定義了一個 enum,用于得到每個符號在符號表中的索引。LATE_BINDING_SYMBOL_TABLE_DECLARE_END 宏除了給 enum 定義添加結(jié)束的 } 和 ; 之外,它還為 enum 增加了最后一個 item,用于得到符號表的大小。此外,LATE_BINDING_SYMBOL_TABLE_DECLARE_END 宏還聲明了動態(tài)鏈接庫名稱,符號表,及具體 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 頭文件中調(diào)用的各個宏都展開之后,得到如下內(nèi)容:
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_BEGIN 、LATE_BINDING_SYMBOL_TABLE_DEFINE_ENTRY 和 LATE_BINDING_SYMBOL_TABLE_DEFINE_END 宏,需要在 C++ 源文件中調(diào)用,用于定義動態(tài)鏈接庫的名稱,及符號表。如在 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 宏的封裝。這些宏展開之后得到如下內(nèi)容:
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),用于方便對符號的調(diào)用:
// 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))))需要訪問動態(tài)加載的庫的符號的組件,需要包含符號聲明的頭文件,如 AudioDeviceLinuxPulse 類需要調(diào)用 Pulse 庫的函數(shù),則在頭文件webrtc/modules/audio_device/linux/audio_device_pulse_linux.h 中包含了 pulse/pulseaudio.h 頭文件。上面看到的 LATESYM_GET 宏,通過符號的聲明及編譯器的 __typeof__ 擴展得到一個符號的類型,以方便對于函數(shù)的調(diào)用。
LATESYM_GET 宏用于借助具體的 LateBindingSymbolTable 類的對象實例,訪問某個函數(shù),LATESYM_INDEXOF 宏用于獲得某個符號在符號表中的索引。對動態(tài)加載的庫的符號的調(diào)用,需要配合在源文件中定義的另一個宏來實現(xiàn),如 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() 函數(shù)調(diào)用 Pulse 庫的 pa_context_get_source_info_list() 函數(shù):
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; }總結(jié)一下,WebRTC Linux ADM 實現(xiàn)中的符號延遲加載機制由如下這些部分組成:
- libdl 的模板 wrapper 類
- 輔助定義具體的 libdl wrapper 類的宏,定義方便訪問符號的 enum 的宏,用于聲明符號表和動態(tài)鏈接庫名稱的宏
- 用于定義動態(tài)鏈接庫名稱和符號表的宏。
- 用于訪問特定符號的宏。
WebRTC 實現(xiàn)的這套東西存在一些問題:
重復(fù)邏輯沒有完全消除。為了訪問延遲加載的符號,重復(fù)定義了用于訪問符號的宏。如在 webrtc/modules/audio_device/linux/audio_device_alsa_linux.cc、webrtc/modules/audio_device/linux/audio_device_pulse_linux.cc、webrtc/modules/audio_device/linux/audio_mixer_manager_alsa_linux.cc、webrtc/modules/audio_device/linux/audio_mixer_manager_pulse_linux.cc 這幾個文件中重復(fù)定義了 LATE 宏。
創(chuàng)建具體的 AudioDeviceGeneric 類型對象時,沒有判斷系統(tǒng)是否真的能夠支持。即在 webrtc/modules/audio_device/audio_device_impl.cc 文件中,僅僅根據(jù)編譯時的宏開關(guān)和傳入的 audio_layer 參數(shù)來決定創(chuàng)建的具體 AudioDeviceGeneric 類型對象,而沒有判斷系統(tǒng)是否能夠支持。
對于上面的第 2 個問題,可以創(chuàng)建一個 AudioManager 類,用于判斷系統(tǒng)是否支持,如 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 中的相關(guān)邏輯則可以修改為如下這樣:
#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)不過這種做法也有一些問題,即在判斷系統(tǒng)是否支持時,就提前加載了對應(yīng)的系統(tǒng)庫。
總結(jié)
以上是生活随笔為你收集整理的WebRTC Linux ADM 实现中的符号延迟加载机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RTC 技术知识体系
- 下一篇: WebRTC 的 log 系统实现分析