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