Android移植之LAME编码器完整版

LAME MP3 Encoder是一个非常著名的mp3制作工具,可以说是MP3历史上革命性的东西。本文将其移植到Android上,并利用lame的接口将WAV音频文件格式转换成MP3格式。1.lame源文件件下载及预处理

程序运行截图:

从网上下载lame-3.98.4的源码文件lame-3.98.4.tar.gz,(也可以去网站直接下载本工程文件进行学习,网址),解压,进入子文件夹libmp3lame_3.984,把其中的.h和.c格式的文件全部取出来,再把lame-3.98.4\include\下的lame.h文件也取出来,创建一个文件夹jni,把这些头文件和定义文件放里面备用。注意在而libmp3lame_3.984的子文件夹/i386 和 /vector里的文件一律舍弃不要,你将得到41个C代码文件,有截图如下,仅供参考:

2.文件解析

仅有上边的这些来自于LAME的C文件还不行,它们各自为政成不了事,所以还需要一个管理者。这个管理者对上边的文件进行封装和调用,并提供用于Java调用的接口,这个管理者需要我们用C代码去写。下边就是这个管理者的代码,他向上层(也就是Java框架层)提供了三个接口,分别是getLameVersion(获取lame的版本)、convertAudio(转换音频格式)。还可以看到一个叫做Jstring2CStr的函数,它被用来将Java字符串对象转变成C字符串。还有一个setProcess函数,它比较特殊,它用来调用上层的Java函数,对进度条进行控制。

#include <stdio.h>#include<jni.h>#include<stdlib.h>#include <string.h>//#include"com_guoshisp_lame_MainActivity.h"#include"lame.h"//一定不要忘记将该头文件引入进来,否则不可以调用源文件中的方法#include <android/log.h>#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__)//C语言对应的工具方法:将Java端传递过来的String类型的数据,转换为C语言的char数组类型char* Jstring2CStr(JNIEnv* env, jstring jstr) {char* rtn = NULL;jclass clsstring = (*env)->FindClass(env, "java/lang/String");jstring strencode = (*env)->NewStringUTF(env, "GB2312");jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes","(Ljava/lang/String;)[B");jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,strencode); // String .getByte("GB2312");jsize alen = (*env)->GetArrayLength(env, barr);jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);if (alen > 0) {rtn = (char*) malloc(alen + 1); //"\0"memcpy(rtn, ba, alen);rtn[alen] = 0;}(*env)->ReleaseByteArrayElements(env, barr, ba, 0); //释放内存return rtn;}//调用MainActivity中的setProgressbar方法,显示转化的进度void setProcess(JNIEnv *env, jobject thiz, int process) {//接收到要调用的Java端方法所在的全类名char* classname = "com/guoshisp/lame/MainActivity";jclass clazz;//获取到MainActivity对象clazz = (*env)->FindClass(env, classname);//判断是否获取到MainActivity对象if (clazz == 0) {LOGI("CAN'T FIND CLAZZ ");} else {LOGI("FIND CLASS");}//获取到MainActivity对象中的方法jmethodID java_method = (*env)->GetMethodID(env, clazz, "setProgressbar","(I)V");if (java_method == 0) {LOGI("CAN'T FIND java_method ");} else {LOGI("FIND java_method");}//执行MainActivity对象中的setProgressbar方法(*env)->CallVoidMethod(env, thiz, java_method, process);}//获取到LAME编码器的版本号JNIEXPORT jstring JNICALL Java_com_guoshisp_lame_MainActivity_getLameVersion(JNIEnv * env, jobject thiz) {//返回LAME的版本号信息return (*env)->NewStringUTF(env, get_lame_version());}//将wav格式的文件转化为mp3格式JNIEXPORT void JNICALL Java_com_guoshisp_lame_MainActivity_convertAudio(JNIEnv * env, jobject thiz, jstring input, jstring output) {//获取到Java端传递过来的要转化的文件名char* inputname = Jstring2CStr(env,input);//获取到Java端传递过来的转化后的文件名char* outputname = Jstring2CStr(env,output);//在LogCat中,,分别打印出传递过来的文件名LOGI("inputname = %s",inputname);LOGI("outputname = %s", outputname);LOGI("init");//打开wav格式的文件,文件的操作为模式为:只读打开一个二进制文件,只允许读数据FILE * wav = fopen(inputname,"rb");//打开mp3格式的文件(如果该文件不存在,将会创建该文件),文件的操作为模式为:只写打开或建立一个二进制文件,只允许写数据FILE * mp3 = fopen(outputname,"wb");//每次读取wav文件的数据长度int read;//往mp3格式对应的缓存区中转化了多少次数据int write;//创建一个缓存区,该缓存区用于存放读取到的wav格式的数据short int wav_buffer[8192*2];//创建一个缓存区,该缓存区用于存放转化后的mp3格式的数据unsigned char mp3_buffer[8192];//调用lame编码器之前,需要将编码器进行初始化lame_t lame = lame_init();//设置输出的采样率lame_set_in_samplerate(lame,44100);//设置输出的采样数(声道数)lame_set_num_channels(lame,2);//设置mp3格式数据的具体编码方式(这里设置的编码方式为VBR的默认方式去编码)lame_set_VBR(lame,vbr_default);//完成了lame编码器中的参数设置lame_init_params(lame);//LAME编码器初始化操作完成,可以开始转码了LOGI("lame init finish");//记录一共读取了多少wav格式的数据,单位为byte,total变量用于设置Java端的进度条的显示进度int total=0;//循环的读取wav文件中的数据do {//参数一:读到哪一块内存中,参数二:每一次读的长度(涉及到左、右声道,所以,读取的长度应该为sizeof(short int)*2)//参数三:读取的数据的长度,参数四:从哪个文件中读取read = fread(wav_buffer,sizeof(short int)*2, 8192,wav);total += (read*4);LOGI("当前进度 %d",total);//调用Java中的setProcess方法,实时更新进度条显示的进度setProcess(env,thiz,total);if(read==0) { // 读到了文件的末尾//往mp3文件对应的缓存区中写入mp3音频格式的末尾数据,参数二:往哪个缓存区中写,参数三:写入多少数据(大一点没有问题)。write = lame_encode_flush(lame,mp3_buffer,8192);//将转化为mp3格式的数据写入到具体的文件中。参数一:从哪一个缓存中获取到要写入的数据,参数二:每一次要写入的数据长度//参数三:写了多少次(我们这里使用的是转化了多少次对应的变量),参数四:写到哪个文件中fwrite(mp3_buffer,sizeof(char),write,mp3);} else {//将wav中的数据转化为mp3格式的数据后,写入到mp3对应的缓存区中//参数一:lame编码器对象,参数二:从哪一块内存中读取数据,参数三:每次读取到的长度,//参数四:将数据一lame转码器设置好的格式转码后,要读取到哪一块内存中,参数五:参数四对应的缓存的大小,返回值:是转化了多少数据write = lame_encode_buffer_interleaved(lame,wav_buffer,read,mp3_buffer,8192);//将转化为mp3格式的数据写入到具体的文件中。参数一:从哪一个缓存中获取到要写入的数据,参数二:每一次要写入的数据长度//参数三:写了多少次(我们这里使用的是转化了多少次对应的变量),参数四:写到哪个文件中fwrite(mp3_buffer,sizeof(char),write,mp3);}}while(read!=0); //循环条件是:从wav中读取到的数据长度不为0,当为0时,结束循环。LOGI("encode finish");//销毁编码器lame_close(lame);//关闭wav和mp3对应的文件,否则,别的应用将不可使用fclose(mp3);fclose(wav);LOGI("convert complete");}

下面是Android.mk文件,与上边的40多个文件一块放在工程目录的jni目录下。可以看到它把C文件编译成了一个叫做Hi的共享库。简要介绍各字段含义:

每天告诉自己一次,我真的很不错

Android移植之LAME编码器完整版

相关文章:

你感兴趣的文章:

标签云: