FFmpeg音视频开发_AAC编码实战

一、FFmpeg 命令行实现 AAC 编码 1.1、基本使用:

# pcm -> aac $ ffmpeg -ar 44100 -ac 2 -f s16le -i ar44100ac2s16le.pcm -c:a libfdk_aac out.aac # wav -> aac $ ffmpeg -i in.wav out.aac

-c:a 设置音频编码器,c表示codec(编解码器),a表示audio(音频)。等价写法 -codec:a 或者 -acodec。需要注意的是这是输出参数。
默认生成的 aac 文件是 LC 规格的。
1.2、libfdk_aac 常用参数:
-b:a 设置输出比特率(比如 -b:a 96k,开启 VBR 模式会忽略此选项)
$ ffmpeg -i in.wav -b:a 96k out.aac

-profile:a 设置编码规格
推荐的取值选项:
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)
设置了音频编码规格,会自动设置一个合适的输出比特率,也可以通过 -b:a 自行设置。
$ ffmpeg -i in.wav -profile:a aac_low out.aac

设置 aac_low 以外的参数报错:
[adts @ 0x7fe95e83d000] MPEG-4 AOT 21 is not allowed in ADTS Could not write header for output file #0 (incorrect codec parameters ?): Invalid data found when processing input Error initializing output stream 0:0 --

-vbr 设置可变比特率模式
取值选项(取值选项与对应的比特率大致范围,当前仅 aac_low 支持 VBR 模式):
0: 默认值,关闭 VBR,开启 CBR
1: 32 kbps/channel,质量最低,音质仍很好
2: 40 kbps/channel
3: 48-56 kbps/channel
4: 64 kbps/channel
5: about 80-96 kbps/channel,质量最高
$ ffmpeg -i in.wav -c:a libfdk_aac -vbr 1 out.aac

【FFmpeg音视频开发_AAC编码实战】AAC 编码文件扩展名主要有 3 种:aac、m4a、mp3。
二、FFmpeg 编程实现 AAC 编码 AAC 编码流程:
FFmpeg音视频开发_AAC编码实战
文章图片
1、AAC 编码需要用到 2 个库:
extern "C" { #include #include }

2、定义变量:
// 返回值 int ret; // 编码器 AVCodec *codec = nullptr; // 上下文 AVCodecContext *ctx = nullptr; // 用来存放编码前的数据 AVFrame *frame; // 用来存放编码后的数据 AVPacket *pkt; QFile inFile(in.filename); QFile outFile(outFilename);

3、查找音频编码器:
codec = avcodec_find_encoder_by_name("libfdk_aac"); if (!codec) { qDebug() << "encoder libfdk_aac not found"; return; }

以下方式可以获取 FFmpeg 默认 AAC 编码器:
AVCodec *codec1 = avcodec_find_decoder(AV_CODEC_ID_AAC); AVCodec *codec2 = avcodec_find_decoder_by_name("aac”); qDebug() << "codec1->name:" <name; qDebug() << "isTheSameCodec: " << (codec1 == codec2);

打印:
codec1->name: aac isTheSameCodec:true

4、检查编码器是否支持编码格式:
if (!check_sample_fmt(codec, in.sampleFmt)) { qDebug() << "Encoder dose not support sample format: " << av_get_sample_fmt_name(in.sampleFmt); goto end; }

int check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt) { const enum AVSampleFormat *p = codec->sample_fmts; while (*p != AV_SAMPLE_FMT_NONE) { if (*p == sample_fmt) return 1; p++; } return 0; }

5、设置编码上下文参数:
// 音频采样格式 ctx->sample_fmt = in.sampleFmt; // 音频采样率 ctx->sample_rate = in.sampleRate; // 声道布局 ctx->channel_layout = in.chLayout; // 音频编码规格 ctx->profile = FF_PROFILE_AAC_HE_V2; // 比特率 ctx->bit_rate = 32000;

6、打开编码器:
ret = avcodec_open2(ctx, codec, nullptr); if (ret < 0) { ERRBUF(ret); qDebug() << "could not open codec: " << errbuf; goto end; }

7、创建 packet:
pkt = av_packet_alloc(); if (!pkt) { qDebug() << "could not allocate audio packet"; goto end; }

8、创建 frame 并创建 frame 的数据缓冲区:
frame = av_frame_alloc(); if (!frame) { qDebug() << "could not allocate audio frame"; goto end; }// 此帧的音频采样数 frame->nb_samples = ctx->frame_size; // 音频采样格式 frame->format = ctx->sample_fmt; // 声道布局 frame->channel_layout = ctx->channel_layout; // 创建frame的数据缓冲区 ret = av_frame_get_buffer(frame, 0); if (ret < 0) { ERRBUF(ret); qDebug() << "could not allocate audio data buffers: " << errbuf; goto end; }

9、打开文件:
if (!inFile.open(QFile::ReadOnly)) { qDebug() << "open file failure: " << in.filename; goto end; }if (!outFile.open(QFile::WriteOnly)) { qDebug() << "open file failure: " << outFilename; goto end; }

10、编码:
while ((ret = inFile.read((char *)frame->data[0], frame->linesize[0])) > 0) { // 最后一次读取文件数据 if (ret < frame->linesize[0]) { int chs = av_get_channel_layout_nb_channels(frame->channel_layout); int bytes = av_get_bytes_per_sample((AVSampleFormat)frame->format); frame->nb_samples = ret / (bytes * chs); }if (encode(ctx, frame, pkt, outFile) < 0) { goto end; } }// 刷出输出缓冲区中剩余数据 encode(ctx, nullptr, pkt, outFile);

static int encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, QFile &outFile) { // 发送数据到音频编码器 int ret = avcodec_send_frame(ctx, frame); if (ret < 0) { ERRBUF(ret); qDebug() << "error sending the frame to the codec: " << errbuf; return ret; }while (true) { // 从编码器中获取编码后的数据 ret = avcodec_receive_packet(ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { return 0; } else if (ret < 0) { ERRBUF(ret); qDebug() << "error encode audio frame: " << errbuf; return ret; }outFile.write((const char *)pkt->data, pkt->size); av_packet_unref(pkt); } return 0; }

11、最后关闭文件,释放资源:
end: inFile.close(); outFile.close(); av_packet_free(&pkt); av_frame_free(&frame); avcodec_free_context(&ctx);

参考 FFmpeg 源码 example: /ffmpeg-4.3.2/doc/examples/encode_audio.c

    推荐阅读