什么是Lame
- Lame是一个C语言MP3编码库
- Lame源码本身是运行在PC平台的,我们对其进行了稍加修改,使其适用于Android平台
- PCM转MP3是安卓开发中常见的需求,PCM是未经处理的原生音频数据,安卓录音得到的都是PCM数据,但是一般网站都会要求MP3格式的,因为PCM体积太大,所以PCM转MP3是非常常见的需求
- C++一般负责的都是音视频开发,硬件通信,进程控制等工作,而Lame库又是音视频开发当中最常见最简单的功能,很适合作为我们学习NDK的入门材料
下载Android Lame源码和库
这里先给出源码,对于刚学习NDK的新手来说,很多细节都可能出问题,有一个问题就编译不过,所以先给出代码
除了Lame源码部分,建议新手亲自编写全部代码,并弄清全部原理。其实NDK开发就那么多东西,所有坑都踩过,知道解决方法和其中原理,NDK就学得差不多了。早点踩坑,就离熟练掌握更近一步
这里给出两个链接,一个是Demo源码,适用于那些抱着学习态度的人,另一个是编译好的so库和JNA接口,适用于那些急着拿成品直接开发应用的人
AndroidLame完整源码:Project-Lame
编译好的so库和JNA接口:Liblame
核心代码
这里给出除了Lame源码外的所有代码和编译配置
开启NDK编译功能,添加JNA依赖,添加so库依赖 注意,安卓上的JNA的需要依赖一个libjnidispatch.so文件,这和在PC上通过JNA加载DLL是不一样的
//build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.easing.android"
minSdkVersion 23
targetSdkVersion 29
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath = true
}
}
//过滤CPU架构,只使用armv7的库
ndk {
abiFilters "armeabi-v7a"
}
}
//加载指定位置的so库
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
//编译jni目录,开启这个选项后,会自动编译C++代码生成so文件,并自动引用
//开启此选项后,就不需要再将so库拷贝到sourceSets指定的位置了,否则会引起so库重复错误
externalNativeBuild {
ndkBuild {
path 'src/jni/Android.mk'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
dependencies {
api project(':android-commons')
api 'net.java.dev.jna:jna:5.5.0'
}
NDK编译配置
//Android.mk
#记录当前路径
LOCAL_PATH := $(call my-dir)
#清除默认的LOCAL变量
include $(CLEAR_VARS)
#模块名称
LOCAL_MODULE := liblame
#链接系统模块
LOCAL_LDLIBS := -lm -llog
#源码列表
#建议通过工具自动遍历所有源码文件,生成路径列表
#不建议通过指令遍历所有源码,较为复杂且难以阅读
LOCAL_SRC_FILES := JnaLame.cpp logcat.cpp liblame/bitstream.c liblame/bitstream.h liblame/encoder.c liblame/encoder.h liblame/fft.c liblame/fft.h liblame/gain_analysis.c liblame/gain_analysis.h liblame/id3tag.c liblame/id3tag.h liblame/l3side.h liblame/lame-analysis.h liblame/lame.c liblame/lame.h liblame/lameerror.h liblame/lame_global_flags.h liblame/machine.h liblame/mpglib_interface.c liblame/newmdct.c liblame/newmdct.h liblame/presets.c liblame/psymodel.c liblame/psymodel.h liblame/quantize.c liblame/quantize.h liblame/quantize_pvt.c liblame/quantize_pvt.h liblame/reservoir.c liblame/reservoir.h liblame/set_get.c liblame/set_get.h liblame/tables.c liblame/tables.h liblame/takehiro.c liblame/util.c liblame/util.h liblame/vbrquantize.c liblame/vbrquantize.h liblame/VbrTag.c liblame/VbrTag.h liblame/version.c liblame/version.h
#引入本地库
#此处引入C++标准库作为测试
#由于Application.mk里面已经开启了标准库,这里其实是多余的
LOCAL_C_INCLUDES := D:/dev/sdk/ndk/21.0.6113669/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a
LOCAL_C_INCLUDES += D:/dev/sdk/ndk/21.0.6113669/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_static.a
#指示编译器生成动态库
#如果不生成动态库,则不需要Application.mk文件
include $(BUILD_SHARED_LIBRARY)
so库编译配置
//Application.mk
#指定要适配的CPU架构
APP_ABI := armeabi-v7a
#最低安卓版本要求
APP_PLATFORM := android-23
#忽略格式安全检查
APP_CPPFLAGS := -Wno-error=format-security
#开启C++标准库
APP_STL := c++_static
通过std::cout打印内容到控制台
//logcat.cpp
#ifndef LOGCAT_CPP
#define LOGCAT_CPP
#include
#include
#include
#include
#include
#include
static int pfd[2];
static pthread_t tid;
static const char *TAG = "NativeLogger";
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
//子线程,不断从FileDescription中读取数据并打印
static void *threadFunc(void *) {
ssize_t byteCount;
char buffer[1024];
while ((byteCount = read(pfd[0], buffer, sizeof buffer - 1)) > 0) {
if (buffer[byteCount - 1] == '\n') --byteCount;
buffer[byteCount] = 0;
LOGI(buffer);
}
return 0;
}
//开启JNI日志服务
static int startNativeLogger(const char *tag) {
TAG = tag;
//将stdout与stderror与缓冲区解绑
setvbuf(stdout, nullptr, _IOLBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
//打开FileDescription,并将stdout和stderror重定向到FileDescription
pipe(pfd);
dup2(pfd[1], 1);
dup2(pfd[1], 2);
//开启子线程,不断从FileDescription中读取数据并打印
if (pthread_create(&tid, 0, threadFunc, 0) == -1)
return -1;
pthread_detach(tid);
return 0;
}
#endif
Lame暴漏接口给JNA调用
//JnaLame.cpp
#include "logcat.cpp"
#include "liblame/lame.h"
//使用std::cout前需先调用此方法,设置打印标志,开启打印服务
extern "C" int initNativeLogger(const char *tag) {
startNativeLogger(tag);
std::cout
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?