2018-05-24 14:37:28 yinsui1839 阅读数 4423

下面将aac解码成pcm的代码,主要是参考某位博主的博客,原文地址忘记在哪了!

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define __STDC_CONSTANT_MACROS
extern "C" {
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavdevice/avdevice.h>
    #include<libswresample/swresample.h>
}

#define MAX_AUDIO_FRAME_SIZE  192000

#define SAMPLE_PRT(fmt...)   \
    do {\
        printf("[%s]-%d: ", __FUNCTION__, __LINE__);\
        printf(fmt);\
       }while(0)

const char *in_file = "./hefang.aac";
const char *out_file = "./hefang.pcm";
int main()
{
    //注册所有的工具
    av_register_all();

    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext  *cod_ctx = NULL;
    AVCodec         *cod   = NULL;

    //分配一个avformat
    fmt_ctx = avformat_alloc_context();
    if (fmt_ctx == NULL)
        printf("alloc fail");

    //打开文件,解封装
    if (avformat_open_input(&fmt_ctx, in_file, NULL, NULL) != 0)
        printf("open fail");

    //查找文件的相关流信息
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
        printf("find stream fail");

    //输出格式信息
    av_dump_format(fmt_ctx, 0, in_file, 0);

    //查找解码信息
    int stream_index = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++)
        if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            stream_index = i;
            break;
        }

    if (stream_index == -1)
        printf("find stream fail");

    //保存解码器
    cod_ctx = fmt_ctx->streams[stream_index]->codec;
    cod = avcodec_find_decoder(cod_ctx->codec_id);

    if (cod == NULL)
        printf("find codec fail");

    if (avcodec_open2(cod_ctx, cod, NULL) < 0)
        printf("can't open codec");

    FILE *out_fb = NULL;
    out_fb = fopen(out_file, "wb");

    //创建packet,用于存储解码前的数据
    AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
    av_init_packet(packet);

    //设置转码后输出相关参数
    //采样的布局方式
    uint64_t out_channel_layout = AV_CH_LAYOUT_MONO;
    //采样个数
    int out_nb_samples = 1024;
    //采样格式
    enum AVSampleFormat  sample_fmt = AV_SAMPLE_FMT_S16;
    //采样率
    int out_sample_rate = 44100;
    //通道数
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
    printf("%d\n",out_channels);
    //创建buffer
    int buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, sample_fmt, 1);


    //注意要用av_malloc
    uint8_t *buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);


    //创建Frame,用于存储解码后的数据
    AVFrame *frame = av_frame_alloc();

    int got_picture;

    int64_t in_channel_layout = av_get_default_channel_layout(cod_ctx->channels);
    //打开转码器
    struct SwrContext *convert_ctx = swr_alloc();
    //设置转码参数
    convert_ctx = swr_alloc_set_opts(convert_ctx, out_channel_layout, sample_fmt, out_sample_rate, \
            in_channel_layout, cod_ctx->sample_fmt, cod_ctx->sample_rate, 0, NULL);
    //初始化转码器
    swr_init(convert_ctx);

    //while循环,每次读取一帧,并转码

    while (av_read_frame(fmt_ctx, packet) >= 0) {

        if (packet->stream_index == stream_index) {

            //解码声音
            if (avcodec_decode_audio4(cod_ctx, frame, &got_picture, packet) < 0) {
                printf("decode error");
                return -1;
            }

            if (got_picture > 0) {
                //转码
                swr_convert(convert_ctx, &buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)frame->data, frame->nb_samples);

                printf("pts:%10lld\t packet size:%d\n", packet->pts, packet->size);

                fwrite(buffer, 1, buffer_size, out_fb);
            }
            got_picture=0;
        }

        av_free_packet(packet);
    }

    swr_free(&convert_ctx);

    fclose(out_fb);

    return 0;
}

下面是linux下的编译:g++ main.cpp  -lavformat -lavcodec -lavdevice -lavutil -lz -lm -lswresample

windows:不懂

音频文件可以从我这里下载 https://download.csdn.net/download/yinsui1839/10434869 


如果没有积分可以在评论区留下邮箱


2019-08-14 18:57:36 aa804738534 阅读数 145

 g++ pcm2aacts2mp4.cpp -o pcm2aacaaaaaa -L/usr/local/lib -lfaac -I/usr/local/include -I/monchickey/ffmpeg/include -L/monchickey/ffmpeg/lib -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil
 

2018-10-19 19:57:10 u013484772 阅读数 2051

最近做语音转写。

一般语音转写都用pcm没有压缩的格式。而AAC是android常用的录音保存编码格式(比较小方便储存和传输)。

下面介绍下pcm,aac互转的方法。直接上代码吧。

 

android版的AAC和PCM互转
PCM->AAC, new PCMToAAC(aacPath,pcmPath).Encode();
AAC->PCM, new AACToPCM(aacPath,pcmPath).decode();

AAC->PCM解码

public void decode() {
    boolean inputSawEos = false;
    boolean outputSawEos = false;
    long kTimes = 5000;//循环时间
    while (!outputSawEos) {
        if (!inputSawEos) {
            //每5000毫秒查询一次
            int inputBufferIndex = mMediaDecode.dequeueInputBuffer(kTimes);
            //输入缓存index可用
            if (inputBufferIndex >= 0) {
                //获取可用的输入缓存
                ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
                //从MediaExtractor读取数据到输入缓存中,返回读取长度
                int bufferSize = mMediaExtractor.readSampleData(inputBuffer, 0);
                if (bufferSize <= 0) {//已经读取完
                    //标志输入完毕
                    inputSawEos = true;
                    //做标识
                    mMediaDecode.queueInputBuffer(inputBufferIndex, 0, 0, kTimes, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                } else {
                    long time = mMediaExtractor.getSampleTime();
                    //将输入缓存放入MediaCodec中
                    mMediaDecode.queueInputBuffer(inputBufferIndex, 0, bufferSize, time, 0);
                    //指向下一帧
                    mMediaExtractor.advance();
                }
            }
        }
        //获取输出缓存,需要传入MediaCodec.BufferInfo 用于存储ByteBuffer信息
        int outputBufferIndex = mMediaDecode.dequeueOutputBuffer(bufferInfo, kTimes);
        if (outputBufferIndex >= 0) {
            int id = outputBufferIndex;
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                mMediaDecode.releaseOutputBuffer(id, false);
                continue;
            }
            //有输出数据
            if (bufferInfo.size > 0) {
                //获取输出缓存
                ByteBuffer outputBuffer = outputBuffers[id];
                //设置ByteBuffer的position位置
                outputBuffer.position(bufferInfo.offset);
                //设置ByteBuffer访问的结点
                outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                byte[] targetData = new byte[bufferInfo.size];
                //将数据填充到数组中
                outputBuffer.get(targetData);
                try {
                    fileOutputStream.write(targetData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //释放输出缓存
            mMediaDecode.releaseOutputBuffer(id, false);
            //判断缓存是否完结
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                outputSawEos = true;
            }
        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            outputBuffers = mMediaDecode.getOutputBuffers();

        }else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
            MediaFormat  mediaFormat = mMediaDecode.getOutputFormat();
        }
    }
    //释放资源
    try {
        fileOutputStream.flush();
        fileOutputStream.close();
        mMediaDecode.stop();
        mMediaDecode.release();
        mMediaExtractor.release();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

PCM->AAC 编码

/**
 * 编码PCM数据 得到AAC格式的音频文件
 */
private void dstAudioFormatFromPCM(byte[] pcmData) {

    int inputIndex;
    ByteBuffer inputBuffer;
    int outputIndex;
    ByteBuffer outputBuffer;

    int outBitSize;
    int outPacketSize;
    byte[] PCMAudio;
    PCMAudio = pcmData;

    encodeInputBuffers = mediaEncode.getInputBuffers();
    encodeOutputBuffers = mediaEncode.getOutputBuffers();
    encodeBufferInfo = new MediaCodec.BufferInfo();


    inputIndex = mediaEncode.dequeueInputBuffer(0);
    inputBuffer = encodeInputBuffers[inputIndex];
    inputBuffer.clear();
    inputBuffer.limit(PCMAudio.length);
    inputBuffer.put(PCMAudio);//PCM数据填充给inputBuffer
    mediaEncode.queueInputBuffer(inputIndex, 0, PCMAudio.length, 0, 0);//通知编码器 编码


    outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 0);
    while (outputIndex >0) {

        outBitSize = encodeBufferInfo.size;
        outPacketSize = outBitSize + 7;//7为ADT头部的大小
        outputBuffer = encodeOutputBuffers[outputIndex];//拿到输出Buffer
        outputBuffer.position(encodeBufferInfo.offset);
        outputBuffer.limit(encodeBufferInfo.offset + outBitSize);
        chunkAudio = new byte[outPacketSize];
        addADTStoPacket(chunkAudio, outPacketSize);//添加ADTS
        outputBuffer.get(chunkAudio, 7, outBitSize);//将编码得到的AAC数据 取出到byte[]中

        try {
            //录制aac音频文件,保存在手机内存中
            out.write(chunkAudio, 0, chunkAudio.length);
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        outputBuffer.position(encodeBufferInfo.offset);
        mediaEncode.releaseOutputBuffer(outputIndex, false);
        outputIndex = mediaEncode.dequeueOutputBuffer(encodeBufferInfo, 0);

    }

}


/**
 * 添加ADTS头
 *
 * @param packet
 * @param packetLen
 */
private void addADTStoPacket(byte[] packet, int packetLen) {
    int profile = 2; // AAC LC
    int freqIdx = 8; // 16KHz
    int chanCfg = 1; // CPE

    // fill in ADTS data
    packet[0] = (byte) 0xFF;
    packet[1] = (byte) 0xF1;
    packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
    packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
    packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
    packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
    packet[6] = (byte) 0xFC;

}

 

 

完整的代码下载链接https://download.csdn.net/download/u013484772/10732833

 

 

2016-03-10 17:04:29 zydlyq 阅读数 1788
  • 作者:柳大·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编码器,做编码前的准备

  1. 调用faacEncOpen开启FAAC编码器后,得到了单次输入样本数nInputSamples和输出数据最大字节数nMaxOutputBytes
  2. 根据nInputSamplesnMaxOutputBytes,分别为PCM数据和将要得到的AAC数据创建缓冲区;
  3. 调用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

  1. AudioCoding.com - FAAC
  2. Dogfoot – 재밌는 개발

-

转载请注明来自柳大的CSDN博客:blog.csdn.net/poechant

2013-02-16 14:25:15 gyley2 阅读数 1421

转载原文地址:http://blog.csdn.net/poechant/article/details/7435054

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

1 下载安装 FAAC

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

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编码器,做编码前的准备

  1. 调用faacEncOpen开启FAAC编码器后,得到了单次输入样本数nInputSamples和输出数据最大字节数nMaxOutputBytes
  2. 根据nInputSamplesnMaxOutputBytes,分别为PCM数据和将要得到的AAC数据创建缓冲区;
  3. 调用faacEncGetCurrentConfiguration获取当前配置,修改完配置后,调用faacEncSetConfiguration设置新配置。

3.3 开始编码

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

3.4 善后

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

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

  1. AudioCoding.com - FAAC
  2. Dogfoot – 재밌는 개발