AAC编码

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
VBRkbps/channelAOTs
120-32LC、HE、HEv2
232-40LC、HE、HEv2
348-56LC、HE、HEv2
464-72LC
596-112LC

代码

查找编码器

// 使用默认的编码器
// 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");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值