基于上一篇中提到的google网站的一份代码,这个需要git下载,我上传了一份在CSDN,进行了修改,并且有很多人一直来找我问一些问题,很抱
歉都没有认真回复大家,现在打算继续把这个项目做下去,所以代码统一放到了Github上面来管理,以后会持续更新,代码地址是 Android-iLbc
现在开始讲解代码结构搭建环节:
要求: 环境:Ubuntu 12.04 (其他Linux环境皆可),Android 2.2 及以上系统 工具:Elicpse 3.7 ,Android NDK r7 ,Android SDK
打开Eclipse,新建一个Android 程序,名称为 AndroidILBC 。
将下载的源码中的 jni 文件夹复制到新建的工程的根目录下,此时,代码结构如下:
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)
打开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层代码调用的三个函数,现在我们对这三个函数进行改造(仅仅是换个函数名称而已)
在程序的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 方法,不用写任何代码;
(如果不会,请参考之前的文章)
打开终端,定位到第 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方法所在类的包名,因为方法名会也就改变了.
下来就是要编译生成 .so 库了,正如上面Android.mk文件中写的,最终编译生成的库是 libilbc-codec.so,编译方法如下:
打开终端,定位到 jni 文件夹下面,输入 ndk-build ,回车,会看到如下情景:
看到倒数第二行了吗? libs/armeabi/libilbc-codec.so ,说明已经生成了我们需要的动态库,这时你会发现在工程的根目录下多了一个libs 的
文件夹,里面有个armeabi 目录,打开后就有一个 libilbc-codec.so 的文件:
得到这个库之后,我们所有与底层有关的工作全部完成,被Linux 虐了的人可以马上转战Windows下,后续工作已经不需要在Liunx下进行了。
OK ,库编译完成了,后续将会示范音频的采集以及如何通过Java来调用 底层编解码 函数。
光这个编译过程我研究了两天才跑通,就像上一篇所提到的,开始使用同学给的demo,尽管可以运行,但是程序的扩展性不好,你不可
能写个程序后,里面还夹杂一个诡异的包名,而这个包名你连碰都不敢碰,再加上各种代码的嵌套,总结了一句话----自己动手,丰衣足食!
经过几番研究后,对Android.mk 文件的编写积累了一些经验,NDK 编译过程逐渐顺手,现在可以任意修改自己的代码,如果哪里需要改
进,就可以直接该代码,重新编译一个 .so 库出来,甚至这个库随便用在其他程序中都可以。
Android NDK 的确提供了一个非常好的平台,从做视频时的FFMPEG移植,到现在的ILBC库的移植,用的都是C代码,以后如果转战到
游戏开发,说不定会有更广阔的天地.
整整花了三个小时来写这片文章,因为自己也是刚刚学会,一边写代码,一边又去看 ilbc源码,从头到尾顺了一遍,ilbc真正的代码就是几
千行,不多,但是精。要想掌握得花一定的时间。目前这系列教程只是完成了底层的开发,接下来将完善整个系统,文章中有写的不好的,希
望多多指教,互相学习!
--------------------------------
Over~ BY http://blog.csdn.net/ranxiedao 谢绝转载!