您的位置:首页 > 运维架构 > Linux

嵌入式linux:音频编解码PCM转至AAC

2016-03-10 17:04 597 查看
作者:柳大·Poechant

博客:blog.csdn.net/poechant

邮箱:zhongchao.ustc@gmail.com

日期:April 7th, 2012

这里利用FAAC来实现AAC编码。


1 下载安装 FAAC

这里的安装过程是在 Mac 和 Linux 上实现的,Windows可以类似参考。

FAAC开源工程源码下载链接:FAAC源码下载

得到FAAC工程源码后首先执行 configure获得Makefile,并指定目标平台和交叉工具链

./configure--target=arm-linux--host=arm-none-linux-gnueabi

编译:

make

安装:

make install

wget http://downloads.sourceforge.net/faac/faac-1.28.tar.gz tar zxvf faac-1.28.tar.gz
cd faac-1.28
./configure
make
sudo make install


如果才用默认的 configure 中的 prefix path,那么安装后的 lib 和 .h 文件分别在
/usr/local/lib
/usr/local/include
,后面编译的时候会用到。

如果编译过程中发现错误:
mpeg4ip.h:126: error: new declaration ‘char* strcasestr(const char*, const char*)’


解决方法:

从123行开始修改此文件mpeg4ip.h,到129行结束。 修改前:
#ifdef __cplusplus
extern "C" {
#endif
char *strcasestr(const char *haystack, const char *needle);
#ifdef __cplusplus
}
#endif


修改后:
#ifdef __cplusplus
extern "C++" {
#endif
const char *strcasestr(const char *haystack, const char *needle);
#ifdef __cplusplus
}
#endif


2 FAAC API


2.1 Open FAAC engine


Prototype:

faacEncHandle faacEncOpen               // 返回一个FAAC的handle
(
unsigned long   nSampleRate,        // 采样率,单位是bps
unsigned long   nChannels,          // 声道,1为单声道,2为双声道
unsigned long   &nInputSamples,     // 传引用,得到每次调用编码时所应接收的原始数据长度
unsigned long   &nMaxOutputBytes    // 传引用,得到每次调用编码时生成的AAC数据的最大长度
);


2.2 Get/Set encoding configuration


Prototype:

获取编码器的配置:
faacEncConfigurationPtr faacEncGetCurrentConfiguration // 得到指向当前编码器配置的指针
(
faacEncHandle hEncoder  // FAAC的handle
);


设定编码器的配置:
int FAACAPI faacEncSetConfiguration
(
faacDecHandle hDecoder,         // 此前得到的FAAC的handle
faacEncConfigurationPtr config  // FAAC编码器的配置
);


2.3 Encode


Prototype:

int faacEncEncode
(
faacEncHandle hEncoder,     // FAAC的handle
short *inputBuffer,         // PCM原始数据
unsigned int samplesInput,  // 调用faacEncOpen时得到的nInputSamples值
unsigned char *outputBuffer,// 至少具有调用faacEncOpen时得到的nMaxOutputBytes字节长度的缓冲区
unsigned int bufferSize     // outputBuffer缓冲区的实际大小
);


2.4 Close FAAC engine


Prototype

void faacEncClose
(
faacEncHandle hEncoder  // 此前得到的FAAC handle
);


3 流程


3.1 做什么准备?

采样率,声道数(双声道还是单声道?),还有你的PCM的单个样本是8位的还是16位的?


3.2 开启FAAC编码器,做编码前的准备

调用
faacEncOpen
开启FAAC编码器后,得到了单次输入样本数
nInputSamples
和输出数据最大字节数
nMaxOutputBytes


根据
nInputSamples
nMaxOutputBytes
,分别为PCM数据和将要得到的AAC数据创建缓冲区;

调用
faacEncGetCurrentConfiguration
获取当前配置,修改完配置后,调用
faacEncSetConfiguration
设置新配置。


3.3 开始编码

调用
faacEncEncode
,该准备的刚才都准备好了,很简单。


3.4 善后

关闭编码器,另外别忘了释放缓冲区,如果使用了文件流,也别忘记了关闭。

我的程序是根据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);就行了


4 测试程序


4.1 完整代码

PCM
格式音频文件
/home/michael/Development/testspace/in.pcm
转至
AAC
格式文件
/home/michael/Development/testspace/out.aac

#include <faac.h>
#include <stdio.h>

typedef unsigned long   ULONG;
typedef unsigned int    UINT;
typedef unsigned char   BYTE;
typedef char            _TCHAR;

int main(int argc, _TCHAR* argv[])
{
ULONG nSampleRate = 11025;  // 采样率
UINT nChannels = 1;         // 声道数
UINT nPCMBitSize = 16;      // 单样本位数
ULONG nInputSamples = 0;
ULONG nMaxOutputBytes = 0;

int nRet;
faacEncHandle hEncoder;
faacEncConfigurationPtr pConfiguration;

int nBytesRead;
int nPCMBufferSize;
BYTE* pbPCMBuffer;
BYTE* pbAACBuffer;

FILE* fpIn; // PCM file for input
FILE* fpOut; // AAC file for output

fpIn = fopen("/home/michael/Development/testspace/in.pcm", "rb");
fpOut = fopen("/home/michael/Development/testspace/out.aac", "wb");

// (1) Open FAAC engine
hEncoder = faacEncOpen(nSampleRate, nChannels, &nInputSamples, &nMaxOutputBytes);
if(hEncoder == NULL)
{
printf("[ERROR] Failed to call faacEncOpen()\n");
return -1;
}

nPCMBufferSize = nInputSamples * nPCMBitSize / 8;
pbPCMBuffer = new BYTE [nPCMBufferSize];
pbAACBuffer = new BYTE [nMaxOutputBytes];

// (2.1) Get current encoding configuration
pConfiguration = faacEncGetCurrentConfiguration(hEncoder);
pConfiguration->inputFormat = FAAC_INPUT_16BIT;

// (2.2) Set encoding configuration
nRet = faacEncSetConfiguration(hEncoder, pConfiguration);

for(int i = 0; 1; i++)
{
// 读入的实际字节数,最大不会超过nPCMBufferSize,一般只有读到文件尾时才不是这个值
nBytesRead = fread(pbPCMBuffer, 1, nPCMBufferSize, fpIn);

// 输入样本数,用实际读入字节数计算,一般只有读到文件尾时才不是nPCMBufferSize/(nPCMBitSize/8);
nInputSamples = nBytesRead / (nPCMBitSize / 8);

// (3) Encode
nRet = faacEncEncode(
hEncoder, (int*) pbPCMBuffer, nInputSamples, pbAACBuffer, nMaxOutputBytes);

fwrite(pbAACBuffer, 1, nRet, fpOut);

printf("%d: faacEncEncode returns %d\n", i, nRet);

if(nBytesRead <= 0)
{
break;
}
}

/*
while(1)
{
// (3) Flushing
nRet = faacEncEncode(
hEncoder, (int*) pbPCMBuffer, 0, pbAACBuffer, nMaxOutputBytes);

if(nRet <= 0)
{
break;
}
}
*/

// (4) Close FAAC engine
nRet = faacEncClose(hEncoder);

delete[] pbPCMBuffer;
delete[] pbAACBuffer;
fclose(fpIn);
fclose(fpOut);

//getchar();

return 0;
}


4.2 编译运行

将上述代码保存为“pcm2aac.cpp”文件,然后编译:
g++ pcm2aac.cpp -o pcm2aac -L/usr/local/lib -lfaac -I/usr/local/include


运行:
./pcm2aac


然后就生成了
out.aac
文件了,听听看吧!~


5 Reference

AudioCoding.com - FAAC

Dogfoot – 재밌는 개발

-

转载请注明来自柳大的CSDN博客:blog.csdn.net/poechant
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: