一、LAME简介
LAME是目前非常优秀的一种MP3编码引擎,在业界,转码成Mp3格式的音频文件时,最常用的就是LAME库。当达到320Kbit/s时,LAME编码出来的音频质量几乎可以和CD的音质相媲美,并且还能保证整个音频文件的体积非常小,因此若要在移动端平台上编码MP3文件,使用LAME便成为唯一的选择。
二、使用场景
操作系统:Android。
场景:
1.录音时保存Mp3格式的文件
2. 将wav无损音频文件转码成mp3这种体积相对较小的音频文件。
3.可以将获取到的音频流进行录制保存为mp3格式。
三、开发准备
LAME的源码是托管到sourceforge.net上的,我们开发一个基于LAME的项目,就不得不下载其源码用于编译。
如果需要集成到Android系统上,就需要开发者具备一些NDK开发的能力。
四、开发过程
下面针对Android使用Lame做了基本的封装,供实际开发过程中进行参考:
首先,在java类中定义native方法。
private static native long nInit(int inSampleRate, int inChannels, int outSampleRate, int outBitrate, int model, intquality);private static native int nGetVersion(longlamePtr);private static native int mGetMp3bufferSize(longlamePtr);private static native int mGetMp3bufferSizeWithSamples(long lamePtr, intsamples);private static native int nEncodeShortInterleaved(long lamePtr, short[] bufLR, int samples, byte[] outMp3buf);private static native int nEncodeShort(long lamePtr, short[] bufL, short[] bufR, int samples, byte[] outMp3buf);private static native int nFlush(long lamePtr, byte[] outBuf);private static native void nClose(long lamePtr);
生成相应的.h的头文件,并实现该头文件,完成整体逻辑的编写。
#include #include#include#include"com_renhui_lame_Lame.h"#include"libmp3lame/lame.h"
extern "C"JNIEXPORT jlong JNICALL Java_com_renhui_lame_Lame_nInit(JNIEnv*env, jclass type, jint inSampleRate, jint inChannels,
jint outSampleRate, jint outBitrate, jint model, jint quality) {
lame_global_flags*lameFlags;
lameFlags=lame_init();
lame_set_in_samplerate(lameFlags, inSampleRate);
lame_set_num_channels(lameFlags, inChannels);
lame_set_out_samplerate(lameFlags, outSampleRate);
lame_set_brate(lameFlags, outBitrate);
lame_set_mode(lameFlags, (MPEG_mode) model);
lame_set_quality(lameFlags, quality);int code =lame_init_params(lameFlags);if (code != 0) {
lame_close(lameFlags);returncode;
}return (long) lameFlags;
}
JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_nGetVersion(JNIEnv*env, jclass type, jlong lamePtr) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;returnlame_get_version(lameFlags);
}
JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_mGetMp3bufferSize(JNIEnv*env, jclass type, jlong lamePtr) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;returnlame_get_size_mp3buffer(lameFlags);
}
JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_mGetMp3bufferSizeWithSamples(JNIEnv*env, jclass type, jlong lamePtr, jint samples) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;int version =lame_get_version(lameFlags);int bitrate =lame_get_brate(lameFlags);int sampleRate =lame_get_out_samplerate(lameFlags);float p = (bitrate / 8.0f) /sampleRate;if (version == 0) {//MPEG2: num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256
return (jint) ceil(samples * p + 4 * 576 * p + 256);
}else if (version == 1) {//MPEG1: num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512
return (jint) ceil(samples * p + 4 * 1152 * p + 512);
}else{return (jint) ceil((1.25 * samples + 7200));
}
}
JNIEXPORT jint JNICALL Java_com_renhui_lame_Lame_nEncodeShortInterleaved(JNIEnv*env, jclass type, jlong lamePtr,
jshortArray bufLR_, jint samples, jbyteArray outMp3buf_) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;
jshort*bufLR = env->GetShortArrayElements(bufLR_, NULL);
jbyte*outMp3buf = env->GetByteArrayElements(outMp3buf_, NULL);const jsize outMp3bufSize = env->GetArrayLength(outMp3buf_);int result =lame_encode_buffer_interleaved(lameFlags, bufLR, samples,
(u_char*) outMp3buf, outMp3bufSize);
env->ReleaseShortArrayElements(bufLR_, bufLR, 0);
env->ReleaseByteArrayElements(outMp3buf_, outMp3buf, 0);returnresult;
}
JNIEXPORT jint JNICALL
Java_com_renhui_lame_Lame_nEncodeShort(JNIEnv*env, jclass type, jlong lamePtr, jshortArray bufL_,
jshortArray bufR_, jint samples, jbyteArray outMp3buf_) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;
jshort*bufL = env->GetShortArrayElements(bufL_, NULL);
jshort*bufR = env->GetShortArrayElements(bufR_, NULL);
jbyte*outMp3buf = env->GetByteArrayElements(outMp3buf_, NULL);const jsize outMp3bufSize = env->GetArrayLength(outMp3buf_);int result =lame_encode_buffer(lameFlags, bufL, bufR, samples,
(u_char*) outMp3buf, outMp3bufSize);
env->ReleaseShortArrayElements(bufL_, bufL, 0);
env->ReleaseShortArrayElements(bufR_, bufR, 0);
env->ReleaseByteArrayElements(outMp3buf_, outMp3buf, 0);returnresult;
}
JNIEXPORT jint JNICALL
Java_com_renhui_lame_Lame_nFlush(JNIEnv*env, jclass type, jlong lamePtr, jbyteArray outBuf_) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;
jbyte*outBuf = env->GetByteArrayElements(outBuf_, NULL);const jsize outBufSize = env->GetArrayLength(outBuf_);int result = lame_encode_flush(lameFlags, (u_char *) outBuf, outBufSize);
env->ReleaseByteArrayElements(outBuf_, outBuf, 0);returnresult;
}
JNIEXPORTvoidJNICALL
Java_com_renhui_lame_Lame_nClose(JNIEnv*env, jclass type, jlong lamePtr) {
lame_global_flags*lameFlags;
lameFlags= (lame_global_flags *) lamePtr;
lame_close(lameFlags);
}
编写Android.mk和Application.mk,为ndk-build打包做准备。
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=mp3lame
LAME_LIBMP3_DIR :=libmp3lame
LOCAL_SRC_FILES :=\
$(LAME_LIBMP3_DIR)/bitstream.c \
$(LAME_LIBMP3_DIR)/fft.c \
$(LAME_LIBMP3_DIR)/id3tag.c \
$(LAME_LIBMP3_DIR)/mpglib_interface.c \
$(LAME_LIBMP3_DIR)/presets.c \
$(LAME_LIBMP3_DIR)/quantize.c \
$(LAME_LIBMP3_DIR)/reservoir.c \
$(LAME_LIBMP3_DIR)/tables.c \
$(LAME_LIBMP3_DIR)/util.c \
$(LAME_LIBMP3_DIR)/VbrTag.c \
$(LAME_LIBMP3_DIR)/encoder.c \
$(LAME_LIBMP3_DIR)/gain_analysis.c \
$(LAME_LIBMP3_DIR)/lame.c \
$(LAME_LIBMP3_DIR)/newmdct.c \
$(LAME_LIBMP3_DIR)/psymodel.c \
$(LAME_LIBMP3_DIR)/quantize_pvt.c \
$(LAME_LIBMP3_DIR)/set_get.c \
$(LAME_LIBMP3_DIR)/takehiro.c \
$(LAME_LIBMP3_DIR)/vbrquantize.c \
$(LAME_LIBMP3_DIR)/version.c \
com_renhui_lame_Lame.cpp
LOCAL_C_INCLUDES += $(LOCAL_PATH)/mp3lame
LOCAL_LDLIBS := -llog -lz
include $(BUILD_SHARED_LIBRARY)
Application.mk:
APP_ABI := all
#APP_ABI := armeabi armeabi-v7a x86
# APP_ABI :=armeabi
APP_PLATFORM := android-14
五、Lame重点API说明
1. lame_init()
lame_init() 用于初始化lame引擎,初始化完成后可以设置输入的相关参数:比特率、通道数。
注意:这些参数需要了解清楚需求后进行设置,否则转码出来的音频可能出现时长或者播放的问题。
glf =lame_init();
lame_set_in_samplerate(glf, inSampleRate);
lame_set_num_channels(glf, outChannel);
lame_set_out_samplerate(glf, outSampleRate);
lame_set_brate(glf, outBitrate);
lame_set_quality(glf, quality);
lame_init_params(glf);
2. lame_encode_buffer()和 lame_encode_buffer_interleaved()
if (channels == 2) {
write= lame_encode_buffer_interleaved(gfp, input_buffer, read, mp3_buffer,MP3BUFSIZE);//立体声用此方法编码
} else if (channels == 1) {
write= lame_encode_buffer(gfp, input_buffer, input_buffer, read,mp3_buffer, MP3BUFSIZE);//单声道
}
3. lame_mp3_tags_fid(gfp,outfp)
在lame_encode_flush(gfp,mp3_buffer, sizeof(mp3_buffer))方法之后,lame_close(gfp)之前调用lame_mp3_tags_fid(gfp,outfp)方法为MP3文件添加vbr头,播放器才能正读取时间。
六、思维拓展
实战:
1. 录音为Mp3格式:(代码已转private)
2. 将wav格式的音频文件转码为Mp3格式:(代码已转private)
推荐资料: