AAC编码
本篇使用的FFMPEG需要按照WIN下编译FFMPEG
基本要求
fdk-aac对PCM文件有参数要求
采样格式
必须是16位整数的PCM
采样率
支持的采样率有(Hz):
- 8000、11025、12000、16000、22050、24000、32000
- 44100、48000、64000、88200、96000
AAC的规格
- MPEG-2 AAC LC:低复杂度规格(Low Complexity)
- MPEG-2 AAC Main:主规格
- MPEG-2 AAC SSR:可变采样率规格(Scaleable Sample Rate)
- MPEG-4 AAC LC:低复杂度规格(Low Complexity)
现在的手机比较常见的MP4文件中的音频部分使用了该规格 - MPEG-4 AAC Main:主规格
- MPEG-4 AAC SSR:可变采样率规格(Scaleable Sample Rate)
- MPEG-4 AAC LTP:长时期预测规格(Long Term Predicition)
- MPEG-4 AAC LD:低延迟规格(Low Delay)
- MPEG-4 AAC HE:高效率规格(High Efficiency)
查看FFMPEG集成的AAC编解码器
// 查看编解码器
ffmpeg -codecs | findstr aac
// 输出结果
DEAIL. aac AAC (Advanced Audio Coding) (decoders: aac aac_fixed libfdk_aac ) (encoders: aac libfdk_aac aac_mf )
D.AIL. aac_latm AAC LATM (Advanced Audio Coding LATM syntax)
命令行
基本使用
// pcm -> aac
ffmpeg -ar 44100 -ac 2 -f s16le -i 44100_2_s16le.pcm -c:a libfdk_aac out.aac
// pcm -> wav
ffmpeg -ar 44100 -ac 2 -f s16le -i 44100_2_s16le.pcm in.wav
// wav -> aac
ffmpeg -i in.wav -c:a libfdk_aac out.aac
// 其中 -c:a -> c表示codec(编解码器),a表示audio(音频)
常用参数
- 设置输出比特率
// 设置输出比特率
ffmpeg -i in.wav -c:a libfdk_aac -b:a 96k out.aac
// 参数如下:
Input #0, aac, from 'out.aac':
Duration: 00:00:30.96, bitrate: 100 kb/s
Stream #0:0: Audio: aac (LC), 44100 Hz, mono, fltp, 100 kb/s
- 设置输出规格
取值如下:
aac_low:Low Complexity AAC (LC),默认值
aac_he:High Efficiency AAC (HE-AAC)
aac_he_v2:High Efficiency AAC version 2 (HE-AACv2)
aac_ld:Low Delay AAC (LD)
aac_eld:Enhanced Low Delay AAC (ELD)
一旦设置了输出规格,会自动设置一个合适的输出比特率
ffmpeg -i in.wav -c:a libfdk_aac -profile:a aac_he -b:a 32k out.aac
// 参数如下:
Input #0, aac, from 'out.aac':
Duration: 00:00:34.66, bitrate: 30 kb/s
Stream #0:0: Audio: aac (HE-AACv2), 44100 Hz, stereo, fltp, 30 kb/s
- 开启VBR模式(Variable Bit Rate,可变比特率)
如果开启了VBR模式,-b:a选项将会被忽略,但-profile:a选项仍然有效
取值范围 0~5
0 默认值,关闭VBR模式,开启CBR模式(Constant Bit Rate,固定比特率)
1 质量最低(但是音质仍旧很棒)
5 质量最高
ffmpeg -i in.wav -c:a libfdk_aac -vbr 1 out.aac
// 参数如下:
Input #0, aac, from 'out.aac':
Duration: 00:00:30.57, bitrate: 63 kb/s
Stream #0:0: Audio: aac (LC), 44100 Hz, mono, fltp, 63 kb/s
VBR | kbps/channel | AOTs |
---|---|---|
1 | 20-32 | LC、HE、HEv2 |
2 | 32-40 | LC、HE、HEv2 |
3 | 48-56 | LC、HE、HEv2 |
4 | 64-72 | LC |
5 | 96-112 | LC |
代码
查找编码器
// 使用默认的编码器
// AVCodec *codec1 = avcodec_find_encoder(AV_CODEC_ID_AAC);
// 使用指定的编码器
const char * codec_name = "libfdk_aac";
AVCodec * codec = avcodec_find_encoder_by_name(codec_name);
if(!codec){
qDebug() << "not find AVCodec -> " << codec_name;
return;
}
检查当前输入文件的编码器格式是否支持
if(!check_sample_fmt(codec,sample_format)){
qDebug() << "not support format -> " << av_get_sample_fmt_name(sample_format);
return;
}
// av_get_sample_fmt_name 方法的实现如下
int check_sample_fmt(const AVCodec *codec, AVSampleFormat sample_fmt){
const AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE)
{
if (*p == sample_fmt)
{
return 1;
}
p++;
}
return 0;
}
创建上下文,设置参数
AVCodecContext * ctx = avcodec_alloc_context3(codec);
if(!ctx){
qDebug() << "avcodec_alloc_context3 error";
return;
}
// 设置上下文参数
ctx->sample_fmt = sample_format;
ctx->sample_rate = sample_rate;
ctx->channel_layout = ch_layout;
// 设置比特率和规格
ctx->bit_rate = 32000;
ctx->profile = FF_PROFILE_AAC_HE;
// 如果需要设置特殊参数,例如vbr
// AVDictionary * options = nullptr;
// av_dict_set(&options,"vbr","1",0);
打开编码器
// int ret = avcodec_open2(ctx,codec,&options);
int ret = avcodec_open2(ctx,codec,nullptr);
if(ret < 0){
ERROR_BUF(ret);
qDebug() << "avcodec_open2 error " << errbuf;
avcodec_free_context(&ctx);
return;
}
创建样本帧,设置参数
AVFrame * frame = av_frame_alloc();
if(!frame){
qDebug() << "av_frame_alloc error ";
avcodec_free_context(&ctx);
return;
}
// 设置样本帧参数
// 样本帧数量
frame->nb_samples = ctx->frame_size;
// 采样格式
frame->format = sample_format;
// 声道布局
frame->channel_layout = ch_layout;
// 创建内部缓冲区
ret = av_frame_get_buffer(frame,0);
if(ret < 0){
ERROR_BUF(ret);
qDebug() << "av_frame_get_buffer error " << errbuf;
av_frame_free(&frame);
avcodec_free_context(&ctx);
return;
}
创建数据包
AVPacket * pkt = av_packet_alloc();
if(!pkt){
qDebug() << "av_frame_alloc error ";
av_frame_free(&frame);
avcodec_free_context(&ctx);
return;
}
打开文件
QFile inFile(in_file_name);
if(!inFile.open(QFile::ReadOnly)){
qDebug() << "open file error " << in_file_name;
av_packet_free(&pkt);
av_frame_free(&frame);
avcodec_free_context(&ctx);
return;
}
QFile outFile(out_file_name);
if(!outFile.open(QFile::WriteOnly)){
qDebug() << "open file error " << out_file_name;
av_packet_free(&pkt);
av_frame_free(&frame);
avcodec_free_context(&ctx);
inFile.close();
}
交换数据
while((ret = inFile.read((char *)frame->data[0],frame->linesize[0])) > 0){
// 最后一次数据如果低于linesize
if(ret < frame->linesize[0]){
// 声道数
int chs = av_get_channel_layout_nb_channels(frame->channel_layout);
// 每个样本的大小
int sample_size = av_get_bytes_per_sample((AVSampleFormat)frame->format);
// 改为真正有效的样本帧数量
frame->nb_samples = ret/(sample_size * chs);
}
if(encode_target(ctx,frame,pkt,outFile) < 0){
// 回收资源
inFile.close();
outFile.close();
// 释放资源
av_packet_free(&pkt);
av_frame_free(&frame);
avcodec_free_context(&ctx);
return;
}
}
// 刷新编码器
encode_target(ctx,nullptr,pkt,outFile);
编码
// ret = 0 单次数据读取结束
// ret < 0 错误
// else 正常
int encode_target(AVCodecContext *ctx,
AVFrame *frame,
AVPacket *pkt,
QFile &outFile){
// 发送数据到编码器
int ret = avcodec_send_frame(ctx,frame);
if(ret < 0){
ERROR_BUF(ret);
qDebug() << "avcodec_send_frame error " << errbuf;
return ret;
}
while (true) {
// 从编码器中获取编码后的数据
ret = avcodec_receive_packet(ctx,pkt);
// 编码器中无数据,需要再向编码器中send数据
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
return 0;
}else if(ret < 0){
// 其他错误
ERROR_BUF(ret);
qDebug() << "avcodec_receive_packet error " << errbuf;
return ret;
}
// 写入数据
outFile.write((char *)pkt->data,pkt->size);
// 释放资源
av_packet_unref(pkt);
}
return 0;
}
回收资源
inFile.close();
outFile.close();
// 释放资源
av_packet_free(&pkt);
av_frame_free(&frame);
avcodec_free_context(&ctx);
调用方法
// AV_CH_LAYOUT_MONO 单声道 AV_CH_LAYOUT_STEREO 立体音
encode("44100_s16le_2.pcm",44100,AV_SAMPLE_FMT_S16,AV_CH_LAYOUT_MONO,"out.aac");