Android 集成 Lame 实现 pcm 转 mp3
下载 lame 库
安装 NDK 和 CMake
新建 Android Native Module
修改源码
然后将 lame/include
和 lame/libmp3lame
目录下的所有 .h
和 .c
文件复制到 module 中
修改 lame 中的文件
- 删除
fft.c
文件中 47 行的include "vector/lame_intrin.h"
- 修改
set_get.h
文件的 24 行的#include <lame.h>
为#include "lame.h"
- 将
util.h
文件的 574 行的extern ieee754_float32_t fast_log2(ieee754_float32_t x);
替换为extern float fast_log2(float x);
修改 CMakeLists.txt 文件
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("lib_lame")
#设置so库的输出路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
lib_lame
# Sets the library as a shared library.
SHARED
# 主要是修改这里,native-lib.cpp 为调用c方法的接口
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
)
# 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_link_libraries( # Specifies the target library.
lib_lame
# Links the target library to the log library
# included in the NDK.
${log-lib})
修改 build.gradle
android {
compileSdk 31
defaultConfig {
// ...
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
cFlags "-DSTDC_HEADERS"
}
}
ndk{
abiFilters 'x86','x86_64','armeabi-v7a','arm64-v8a'
}
// ...
}
// ...
}
编写 native-lib.cpp 文件
获取 lame 版本
#include "lame/lame.h"
extern "C" JNIEXPORT jstring JNICALL
Java_com_github_crayonxiaoxin_lib_1lame_NativeLib_getLameVersion(JNIEnv *env, jobject thiz) {
return env->NewStringUTF(get_lame_version());
}
在 java 文件中定义对应的 c 方法
class NativeLib {
/**
* A native method that is implemented by the 'lib_lame' native library,
* which is packaged with this application.
*/
// external fun stringFromJNI(): String
external fun getLameVersion(): String
companion object {
// Used to load the 'lib_lame' library on application startup.
init {
System.loadLibrary("lib_lame")
}
}
}
然后点击 “Make Project”
调用
引入 lib-lame 库,在 log 中调用
继续添加 mp3 Encoder
创建 mp3_encoder.h
#ifndef LAME_MP3ENCODER_H
#define LAME_MP3ENCODER_H
#include <stdio.h>
#include "lame.h"
class Mp3Encoder {
private:
FILE* pcmFile;
FILE* mp3File;
lame_t lameClient;
public:
Mp3Encoder();
~Mp3Encoder();
int Init(const char* pcmFilePath, const char* mp3FilePath, int sampleRate, int channels, int bitRate);
void Encode();
void Destroy();
};
#endif //LAME_MP3ENCODER_H
创建 mp3_encoder.cpp
#include "mp3_encoder.h"
#include "lame.h"
Mp3Encoder::Mp3Encoder() {
}
int Mp3Encoder::Init(const char* pcmFilePath, const char* mp3FilePath, int sampleRate, int channels, int bitRate) {
int ret = -1;
pcmFile = fopen(pcmFilePath, "rb");
if (pcmFile) {
mp3File = fopen(mp3FilePath, "wb");
if (mp3File) {
lameClient = lame_init();
// in 采样率
lame_set_in_samplerate(lameClient, sampleRate);
// out 采样率
lame_set_out_samplerate(lameClient, sampleRate);
lame_set_num_channels(lameClient, channels);
lame_set_brate(lameClient, bitRate / 1000);
lame_init_params(lameClient);
ret = 0;
}
}
return ret;
}
void Mp3Encoder::Encode() {
int bufferSize = 1024 * 256;
short *buffer = new short[bufferSize / 2];
short *leftBuffer = new short[bufferSize / 4];
short *rightBuffer = new short[bufferSize / 4];
unsigned char* mp3_buffer = new unsigned char[bufferSize];
size_t readBufferSize = 0;
while ((readBufferSize = fread(buffer, 2, bufferSize / 2, pcmFile)) > 0) {
for (int i = 0; i < readBufferSize; i++) {
if (i % 2 == 0) {
leftBuffer[i / 2] = buffer[i];
} else {
rightBuffer[i / 2] = buffer[i];
}
}
size_t wroteSize = lame_encode_buffer(lameClient, (short int *) leftBuffer, (short int *) rightBuffer, (int)(readBufferSize / 2), mp3_buffer, bufferSize);
fwrite(mp3_buffer, 1, wroteSize, mp3File);
}
delete [] buffer;
delete [] leftBuffer;
delete [] rightBuffer;
delete [] mp3_buffer;
}
void Mp3Encoder::Destroy() {
if (pcmFile) {
fclose(pcmFile);
}
if (mp3File) {
fclose(mp3File);
lame_close(lameClient);
}
}
Mp3Encoder::~Mp3Encoder() {
}
在 CMakeLists.txt 中添加源文件
修改 native-lib.cpp
#include <cstdio>
#include <jni.h>
#include <malloc.h>
#include <cstring>
#include <android/log.h>
#include <jni.h>
#include <string>
#include "lame/lame.h"
#include "lame/mp3_encoder.h"
#define LOG_TAG "System.out.c"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
extern "C" JNIEXPORT jstring JNICALL
Java_com_github_crayonxiaoxin_lib_1lame_NativeLib_getLameVersion(JNIEnv *env, jobject thiz) {
return env->NewStringUTF(get_lame_version());
}
Mp3Encoder *encoder;
extern "C" JNIEXPORT jint JNICALL
Java_com_github_crayonxiaoxin_lib_1lame_NativeLib_pcmToMp3JNI(
JNIEnv *env,
jobject,
jstring pcm_path,
jstring mp3_path,
jint sample_rate,
jint channel,
jint bit_rate) {
const char *pcmPath = env->GetStringUTFChars(pcm_path, NULL);
const char *mp3Path = env->GetStringUTFChars(mp3_path, NULL);
encoder = new Mp3Encoder();
encoder->Init(pcmPath, mp3Path, sample_rate, channel, bit_rate);
encoder->Encode();
env->ReleaseStringUTFChars(pcm_path, pcmPath);
env->ReleaseStringUTFChars(mp3_path, mp3Path);
return 0;
}
在 java/NativeLib.kt 中添加
external fun pcmToMp3JNI(
pcmPath: String, mp3Path: String,
sampleRate: Int, channel: Int, bitRate: Int
): Int
参考资料
Android借助LAME库实现MP3编码
安卓音频开发(二)lame编译