Android ilbc 語音對話示範(二)代碼搭建

 

基於上一篇中提到的google網站的一份代碼,這個需要git下載,我上傳了一份在CSDN,進行了修改,並且有很多人一直來找我問一些問題,很抱

歉都沒有認真回覆大家,現在打算繼續把這個項目做下去,所以代碼統一放到了Github上面來管理,以後會持續更新,代碼地址是 Android-iLbc


現在開始講解代碼結構搭建環節:

要求:
環境:Ubuntu 12.04 (其他Linux環境皆可),Android 2.2 及以上系統

工具:Elicpse 3.7 ,Android NDK r7 ,Android SDK

1.新建工程:

打開Eclipse,新建一個Android 程序,名稱爲 AndroidILBC 


2.添加底層代碼:

     將下載的源碼中的 jni 文件夾複製到新建的工程的根目錄下,此時,代碼結構如下:

                              


3.Android.mk編寫:

LOCAL_PATH := $(call my-dir)
 
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libilbc
 
 codec_dir := iLBC_RFC3951  #ilbc 源代碼的目錄
 
 LOCAL_SRC_FILES := \
     $(codec_dir)/anaFilter.c \
     $(codec_dir)/constants.c \
     $(codec_dir)/createCB.c \
     $(codec_dir)/doCPLC.c \
     $(codec_dir)/enhancer.c \
     $(codec_dir)/filter.c \
     $(codec_dir)/FrameClassify.c \
     $(codec_dir)/gainquant.c \
     $(codec_dir)/getCBvec.c \
     $(codec_dir)/helpfun.c \
     $(codec_dir)/hpInput.c \
     $(codec_dir)/hpOutput.c \
     $(codec_dir)/iCBConstruct.c \
     $(codec_dir)/iCBSearch.c \
     $(codec_dir)/iLBC_decode.c \
     $(codec_dir)/iLBC_encode.c \
     $(codec_dir)/LPCdecode.c \
     $(codec_dir)/LPCencode.c \
     $(codec_dir)/lsf.c \
     $(codec_dir)/packing.c \
     $(codec_dir)/StateConstructW.c \
     $(codec_dir)/StateSearchW.c \
     $(codec_dir)/syntFilter.c
 
 LOCAL_C_INCLUDES += $(common_C_INCLUDES)
 
 LOCAL_PRELINK_MODULE := false
 
 include $(BUILD_STATIC_LIBRARY)
 
 # Build JNI wrapper
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libilbc-codec #生成的 .so庫名,可以自行修改 
 LOCAL_C_INCLUDES += \
     $(JNI_H_INCLUDE) \
     $(codec_dir)
 
 LOCAL_SRC_FILES := ilbc-codec.c
 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
 
 LOCAL_STATIC_LIBRARIES := libilbc
 LOCAL_PRELINK_MODULE := false
 
 include $(BUILD_SHARED_LIBRARY)

4.代碼分析:

      打開jni 文件夾下的 ilbc-codec.c 文件,裏面總共只有五個函數,負責音頻編解碼器的初始化,以及音頻的編碼和解碼。其中的三個方法:

jint Java_com_googlecode_androidilbc_Codec_init(
        JNIEnv *env, jobject this, jint mode)
和

jint Java_com_googlecode_androidilbc_Codec_encode(
        JNIEnv *env, jobject this,
        jbyteArray sampleArray, jint sampleOffset, jint sampleLength,
        jbyteArray dataArray, jint dataOffset)
和
jint Java_com_googlecode_androidilbc_Codec_decode(
        JNIEnv *env, jobject this,
        jbyteArray dataArray, jint dataOffset, jint dataLength,
        jbyteArray sampleArray, jint sampleOffset)

根據這三個函數的名稱就可以知道,使用來讓 Java層代碼調用的三個函數,現在我們對這三個函數進行改造(僅僅是換個函數名稱而已)


5.寫Java層native 方法:

   在程序的Java層代碼中,建立一個包,用於放 NDK 的java層代碼,比如我建一個名爲 xmu.swordbearer.audio 的包,裏面新建一個類:

   AudioCodec.java ,在這個類中只負責對底層C函數進行調用,相當於一個工具類。新建三個 public staic native int 方法:

package xmu.swordbearer.audio;

public class AudioCodec {
    // initialize decoder and encoder
    public static native int audio_codec_init(int mode);

    // encode
    public static native int audio_encode(byte[] sample, int sampleOffset,
            int sampleLength, byte[] data, int dataOffset);

    // decode
    public static native int audio_decode(byte[] data, int dataOffset,
            int dataLength, byte[] sample, int sampleLength);
}

三個方法分別用於初始化,音頻編碼,音頻解碼,在這裏只需聲明爲 native 方法,不用寫任何代碼;

6.編譯.h 頭文件:

   (如果不會,請參考之前的文章)
打開終端,定位到第 5步建立的 AudioCodec.java 目錄下,如下: 

   這一步很關鍵,進入到src目錄後,就要帶上 AudioCodec 這個類的包名,此例中的包名爲: xmu.swordbearer.audio如果上述步

驟正確,就會在該包下生成一個 xmu_swordbearer_audio_AudioCodec.h 的頭文件,內容如下:

/*
 * Class:     xmu_swordbearer_audio_AudioCodec
 * Method:    audio_codec_init
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_xmu_swordbearer_audio_AudioCodec_audio_1codec_1init
  (JNIEnv *, jclass, jint);

/*
 * Class:     xmu_swordbearer_audio_AudioCodec
 * Method:    audio_encode
 * Signature: ([BII[BI)I
 */
JNIEXPORT jint JNICALL Java_xmu_swordbearer_audio_AudioCodec_audio_1encode
  (JNIEnv *, jclass, jbyteArray, jint, jint, jbyteArray, jint);

/*
 * Class:     xmu_swordbearer_audio_AudioCodec
 * Method:    audio_decode
 * Signature: ([BII[BI)I
 */
JNIEXPORT jint JNICALL Java_xmu_swordbearer_audio_AudioCodec_audio_1decode
  (JNIEnv *, jclass, jbyteArray, jint, jint, jbyteArray, jint);

第4步中分析的三個方法修改,打開jni 下的 ilbc-codec.c 文件,,把那三個名稱分別用剛剛生成的這三個方法名替換,具體對應如下:

Java_com_googlecode_androidilbc_Codec_init
改爲:
Java_xmu_swordbearer_audio_AudioCodec_audio_1codec_1init

Java_com_googlecode_androidilbc_Codec_encode
改爲:
Java_xmu_swordbearer_audio_AudioCodec_audio_1encode

Java_com_googlecode_androidilbc_Codec_decode
改爲:
Java_xmu_swordbearer_audio_AudioCodec_audio_1decode

僅是一個 複製,粘貼的過程!!!

當然,如果你寫的JAVA代碼的包名或者方法名不一樣,那生成的 .h 文件中的方法也就不一樣,這就是爲什麼編譯好一個.so庫後,不能隨

便修改 native方法所在類的包名,因爲方法名會也就改變了.


7. 編譯 .so 庫

    下來就是要編譯生成 .so 庫了,正如上面Android.mk文件中寫的,最終編譯生成的庫是 libilbc-codec.so,編譯方法如下:

     打開終端,定位到 jni 文件夾下面,輸入 ndk-build ,回車,會看到如下情景:

     

   看到倒數第二行了嗎? libs/armeabi/libilbc-codec.so ,說明已經生成了我們需要的動態庫,這時你會發現在工程的根目錄下多了一個libs 

文件夾,裏面有個armeab目錄,打開後就有一個 libilbc-codec.so 的文件

      

得到這個庫之後,我們所有與底層有關的工作全部完成,被Linux 虐了的人可以馬上轉戰Windows下,後續工作已經不需要在Liunx下進行了。

OK ,庫編譯完成了,後續將會示範音頻的採集以及如何通過Java來調用 底層編解碼 函數。


8.總結:

     光這個編譯過程我研究了兩天才跑通,就像上一篇所提到的,開始使用同學給的demo,儘管可以運行,但是程序的擴展性不好,你不可

寫個程序後,裏面還夾雜一個詭異的包名,而這個包名你連碰都不敢碰,再加上各種代碼的嵌套,總結了一句話----自己動手,豐衣足食!

 經幾番研究後,對Android.mk 文件的編寫積累了一些經驗,NDK 編譯過程逐漸順手,現在可以任意修改自己的代碼,如果哪裏需要改

進,就可以直接該代碼,重新編譯一個 .so 庫出來,甚至這個庫隨便用在其他程序中都可以。

   Android NDK 的確提供了一個非常好的平臺,從做視頻時的FFMPEG移植,到現在的ILBC庫的移植,用的都是C代碼,以後如果轉戰

遊戲開發,說不定會有更廣闊的天地.

   整整花了三個小時來寫這片文章,因爲自己也是剛剛學會,一邊寫代碼,一邊又去看 ilbc源碼,從頭到尾順了一遍,ilbc真正的代碼就是幾

千行,不多,但是精。要想掌握得花一定的時間。目前這系列教程只是完成了底層的開發,接下來將完善整個系統,文章中有寫的不好的,希

望多多指教,互相學習!


--------------------------------

  Over~  BY http://blog.csdn.net/ranxiedao 謝絕轉載!


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