Android编译FAAC源码-代码有完整注释

FAAC(Freeware Advanced Audio Coder)是一个开源的音频编码器,主要用于将音频数据编码为AAC(Advanced Audio Coding)格式。

在Android平台上,如果你想在自己的应用中集成FAAC,你可以通过以下步骤来实现:

1. 下载FAAC源代码

首先,你需要从FAAC的官方网站或者GitHub仓库下载源代码。下载地址:https://sourceforge.net/projects/faac/files/faac-src/faac-1.28/

选择faac-1.28.tar.gzAndroid编译FAAC源码-代码有完整注释_第1张图片

 下载完成后解压源码

2. 创建项目Android Library库用于配置和编译FAAC

Android编译FAAC源码-代码有完整注释_第2张图片

 将解压出来的FAAC源码里面的这个目录复制到创建好的Library库中的cpp文件夹下面

Android编译FAAC源码-代码有完整注释_第3张图片

Android编译FAAC源码-代码有完整注释_第4张图片 

调整cpp中的CMakeLists.txt文件目录位置

Android编译FAAC源码-代码有完整注释_第5张图片

配置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);
}

你可能感兴趣的:(android)