Android Studio接入lame

在项目中要实装lame的开源库实现录音转码mp3的功能。
期间遇到诸多问题,度娘了N多博客,最终总结了接入了lame

首先在gradle.properties里面添加 android.useDeprecatedNDK = true

1、Android Studio中配置NDK路径,步骤为:

1)先下载NDK并安装(这句基本是废话)

2)点菜单栏的File->ProjectStructure…->在打开的窗口中左侧选中SDKLocation->在右侧Android NDK Location中填入NDK目录所在路径,如下图所示:

第一步
第二步

2、项目中配置JNI

1)项目切换到Project开发模式

2)在app工程下面的main/src/main/目录下面新建jni目录

默认在src/main/jni目录下面找c/c++文件编译,也可以在android{}下添加sourceSet{}

sourceSet{  
    main{  
        jni.srcDirs=['src/main/cpp']  
    }  
}

就能将指定目录下的c/c++文件编译成指定的模块名.so。
注意:上面这种方式通过gradle无法编译成多个模块,编译多个模块还是要自己写Android.mk文件实现,通过手动执行ndk-build编译成的*.so文件默认在src/main/libs下面(下面会详细讲到配置Android.mk文件)。

打包APK时,默认是寻找so的目录是/build/intermediates/ndk和/build/intermediates/jniLibs,前者是使用源码由Gradle编译成的so所处的位置,后者一般是一些第三方的so或者自己手动使用ndk编译生成的so文件所生成的,这个位置在src/main/jniLibs。

因此手动编译好so文件后,将其复制到jniLibs下面,或者在此放置第三方so,或者在build.gradle文件里面的android下面添加jniLib.srcDir定义了Gradle在哪里寻找生成的so库文件

3)在app工程下的build.gradle文件android下添加配置sourceSets
默认情况下,你需要把C/C++源代码放在 [module]/src/main/jni/ 路径下。
当然,也可以自定义源代码路径:

sourceSets {
        main {
            // jni.srcDirs禁用通过Gradle来编译本地c/c++代码
            jni.srcDirs = []
            // jniLib.srcDirs定义了Gradle在哪里寻找生成的so库文件 
            jniLibs.srcDir 'src/main/libs'  
        }
}
配置jni

NDK和JNI配置完成后,接下来就是配置lame相关

3、首先下载lame的源码,可能需要自备梯子。

http://lame.sourceforge.net/download.php
http://sourceforge.net/projects/lame/files/lame/3.99/lame-3.99.5.tar.gz

1)下载好之后解压
2)在jni下面创建文件目录(命名自己随意):lame-3.99.5_libmp3lame;
3)将解压文件夹下lame-x.xx.x/libmp3lame/ 目录下面的.c和.h文件拷贝到项目下的jni/lame-3.99.5_libmp3lame目录下,同时将将 lame-x.xx.x/include/ 目录下的 lame.h 也复制到 jni/lame-3.99.5_libmp3lame/中

下载的lame包
配置.c和.h文件

4、修改部分.c和.h文件的代码

修改源代码

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);"

5、在jni文件夹下创建Application.mk和Android.mk文件

mk文件

Application.mk里面的代码为:

APP_ABI := armeabi armeabi-v7a arm64-v8a x86 x86_64
APP_MODULES := mp3lame
APP_CFLAGS += -DSTDC_HEADERS
#APP_ABI:=x86_64
#APP_PLATFORM := android-21

注意:
如果是x86_64的话需要在Application.mk中加上
APP_CFLAGS += -DSTDC_HEADERS

 /*--以下为Native部分--*/
 static {
        System.loadLibrary("mp3lame");
 }

Android.mk里面的代码为:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LAME_LIBMP3_DIR := lame-3.99.5_libmp3lame   // 此处对于jni下面的lame目录

// 此处表示的是java代码引入.so的名称与Application文件的APP_MODULES := mp3lame 保持一致
LOCAL_MODULE    := mp3lame  

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 \
MP3Recorder.c

include $(BUILD_SHARED_LIBRARY)

6、书写jni下面的MP3Recorder.c和MP3Recorder.h文件的代码

MP3Recorder.c文件代码:

#include "lame-3.99.5_libmp3lame/lame.h"
#include "MP3Recorder.h"

static lame_global_flags *glf = NULL;

JNIEXPORT void JNICALL Java_com_babi_story_audio_MP3Recorder_init(
        JNIEnv *env, jclass cls, jint inSamplerate, jint outChannel,
        jint outSamplerate, jint outBitrate, jint quality) {
    if (glf != NULL) {
        lame_close(glf);
        glf = NULL;
    }
    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);
}

JNIEXPORT jint JNICALL Java_com_babi_story_audio_MP3Recorder_encode(
        JNIEnv *env, jclass cls, jshortArray buffer_l, jshortArray buffer_r,
        jint samples, jbyteArray mp3buf) {
    jshort* j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL);

    jshort* j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL);

    const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
    jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);

    int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r,
            samples, j_mp3buf, mp3buf_size);

    (*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0);
    (*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0);
    (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);

    return result;
}

JNIEXPORT jint JNICALL Java_com_babi_story_audio_MP3Recorder_flush(
        JNIEnv *env, jclass cls, jbyteArray mp3buf) {
    const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
    jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);

    int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size);

    (*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);

    return result;
}

JNIEXPORT void JNICALL Java_com_babi_story_audio_MP3Recorder_close(
        JNIEnv *env, jclass cls) {
    lame_close(glf);
    glf = NULL;
}

MP3Recorder.h文件代码:


/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_kubility_demo_MP3Recorder */

#ifndef _Included_MP3Recorder
#define _Included_MP3Recorder
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_kubility_demo_MP3Recorder
 * Method:    init
 * Signature: (IIIII)V
 */
JNIEXPORT void JNICALL Java_com_babi_story_audio_MP3Recorder_init
  (JNIEnv *, jclass, jint, jint, jint, jint, jint);

/*
 * Class:     com_kubility_demo_MP3Recorder
 * Method:    encode
 * Signature: ([S[SI[B)I
 */
JNIEXPORT jint JNICALL Java_com_babi_story_audio_MP3Recorder_encode
  (JNIEnv *, jclass, jshortArray, jshortArray, jint, jbyteArray);

/*
 * Class:     com_kubility_demo_MP3Recorder
 * Method:    flush
 * Signature: ([B)I
 */
JNIEXPORT jint JNICALL Java_com_babi_story_audio_MP3Recorder_flush
  (JNIEnv *, jclass, jbyteArray);

/*
 * Class:     com_kubility_demo_MP3Recorder
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_babi_story_audio_MP3Recorder_close
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

7、在终端进入jni目录,然后执行ndk-build命令
执行命令后将会在libs文件夹目录生成项目配置相关的.so文件

生成.so文件

参考:http://blog.csdn.net/superdhq/article/details/53366483

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容