使用faac库编码无损音频pcm为aac时,主要使用以下几个函数:
faacEncHandle faacEncOpen(unsigned long sampleRate,
unsigned int numChannels,
unsigned long *inputSamples,
unsigned long *maxOutputBytes);
faacEncConfigurationPtr faacEncGetCurrentConfiguration(faacEncHandle hEncoder);
int faacEncSetConfiguration(faacEncHandle hEncoder,
faacEncConfigurationPtr config);
int faacEncEncode(faacEncHandle hEncoder,
int32_t * inputBuffer,
unsigned int samplesInput,
unsigned char *outputBuffer,
unsigned int bufferSize);
int faacEncClose(faacEncHandle hEncoder);
我们从上往下进行介绍:
typedef void *faacEncHandle;
/*
* @brief 打开一个用于aac编码的handle
* @param sampleRate[in] 音频采样率
* numChannels[in] 音频通道数量(一般是1或2,表示单通道和立体声)
* inputSamples[out] aac编码器一次需要输入的音频采样
* maxOutputBytes[out] 编码后aac数据的最大长度
* @return 失败返回NULL
*/
faacEncHandle faacEncOpen(unsigned long sampleRate,
unsigned int numChannels,
unsigned long *inputSamples,
unsigned long *maxOutputBytes);
//关闭一个用于aac编码的handle
int faacEncClose(faacEncHandle hEncoder);
sampleRate、numChannels是输入参数,需要在打开handle时告诉编码器,等会儿需要编码的pcm音频的采样率和声道(音频通道)是多少。
inputSamples、maxOutputBytes是输出参数,需要传int类型的地址(不能为nullptr),当调用faacEncOpen成功后,返回用于编码的句柄,并且填充inputSamples、maxOutputBytes两个参数,告诉用户等会儿编码时,每次需要输入的音频采样数量,以及编码成aac数据的最大数据长度。用户需要提前分配好至少maxOutputBytes大小的空间用于存储编码后aac音频数据。
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
/* MPEG ID's */
#define MPEG2 1
#define MPEG4 0
/* AAC object types */
#define MAIN 1
#define LOW 2
#define SSR 3
#define LTP 4
/* Input Formats */
#define FAAC_INPUT_NULL 0
#define FAAC_INPUT_16BIT 1
#define FAAC_INPUT_24BIT 2
#define FAAC_INPUT_32BIT 3
#define FAAC_INPUT_FLOAT 4
#pragma pack(push, 1)
typedef struct faacEncConfiguration
{
/* config version */
int version;
/* library version */
char *name;
/* copyright string */
char *copyright;
/* MPEG version, 2 or 4 */
/* mpeg的版本号,MPEG2 or MPEG2 */
unsigned int mpegVersion;
/* AAC object type */
/* AAC规格 MAIN、LOW、SSR、LTP */
unsigned int aacObjectType;
union {
/* Joint coding mode */
unsigned int jointmode;
/* compatibility alias */
unsigned int allowMidside;
};
/* Use one of the channels as LFE channel */
/*
* 低频音效加强通道LFE (low frequency effects)
* 低频效果通道是一个有限带宽的音轨,用于再现3–120 Hz频率范围内深沉而强烈的低频声音。
* 此轨道通常会发送到超低音扬声器,即用于重现极低频率的扬声器。
*/
unsigned int useLfe;
/* Use Temporal Noise Shaping */
/*
* 瞬时噪声定形TNS
*
* TNS是用来消除由激发讯号所造成pre-echo现象的一个模组,
* 它可以控制量化误差并且将误差塑形在遮避能力较大的激发讯号里,进而改进音乐品质。
*
* TNS 是感官式音訊編碼(Perceptual Audio Coding) 中的一個新的觀念,
* 目的在於使單一區塊在經過時間 -頻率轉換、量化及編碼之後,
* 音訊在區塊內時間上的噪音遮蔽效果仍然能夠維持。
*/
unsigned int useTns;
/* bitrate / channel of AAC file */
/* 每个通道的码率(比特率) */
unsigned long bitRate;
/* AAC file frequency bandwidth */
unsigned int bandWidth;
/* Quantizer quality */
/* 默认100, 值越大音质越好 */
unsigned long quantqual;
/* Bitstream output format (0 = Raw; 1 = ADTS) */
/* 是纯AAC音频数据,还是带了ADTS头的AAC(一般使用ADTS)*/
unsigned int outputFormat;
/* psychoacoustic model list */
psymodellist_t *psymodellist;
/* selected index in psymodellist */
unsigned int psymodelidx;
/*
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
*/
/*
* 输入音频的位深度,也叫位宽,量化精度。
* 位数越多,表示得就越精细,声音质量自然就越好,当然,数据量也会成倍增大。
* 16bit被认为是专业音频领域里面最低的位深度标准。
*/
unsigned int inputFormat;
/* block type enforcing (SHORTCTL_NORMAL/SHORTCTL_NOSHORT/SHORTCTL_NOLONG) */
int shortctl;
/*
Channel Remapping
Default 0, 1, 2, 3 ... 63 (64 is MAX_CHANNELS in coder.h)
WAVE 4.0 2, 0, 1, 3
WAVE 5.0 2, 0, 1, 3, 4
WAVE 5.1 2, 0, 1, 4, 5, 3
AIFF 5.1 2, 0, 3, 1, 4, 5
*/
int channel_map[64];
int pnslevel;
} faacEncConfiguration, *faacEncConfigurationPtr;
/*
* 获取编码配置的结构体指针
* 在调用设置编码参数之前,需要先调用该函数获取结构体指针,再进行参数填充
*/
faacEncConfigurationPtr faacEncGetCurrentConfiguration(faacEncHandle hEncoder);
/*
* 设置编码参数
* 在调用编码函数之前,需要调用该函数设置编码参数
*/
int faacEncSetConfiguration(faacEncHandle hEncoder,
faacEncConfigurationPtr config);
其中faacEncConfigurationPtr 是编码参数的结构体指针。经常使用的参数已经在上面的代码中用中文进行了注释,inputFormat、outputFormat、aacObjectType、mpegVersion、(bitRate一般编码器会自动设置成64kpbs,即每个通道的码率为64kpbs)、(useTns、useLfe一般设置为0,即不使用)
/*
* @brief 编码pcm音频为aac音频
* @param hEncoder[in] 调用faacEncOpen函数成功后获取的句柄
* inputBuffer[in] pcm音频数据
* samplesInput[in] 通过faacEncOpen得到的“每次需要输入的音频采样数量”
* outputBuffer[out] 用于存储编码后数据空间的首地址(需要提前分配好空间)
* bufferSize[out] 编码后aac数据的最大长度
* @return 返回编码后aac实际数据大小
*/
int faacEncEncode(faacEncHandle hEncoder,
int32_t * inputBuffer,
unsigned int samplesInput,
unsigned char *outputBuffer,
unsigned int bufferSize);
samplesInput是指音频采样数量,而非pcm音频数据的大小!是通过调用faacEncOpen后得到的。输入的pcm音频数据的大小 = samplesInput * 音频位深 / 8;
outputBuffer是提前分配好的空间,存储编码后的aac数据,至少需要分配的大小 = 通过调用faacEncOpen后得到的maxOutputBytes。
1. 调用faacEncOpen打开一个用于编码pcm为aac的句柄,需要传入采样率、声道数。成功返回编码句柄,并且得到每次需要输入的采样数量inputSamples,和编码后aac数据的最大数据大小maxOutputBytes。
2. 通过得到的maxOutputBytes分配空间,用于之后存储aac数据。
3. 调用faacEncGetCurrentConfiguration获取编码参数的结构体指针,并填充编码参数,主要填充inputFormat、outputFormat、aacObjectType、mpegVersion等参数;useTns、useLfe通常不使用,设置为0; bitRate是每个声道的码率,而非总码率,一般会自动填充好,也可以手动赋值。
4. 调用faacEncSetConfiguration设置编码参数。
5. 调用faacEncEncode进行音频编码,并把编码后的数据保存下来。(正常情况下前面几帧会返回0,即没有aac数据,属于正常现象)。
6. 编码结束后,调用faacEncClose关闭句柄。
PcmToAac.cpp
#include <cstdio>
#include <string>
#include <cstring>
#include "PcmToAac.h"
PcmToAac::PcmToAac(uint32_t maxPcmSize, uint32_t maxAacQueueSize)
: hEncoder(nullptr)
, pConfiguration(nullptr)
, m_AacBuffer(nullptr)
, m_nInputSamples(0)
, m_nMaxOutputBytes(0)
, m_nMaxInputBytes(0)
, m_nPCMBitSize(0)
, m_PcmMaxSize(maxPcmSize)
, m_AacMaxQueueSize(maxAacQueueSize)
{
}
PcmToAac::~PcmToAac()
{
//关闭句柄
if (nullptr != hEncoder) {
faacEncClose(hEncoder);
}
//释放内存
delete []m_AacBuffer;
}
int PcmToAac::Init(uint8_t Channel, uint32_t SampleRate, uint32_t BitSize)
{
m_nPCMBitSize = BitSize;
//打开句柄
hEncoder = faacEncOpen(SampleRate, Channel, &m_nInputSamples, &m_nMaxOutputBytes);
if (hEncoder == nullptr) {
fprintf(stderr, "faac enc open failed\n");
return -1;
}
//计算每次输入pcm数据大小
m_nMaxInputBytes = m_nInputSamples * m_nPCMBitSize / 8;
//分配空间用于存储aac数据
m_AacBuffer = new unsigned char[m_nMaxOutputBytes];
//获取结构体指针用于填充编码参数
pConfiguration = faacEncGetCurrentConfiguration(hEncoder);
pConfiguration->inputFormat = FAAC_INPUT_16BIT; //位深16
pConfiguration->outputFormat = 1; //ADTS
pConfiguration->useTns = 0;
pConfiguration->useLfe = 0;
pConfiguration->aacObjectType = LOW; //AAC-LC
pConfiguration->mpegVersion = MPEG2;
//设置编码参数
faacEncSetConfiguration(hEncoder, pConfiguration);
return 0;
}
int PcmToAac::InputPcmData(const uint8_t* pcmData, uint32_t dataSize) {
if (dataSize + m_PcmStr.size() > m_PcmMaxSize) {
fprintf(stderr, "Input pcm data is too large\n");
return -1;
}
m_PcmStr.append((const char*)pcmData, dataSize);
if (m_AacQueue.size() > m_AacMaxQueueSize) {
fprintf(stderr, "Aac queue is full\n");
return -1; //aac队列已满
}
int ret = 0;
while(m_PcmStr.size() >= m_nMaxInputBytes) {
memset(m_AacBuffer, 0, m_nMaxOutputBytes);
ret = faacEncEncode(hEncoder, (int32_t*)m_PcmStr.data(), m_nInputSamples, m_AacBuffer, m_nMaxOutputBytes);
if (ret > 0) {
m_AacQueue.emplace((const char*)m_AacBuffer, ret);
}
m_PcmStr.erase(0, m_nMaxInputBytes);
}
return 0;
}
std::string PcmToAac::GetAacFrame() {
if (m_AacQueue.empty()) {
return "";
}
std::string aac = m_AacQueue.front();
m_AacQueue.pop();
return aac;
}
PcmToAac.h
#ifndef _PCM_TO_AAC_H
#define _PCM_TO_AAC_H
#include <queue>
extern "C"
{
#include <faac.h>
}
class PcmToAac
{
public:
explicit PcmToAac(uint32_t maxPcmSize = 1024 * 1024, uint32_t maxAacQueueSize = 1024);
~PcmToAac();
int InputPcmData(const uint8_t* pcmData, uint32_t dataSize);
std::string GetAacFrame();
int Init(uint8_t Channel, uint32_t SampleRate, uint32_t BitSize);
private:
faacEncHandle hEncoder;
faacEncConfigurationPtr pConfiguration;
uint32_t m_nPCMBitSize /*= 16*/;
unsigned long m_nInputSamples /*= 0*/;
unsigned long m_nMaxOutputBytes /*= 0*/;
unsigned long m_nMaxInputBytes /*= 0*/;
const uint32_t m_PcmMaxSize; //内存中能存储的最大pcm
const uint32_t m_AacMaxQueueSize; //aac队列最大数量
std::string m_PcmStr;
std::queue<std::string> m_AacQueue;
unsigned char* m_AacBuffer;
};
#endif
main.cpp
#include <memory>
#include <cstring>
#include "PcmToAac.h"
int main() {
//打开2个文件,我们目标是读取pcm文件并编码,最后保存为aac文件
FILE *fpIn = fopen("D:\\music.pcm", "rb");
if (!fpIn) {
return -1;
}
FILE *fpOut = fopen("D:\\music.aac", "wb");
if (!fpOut) {
return -1;
}
//构造类对象PcmToAac并初始化, 最大编码大小 和 缓存中的aac队列大小 可以自己定义
std::shared_ptr<PcmToAac> pcmToAacPtr = std::make_shared<PcmToAac>();
//输入pcm音频:采样率44100, 立体声, 位深16
if(pcmToAacPtr->Init(2, 44100, 16)) {
return -1;
}
char buf[1024] = {0};
for(; ;) {
memset(buf, 0, sizeof(buf));
int ret = fread(buf, 1, sizeof(buf), fpIn);
if (ret <= 0) {
break;
}
pcmToAacPtr->InputPcmData((const uint8_t *)buf, ret);
//循环从缓存中输出aac数据并保存为文件
std::string aacFrame;
for( ; ; ) {
aacFrame = pcmToAacPtr->GetAacFrame();
if (aacFrame.empty()) {
break;
}
fwrite(aacFrame.data(), 1, aacFrame.size(), fpOut);
}
}
return 0;
}
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓