FAAC(Freeware Advanced Audio Coder)是一个开源的音频编码器,主要用于将音频数据编码为AAC(Advanced Audio Coding)格式。
在Android平台上,如果你想在自己的应用中集成FAAC,你可以通过以下步骤来实现:
首先,你需要从FAAC的官方网站或者GitHub仓库下载源代码。下载地址:https://sourceforge.net/projects/faac/files/faac-src/faac-1.28/
下载完成后解压源码
将解压出来的FAAC源码里面的这个目录复制到创建好的Library库中的cpp文件夹下面
调整cpp中的CMakeLists.txt文件目录位置
配置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.22.1)
set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# Declares and names the project.
project("libfaac")
# 设置第三方头文件路径
set(libs_include_DIR src/main/cpp/include)
# 将第三方头文件路径加入到搜索路径中
include_directories(${libs_include_DIR})
# 设置第三方库文件路径
set(libs_DIR src/main/jniLibs/${ANDROID_ABI})
# 将第三方库文件路径加入到链接路径中
link_directories(${libs_DIR})
file(GLOB SRC_FILES src/main/cpp/libfaac/*.h src/main/cpp/libfaac/*.c src/main/cpp/include/*.h)
# 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.
libfaac
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/libfaac.cpp
${SRC_FILES})
# 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.
libfaac
# Links the target library to the log library
# included in the NDK.
${log-lib})
编写代码桥接libfaac.cpp
/**
* FAAC编码器封装类
*
* 1.支持PCM文件编码成AAC文件 [FAACEncoder.encodePcmToAac]
*
* 2.支持从麦克风实时编码成AAC [FAACEncoder.init]->[FAACEncoder.encode]->[FAACEncoder.release]
*/
class FAACEncoder {
companion object {
init {
System.loadLibrary("libfaac")
}
}
/**
* 获取FAAC版本信息
*/
external fun getVersion(): String
/**
* 将PCM文件编码到AAC
* @param sampleRate: 采样率
* @param channels: 声道数
* @param bitRate: 比特率
* @param inPcmFilePath PCM文件地址
* @param outAacFilePath 目标AAC存储地址
* @return 0:成功、-1:PCM文件打开失败或者不存在、-2:AAC文件打开失败、-3:FAAC编码器打开失败
*/
external fun encodePcmToAac(sampleRate: Int, channels: Int, bitRate: Int, inPcmFilePath: String, outAacFilePath: String): Int
/**
* 初始化FAAC编码器
* @param sampleRate: 采样率
* @param channels: 声道数
* @param bitRate: 比特率
* @return 成功初始化返回true,否则返回false
*/
external fun init(sampleRate: Int, channels: Int, bitRate: Int): Boolean
/**
* 编码PCM音频数据
* @param pcmData 输入的PCM音频数据,作为字节数组
* @return 成功返回编码后的AAC数据的字节数组,否则返回null
*/
external fun encode(pcmData: ByteArray): ByteArray?
/**
* 释放FAAC编码器资源
*/
external fun release()
}
libfaac.cpp实现代码
#include
#include
#include "faac.h"
extern "C" {
faacEncHandle encoder;
unsigned long inputSamples = 0, maxOutputBytes = 0;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_actionbar_libfaac_FAACEncoder_getVersion(JNIEnv *env, jobject /* this */) {
char *faac_id_string;
char *faac_copyright_string;
faacEncGetVersion(&faac_id_string, &faac_copyright_string);
std::string faacVersion = faac_id_string;
faacVersion += "\n";
faacVersion += faac_copyright_string;
return env->NewStringUTF(faacVersion.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_actionbar_libfaac_FAACEncoder_encodePcmToAac(JNIEnv *env, jobject thiz,
jint sample_rate,
jint channels,
jint bit_rate,
jstring in_pcm_file_path,
jstring out_aac_file_path) {
unsigned long nSampleRate = sample_rate;//采样率
unsigned int nChannels = channels;//声道数
unsigned int nPCMBitSize = 16;//单样本位数
unsigned long nInputSamples = 0;
unsigned long nMaxOutputBytes = 0;
int nRet;
faacEncHandle hEncoder;
faacEncConfigurationPtr config;
size_t nBytesRead;
unsigned long nPCMBufferSize;
int32_t *pbPCMBuffer;
unsigned char *pbAACBuffer;
FILE *fpIn; // PCM file for input
FILE *fpOut; // AAC file for output
/// 获取 faac 版本信息
{
char *version;
char *copyright;
faacEncGetVersion(&version, ©right);
printf("FAAC version: %s, copyright: %s", version, copyright);
}
fpIn = fopen(env->GetStringUTFChars(in_pcm_file_path, JNI_FALSE), "rb");
if (!fpIn) {
perror("Failed to open input file");
return -1;
}
fpOut = fopen(env->GetStringUTFChars(out_aac_file_path, JNI_FALSE), "wb");
if (!fpOut) {
perror("Failed to open output file");
fclose(fpIn);
return -2;
}
/// 1. 打开 FAAC
hEncoder = faacEncOpen(nSampleRate, nChannels, &nInputSamples, &nMaxOutputBytes);
if (!hEncoder) {
fprintf(stderr, "[ERROR] Failed to call faacEncOpen()\n");
fclose(fpIn);
fclose(fpOut);
return -3;
}
nPCMBufferSize = nInputSamples * nPCMBitSize / 8;
pbPCMBuffer = (int32_t *) malloc(nPCMBufferSize);
pbAACBuffer = (unsigned char *) malloc(nMaxOutputBytes);
/// 2.0. 获取当前的编码器配置
config = faacEncGetCurrentConfiguration(hEncoder);
// 设置MPEG版本为MPEG4
config->mpegVersion = MPEG4;
// 设置AAC对象类型为LOW,以确保iOS的AVAudioPlayer能够播放
config->aacObjectType = LOW;
// 设置输入格式为16位
config->inputFormat = FAAC_INPUT_16BIT;
// 设置输出格式为ADTS
config->outputFormat = 1;
// 启用TNS(Temporal Noise Shaping)
config->useTns = 1;
// 根据声道数计算比特率并设置
config->bitRate = bit_rate / channels;
/// 2.1. 配置编码器
nRet = faacEncSetConfiguration(hEncoder, config);
do {
//读入的实际字节数,最大不会超过 nPCMBufferSize
nBytesRead = fread(pbPCMBuffer, 1, nPCMBufferSize, fpIn);
//输入样本数,用实际读入字节数计算
//一般只有读到文件尾时才不是 nPCMBufferSize/(nPCMBitSize/8)
nInputSamples = nBytesRead / (nPCMBitSize / 8);
/// 3. 编码
nRet = faacEncEncode(
hEncoder,
(int *) pbPCMBuffer,
(unsigned int) nInputSamples,
pbAACBuffer,
(unsigned int) nMaxOutputBytes
);
fwrite(pbAACBuffer, 1, nRet, fpOut);
printf("FaacEncEncode returns %d\n", nRet);
} while (nBytesRead > 0);
/// 4. 关闭 FAAC
nRet = faacEncClose(hEncoder);
free(pbPCMBuffer);
free(pbAACBuffer);
fclose(fpIn);
fclose(fpOut);
return 0;
}
extern "C"
// 初始化FAAC编码器
// 参数 env: 指向JNI环境的指针,用于在Java和C++之间进行交互
// 参数 thiz: 调用该方法的Java对象的引用
// 参数 sample_rate: 采样率
// 参数 channels: 声道数
// 参数 bit_rate: 比特率
// 返回值: 成功初始化返回JNI_TRUE,否则返回JNI_FALSE
JNIEXPORT jboolean JNICALL
Java_com_actionbar_libfaac_FAACEncoder_init(JNIEnv *env, jobject thiz,
jint sample_rate,
jint channels,
jint bit_rate) {
// 初始化FAAC编码器
encoder = faacEncOpen(sample_rate, channels, &inputSamples, &maxOutputBytes);
// 如果编码器初始化失败,返回JNI_FALSE
if (!encoder) return JNI_FALSE;
// 获取当前编码器的配置参数指针
faacEncConfigurationPtr config = faacEncGetCurrentConfiguration(encoder);
// 设置MPEG版本为MPEG4
config->mpegVersion = MPEG4;
// 设置AAC对象类型为LOW,以确保iOS的AVAudioPlayer能够播放
config->aacObjectType = LOW;
// 设置输入格式为16位
config->inputFormat = FAAC_INPUT_16BIT;
// 设置输出格式为ADTS
config->outputFormat = 1;
// 启用TNS(Temporal Noise Shaping)
config->useTns = 1;
// 根据声道数计算比特率并设置
config->bitRate = bit_rate / channels;
// 将配置参数应用到编码器
faacEncSetConfiguration(encoder, config);
// 初始化成功,返回JNI_TRUE
return JNI_TRUE;
}
extern "C"
// 编码PCM音频数据
// 参数 env: 指向JNI环境的指针,用于在Java和C++之间进行交互
// 参数 thiz: 调用该方法的Java对象的引用
// 参数 pcm_data: 输入的PCM音频数据,作为字节数组
// 返回值: 成功返回编码后的AAC数据,否则返回空
JNIEXPORT jbyteArray JNICALL
Java_com_actionbar_libfaac_FAACEncoder_encode(JNIEnv *env,
jobject thiz,
jbyteArray pcm_data) {
// 获取PCM数据数组的元素指针
jbyte *pcmBuffer = env->GetByteArrayElements(pcm_data, nullptr);
// 为AAC编码数据分配缓冲区
unsigned char *aacBuffer = (unsigned char *) malloc(maxOutputBytes);//new unsigned char[maxOutputBytes];
// 执行编码
int encodedBytes = faacEncEncode(
encoder,
(int32_t *) pcmBuffer,
inputSamples,
aacBuffer,
maxOutputBytes
);
// 释放PCM数据数组的元素指针
env->ReleaseByteArrayElements(pcm_data, pcmBuffer, 0);
// 如果编码成功,返回编码后的AAC数据
if (encodedBytes > 0) {
// 创建一个新的字节数组用于存储编码后的AAC数据
jbyteArray result = env->NewByteArray(encodedBytes);
// 将编码后的AAC数据复制到新的字节数组中
env->SetByteArrayRegion(result, 0, encodedBytes, (jbyte *) aacBuffer);
// 释放AAC缓冲区
delete[] aacBuffer;
// 返回编码后的AAC数据
return result;
}
// 如果编码失败,释放AAC缓冲区并返回空
delete[] aacBuffer;
return nullptr;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_actionbar_libfaac_FAACEncoder_release(JNIEnv *env, jobject thiz) {
// 释放资源
if (encoder) faacEncClose(encoder);
}