前言
在寫【單獨編譯使用WebRTC的音頻處理模塊 - android】一文之前,就一直想直接把WebRTC的音頻處理引擎VoE整個兒編譯出來供自己的項目使用,但限於技術拙劣、時間緊迫,所以沒能成功。只得挨個挨個把引擎中的Aecm、Agc、Ns以及Vad模塊單獨編譯出來湊合着用。雖能達到一定效果,但始終不甚理想。5個月後,bill需要優化之前的項目,於是就下了狠心,定要將整個音頻處理模塊用上 ...
正文
然而本次優化仍然沒能用上整套VoE,因爲VoE不僅僅包含音頻預處理,它將音頻編碼模塊、傳輸模塊一併融入了引擎,而bill的項目需要使用既有的編碼、傳輸層,因此使用整個VoE對我來說顯得冗餘且不可操作。天無絕人之路,拋開VoE不談,bill找到了僅次於VoE層級的模塊 —— APM(Audio Preprocessing Module) —— 一個整合了前文所有模塊且純粹的音頻預處理單元。
Step 1 - 下載Google WebRTC源碼
Google WebRTC的開發進度還是可觀的,本文將以WebRTC的最新trunk revision 5125爲例進行講解。請自行使用SVN同步以下目錄(至於同步的方法,請自行google):
http://webrtc.googlecode.com/svn/trunk/
Step 2 - 提取編譯APM所需資源
APM的整體編譯需要WebRTC源碼目錄下的如下資源:
1)common_audio 整個目錄
2)modules 目錄(不包含 video 部分)
3)system_wrappers 整個目錄
4)位於 WebRTC 源碼根目錄下的 common_types.h | common.h | typedefs.h 三個頭文件。
5)位於 WebRTC 主目錄下的 android-webrtc.mk 文件。
Step 3 - 在Eclipse中編譯APM基礎步驟及部分要點
對於Eclipse中jni的編譯及使用請參見上篇文章所述,在此不再贅述。
此節僅按照自己的jni目錄組織結構進行講解,讀者可根據自己需要進行調整。
在Eclipse中的jni組織結構如下:
Step-2中的所有文件夾及頭文件均位於 webrtc 子目錄下。android-webrtc.mk 位於 jni 根目錄下。
下面我們逐步進行分解:
step 3.1
首先我們需要對整個 android 工程進行描述和設定,打開 jni 根目錄下的 Application.mk 文件,編輯如下:
# Copyright (c) 2013 BillHoo. All Rights Reserved. # 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. APP_STL := gnustl_static APP_CPPFLAGS := -frtti -fexceptions APP_ABI := armeabi armeabi-v7a APP_PLATFORM := android-9
其中 APP_STL 的官方說明如下:
APP_STL By default, the NDK build system provides C++ headers for the minimal C++ runtime library (/system/lib/libstdc++.so) provided by the Android system. However, the NDK comes with alternative C++ implementations that you can use or link to in your own applications. Define APP_STL to select one of them. Examples are: APP_STL := stlport_static --> static STLport library APP_STL := stlport_shared --> shared STLport library APP_STL := system --> default C++ runtime library For more information on the subject, please read docs/CPLUSPLUS-SUPPORT.html
由於 NDK 默認使用最小 C++ 運行時庫進行項目的編譯,導致無法編譯 WebRTC 中使用諸如 std::map 等 STL 容器的源碼。因此我們需要自行設定適合本項目的 C++ 運行時庫 gnustl_static。
step 3.2
打開並編輯 jni 根目錄下的 Android.mk 文件如下,本文件只需告訴 NDK 去調用所有子目錄下的 Android.mk 文件即可:
# Copyright (c) 2013 BillHoo. All Rights Reserved. # 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 $(call all-subdir-makefiles)
step 3.3
準備工作就緒,下面就可以開始着手編譯整個 APM 單元了,首先打開 jni/webrtc 目錄,新建 Android.mk 文件如下:
# Copyright (c) 2013 BillHoo. All Rights Reserved. # 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. # MY_WEBRTC_ROOT_PATH := $(call my-dir) # # voice include $(MY_WEBRTC_ROOT_PATH)/common_audio/signal_processing/Android.mk include $(MY_WEBRTC_ROOT_PATH)/common_audio/vad/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_processing/aec/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_processing/aecm/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_processing/agc/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_processing/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_processing/ns/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_processing/utility/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/utility/source/Android.mk include $(MY_WEBRTC_ROOT_PATH)/modules/audio_device/Android.mk include $(MY_WEBRTC_ROOT_PATH)/system_wrappers/source/Android.mk # # build .so LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_ARM_MODE := arm LOCAL_MODULE := liblu_audio_preprocessing LOCAL_MODULE_TAGS := optional LOCAL_WHOLE_STATIC_LIBRARIES := \ libwebrtc_spl \ libwebrtc_apm \ libwebrtc_apm_utility \ libwebrtc_vad \ libwebrtc_ns \ libwebrtc_agc \ libwebrtc_aec \ libwebrtc_aecm \ libwebrtc_system_wrappers \ libwebrtc_audio_device \ libwebrtc_utility # # Add Neon libraries. ifeq ($(WEBRTC_BUILD_NEON_LIBS),true) LOCAL_WHOLE_STATIC_LIBRARIES += \ libwebrtc_aecm_neon \ libwebrtc_ns_neon \ libwebrtc_spl_neon endif LOCAL_STATIC_LIBRARIES := \ libprotobuf-cpp-2.3.0-lite LOCAL_SHARED_LIBRARIES := \ libcutils \ libdl \ libstlport LOCAL_PRELINK_MODULE := false # #TODO(billhoo) find a properway to do this. LOCAL_LDLIBS += $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi/libgnustl_static.a LOCAL_LDLIBS += -lOpenSLES ifndef NDK_ROOT include external/stlport/libstlport.mk endif include $(BUILD_SHARED_LIBRARY)
需要注意的幾點:
1)在編譯時如提示找不到 ../../../Android.mk 文件等錯誤,請檢查並修正你的相對路徑。
2)位於第60行的gnu靜態庫鏈接路徑是針對NDK版本 r8d 的,如讀者版本不匹配,請自行找到 libgnustl_static.a 靜態庫的路徑進行替換。
3)本示例並不打算編譯 WebRTC 的測試工程,請使用 Eclipse 搜索文件功能,找到 Android.mk 文件中的 -DWEBRTC_AUDIOPROC_DEBUG_DUMP 並註釋掉。
step 3.4
萬事俱備,我們可以開始編譯 APM 了,不過在編譯過程中肯定還會有很多小問題出現(比如相對路徑不正確、找不到某某函數的符號等等),這些問題就留給讀者自行google、SO解決了,bill就不再贅述。
Step 4 - 在android應用中使用APM的注意事項
經過上述步驟,讀者便能夠得到 libwebrtc_audio_preprocessing.so 這個動態鏈接庫。我們需要做的僅僅是編寫自己的 jni 包裝函數向 android 應用層提供 APM 的接口。具體做法bill之前的文章已經詳細介紹過。這裏需要注意的是,如果讀者打算在自己的動態庫中引用已經編譯好的 APM 庫,那麼在 android 類加載這兩個庫時的順序是敏感的。
假設讀者將自己的 JNI 接口封裝成單獨的庫 libmy_jni_wrapper.so,而該庫引用了 libwebrtc_audio_preprocessing.so,那麼在加載這兩個庫時應該參照如下順序:
static { // Ordering of loading these shared libraries is significant. System.loadLibrary("webrtc_audio_preprocessing"); System.loadLibrary("my_jni_wrapper"); }
若順序寫反,在運行時將得到找不到 webrtc_audio_preprocessing 庫中符號的異常。
總結
整個編譯工作在現在看來非常簡單,但需要很多的耐心和搜索,不過結果還是令人比較滿意的,APM出來的效果比之前自己單獨使用各個音頻模塊要好很多。不過對於抖動等因素的影響,APM就力不從心了。也許bill接下來該花時間去看看NetEq、Jitter Buffer等模塊了。如何使用他們,如何融進自己的項目,到時候就知道了。