【WebRTC音頻預處理單元APM的整體編譯及使用 - android】

前言

在寫【單獨編譯使用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基礎步驟及部分要點

對於Eclipsejni的編譯及使用請參見上篇文章所述,在此不再贅述。

此節僅按照自己的jni目錄組織結構進行講解,讀者可根據自己需要進行調整。

Eclipse中的jni組織結構如下:

102738855.png

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::mapSTL 容器的源碼。因此我們需要自行設定適合本項目的 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 了,不過在編譯過程中肯定還會有很多小問題出現(比如相對路徑不正確、找不到某某函數的符號等等),這些問題就留給讀者自行googleSO解決了,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接下來該花時間去看看NetEqJitter Buffer等模塊了。如何使用他們,如何融進自己的項目,到時候就知道了。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章