转自:http://wenku.baidu.com/view/70cebd3e580216fc700afd51?from_page=view&from_mod=download
练习Jni 项目
一 下载 lame-3.98.4.tar.gz
二 创建 android 项目 lame
创建jni目录 并
复制 lame-3.98.4 包下的libmp3lame 目录下的所有 .c和.h文件和 include目录下的lame.h
三 在jni目录下创建 Android.mk文件
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := mp3lame LOCAL_SRC_FILES := bitstream.c fft.c id3tag.c mpglib_interface.c presets.c quantize.c reservoir.c tables.c util.c VbrTag.c encoder.c gain_analysis.c lame.c newmdct.c psymodel.c quantize_pvt.c set_get.c takehiro.c vbrquantize.c version.c include $(BUILD_SHARED_LIBRARY) |
四, 编辑 jni/utils.h,
把 extern ieee754_float32_t fast_log2(ieee754_float32_t x); 替换为 extern float fast_log2(float x);
五 ndk-build
六 导入libmp3lame.so, 并创建native方法 然后生成头文件 cn_kq_lame_LameActivity.h
七 创建hello.c文件并将其加入Andorid.mk文件中的源代码位置后面, 然后按上面的步骤重新编译
好了我的测试通过 了, 现在来为它加入新的功能 mp3转码的功能
********************************************************************************
一 修改LameActivity 为其加入新的本地执行方法 并加入访问sdcard卡和外部存储权限
package cn.kq.lame;
import android.app.Activity; import android.app.ProgressDialog; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.EditText; import android.widget.Toast; /** * @author zhaopan - 2011-10-5 */ public class LameActivity extends Activity { static{ System.loadLibrary("mp3lame"); } EditText etInput; EditText etOutput; private ProgressDialog pd; private Handler handler = new Handler(){ public void handleMessage(Message msg) { //主线程收到通知消毁对话框 pd.dismiss(); super.handleMessage(msg); } }; public native String getVersion(); public native void convert(String input, String output); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); etInput = (EditText) this.findViewById(R.id.input); etOutput = (EditText) this.findViewById(R.id.output); }
public void getVersion(View v){ Toast.makeText(this, getVersion(), 1).show(); } //完成从源文件到目标文件的转换 public void convert(View v){ final String input = etInput.getText().toString(); final String output = etOutput.getText().toString(); //调用c代码来完成 文件的转换
pd = new ProgressDialog(this); pd.setMessage("转换中"); pd.show();
//起动一个线程来完成 这个耗时的操作 new Thread(new Runnable(){ public void run() { convert(input, output); //转换工作完成 后发送一条消息到主线程 handler.sendEmptyMessage(0); } }).start(); }
} |
xmlversion="1.0"encoding="utf-8"?> <manifestxmlns:android="http://schemas.android.com/apk/res/android" package="cn.kq.lame" android:versionCode="1" android:versionName="1.0"> <applicationandroid:icon="@drawable/icon"android:label="@string/app_name"> <activityandroid:name=".LameActivity" android:label="@string/app_name"> <intent-filter> <actionandroid:name="android.intent.action.MAIN"/> <categoryandroid:name="android.intent.category.LAUNCHER"/> intent-filter> activity>
application> <uses-sdkandroid:minSdkVersion="8"/> <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS">uses-permission> <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE">uses-permission> manifest> |
二 产生新的头文件cn_kq_lame_LameActivity.h 并将其复制到jni目录下
三 为Android.mk文件加入日志处理的库文件并 修改 hello.c文件加入新的处理方法 然后重新编译
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := mp3lame LOCAL_SRC_FILES := bitstream.c fft.c id3tag.c mpglib_interface.c presets.c quantize.c reservoir.c tables.c util.c VbrTag.c encoder.c gain_analysis.c lame.c newmdct.c psymodel.c quantize_pvt.c set_get.c takehiro.c vbrquantize.c version.c hello.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY) |
#include "lame.h" #include "cn_kq_lame_LameActivity.h" #include #include #define LOG_TAG "System.out" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
char* Jstring2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; //rtn 一块能够存放char类型内存的首地址 // 找到 jvm里面 String类 jclass clsstring = (*env)->FindClass(env,"java/lang/String"); // 创建一个jstring的字符串内容 GB2312 jstring strencode = (*env)->NewStringUTF(env,"GB2312"); // 寻找 String类的一个方法 getBytes getBytes方法的参数 Ljava/lang/String; // getBytes 方法的返回值 [B jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
// "String".getBytes("GB2312"); jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); //获取到barr的长度 jsize alen = (*env)->GetArrayLength(env,barr); //获取到的jbyte 数据放入到以ba 为开始地址的内存空间中 jbyte* ba = (*env)->GetByteArrayElements(env,barr,JNI_FALSE); if(alen > 0) { rtn = (char*)malloc(alen+1); //new char[alen+1]; "\0" 字符串的结束 //内存拷贝的操作 , memcpy(rtn,ba,alen); //给字符数组最后一个元素 表示字符串结束 rtn[alen]=0; } (*env)->ReleaseByteArrayElements(env,barr,ba,0);
return rtn; } JNIEXPORT jstring JNICALL Java_cn_kq_lame_LameActivity_getVersion (JNIEnv * env, jobject obj){
//把 lame版本号返回给java return (*env)->NewStringUTF(env, get_lame_version()); }
JNIEXPORT void JNICALL Java_cn_kq_lame_LameActivity_convert (JNIEnv * env, jobject obj, jstring input, jstring output, jint channel , jint sample){ //把java的string类型的文件转化成c能识别的char 字符串 char* inputfile = Jstring2CStr(env,input); LOGI("%s",inputfile);
char* outputfile = Jstring2CStr(env,output); LOGI("%s",outputfile);
FILE *pcm = fopen(inputfile, "rb"); FILE *mp3 = fopen(outputfile, "wb");
int read, write;
const int PCM_SIZE = 8192; const int MP3_SIZE = 8192;
short int pcm_buffer[PCM_SIZE*2]; unsigned char mp3_buffer[MP3_SIZE]; //初始化lame解码器 lame_t lame = lame_init(); // 设置音频的采样率 lame_set_in_samplerate(lame, 44100); // 设置lame编码器的声道 lame_set_num_channels(lame,2); // vbr 设置采样压缩格式 lame_set_VBR(lame, vbr_default); //初始化lame的编码器 lame_init_params(lame); LOGI("INIT LAME FININSH "); do { read = fread(pcm_buffer, 2*sizeof(short int), PCM_SIZE, pcm); //读出来的pcm数据放在 pcm_buffer里面 //从pcm文件里面读PCM_SIZE个 2*sizeof(short int)个数据,放倒pcm_buffer if (read == 0) write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE); else write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE); //pcm_buffer 是pcm数据 //read /* number of samples per channel, //mp3_buffer 编码后mp3数据 // 要写入多少个数据
fwrite(mp3_buffer, 1, write, mp3); //mp3_buffer 缓冲区 // 1是一个字节 //write是要写的长度 //mp3 是写入的文件名 } while (read != 0);
LOGI("LAME ENCODE FININSH"); lame_close(lame); fclose(mp3); fclose(pcm); }
|
四
由于后面在convert方法中多加了两个参数指定采样率和通道的入参来替换c代码中的 441000 和2
所以报了下面的错误 , 把那两个参数加入或删除就行了, 28和30的warning不用管。