Android使用FAAC进行AAC编码详解必看以及注意事项
2016-07-21 11:38
701 查看
使用FAAC转换PCM为AAC
2、每次给到Faac进行编码的数据 必须根据通道数、分辨率等算出来,可不是想给多少就给多少?同样上层的APP产生的数据也应该是这个值、Android中测试若不是规定值编解码有问题。
把FAAC的代码抠出来,直接加到我们的工程中去,或者抠出来编译一个静态库,简单暴力有效,需要的代码是libfaac和include两个目录下的所有文件(不包括子目录文件)。 目录文件列表如下所示:
打开输入输出文件
使用faacEncOpen打开编码器引擎
使用faacEncGetCurrentConfiguratio获取编码器配置
配置编码器参数
使用faacEncSetConfiguration设置编码器配置
读取一桢输入数据
使用faacEncEncode编码帧数据
写入编码数据到输出文件
使用faacEncClose关闭编码引擎
编码器的参数设置,可以找正确的解码例子照例修改或者问专业人士吧,如果出错了的话。
打开FAAC编码器
m_faacHandle = faacEncOpen(isamplerate, ichannels, &m_uSampleInput, &m_uOutputBytes);
if( 0 == m_faacHandle )
return false ;
faacEncConfigurationPtr faacCfg;
faacCfg = faacEncGetCurrentConfiguration(m_faacHandle);
if (faacCfg->version != FAAC_CFG_VERSION){
return false ;
}
//* 设置配置参数
faacCfg->aacObjectType = LOW; //LC编码
faacCfg->mpegVersion = MPEG4; //
faacCfg->useTns = 1 ; //时域噪音控制,大概就是消爆音
faacCfg->allowMidside = 0 ; //
faacCfg->bitRate = m_nBitRate/m_uChannelNums;
faacCfg->bandWidth = 0 ; //频宽
faacCfg->outputFormat = isADTS; //输出是否包含ADTS头
faacCfg->inputFormat = FAAC_INPUT_16BIT;
//faacCfg->shortctl = 0 ;
faacCfg->quantqual = 50 ;
//* 获取解码信息.
//unsigned char* ucBuffer = NULL;
//unsigned long ulDecoderSpecificInfoSize;
//faacEncGetDecoderSpecificInfo(m_faacHandle, &ucBuffer, &ulDecoderSpecificInfoSize);
if (!faacEncSetConfiguration(m_faacHandle, faacCfg)){
return false ;
}
m_uSampleInput这个参数要注意,需要在编码时使用。是faac所使用的音频样片数量
随后就可以解码了
int iBytesWritten = faacEncEncode(m_faacHandle, (int32_t*)input, m_uSampleInput , output, outlen );
判断下iBytesWritten初始编码的几帧数据会返回0,0是数据被缓冲,并不是错误。
解码相对编码更简单。
但是遇到个问题,就是编码单声道的数据,解码会返回双声道,这对打开播放设备播放时造成了一定的
困扰。因为前期是将音频编码参数优先发送出来,接收端收到参数后会打开播放设备,现在得在数据解码后再打开
播放设备。
我的程序是根据faac 1.28 库中的frontend目录下的faac的例子改的。
下面是程序的运行流程:
首先调用faacEncHandle hEncoder=faacEncOpen(samplerate,channels,& samplesInput,
&maxBytesOutput);
1.打开aac编码引擎,创建aac编码句柄。
参数 samplerate 为要编码的音频pcm流的采样率,channels为要编码的音频pcm流的的频道数(原有的例子程序是从wav文件中读出这些信息),sampleInput在编码时要用到,意思是每次要编码的采样数,参数maxBytesOutput为编码时输出地最大字节数。
2.然后在设置一些编码参数,如
int version=MPEG4; //设置版本,录制MP4文件时要用MPEG4
int objecttype=LOW; //编码类型
int midside=1; //M/S编码
int usetns=DEFAULT_TNS; //瞬时噪声定形(temporal noise shaping,TNS)滤波器
int shortctl=SHORTCTL_NORMAL;
int inputformat=FAAC_INPUT_16BIT; //输入数据类型
int outputformat=RAW_STREAM; //录制MP4文件时,要用raw流。检验编码是否正确时可设
//置为adts传输流,把aac 流写入.aac文件中,如编码正确
//用千千静听就可以播放。
其他的参数可根据例子程序设置。
设置完参数后就调用faacEncSetConfiguration(hEncoder, aacFormat)设置编码参数。
3.如编码完的aac流要写入MP4文件时,要调用
faacEncGetDecoderSpecificInfo(hEncoder,&(ASC), &(ASCLength));//得到解码信息
//(mpeg4ip mp4 录制使用)
此函数支持MPEG4版本,得到的ASC 和ACSLength 数据在录制MP4(mpegip库)文件时用。
4.然后就是编码了,每次从实时的pcm音频队列中读出samplesInput* channels*(量化位数/8),
字节数的pcm数据。然后再把得到pcm流转变一下存储位数,我是转化为16位的了,这部分
可以根据例子程序写一个函数,这是我写的一个,
size_t read_int16(AACInfo *sndf, int16_t *outbuf, size_t num, unsigned char *inputbuf)
{
size_t i=0,j=0;
unsigned char bufi[8];
while(i<num)
{
memcpy(bufi,inputbuf+j,sndf->samplebytes);
j+=sndf->samplebytes;
int16_t s=((int16_t*)bufi)[0];
outbuf[i]=s;
i++;
}
return i;
}
也可以写一个read_float32(AACInfo *sndf, float *outbuf, size_t num ,unsigned char *inputbuf),
和size_t read_int24(AACInfo *sndf, int32_t *outbuf, size_t num, unsigned char *inputbuf)。
处理完数据转换后就调用
bytesWritten = faacEncEncode(hEncoder,
(int *)pcmbuf,
samplesInput,
outbuff,
maxbytesoutput);
进行编码,pcmbuf为转换后的pcm流数据,samplesInput为调用faacEncOpen时得到的输入采样数,outbuff为编码后的数据buff,maxbytesoutput为调用faacEncOpen时得到的最大输出字节数。然后每次从outbuff中得到编码后的aac数据流,放到数据队列就行了,如果还要录制MP4文件,在编码完samplesInput(一帧)个采样数时,打上时间戳(mpegip库用于音视频同步)后再放到输出队列中。如果想测试看编码的aac流是否正确,设置输出格式为ADTS_STREAM,把aac数据写入到.aac文件中,看能否用千千静听播放。
5.释放资源,调用faacEncClose(hEncoder);就行了
注意事项、1 、输出格式中 0 = Raw ; 1= ADTS ,用户应根据自己需求来确定参数
2、每次给到Faac进行编码的数据 必须根据通道数、分辨率等算出来,可不是想给多少就给多少?同样上层的APP产生的数据也应该是这个值、Android中测试若不是规定值编解码有问题。关于FAAC
FAAC是一个MPEG-4和MPEG-2的AAC编码器,其特性是:可移植性好,快速,支持LC/Main/LTP,通过Dream支持DRM,代码小相对于FFMPEG的AAC转码,FAAC实在是微乎其微,而且可以直接把代码加到工程里面编译,也可使用静态库,而没有巨大的动态库的烦恼。下载安装
直接按照官方文档所示,编译静态库以供我们程序使用。(我没这么做,个中曲折错误不细数)把FAAC的代码抠出来,直接加到我们的工程中去,或者抠出来编译一个静态库,简单暴力有效,需要的代码是libfaac和include两个目录下的所有文件(不包括子目录文件)。 目录文件列表如下所示:
aacquant.c aacquant.h backpred.c backpred.h bitstream.c bitstream.h channels.c channels.h coder.h faac.h faaccfg.h fft.c fft.h filtbank.c filtbank.h frame.c frame.h huffman.c huffman.h hufftab.h ltp.c ltp.h midside.c midside.h psych.h psychkni.c tns.c tns.h util.c util.h version.h强烈推荐使用第二种方法
主要的函数介绍
faacEncHandle FAACAPI faacEncOpen(unsigned long sampleRate, unsigned int numChannels, unsigned long *inputSamples, unsigned long *maxOutputBytes); // 描述 : 打开并初始化编码器 // sampleRate : 编码输入信息的采样率 // numChannels : 编码输入信息的通道数量,1-单声道 2-立体声 // inputSamples : 编码后的数据长度 // maxOutputBytes : 编码后的信息最大长度
int FAACAPI faacEncClose(faacEncHandle hEncoder); // 描述:关闭编码器 // hEncoder : faacEncOpen返回的编码器句柄
faacEncConfigurationPtr FAACAPI faacEncGetCurrentConfiguration(faacEncHandle hEncoder); // 描述 :获取当前编码器的配置信息 // hEncoder : faacEncOpen返回的编码器句柄
int FAACAPI faacEncSetConfiguration(faacEncHandle hEncoder, faacEncConfigurationPtr config); // 描述 : 配置解码器的参数 // hEncoder : faacEncOpen返回的编码器句柄 // config : 编码器的配置信息
int FAACAPI faacEncEncode(faacEncHandle hEncoder, int32_t * inputBuffer, unsigned int samplesInput, unsigned char *outputBuffer, unsigned int bufferSize); // 描述 : 编码一桢信息 // hEncoder : faacEncOpen返回的编码器句柄 // inputBuffer : 输入信息缓冲区 // samplesInput : faacEncOpen编码后的数据长度,即缓冲区长度 // outputBuffer : 编码后输出信息缓冲区 // bufferSize : 输出信息长度
int FAACAPI faacEncGetVersion(char **faac_id_string, char **faac_copyright_string); // 描述 : 获取FAAC的版本信息,用以参考作用,非必须API // faac_id_string : faac的版本号 // faac_copyright_string : 版权信息
代码示例
代码的工作流程是:打开输入输出文件
使用faacEncOpen打开编码器引擎
使用faacEncGetCurrentConfiguratio获取编码器配置
配置编码器参数
使用faacEncSetConfiguration设置编码器配置
读取一桢输入数据
使用faacEncEncode编码帧数据
写入编码数据到输出文件
使用faacEncClose关闭编码引擎
// // faac example code // PCM to ACC // // Created by arbboter on 15/1/26. // Copyright (c) 2015年 arbboter. All rights reserved. // #include "faac.h" #include <stdio.h> int main() { // 定义别名 typedef unsigned char BYTE; unsigned long nSampleRate = 44100; unsigned int nChannels = 2; unsigned int nPCMBitSize = 16; unsigned long nInputSamples = 0; unsigned long nMaxOutputBytes = 0; faacEncHandle hEncoder = {0}; // 设置输入输出文件 FILE* fpIn = fopen("Beyond.pcm", "rb"); FILE* fpOut = fopen("Beyond.aac", "wb"); if(fpIn==NULL || fpOut==NULL) { printf("打开文件失败!\n"); return -1; } // 打开faac编码器引擎 hEncoder = faacEncOpen(nSampleRate, nChannels, &nInputSamples, &nMaxOutputBytes); if(hEncoder == NULL) { printf("打开faac编码器引擎失败!\n"); return -1; } // 分配内存信息 int nPCMBufferSize = nInputSamples*nPCMBitSize/8; BYTE* pbPCMBuffer = new BYTE[nPCMBufferSize]; BYTE* pbAACBuffer = new BYTE[nMaxOutputBytes]; // 获取当前编码器信息 faacEncConfigurationPtr pConfiguration = {0}; pConfiguration = faacEncGetCurrentConfiguration(hEncoder); // 设置编码配置信息 /* 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 */ pConfiguration->inputFormat = FAAC_INPUT_16BIT; // 0 = Raw; 1 = ADTS pConfiguration->outputFormat = 1; // AAC object types //#define MAIN 1 //#define LOW 2 //#define SSR 3 //#define LTP 4 pConfiguration->aacObjectType = LOW; pConfiguration->allowMidside = 0; pConfiguration->useLfe = 0; pConfiguration->bitRate = 48000; pConfiguration->bandWidth = 32000; // 其他的参数不知道怎么配置,毕竟对音频不熟 // 不过当前的设置可以实现转换,不过声音好像有一丢丢怪异 // 这一块的配置信息很重要,错了会导致转码失败,然后你以为代码其他地方错了 // 重置编码器的配置信息 faacEncSetConfiguration(hEncoder, pConfiguration); size_t nRet = 0; printf("数据转换中: "); int i = 0; while( (nRet = fread(pbPCMBuffer, 1, nPCMBufferSize, fpIn)) > 0) { printf("\b\b\b\b\b\b\b\b%-8d", ++i); nInputSamples = nRet / (nPCMBitSize/8); // 编码 nRet = faacEncEncode(hEncoder, (int*) pbPCMBuffer, nInputSamples, pbAACBuffer, nMaxOutputBytes); // 写入转码后的数据 fwrite(pbAACBuffer, 1, nRet, fpOut); } // 扫尾工作 faacEncClose(hEncoder); fclose(fpOut); fclose(fpIn); delete[] pbAACBuffer; delete[] pbPCMBuffer; return 0; }
编码器的参数设置,可以找正确的解码例子照例修改或者问专业人士吧,如果出错了的话。
打开FAAC编码器
m_faacHandle = faacEncOpen(isamplerate, ichannels, &m_uSampleInput, &m_uOutputBytes);
if( 0 == m_faacHandle )
return false ;
faacEncConfigurationPtr faacCfg;
faacCfg = faacEncGetCurrentConfiguration(m_faacHandle);
if (faacCfg->version != FAAC_CFG_VERSION){
return false ;
}
//* 设置配置参数
faacCfg->aacObjectType = LOW; //LC编码
faacCfg->mpegVersion = MPEG4; //
faacCfg->useTns = 1 ; //时域噪音控制,大概就是消爆音
faacCfg->allowMidside = 0 ; //
faacCfg->bitRate = m_nBitRate/m_uChannelNums;
faacCfg->bandWidth = 0 ; //频宽
faacCfg->outputFormat = isADTS; //输出是否包含ADTS头
faacCfg->inputFormat = FAAC_INPUT_16BIT;
//faacCfg->shortctl = 0 ;
faacCfg->quantqual = 50 ;
//* 获取解码信息.
//unsigned char* ucBuffer = NULL;
//unsigned long ulDecoderSpecificInfoSize;
//faacEncGetDecoderSpecificInfo(m_faacHandle, &ucBuffer, &ulDecoderSpecificInfoSize);
if (!faacEncSetConfiguration(m_faacHandle, faacCfg)){
return false ;
}
m_uSampleInput这个参数要注意,需要在编码时使用。是faac所使用的音频样片数量
随后就可以解码了
int iBytesWritten = faacEncEncode(m_faacHandle, (int32_t*)input, m_uSampleInput , output, outlen );
判断下iBytesWritten初始编码的几帧数据会返回0,0是数据被缓冲,并不是错误。
解码相对编码更简单。
但是遇到个问题,就是编码单声道的数据,解码会返回双声道,这对打开播放设备播放时造成了一定的
困扰。因为前期是将音频编码参数优先发送出来,接收端收到参数后会打开播放设备,现在得在数据解码后再打开
播放设备。
我的程序是根据faac 1.28 库中的frontend目录下的faac的例子改的。
下面是程序的运行流程:
首先调用faacEncHandle hEncoder=faacEncOpen(samplerate,channels,& samplesInput,
&maxBytesOutput);
1.打开aac编码引擎,创建aac编码句柄。
参数 samplerate 为要编码的音频pcm流的采样率,channels为要编码的音频pcm流的的频道数(原有的例子程序是从wav文件中读出这些信息),sampleInput在编码时要用到,意思是每次要编码的采样数,参数maxBytesOutput为编码时输出地最大字节数。
2.然后在设置一些编码参数,如
int version=MPEG4; //设置版本,录制MP4文件时要用MPEG4
int objecttype=LOW; //编码类型
int midside=1; //M/S编码
int usetns=DEFAULT_TNS; //瞬时噪声定形(temporal noise shaping,TNS)滤波器
int shortctl=SHORTCTL_NORMAL;
int inputformat=FAAC_INPUT_16BIT; //输入数据类型
int outputformat=RAW_STREAM; //录制MP4文件时,要用raw流。检验编码是否正确时可设
//置为adts传输流,把aac 流写入.aac文件中,如编码正确
//用千千静听就可以播放。
其他的参数可根据例子程序设置。
设置完参数后就调用faacEncSetConfiguration(hEncoder, aacFormat)设置编码参数。
3.如编码完的aac流要写入MP4文件时,要调用
faacEncGetDecoderSpecificInfo(hEncoder,&(ASC), &(ASCLength));//得到解码信息
//(mpeg4ip mp4 录制使用)
此函数支持MPEG4版本,得到的ASC 和ACSLength 数据在录制MP4(mpegip库)文件时用。
4.然后就是编码了,每次从实时的pcm音频队列中读出samplesInput* channels*(量化位数/8),
字节数的pcm数据。然后再把得到pcm流转变一下存储位数,我是转化为16位的了,这部分
可以根据例子程序写一个函数,这是我写的一个,
size_t read_int16(AACInfo *sndf, int16_t *outbuf, size_t num, unsigned char *inputbuf)
{
size_t i=0,j=0;
unsigned char bufi[8];
while(i<num)
{
memcpy(bufi,inputbuf+j,sndf->samplebytes);
j+=sndf->samplebytes;
int16_t s=((int16_t*)bufi)[0];
outbuf[i]=s;
i++;
}
return i;
}
也可以写一个read_float32(AACInfo *sndf, float *outbuf, size_t num ,unsigned char *inputbuf),
和size_t read_int24(AACInfo *sndf, int32_t *outbuf, size_t num, unsigned char *inputbuf)。
处理完数据转换后就调用
bytesWritten = faacEncEncode(hEncoder,
(int *)pcmbuf,
samplesInput,
outbuff,
maxbytesoutput);
进行编码,pcmbuf为转换后的pcm流数据,samplesInput为调用faacEncOpen时得到的输入采样数,outbuff为编码后的数据buff,maxbytesoutput为调用faacEncOpen时得到的最大输出字节数。然后每次从outbuff中得到编码后的aac数据流,放到数据队列就行了,如果还要录制MP4文件,在编码完samplesInput(一帧)个采样数时,打上时间戳(mpegip库用于音视频同步)后再放到输出队列中。如果想测试看编码的aac流是否正确,设置输出格式为ADTS_STREAM,把aac数据写入到.aac文件中,看能否用千千静听播放。
5.释放资源,调用faacEncClose(hEncoder);就行了
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories