FAAC 在君正平台使用得到aac实时音频流

       本人在日常开发工作中发现君正并没有直接生成aac格式的音频接口,但是有生成pcm格式的接口,所以想着使用faac库来将得到的pcm实时流转化成aac格式的实时流,毕竟aac格式对播放器兼容性比较好,接下来是博主的一些方法,希望可以对大家日常开发嵌入式音视频有所帮助。​

1.FAAC 库详细介绍​

​FAAC​​(Free Advanced Audio Coder)是一个开源的音频编码库,用于将 ​​PCM 音频数据​​编码为 ​​AAC​​(Advanced Audio Coding)格式。AAC 是一种高效的有损音频压缩标准,广泛用于流媒体、数字广播(如DAB+)、MP4容器等场景。

 核心特性​

  • ​支持标准​​:兼容 AAC-LC(低复杂度规格)、HE-AAC(高效AAC,支持SBR技术)。
  • ​输入格式​​:支持 16-bit PCM 音频(单声道/立体声),采样率范围 8kHz 至 48kHz。
  • ​输出格式​​:生成原始 AAC 流或 ADTS/ADIF 格式的封装数据。
  • ​平台支持​​:跨平台(Linux、Windows、macOS、嵌入式系统)。
  • ​许可证​​:GPLv2(开源,商业使用需注意协议约束

2.FAAC下载,编译

2.1 下载

faac 的源码在Github托管,下载地址:https://github.com/knik0/faac/tags,博主下载的是

faac-1.31.1

2.2交叉编译

因为是君正平台是mips架构所以需要交叉编译

tar -vxf faac-faac-1.31.1.tar.gz
cd faac-faac-1.31.1/
./bootstrap
./configure --prefix=$PWD/install --host=mips-linux-gnu CC=mips-linux-gnu-gcc
make && make install 

成功以后可以在install目录下面看到对应的资源

3.FAAC使用

我们需要使用君正自带的音频接口获取PCM然后丢给faac编码即可。

君正API(得到PCM数据)-----> faac编码接口(得到AAC数据)

3.1faac使用流程

主要就是四个步骤:

1.打开编码器
faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate,
				  unsigned int numChannels,
				  unsigned long *inputSamples,
                                  unsigned long *maxOutputBytes
                                 );
2.设置编码器配置
int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder,
				    faacEncConfigurationPtr config);
3.开始编码
int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput,
			 unsigned char *outputBuffer,
			 unsigned int bufferSize);
4.关闭编码器
int FAACAPI faacEncClose(faacEncHandle hEncoder);

3.2接入君正平台实现PCM实时流转变成AAC实时流

主要就是一些参数的设置,注意这里要确保你的君正实时流接口正常取到PCM数据

int pcm_to_aac()
{
    // AAC编码(faac库)
    /* PCM参数 */
    unsigned long u64PcmSampleRate = AUDIO_SAMPLE_RATE_16000; // 采样率
    unsigned int u32PcmSampleBits = AUDIO_BIT_WIDTH_16;       // 采样位数
    unsigned int u32PcmChannels = AUDIO_SOUND_MODE_MONO;      // 声道数

    /* aac编码器相关 */
    faacEncHandle pFaacEncHandle = NULL;
    faacEncConfigurationPtr pFaacEncConf = NULL;

    /* 编码相关参数 */
    unsigned long u64PcmInSampleCnt = 0; // 打开编码器时传出的参数,编码传入的PCM采样数(不是字节)
    unsigned long u64AacOutMaxBytes = 0; // 打开编码器时传出的参数,编码传出最大字节数
    unsigned char *pu8PcmInBuf = NULL;   // 读取pcm并传递进去编码的缓存指针,后面根据编码器传出参数malloc分配
    unsigned char *pu8AacEncBuf = NULL;  // 编码得到的aac缓存,后面根据编码器传出参数malloc分配

    /* AAC编码 1/6:传递参数进去打开编码器 */
    pFaacEncHandle = faacEncOpen(u64PcmSampleRate, u32PcmChannels, &u64PcmInSampleCnt,                 &u64AacOutMaxBytes);
    if (pFaacEncHandle == NULL)
    {
        printf("faacEncOpen(...) error!\n");
    }
    /* 根据上面打开编码器传出的参数分配对应大小的缓存 */
    pu8PcmInBuf = (unsigned char*)malloc(u64PcmSampleRate*u32PcmSampleBits/8);
    pu8AacEncBuf = (unsigned char*)malloc(u64AacOutMaxBytes);

    /* AAC编码 2/6:设置编码器配置 */
    // 设置编码器配置 a/c: 先获取当前配置
    pFaacEncConf = faacEncGetCurrentConfiguration(pFaacEncHandle);
    // 设置编码器配置 b/c: 填充配置
    /*
        PCM Sample Input Format
        0	FAAC_INPUT_NULL			invalid, signifies a misconfigured config
        1	FAAC_INPUT_16BIT		native endian 16bit
        2	FAAC_INPUT_24BIT		native endian 24bit in 24 bits		(not implemented)
        3	FAAC_INPUT_32BIT		native endian 24bit in 32 bits		(DEFAULT)
        4	FAAC_INPUT_FLOAT		32bit floating point
    */
    pFaacEncConf->inputFormat = FAAC_INPUT_16BIT;
#if 0
	/* 下面参数不用设置,保存默认即可 */
	pFaacEncConf->aacObjectType = LOW; 	// MAIN:1  LOW:2  SSR:3  LTP:4
	pFaacEncConf->mpegVersion = MPEG4; 	// MPEG2:0  MPEG4:1
	pFaacEncConf->useTns = 1; 			/* Use Temporal Noise Shaping */
	pFaacEncConf->shortctl = 0; 		// SHORTCTL_NORMAL:0  SHORTCTL_NOSHORT:1  SHORTCTL_NOLONG:2
	pFaacEncConf->allowMidside = 1; 	/* Allow mid/side coding */
	pFaacEncConf->quantqual = 0; 		/* Quantizer quality */
	pFaacEncConf->outputFormat = 1; 	// 0:Raw  1:ADTS
	pFaacEncConf->bandWidth = 32000;//0 /* AAC file frequency bandwidth */
	pFaacEncConf->bitRate = 48000;//0 	/* bitrate / channel of AAC file */
#endif
    // 设置编码器配置 c/c: 重新设置编码器的配置信息
    faacEncSetConfiguration(pFaacEncHandle, pFaacEncConf);

    // 开始编码
    static int frame_real_size = 0;
    int s32needPcmBytes = 0;
    int s32EncAacBytes = 0;
    while(1){
        // 开始编码前先使用君正API获取PCM数据
        IMPAudioFrame frm;
        int ret = IMP_AI_GetFrame(devID, chnID, &frm, BLOCK);
        if (ret != 0)
        {
            IMP_LOG_ERR(TAG, "Audio Get Frame Data error\n");
            return NULL;
        }
        // 因为faccENcEncode函数需要的输入采样点数,要和openfaac函数设置的采样点数一致,所以这里计算需要的采样字节数
        s32needPcmBytes = u64PcmInSampleCnt * (u32PcmSampleBits / 8);
        memcpy(pu8PcmInBuf + frame_real_size, (void *)frm.virAddr, frm.len);
        frame_real_size += frm.len;
        // 释放君正音频帧
        ret = IMP_AI_ReleaseFrame(devID, chnID, &frm);
        if (ret != 0)
        {
            IMP_LOG_ERR(TAG, "Audio release frame data error\n");
            return NULL;
        }
        // 如果当前帧数据长度大于需要的采样字节数,则进行编码  相当于动态缓存
        if (frame_real_size >= s32needPcmBytes)
        {
            s32EncAacBytes = faacEncEncode(pFaacEncHandle, (int32_t *)pu8PcmInBuf,         u64PcmInSampleCnt ,pu8AacEncBuf, (unsigned int)u64AacOutMaxBytes);
            if ((frame_real_size - s32needPcmBytes) > 0)
            {
                frame_real_size -= s32needPcmBytes;
                memmove(pu8PcmInBuf, pu8PcmInBuf + s32needPcmBytes, frame_real_size);
            }
        }
        else
        {
            printf("数据不足下次处理!\n");
            break;
        }
    }
}

这里主要注意打开aac编码器时传入的u64PcmInSampleCnt(采样数)要和编码时保持一致也就是说一次pcm的数据要足够,不然编码出来的声音不对计算公式为:s32needPcmBytes = u32PcmInSampleCnt * (u32PcmSampleBits / 8);s32needPcmBytes需要的字节u32PcmInSampleCnt采样数u32PcmSampleBits采样位数。我这里的做法是动态缓存等数据足够在拿去编码,剩下的数据在用memove函数移到对应的位置。

4.总结

其实主要就是使用君正API得到pcm数据但是一次拿到的数据不一定是刚好符合faac编码的数量,使用动态缓存解决这个问题即可,如果想要读取pcm文件并转化成aac文件可以参考demo。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>


#include "faac.h"


//#define DEBUG(fmt, args...)
#define DEBUG(fmt, args...) 	printf(fmt, ##args)


void print_usage(const char *process)
{
	printf("sample: \n"
		   "\t %s -h\n"
		   "\t %s --help\n"
		   "\t %s -i ./audio/test_8000_16_1.pcm -r 8000 -b 16 -c 1 -o out_8khz_1ch.aac\n"
		   "\t %s --input_pcmfile=./audio/test_44100_16_2.pcm --sample_rate=44100 --sample_bits=16 --channels=2 --output_aacfile=out_44.1khz_2ch.aac\n",
		   process, process, process, process);
}


int main(int argc, char *argv[])
{
	/* 输入/输出文件 */
	FILE *fpPcm = NULL;
	FILE *fpAac = NULL;
	char pcmFileName[128] = {0};
	char aacFileName[128] = {0};

	/* PCM参数 */
	unsigned long u64PcmSampleRate = 0; // 采样率
	unsigned int  u32PcmSampleBits = 0; // 采样位数
	unsigned int  u32PcmChannels   = 0; // 声道数

	/* aac编码器相关 */
	faacEncHandle           pFaacEncHandle = NULL;
	faacEncConfigurationPtr pFaacEncConf   = NULL;

	/* 编码相关参数 */
	unsigned long u64PcmInSampleCnt = 0; // 打开编码器时传出的参数,编码传入的PCM采样数(不是字节)
	unsigned long u64AacOutMaxBytes = 0; // 打开编码器时传出的参数,编码传出最大字节数
	unsigned char *pu8PcmInBuf   = NULL; // 读取pcm并传递进去编码的缓存指针,后面根据编码器传出参数malloc分配
	unsigned char *pu8AacEncBuf  = NULL; // 编码得到的aac缓存,后面根据编码器传出参数malloc分配


	/* 判断输入参数 */
	if(argc == 1)
	{
		print_usage(argv[0]);
		return -1;
	}	

	/* 解析命令行参数 */
	char option = 0;
	int option_index = 0;
	char *short_options = "hi:r:b:c:o:";
	struct option long_options[] =
	{
		{"help",          no_argument,       NULL, 'h'},
		{"input_pcmfile", required_argument, NULL, 'i'},
		{"sample_rate",   required_argument, NULL, 'r'},
		{"sample_bits",   required_argument, NULL, 'b'},
		{"channels",      required_argument, NULL, 'c'},
		{"output_aacfile",required_argument, NULL, 'o'},
		{NULL,            0,                 NULL,  0 },
	};
	while((option = getopt_long_only(argc, argv, short_options, long_options, &option_index)) != -1)
	{
		switch(option)
		{
			case 'h':
				print_usage(argv[0]);
				return 0;
			case 'i':
				strncpy(pcmFileName, optarg, 128);
				break;
			case 'r':
				u64PcmSampleRate = atoi(optarg);
				break;
			case 'c':
				u32PcmChannels = atoi(optarg);
				break;
			case 'b':
				u32PcmSampleBits = atoi(optarg);
				break;
			case 'o':
				strncpy(aacFileName, optarg, 128);
				break;
			defalut:
				printf("Unknown argument!\n");
				break;
		}
 	}
	printf("\n**************************************\n"
		   "input: \n"
		   "\t file name: %s\n"
		   "\t sample rate: %lu Hz\n"
		   "\t sample bits: %d bits\n"
		   "\t channels: %d\n"
		   "\t bits per second: %lu bps\n"
		   "output: \n"
		   "\t file name: %s\n"
		   "**************************************\n\n",
		   pcmFileName, u64PcmSampleRate, u32PcmSampleBits, u32PcmChannels,
		   u64PcmSampleRate*u32PcmSampleBits*u32PcmChannels, aacFileName);

	/* 先打开输入/输出文件 */
	fpPcm = fopen(pcmFileName, "rb");
	if(fpPcm == NULL)
	{
		char errMsg[128] = {0};
		snprintf(errMsg, 128, "open file(%s) error", pcmFileName);
		perror(errMsg);
		return -1;
	}
	fpAac = fopen(aacFileName, "wb");
	if(fpAac == NULL)
	{
		char errMsg[128] = {0};
		snprintf(errMsg, 128, "open file(%s) error", aacFileName);
		perror(errMsg);
		return -1;
	}

	/* AAC编码 1/6:传递参数进去打开编码器 */
	pFaacEncHandle = faacEncOpen(u64PcmSampleRate, u32PcmChannels, &u64PcmInSampleCnt, &u64AacOutMaxBytes);
	if(pFaacEncHandle == NULL)
	{
		printf("faacEncOpen(...) error!\n");
		goto error_exit;
	}

	/* 根据上面打开编码器传出的参数分配对应大小的缓存 */
	pu8PcmInBuf = (unsigned char*)malloc(u64PcmSampleRate*u32PcmSampleBits/8);
	pu8AacEncBuf = (unsigned char*)malloc(u64AacOutMaxBytes);

	/* AAC编码 2/6:设置编码器配置 */
	// 设置编码器配置 a/c: 先获取当前配置
	pFaacEncConf = faacEncGetCurrentConfiguration(pFaacEncHandle);
	// 设置编码器配置 b/c: 填充配置
	/*
		PCM Sample Input Format
		0	FAAC_INPUT_NULL			invalid, signifies a misconfigured config
		1	FAAC_INPUT_16BIT		native endian 16bit
		2	FAAC_INPUT_24BIT		native endian 24bit in 24 bits		(not implemented)
		3	FAAC_INPUT_32BIT		native endian 24bit in 32 bits		(DEFAULT)
		4	FAAC_INPUT_FLOAT		32bit floating point
    */
	pFaacEncConf->inputFormat = FAAC_INPUT_16BIT;
#if 0
	/* 下面参数不用设置,保存默认即可 */
	pFaacEncConf->aacObjectType = LOW; 	// MAIN:1  LOW:2  SSR:3  LTP:4
	pFaacEncConf->mpegVersion = MPEG4; 	// MPEG2:0  MPEG4:1
	pFaacEncConf->useTns = 1; 			/* Use Temporal Noise Shaping */
	pFaacEncConf->shortctl = 0; 		// SHORTCTL_NORMAL:0  SHORTCTL_NOSHORT:1  SHORTCTL_NOLONG:2
	pFaacEncConf->allowMidside = 1; 	/* Allow mid/side coding */
	pFaacEncConf->quantqual = 0; 		/* Quantizer quality */
	pFaacEncConf->outputFormat = 1; 	// 0:Raw  1:ADTS
	pFaacEncConf->bandWidth = 32000;//0 /* AAC file frequency bandwidth */
	pFaacEncConf->bitRate = 48000;//0 	/* bitrate / channel of AAC file */
#endif
	// 设置编码器配置 c/c: 重新设置编码器的配置信息
	faacEncSetConfiguration(pFaacEncHandle, pFaacEncConf);

	/* 循环操作 */
	while(1)
	{
		unsigned int u32PcmInSampleCnt = 0;
		int s32ReadPcmBytes = 0;
		int s32EncAacBytes = 0;

		/* AAC编码 3/6:从文件里读出指定大小(大小由编码器传出参数决定)PCM数据 */
		s32ReadPcmBytes = fread(pu8PcmInBuf, 1, u64PcmInSampleCnt*u32PcmSampleBits/8, fpPcm);
		if(s32ReadPcmBytes <= 0)
		{
			break;
		}
		DEBUG("Read PCM bytes: %d\n", s32ReadPcmBytes);

		// 编码传递进去的是采样数,不是字节数
		u32PcmInSampleCnt = s32ReadPcmBytes/(u32PcmSampleBits/8);
		DEBUG("Encode PCM sample count: %d\n", u32PcmInSampleCnt);

		/* AAC编码 4/6:将PCM数据(pucPcmInBuf)传进去编码得到aac数据(pucAacEncBuf)传出 */
		s32EncAacBytes = faacEncEncode(pFaacEncHandle, (int32_t*)pu8PcmInBuf, u32PcmInSampleCnt, 
														pu8AacEncBuf, (unsigned int)u64AacOutMaxBytes);
		DEBUG("Encode return aac bytes: %d\n", s32EncAacBytes);
		
		/* AAC编码 5/6:将解码得到的数据写入到AAC文件中 */
		fwrite(pu8AacEncBuf, 1, s32EncAacBytes, fpAac);
	}


	/* 记得释放内存 */
	free(pu8PcmInBuf);
	free(pu8AacEncBuf);

	/* AAC编码 6/6:用完了就关闭编码器 */
	faacEncClose(pFaacEncHandle);

error_exit:

	fclose(fpPcm);
	fclose(fpAac);

	printf("\n\033[32m%s ==> %s Success!\033[0m\n", pcmFileName, aacFileName);

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值