AndroidNDK开发(六)——使用开源LAME转码mp3 - 新闻资讯 - 云南小程序开发|云南软件开发|云南网站建设-昆明葵宇信息科技有限公司

159-8711-8523

云南网建设/小程序开发/软件开发

知识

不管是网站,软件还是小程序,都要直接或间接能为您产生价值,我们在追求其视觉表现的同时,更侧重于功能的便捷,营销的便利,运营的高效,让网站成为营销工具,让软件能切实提升企业内部管理水平和效率。优秀的程序为后期升级提供便捷的支持!

您当前位置>首页 » 新闻资讯 » 技术分享 >

AndroidNDK开发(六)——使用开源LAME转码mp3

发表时间:2020-10-19

发布人:葵宇科技

浏览次数:39


        转载请注明出处:http://blog.csdn.net/allen315410/article/details/42456661
        在本专栏的前面几篇博客中讲述了一些Android NDK开辟的基本,大年夜情况搭建一向到应用JNI进行Java端和C端代码的互相调用,并且的讲解的Demo也是很简单易懂的,信赖控制前面博客的大年夜部分内容,就可以着手在实际项目中应用JNI进行NDK开辟了,那么既然基本过了,接下来我在这里测验测验去应用真实项目中去。我们知道,C说话因为高效,并且又是最早期的高等编程之一,一向存活至今近40年了,所以很多用C开辟出来高效类库是可以被复竽暌姑的,如许不仅做到高效力,并且削减了项目开辟周期。在这里我找到了一个关于音频文件转码的最常用的类库——LAME,这篇博客就是基于LAME开辟一个wav音频文件转码成mp3音频文件的小项目。

一、LAME简介


        LAME是今朝最好的MP3编码引擎。LAME编码出来的MP3音色纯厚、空间宽广、低音清楚、细节表示优胜,它独创的心理音响模型技巧包管了CD音频还原的┞锋实性,合营VBR和ABR参数,音质几乎可以媲美CD音频,但文件体积却异常小。对于一个免费引擎,LAME的优势不问可知。关于LAME的介绍可以在百度百科,维诽谤科中找到,我在这里不再赘述了,然则要知道LAME可以赞助我们将wav无损音频文件转码成mp3这种体积相对较小的音频格式文件。
        注:关于wav和mp3是一系列的音频编解码算法,具体我也不是特别清跋扈它的厉害之处和实现道理,想要深刻懂得音频编码还须要找一些相干的材料文档来读,然则不清跋扈这些也不妨碍我们开辟如许的一个项目。
LAME的源码是托管到sourceforge.net上的,我们开辟一个基于LAME的项目,就不得不下载其源码用于编译。
LAME主页:http://lame.sourceforge.net/

二、编写本处所法


        开辟这个转码项目标时刻,做一下简单的处理,编写两个简单的Native办法,一个是convertmp3(String wav,String mp3),这个办法是重要的Native办法,目标是将大年夜Java层获取到的两个音频文件的路径已字符串情势传递给C端,C端拿到这两个路径,就可以进行读写和编解码操作了,别的一个Native办法是getLameVersion(),该办法是获取LAME的版本号的,返回值为字符串,用处是校验在本地是否成功编译.so文件。源码如下
/**
	 * wav转换成mp3的本处所法
	 * 
	 * @param wav
	 * @param mp3
	 */
	public native void convertmp3(String wav, String mp3);

	/**
	 * 获取LAME的版本信息
	 * 
	 * @return
	 */
	public native String getLameVersion();

三、编译头文件


        经由过程前面几篇博客进修,我们知道在Java层中定义完我们须要的Native办法后,须要应用JDK供给的javah敕令编译C文件中须要的办法签名,具体做法是1,如不雅你应用的是jdk1.6及以下版本,将目次切换到工程目次下的classes目次;2,如不雅你应用的jdk1.7版本,请将目次切换到工程目次下的src目次,履行如下的javah敕令:、
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := convert

LOCAL_SRC_FILES := convert.c 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
	
LOCAL_LDLIBS += -llog

include $(BUILD_SHARED_LIBRARY)
Application.mk
[img]http://img.blog.csdn.net/20150106143406183
        好!看到没有报错的话,就解释我们的办法签名文件以及生成了,在src目次下找到这个办法签名的文件,在工程目次下新建一个jni的目次,将办法签名文件拷贝进jni目次之中,我们先打开这个文件看看内容好了。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_lame_MainActivity */

#ifndef _Included_com_example_lame_MainActivity
#define _Included_com_example_lame_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_lame_MainActivity
 * Method:    convertmp3
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_example_lame_MainActivity_convertmp3
  (JNIEnv *, jobject, jstring, jstring);

/*
 * Class:     com_example_lame_MainActivity
 * Method:    getLameVersion
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_lame_MainActivity_getLameVersion
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

          项目进行到这里就要显得特别重要也特别当心了,因为此时须要引用开源库LAME到本项目中并且还要从新编译成实用于本平台的库。
1,将下载来的LAME源码解压到本地,打开解压后的目次,找到libmp3lame目次,将该目次下的所有的文件都拷贝到jni目次下。
2,剔除不须要的文件目次。例如i386这个目次要删除,还要删除几个非.h,.c作为扩大名的文件,已经Linux下的批处理文件,因为这些文件都是Android平台下非须要的。
3,惹人lame.h头文件。在LAME解压目次下找到include目次,将其下的lame.h头文件拷贝到jni目次下,如不雅这个目次没有被惹人,会报如下的缺点
4,修改util.h的源码。在JNI目次下找到util.h文件,在574行找到ieee754_float32_t数据类型,将其修改为float类型,因为ieee754_float32_t是Linux或者是Unix下支撑的数据         类型,在Android下并不支撑。如不雅不修改,则编译源码的时刻会报如下缺点
[img]http://img.blog.csdn.net/20150106145254829

五、编写本地C说话代码,实现音频文件的转码


        关于怎么编写这段C说话代码,确切是有些难度,须要必定的C说话常识,并且还要包管可以或许看懂lame.h文件中供给的API和注释,在这里我就不在说清楚明了,因为这个工作说起来那就复杂多了,单单就一篇博客是将不完全的,下面贴出C说话代码,代码中供给了较为详尽的注释,带着注释去看代码,便于懂得的!
#include<stdio.h>
#include<jni.h>
#include<malloc.h>
#include<string.h>
#include<lame.h>
#include"com_example_lame_MainActivity.h"
#include<android/log.h>
#define LOG_TAG "System.out.c"
#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* 这个代表char数组的首地址
 *  Jstring2CStr 把java中的jstring的类型转化成一个c说话中的char 字符串
 */
char* Jstring2CStr(JNIEnv* env, jstring jstr) {
	char* rtn = NULL;
	jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //String
	jstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 获得一个java字符串 "GB2312"
	jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
			"(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312");
	jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
			strencode); // String .getByte("GB2312");
	jsize alen = (*env)->GetArrayLength(env, barr); // byte数组的长度
	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;
}

int flag = 0;
/**
 * wav转换mp3
 */
JNIEXPORT void JNICALL Java_com_example_lame_MainActivity_convertmp3
(JNIEnv * env, jobject obj, jstring jwav, jstring jmp3) {
	char* cwav =Jstring2CStr(env,jwav) ;
	char* cmp3=Jstring2CStr(env,jmp3);
	LOGI("wav = %s", cwav);
	LOGI("mp3 = %s", cmp3);

	//1.打开 wav,MP3文件
	FILE* fwav = fopen(cwav,"rb");
	FILE* fmp3 = fopen(cmp3,"wb");

	short int wav_buffer[8192*2];
	unsigned char mp3_buffer[8192];

	//1.初始化lame的编码器
	lame_t lame =  lame_init();
	//2. 设置lame mp3编码的采样率
	lame_set_in_samplerate(lame , 44100);
	lame_set_num_channels(lame,2);
	// 3. 设置MP3的编码方法
	lame_set_VBR(lame, vbr_default);
	lame_init_params(lame);
	LOGI("lame init finish");
	int read ; int write; //代表读了若干个次 和写了若干次
	int total=0; // 当前读的wav文件的byte数量
	do{
		if(flag==404){
			return;
		}
		read = fread(wav_buffer,sizeof(short int)*2, 8192,fwav);
		total +=  read* sizeof(short int)*2;
		LOGI("converting ....%d", total);
		publishJavaProgress(env,obj,total);
		// 调用java代码 完成进度条的更新
		if(read!=0){
			write = lame_encode_buffer_interleaved(lame,wav_buffer,read,mp3_buffer,8192);
			//把转化后的mp3数据写到文件里
			fwrite(mp3_buffer,sizeof(unsigned char),write,fmp3);
		}
		if(read==0){
			lame_encode_flush(lame,mp3_buffer,8192);
		}
	}while(read!=0);
	LOGI("convert  finish");

	lame_close(lame);
	fclose(fwav);
	fclose(fmp3);
}
/**
 * 调用java代码 更新法度榜样的进度条
 */
void publishJavaProgress(JNIEnv * env, jobject obj, jint progress) {
	// 1.找到java的MainActivity的class
	jclass clazz = (*env)->FindClass(env, "com/example/lame/MainActivity");
	if (clazz == 0) {
		LOGI("can't find clazz");
	}
	LOGI(" find clazz");

	//2 找到class 琅绫擎的办法定义
	jmethodID methodid = (*env)->GetMethodID(env, clazz, "setConvertProgress","(I)V");
	if (methodid == 0) {
		LOGI("can't find methodid");
	}
	LOGI(" find methodid");

	//3 .调用办法
	(*env)->CallVoidMethod(env, obj, methodid, progress);
}

/**
 * 获取LAME的版本号
 */
JNIEXPORT jstring JNICALL Java_com_example_lame_MainActivity_getLameVersion(
	JNIEnv * env, jobject obj) {
	return (*env)->NewStringUTF(env, get_lame_version());
}
       膳绫擎新建了一个convert.c的C源文件,源码中起首引用一些相干的类库来应用,例如<stdio.h><jni.h><malloc.h><string.h>这几个是C的标准类库,代码中有须要就惹人。接下来,我们还须要引用上述第三主题中编译好的办法签名文件com_example_lame_MainActivity.h,这个文件定义了Native办法的签名,便利编译器可以或许找到相对应的Native办法,还有一个异常重要的头文件lame.h,这个项目顶用的就是LAME,所以这个lame.h的头文件是必须引用上的,最后就是log.h的惹人和定义了,都是固定的内容,看源码就好了。

六、编写设备文件和交叉编译


        完成上述步调以及源码的编写之后,剩下的就是设备文件了。起首看看Android.mk文件,这个就麻烦了棘因为膳绫擎我们将LAME全部源码文件全部拷贝到jni目次下了,这些文件都是要从新编译的,所以我们须要在Android.mk文件的LOCAL_SRC_FILES这个字段上,将所以的源码文件都要设备上去,不仅包含我们自定义的c文件,更有LAME中的c源码文件,因为LAME包含的文件太多了,所以在编写个LOCAL_SRC_FILES时要格外当心,写错一个就有可能编码不经由过程。下面是我的设备筹划:

APP_PLATFORM := android-8
cygwin下交叉编译:
[img]http://img.blog.csdn.net/20150106153948528
好!看到上图底部的提示,解释我们的.so是编译好了,因为文件较多,编译是须要花费一点时光的,并且膳绫擎报错了很多的warnning警告提示,然则没紧要,可以忽视,只要不是报错,那这个工程依然可以运行。

七、在Java层编写调用C端的代码


        在编写Java写调用代码之前,我们推敲到因为编解码是个异常耗时的操作,所以这个操作是不克不及够放在主线程中履行的,必须得开启新的线程,又是因为这是一个耗时操作,所认为了优胜的用户体验,我们须要在转码的过程中添加一个进度条对话框,提示一个转码的过程,关于这个进度条的进度问题也是交给C说话实现的,我们在Java中只须要定义一个如许的设置进度的办法,在C说话中回调这个java办法,将进度数据传递给Java中的进度条对话框即可。
LAME下载:http://sourceforge.net/projects/lame/files/lame/3.99/
public class MainActivity extends Activity {

	static {
		System.loadLibrary("convert");
	}
	private EditText et_wav;
	private EditText et_mp3;
	private ProgressDialog pd;

	/**
	 * wav转换成mp3的本处所法
	 * 
	 * @param wav
	 * @param mp3
	 */
	public native void convertmp3(String wav, String mp3);

	/**
	 * 获取LAME的版本信息
	 * 
	 * @return
	 */
	public native String getLameVersion();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		et_wav = (EditText) this.findViewById(R.id.et_wav);
		et_mp3 = (EditText) this.findViewById(R.id.et_mp3);
		pd = new ProgressDialog(this);
	}

	/**
	 * wav转换mp3
	 */
	public void convert(View view) {
		final String mp3Path = et_mp3.getText().toString().trim();
		final String wavPath = et_wav.getText().toString().trim();
		File file = new File(wavPath);
		int size = (int) file.length();
		System.out.println("文件大年夜小 " + size);
		if ("".equals(mp3Path) || "".equals(wavPath)) {
			Toast.makeText(MainActivity.this, "路径不克不及为空", 1).show();
			return;
		}
		pd.setMessage("转换中....");
		pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		pd.setMax(size); // 设置进度条的最大年夜值
		pd.setCancelable(false);
		pd.show();
		// 转码是个耗时的操作,所以这里须要开启新线程去履行
		new Thread() {

			@Override
			public void run() {
				convertmp3(wavPath, mp3Path);
				pd.dismiss();
			}

		}.start();
	}

	/**
	 * 设置进度条的进度,供给给C说话调用
	 * 
	 * @param progress
	 */
	public void setConvertProgress(int progress) {
		pd.setProgress(progress);
	}

	/**
	 * 获取LAME的版本号
	 */
	public void getVersion(View view) {
		Toast.makeText(MainActivity.this, getLameVersion(), 0).show();
	}

}
留意须要添加权限两条:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

八、测试

        好吧,写到这里总算是一个项目完成了,下面要进行一下测试了,先开一个arm的模仿器,然后运行一下工程。
1,测试版本号
[img]http://img.blog.csdn.net/20150106155024713
[img]http://img.blog.csdn.net/20150106144735500
2,测试wav转mp3
今朝LAME的最新版本是3.99.5,不克不及连上SourceForge主页的同伙可以点击csdn下载,我已经上传,地址是:http://download.csdn.net/detail/lee_tianya/8332357
[img]http://img.blog.csdn.net/20150106155234099
好,再来看看sdcard下的原wav音频文件和转码后的mp3文件
[img]http://img.blog.csdn.net/20150106155411412
大年夜上图可以看到我们的转码是可以应用的,原wav文件确切转码成了mp3文件,并且大年夜小只有原文件的1/10了,导出这个mp3文件,用音频播放器播放一下,也是可以听得,文件并没有破坏。怎么样?项目做完了,你有没有兴趣尝尝呢?
源码请在这里下载

四、将LAME的源码导入jni目次



相关案例查看更多