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 文件夹复制到新建的工程的根目录下,此时,代码结构如下:

                              Android ilbc 语音对话示范(二)代码搭建_第1张图片


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 ,回车,会看到如下情景:

     Android ilbc 语音对话示范(二)代码搭建_第2张图片

   看到倒数第二行了吗? libs/armeabi/libilbc-codec.so ,说明已经生成了我们需要的动态库,这时你会发现在工程的根目录下多了一个libs 

文件夹,里面有个armeab目录,打开后就有一个 libilbc-codec.so 的文件

      Android ilbc 语音对话示范(二)代码搭建_第3张图片

得到这个库之后,我们所有与底层有关的工作全部完成,被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 谢绝转载!


你可能感兴趣的:(java,android,jni,audio,Codec)