Android ilbc 语音对话示范(二)代码搭建

上一篇中提到的google网站的一份代码(http://code.google.com/p/android-ilbc/)这个需要git下载,我上传了一份在CSDN,稍微进行了修改(http://download.csdn.net/detail/ranxiedao/4450917),先开始讲解代码结构搭建环节。

要求:环境:Ubuntu 12.04 (其他Linux环境皆可),Android 2.2 及以上系统
工具:Elicpse 3.7 ,Android NDK r7 ,Android SDK r7

1. 新建工程:
       打开Eclipse,新建一个Android 程序,名称为 AndroidILBC,要求SDK 使用2.2及以上版本,因为Android是从2.2开始正式支持NDK 开发。

2. 添加底层代码:

      将下载的源码中的 jni 文件夹复制到新建的工程的根目录下,此时,代码结构如下:
Android ilbc 语音对话示范(二)代码搭建_第1张图片 
3. jni 目录下的Android.mk 的内容为:

  1. LOCAL_PATH := $(call my-dir)

  2. include $(CLEAR_VARS)

  3. LOCAL_MODULE := libilbc

  4. codec_dir := ilbc_src  #ilbc 源代码的目录

  5. LOCAL_SRC_FILES := \
  6.      $(codec_dir)/anaFilter.c \
  7.      $(codec_dir)/constants.c \
  8.      $(codec_dir)/createCB.c \
  9.      $(codec_dir)/doCPLC.c \
  10.      $(codec_dir)/enhancer.c \
  11.      $(codec_dir)/filter.c \
  12.      $(codec_dir)/FrameClassify.c \
  13.      $(codec_dir)/gainquant.c \
  14.      $(codec_dir)/getCBvec.c \
  15.      $(codec_dir)/helpfun.c \
  16.      $(codec_dir)/hpInput.c \
  17.      $(codec_dir)/hpOutput.c \
  18.      $(codec_dir)/iCBConstruct.c \
  19.      $(codec_dir)/iCBSearch.c \
  20.      $(codec_dir)/iLBC_decode.c \
  21.      $(codec_dir)/iLBC_encode.c \
  22.      $(codec_dir)/LPCdecode.c \
  23.      $(codec_dir)/LPCencode.c \
  24.      $(codec_dir)/lsf.c \
  25.      $(codec_dir)/packing.c \
  26.      $(codec_dir)/StateConstructW.c \
  27.      $(codec_dir)/StateSearchW.c \
  28.      $(codec_dir)/syntFilter.c

  29. LOCAL_C_INCLUDES += $(common_C_INCLUDES)

  30. LOCAL_PRELINK_MODULE := false

  31. include $(BUILD_STATIC_LIBRARY)

  32. # Build JNI wrapper
  33. include $(CLEAR_VARS)

  34. LOCAL_MODULE := libilbc-codec #生成的 .so库名,可以自行修改 
  35. LOCAL_C_INCLUDES += \
  36.      $(JNI_H_INCLUDE) \
  37.      $(codec_dir)

  38. LOCAL_SRC_FILES := ilbc-codec.c
  39. LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

  40. LOCAL_STATIC_LIBRARIES := libilbc
  41. LOCAL_PRELINK_MODULE := false

  42. include $(BUILD_SHARED_LIBRARY)
复制代码
代码第43行指定了最后编译生成的 .so 库的名称,你可以根据自己需要更改。

4. 代码分析:

      打开jni 文件夹下的 ilbc-codec.c 文件,里面总共只有五个函数,负责音频编解码器的初始化,以及音频的编码和解码。其中的三个方法:

  1. jint Java_com_googlecode_androidilbc_Codec_init(
  2.         JNIEnv *env, jobject this, jint mode)

  3. jint Java_com_googlecode_androidilbc_Codec_encode(
  4.         JNIEnv *env, jobject this,
  5.         jbyteArray sampleArray, jint sampleOffset, jint sampleLength,
  6.         jbyteArray dataArray, jint dataOffset)

  7. jint Java_com_googlecode_androidilbc_Codec_decode(
  8.         JNIEnv *env, jobject this,
  9.         jbyteArray dataArray, jint dataOffset, jint dataLength,
  10.         jbyteArray sampleArray, jint sampleOffset)
复制代码
根据这三个函数的名称就可以知道,使用来让 Java层代码调用的三个函数,现在我们对这三个函数进行改造(仅
仅是换个函数名称而已)

5. 写Java层native 方法:

      在程序的Java层代码中,建立一个包,用于放 NDK 的java层代码,比如我建一个名为 xmu.swordbearer.audio 的包,里面新建一个类:AudioCodec.java ,在这个类中只负责对底层C函数进行调用,相当于一个工具类。

新建三个 public staic native int 方法:

  1. package xmu.swordbearer.audio;
  2. public class AudioCodec {
  3.     // initialize decoder and encoder
  4.     public static native int audio_codec_init(int mode);
  5.     // encode
  6.     public static native int audio_encode(byte[] sample, int sampleOffset,
  7.             int sampleLength, byte[] data, int dataOffset);
  8.     // decode
  9.     public static native int audio_decode(byte[] data, int dataOffset,
  10.             int dataLength, byte[] sample, int sampleLength);
  11. }
复制代码
三个方法分别用于初始化,音频编码,音频解码,在这里只需声明为 native 方法,不用写任何代码;

6. 编译.h 头文件:(如果不会,请参考之前的文章)

    打开终端,定位到第 5步建立的 AudioCodec.java 目录下,如下:
2012072104393416.png 

这一步很关键,进入到src目录后,就要带上 AudioCodec 这个类的包名,此例中的包名为: xmu.swordbearer.audio
   如果上述步骤正确,就会在该包下生成一个 xmu_swordbearer_audio_AudioCodec.h 的头文件,内容如下:

  1. /*
  2. * Class:     xmu_swordbearer_audio_AudioCodec
  3. * Method:    audio_codec_init
  4. * Signature: (I)I
  5. */
  6. JNIEXPORT jint JNICALL Java_xmu_swordbearer_audio_AudioCodec_audio_1codec_1init
  7.   (JNIEnv *, jclass, jint);
  8. /*
  9. * Class:     xmu_swordbearer_audio_AudioCodec
  10. * Method:    audio_encode
  11. * Signature: ([BII[BI)I
  12. */
  13. JNIEXPORT jint JNICALL Java_xmu_swordbearer_audio_AudioCodec_audio_1encode
  14.   (JNIEnv *, jclass, jbyteArray, jint, jint, jbyteArray, jint);
  15. /*
  16. * Class:     xmu_swordbearer_audio_AudioCodec
  17. * Method:    audio_decode
  18. * Signature: ([BII[BI)I
  19. */
  20. JNIEXPORT jint JNICALL Java_xmu_swordbearer_audio_AudioCodec_audio_1decode
  21.   (JNIEnv *, jclass, jbyteArray, jint, jint, jbyteArray, jint);
复制代码
看到没有?这三个方法分别对应第5步中写的三个方法。得到这三个方法的名称后,我们就对第4步中分析的三个方法修改,打开jni 下的 ilbc-codec.c 文件,,把那三个名称分别用刚刚生成的这三个方法名替换,具体对应如下

  1. Java_com_googlecode_androidilbc_Codec_init
  2. 改为:
  3. Java_xmu_swordbearer_audio_AudioCodec_audio_1codec_1init
  4. Java_com_googlecode_androidilbc_Codec_encode
  5. 改为:
  6. Java_xmu_swordbearer_audio_AudioCodec_audio_1encode
  7. Java_com_googlecode_androidilbc_Codec_decode
  8. 改为:
  9. 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 的文件夹,里面有个armeabi 目录,打开后就有一个 libilbc-codec.so 的文件:
Android ilbc 语音对话示范(二)代码搭建_第3张图片 

得到这个库之后,我们所有与底层有关的工作全部完成,被Linux 虐了的人可以马上转战Windows下,后续工作已经不需要在Liunx下进行了。

OK ,库编译完成了,后续将会示范音频的采集以及如何通过Java来调用 底层编解码 函数。

8.总结:

     光这个编译过程我研究了两天才跑通,就像上一篇所提到的,开始使用同学给的demo,尽管可以运行,但是程序的扩展性不好,你不可能写个程序后,里面还夹杂一个诡异的包名,而这个包名你连碰都不敢碰,再加上各种代码的嵌套,总结了一句话----自己动手,丰衣足食! 经过几番研究后,对Android.mk 文件的编写积累了一些经验,NDK 编译过程逐渐顺手,现在可以任意修改自己的代码,如果哪里需要改进,就可以直接该代码,重新编译一个 .so 库出来,甚至这个库随便用在其他程序中都可以。

   Android NDK 的确提供了一个非常好的平台,从做视频时的FFMPEG移植,到现在的ILBC库的移植,用的都是C代码,以后如果转战到游戏开发,说不定会有更广阔的天地.

原文:http://www.cnblogs.com/swordbearer/archive/2012/07/21/2601918.htm

你可能感兴趣的:(Android ilbc 语音对话示范(二)代码搭建)