app方如果要传输音频文件到服务器,wav文件整体来说还是太大了,所以选择使用有损压缩mp3格式,编译使用lame库
1.编译lame库
lame官网: lame下载地址
下载下来后解压缩,后android studio创建一个支持c++的项目
将include和libmp3lame文件夹下的.h和.c文件全部拷贝到android项目里
|
在android cpp下面新建文件夹lame,粘贴进去,共42个文件
源码部分修改
1)删除fft.c文件的47行的"include "vector/lame_intrin.h""
2)删除set_get.h文件的24行的"#include <lame.h>"
3)将util.h文件的574行的"extern ieee754_float32_t fast_log2(ieee754_float32_t x);" 替换为 "extern float fast_log2(float x);"
在cmakeLists里面添加如下代码,最上面是你的项目名和cmake版本
#设置so库的输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
#设置编译类型
#add_executable(demo demo.cpp) # 生成可执行文件
#生成动态共享库
add_library( # 设置编译成so库的名称
lamecompile
# 生成动态库或共享库,此处如果SHARED改为STATIC,其含义是生成静态库
SHARED
# 提供一个需要编译的源文件的相对路径(),native-lib.cpp就是需要编译的源文件
native-lib.cpp
lame/bitstream.c
lame/encoder.c
lame/fft.c
lame/gain_analysis.c
lame/id3tag.c
lame/lame.c
lame/mpglib_interface.c
lame/newmdct.c
lame/presets.c
lame/psymodel.c
lame/quantize.c
lame/quantize_pvt.c
lame/reservoir.c
lame/set_get.c
lame/tables.c
lame/takehiro.c
lame/util.c
lame/vbrquantize.c
lame/VbrTag.c
lame/version.c
)
#明确指定编译时需要编译哪些源文件
#add_library(demo demo.cpp test.cpp util.cpp)
#aux_source_directory(dir VAR) 发现一个目录下所有的源代码文件并将列表存储在一个变量中。
#例如:aux_source_directory(. SRC_LIST) # 搜索当前目录下的所有.cpp文件
#add_library(demo ${SRC_LIST})
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
#查找到指定的预编译库,并将它的路径存储在变量中
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
#设置target需要链接的库
target_link_libraries( # Specifies the target library.目标库
lamecompile
# Links the target library to the log library 目标库需要链接的库,log-lib是上面find_library指定的变量名
# included in the NDK.
${log-lib})
在native-lib.cpp中添加如下代码
#include <jni.h>
#include <string>
#include "lame/lame.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_dfl_lamecompile_LameUtils_getLameVersion(JNIEnv *env, jobject thiz) {
return env->NewStringUTF(get_lame_version());
}
char* Jstring2CStr(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312"); // 指定编码为GB2312
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0) {
rtn = (char*)malloc(alen + 1); // +1 for the null terminator
memcpy(rtn, ba, alen);
rtn[alen] = 0; // Add null terminator
env->ReleaseByteArrayElements(barr, ba, 0);
}
return rtn;
}
// 错误码
enum ErrorCode {
SUCCESS = 0,
FILE_OPEN_ERROR = 1,
LAME_INIT_ERROR = 2,
CONVERSION_ERROR = 3
};
extern "C"
JNIEXPORT int JNICALL
Java_com_dfl_lamecompile_LameUtils_wavToMp3
(JNIEnv *env, jobject obj, jstring jwav, jstring jmp3, jint inSamplerate, jint inChannel,
jint outBitrate) {
int flag = 0;
char *cwav = Jstring2CStr(env, jwav);
char *cmp3 = Jstring2CStr(env, jmp3);
//1.打开 wav,MP3文件
FILE *fwav = fopen(cwav, "rb");
fseek(fwav, 4 * 1024, SEEK_CUR);
FILE *fmp3 = fopen(cmp3, "wb+");
if (!fmp3) {
fclose(fwav);
return FILE_OPEN_ERROR;
}
int channel = inChannel;//单声道
short int wav_buffer[8192 * channel];
unsigned char mp3_buffer[8192];
//1.初始化lame的编码器
lame_t lameConvert = lame_init();
if (!lameConvert) {
fclose(fwav);
fclose(fmp3);
return LAME_INIT_ERROR;
}
//2. 设置lame mp3编码的采样率
lame_set_in_samplerate(lameConvert, inSamplerate);
lame_set_out_samplerate(lameConvert, inSamplerate);
lame_set_num_channels(lameConvert, channel);
lame_set_mode(lameConvert, MONO);
// 3. 设置MP3的编码方式
lame_set_VBR(lameConvert, vbr_default);
lame_init_params(lameConvert);
int read;
int write; //代表读了多少个次 和写了多少次
int total = 0; // 当前读的wav文件的byte数目
try{
do {
if (flag == 404) {
throw CONVERSION_ERROR;
}
read = fread(wav_buffer, sizeof(short int) * channel, 8192, fwav);
total += read * sizeof(short int) * channel;
if (read != 0) {
write = lame_encode_buffer(lameConvert, wav_buffer, NULL, read, mp3_buffer, 8192);
//write = lame_encode_buffer_interleaved(lame,wav_buffer,read,mp3_buffer,8192);
} else {
write = lame_encode_flush(lameConvert, mp3_buffer, 8192);
}
if (fwrite(mp3_buffer, 1, write, fmp3) != write) {
throw CONVERSION_ERROR;
}
//把转化后的mp3数据写到文件里
// fwrite(mp3_buffer, 1, write, fmp3);
} while (read != 0);
lame_mp3_tags_fid(lameConvert, fmp3);
}catch (...){
lame_close(lameConvert);
fclose(fwav);
fclose(fmp3);
return CONVERSION_ERROR;
}
lame_close(lameConvert);
fclose(fwav);
fclose(fmp3);
return SUCCESS;
}
可以把这段代码里面加上try-catch flag放入方法体内 返回int类型可以在Java调用时候判断返回值来check是否转换成功
在你包名下创建LameUtils类,注意与c++代码到包名类名方法名对应
public class LameUtils {
static {
System.loadLibrary("lamecompile");
}
public native String getLameVersion();
/**
*
* @param wavFilePath
* @param mp3FilePath
* @param samplerate 采样率 41000 16000
* @param channels 声道数 1 2
* @param bitrate 位数(实际没有使用)
*/
public native int wavToMp3(String wavFilePath,String mp3FilePath,int samplerate,int channels,int bitrate);
}
然后点击build-make project成功后会自动多出一个lib文件夹,里面放置的是so文件
2.使用lame库
把so文件放入jniLibs文件夹中(新开一个项目),建立与编译lame库相同的包名-类名-方法名,调用版本方法测试是否可以成功。3.99.5为此lame方法,后调用wavtomp3方法进行压缩wav文件。java里面调用这个库的时候最好加锁 线程安全 当然如果只是想测一下能不能跑 直接用就可以。
压缩结果如上图,如果害怕压缩耗性能可以新开一个线程,使用生产者-消费者模式,从队列里面拿数据后进行压缩,压缩比大概1/10
使用so文件,我在macos上使用不需要编写cmakelists文件,但是windows上好像又需要
在cmakelists里面新增(set后面是绝对路径有点冗余后期会查阅资料给它精简普遍化一下适合放在项目里面的写法)
后期发现windows也不需要 直接加在jniLibs里面就可以直接使用 build.gradle也不需要修改,两个系统都直接拉进macos里面直接使用 包名类名方法名跟打包so文件时候一样就可以
add_library(lame SHARED IMPORTED)
set_target_properties(lame PROPERTIES
IMPORTED_LOCATION D:\\Code\\RadioWithPhone\\app\\src\\main\\jniLibs\\${ANDROID_ABI}\\liblamecompile.so
)
然后在target_link_libraries里面加上lame库 应该就可以使用了。