fdk-aac交叉编译与使用

简介

fdk-aac可用于aac编解码器,源码链接:

https://github.com/mstorsjo/fdk-aac

选择Tags下的一个稳定版本,如v2.0.2,进行下载

编译

  • 解压压缩文件
tar -xvf fdk-aac-2.0.2.tar.gz

添加sigmastar_uclibc_config.cmake文件:

# 参考 https://cmake.org/cmake/help/v3.22/manual/cmake-toolchains.7.html#cross-compiling-for-linux
SET(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

# 指定交叉编译器路径
SET(TOOLCHAIN_DIR "/opt/sigmaStar/arm-buildroot-linux-uclibcgnueabihf-4.9.4-uclibc-1.0.31/")
set(TOOLCHAIN_HOST "${TOOLCHAIN_DIR}/bin/arm-buildroot-linux-uclibcgnueabihf")

set(CMAKE_CXX_COMPILER ${TOOLCHAIN_HOST}-g++)
set(CMAKE_C_COMPILER   ${TOOLCHAIN_HOST}-gcc)

SET(CMAKE_FIND_ROOT_PATH  ${TOOLCHAIN_DIR}
 ${TOOLCHAIN_DIR}/include
 ${TOOLCHAIN_DIR}/lib )

# 从来不在指定目录下查找工具程序
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# 只在指定目录下查找库文件
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# 只在指定目录下查找头文件
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)


  • 添加编译脚本build.sh
#!/bin/bash

ROOT_PATH=`pwd`
TARGET_PATH=$ROOT_PATH/output

rm -rf output
rm -rf build
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=$TARGET_PATH \
         -DCMAKE_BUILD_TYPE=Release \
         -DCMAKE_TOOLCHAIN_FILE=$ROOT_PATH/sigmastar_uclibc_config.cmake \
         -DFDK_AAC_INSTALL_CMAKE_CONFIG_MODULE=OFF\
         -DFDK_AAC_INSTALL_PKGCONFIG_MODULE=OFF\
         -DBUILD_SHARED_LIBS=OFF

make -j 3
make install
  • 运行编译脚本
sh build.sh
  • 编译结果
root@ubuntu:~/fdk-aac-2.0.2/output# tree
.
├── include
│   └── fdk-aac
│       ├── aacdecoder_lib.h
│       ├── aacenc_lib.h
│       ├── FDK_audio.h
│       ├── genericStds.h
│       ├── machine_type.h
│       └── syslib_channelMapDescr.h
└── lib
    └── libfdk-aac.a
  • 拷贝include和lib到工程路径下

编码样例

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

#include <stdlib.h>
#include "fdk-aac/aacenc_lib.h"

typedef struct {
    
    
    HANDLE_AACENCODER encoder;
    int input_size;
    int out_size;
} AAC_ENCODE_INFO_t, *PAAC_ENCODE_INFO_t;

int initEncoder(int sample_rate, int channels, PAAC_ENCODE_INFO_t encode_info)
{
    
    
    if (encode_info == NULL) {
    
    
        return -1;
    }

    int ret = -1;
    int bitrate = 64000;
    int vbr = 0;
    HANDLE_AACENCODER encoder = NULL;
    int aot = AOT_AAC_LC;
    int afterburner = 1;
    CHANNEL_MODE mode;
    AACENC_InfoStruct info = {
    
     0 };

    int input_size;

    switch (channels) {
    
    
        case 1: mode = MODE_1;       break;
        case 2: mode = MODE_2;       break;
        case 3: mode = MODE_1_2;     break;
        case 4: mode = MODE_1_2_1;   break;
        case 5: mode = MODE_1_2_2;   break;
        case 6: mode = MODE_1_2_2_1; break;
        default:
            fprintf(stderr, "Unsupported WAV channels %d\n", channels);
            goto CleanUp;
    }

    if (aacEncOpen(&encoder, 0, channels) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to open encoder\n");
        goto CleanUp;
    }

    if (aacEncoder_SetParam(encoder, AACENC_AOT, aot) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to set the AOT\n");
        goto CleanUp;
    }

    if (aacEncoder_SetParam(encoder, AACENC_SAMPLERATE, sample_rate) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to set the sample rate\n");
        goto CleanUp;
    }

    if (aacEncoder_SetParam(encoder, AACENC_CHANNELMODE, mode) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to set the channel mode\n");
        goto CleanUp;
    }

    if (aacEncoder_SetParam(encoder, AACENC_CHANNELORDER, 1) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to set the wav channel order\n");
        goto CleanUp;
    }

    if (vbr != 0) {
    
    
        if (aacEncoder_SetParam(encoder, AACENC_BITRATEMODE, vbr) != AACENC_OK) {
    
    
            fprintf(stderr, "Unable to set the VBR bitrate mode\n");
            goto CleanUp;
        }
    } else {
    
    
        if (aacEncoder_SetParam(encoder, AACENC_BITRATE, bitrate) != AACENC_OK) {
    
    
            fprintf(stderr, "Unable to set the bitrate\n");
            goto CleanUp;
        }
    }

    // It is usually TT_MP4_ADTS and TT_MP4_RAW
    if (aacEncoder_SetParam(encoder, AACENC_TRANSMUX, TT_MP4_ADTS) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to set the ADTS transmux\n");
        goto CleanUp;;
    }

    if (aacEncoder_SetParam(encoder, AACENC_AFTERBURNER, afterburner) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to set the afterburner mode\n");
        goto CleanUp;;
    }

    if (aacEncEncode(encoder, NULL, NULL, NULL, NULL) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to initialize the encoder\n");
        goto CleanUp;;
    }

    if (aacEncInfo(encoder, &info) != AACENC_OK) {
    
    
        fprintf(stderr, "Unable to get the encoder info\n");
        goto CleanUp;;
    }

    ret = 0;
    printf( "info.maxOutBufBytes = %u, info.frameLength = %u\n", info.maxOutBufBytes, info.frameLength);

    // default bit width is 16, 16 / 8 = 2
    input_size = channels * 2 * info.frameLength;

CleanUp:
    if (ret != 0) {
    
    
        if (encoder != NULL) {
    
    
            aacEncClose(&encoder);
        }
    } else {
    
    
        encode_info->encoder= encoder;
        encode_info->input_size = input_size;
        encode_info->out_size = info.maxOutBufBytes;
    }

    return ret;
}

void deInitEncoder(HANDLE_AACENCODER encoder)
{
    
    
    aacEncClose(&encoder);
}

void encodeFrame(HANDLE_AACENCODER encoder, const uint8_t* in_buffer, int in_size, uint8_t* out_buffer, int out_size, int* out_bytes)
{
    
    
    if (encoder == NULL || in_buffer == NULL || in_size == 0 || out_buffer == NULL || out_size == 0) {
    
    
        return;
    }

    AACENC_BufDesc in_buf = {
    
     0 }, out_buf = {
    
     0 };
    AACENC_InArgs in_args = {
    
     0 };
    AACENC_OutArgs out_args = {
    
     0 };
    int in_identifier = IN_AUDIO_DATA;
    int in_elem_size;
    int out_identifier = OUT_BITSTREAM_DATA;
    int out_elem_size;
    AACENC_ERROR err;

    // 2 bytes per sample point
    in_args.numInSamples = in_size / 2;

    in_elem_size = 2;
    in_buf.numBufs = 1;
    in_buf.bufs = (void**)&in_buffer;
    in_buf.bufferIdentifiers = &in_identifier;
    in_buf.bufSizes = &in_size;
    in_buf.bufElSizes = &in_elem_size;

    out_elem_size = 1;
    out_buf.numBufs = 1;
    out_buf.bufs = (void**)&out_buffer;
    out_buf.bufferIdentifiers = &out_identifier;
    out_buf.bufSizes = &out_size;
    out_buf.bufElSizes = &out_elem_size;

    if ((err = aacEncEncode(encoder, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) {
    
    
        fprintf(stderr, "Encoding failed\n");
    }

    *out_bytes = out_args.numOutBytes;

    printf("out_args.numOutBytes = %d, out_args.numInSamples = %d\n", out_args.numOutBytes, out_args.numInSamples);
}

说明:

  1. 输入默认音频为8k采样率,单通道,16bit位宽的PCM格式,每秒数据为8000116/8=16000字节
  2. 编码后的aac数据为ADTS格式,可改变TT_MP4_ADTS为TT_MP4_RAW获取裸数据,也可以通过跳过ADTS帧头
  3. info.maxOutBufBytes表示编码后的每帧的最大数据,当前为768字节
  4. 当前编码输出为固定的768字节,如果需要可变码率设置vbr为1-5,码率依次升高,编码输出长度为1-768字节
  5. 编码后的实际长度根据out_bytes 对输出数据进行存储
  6. aac是1024个采样点数据进行一次编码,对应的PCM格式长度为2048字节,那么对应的aac帧率为16000/2048=7.813。由于AENC编码的数据不是刚好2048字节,通常为640字节,那么需要一个循环数组对数据进行存储,当长度满足input_size后再进行一次编码

猜你喜欢

转载自blog.csdn.net/bajie7045/article/details/123354600