如何使用FFmpeg进行高效AAC音频编解码操作?

2026-04-11 22:361阅读0评论SEO问题
  • 内容介绍
  • 相关推荐

本文共计1031个文字,预计阅读时间需要5分钟。

如何使用FFmpeg进行高效AAC音频编解码操作?

AAC解码的三种方式:AAC:FFmpeg本身的AAC编码实现,libaac:第三方的AAC编码器,libfdk_aac:第三方的AAC编码器。当前采用FFmpeg自带的aac实现AAC的解码,音频帧采样率为1024,2。

AAC解码的三种方式

aac: FFmpeg本身的AAC编码实现

libaac:第三方的AAC编码器

libfdk_aac:第三方的AAC编码器

当前采用FFmpeg自带的aac实现AAC的编解码

AAC音频帧特点

1)音频帧采样个数是1024

2)FFmpeg新版本只支持采样位深是32位浮点型 对应AV_SAMPLE_FMT_FLTP

FFmpeg支持的AAC编解码格式

F:\ffmpeg\msvc\bin\x64>ffmpegd -formats |findstr AAC ffmpeg version 4.1.git Copyright (c) 2000-2019 the FFmpeg developers built with msvc configuration: --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-sdl2 --enable-zlib --enable-avisynth --enable-libmp3lame --enable-libvorbis --enable-libspeex --enable-libopus --enable-libilbc --enable-libtheora --enable-libx264 --enable-libx265 --enable-libxvid --enable-libvpx --enable-libgme --enable-libmodplug --enable-libsoxr --enable-libfreetype --enable-fontconfig --enable-libfribidi --enable-libass --enable-libxml2 --enable-gnutls --disable-schannel --enable-gcrypt --enable-libssh --enable-libcdio --enable-libbluray --enable-opengl --enable-libmfx --enable-ffnvcodec --enable-cuda --enable-amf --toolchain=msvc libavutil 56. 29.100 / 56. 29.100 libavcodec 58. 53.100 / 58. 53.100 libavformat 58. 28.101 / 58. 28.101 libavdevice 58. 7.100 / 58. 7.100 libavfilter 7. 55.100 / 7. 55.100 libswscale 5. 4.101 / 5. 4.101 libswresample 3. 4.100 / 3. 4.100 libpostproc 55. 4.100 / 55. 4.100 D aac raw ADTS AAC (Advanced Audio Coding) E adts ADTS AAC (Advanced Audio Coding)

编解码宏定义

AV_CODEC_ID_AAC

注意:FFmpeg自带的AAC编解码宏定义都是AV_CODEC_ID_AAC

avcodec_find_encoder(AV_CODEC_ID_AAC)

avcodec_find_decoder(AV_CODEC_ID_AAC)


编码器结构体

libavcodec\aacenc.c

如何使用FFmpeg进行高效AAC音频编解码操作?

AVCodec ff_aac_encoder = { .name = "aac", .long_name = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_AAC, .priv_data_size = sizeof(AACEncContext), .init = aac_encode_init, .encode2 = aac_encode_frame, .close = aac_encode_end, .defaults = aac_encode_defaults, .supported_samplerates = mpeg4audio_sample_rates, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .priv_class = &aacenc_class, };

采样格式

新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP,老版本的是AV_SAMPLE_FMT_S16,如果输入的PCM数据是AV_SAMPLE_FMT_S16的,avcodec_encode_audio2会返回-22错误.

我们可以在AVCodec结构体中的sample_fmts字段中判断编码器是否支持你的格式


解码器结构体

libavcodec\aacdec.c

AVCodec ff_aac_decoder = { .name = "aac", .long_name = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_AAC, .priv_data_size = sizeof(AACContext), .init = aac_decode_init, .close = aac_decode_close, .decode = aac_decode_frame, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, .channel_layouts = aac_channel_layout, .flush = flush, .priv_class = &aac_decoder_class, .profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), };

单声道PCM编码为AAC格式写入MP4文件

int EncodeMONOPCMToAACAndWriteMP4File() { const char *pszPCMFileName = "F:/input.pcm"; const char *pszMP4FileName = "F:/outputAAC.mp4"; AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC); if (!codec) { //std::cout << "查找AV_CODEC_ID_AAC编码器失败" << std::endl; exit(1); } //设置音频编码参数 AVCodecContext *pAVCodecContext = avcodec_alloc_context3(codec); pAVCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP; pAVCodecContext->sample_rate = 8000; pAVCodecContext->codec_type = AVMEDIA_TYPE_AUDIO; pAVCodecContext->channel_layout = AV_CH_LAYOUT_MONO; pAVCodecContext->channels = av_get_channel_layout_nb_channels(pAVCodecContext->channel_layout); if (avcodec_open2(pAVCodecContext, codec, NULL) < 0) { //失败的原因通常是编码器不支持设置的采样格式,AAC只支持AV_SAMPLE_FMT_FLTP采样格式 //std::cout << "打开AV_CODEC_ID_AAC编码器失败" << std::endl; exit(1); } AVFormatContext *pAVFormatOutputContext = NULL; //设置MP4为输出容器 avformat_alloc_output_context2(&pAVFormatOutputContext, NULL, "mp4", pszMP4FileName); if (!pAVFormatOutputContext) { //std::cout << "分配输出上下文失败" << std::endl; } AVStream* pAudioStream = avformat_new_stream(pAVFormatOutputContext, NULL); //拷贝编码器的参数到音频流中 avcodec_parameters_from_context(pAudioStream->codecpar, pAVCodecContext); int nRet = avio_open(&pAVFormatOutputContext->pb, pszMP4FileName, AVIO_FLAG_WRITE); if (nRet < 0) { //std::cout << "打开输出文件失败" << std::endl; } nRet = avformat_write_header(pAVFormatOutputContext, NULL); AVFrame *pAudioFrame = av_frame_alloc(); pAudioFrame->format = AV_SAMPLE_FMT_FLTP; pAudioFrame->channel_layout = AV_CH_LAYOUT_MONO; pAudioFrame->channels = av_get_channel_layout_nb_channels(pAudioFrame->channel_layout); pAudioFrame->nb_samples = 1024; //开辟一块内存空间保存即将从文件中读取的音频数据 nRet = av_frame_get_buffer(pAudioFrame, 0); // 计算出每一帧的数据 单个采样点的字节 * 通道数目 * 每帧采样点数量 int nAudioFrameLen = av_get_bytes_per_sample((enum AVSampleFormat)pAudioFrame->format) \ * pAudioFrame->channels \ * pAudioFrame->nb_samples; FILE *pInputHandle = fopen(pszPCMFileName, "rb"); int64_t nPTS = 0; AVPacket* pAudioPacket = av_packet_alloc(); for (;;) { //如下的写法是一样的效果 //int nReadLen = fread(pAudioFrame->data[0], 1, nAudioFrameLen, pInputHandle); int nReadLen = fread(pAudioFrame->data[0], 1, pAudioFrame->linesize[0], pInputHandle); if (nReadLen <= 0) { break; } nPTS += pAudioFrame->nb_samples; //使用采样率作为pts的单位,具体换算成秒 pts*1/采样率 pAudioFrame->pts = nPTS; nRet = avcodec_send_frame(pAVCodecContext, pAudioFrame); if (nRet != 0) continue; nRet = avcodec_receive_packet(pAVCodecContext, pAudioPacket); if (nRet != 0) continue; pAudioPacket->stream_index = 0; nRet = av_interleaved_write_frame(pAVFormatOutputContext, pAudioPacket); } av_write_trailer(pAVFormatOutputContext); avio_close(pAVFormatOutputContext->pb); //对应上述的av_frame_alloc av_frame_free(&pAudioFrame); //对应上述的av_packet_alloc av_packet_free(pAudioPacket); avformat_free_context(pAVFormatOutputContext); avcodec_close(pAVCodecContext); avcodec_free_context(&pAVCodecContext); }

AAC相关参数设置

av_opt_set(pAudioCodecContext->priv_data, "profile", "aac_low", 0);


PCM转码为AAC

ffmpeg -f f32le -ar 8000 -ac 1 -i input.pcm output.aac


问题注意

[aac @ 000001718e41df40] Format aac detected only with low score of 1, misdetection possible!

读取PCM文件转码为AAC,读取的数据缓存不对,导致转码以后的数据不正确

// 循环读取数据 // 计算出每一帧的数据 单个采样点的字节 * 通道数目 * 每帧采样点数量 int frame_bytes = av_get_bytes_per_sample((AVSampleFormat)frame->format) \ * frame->channels \ * frame->nb_samples;

本文共计1031个文字,预计阅读时间需要5分钟。

如何使用FFmpeg进行高效AAC音频编解码操作?

AAC解码的三种方式:AAC:FFmpeg本身的AAC编码实现,libaac:第三方的AAC编码器,libfdk_aac:第三方的AAC编码器。当前采用FFmpeg自带的aac实现AAC的解码,音频帧采样率为1024,2。

AAC解码的三种方式

aac: FFmpeg本身的AAC编码实现

libaac:第三方的AAC编码器

libfdk_aac:第三方的AAC编码器

当前采用FFmpeg自带的aac实现AAC的编解码

AAC音频帧特点

1)音频帧采样个数是1024

2)FFmpeg新版本只支持采样位深是32位浮点型 对应AV_SAMPLE_FMT_FLTP

FFmpeg支持的AAC编解码格式

F:\ffmpeg\msvc\bin\x64>ffmpegd -formats |findstr AAC ffmpeg version 4.1.git Copyright (c) 2000-2019 the FFmpeg developers built with msvc configuration: --enable-gpl --enable-version3 --enable-bzlib --enable-iconv --enable-lzma --enable-sdl2 --enable-zlib --enable-avisynth --enable-libmp3lame --enable-libvorbis --enable-libspeex --enable-libopus --enable-libilbc --enable-libtheora --enable-libx264 --enable-libx265 --enable-libxvid --enable-libvpx --enable-libgme --enable-libmodplug --enable-libsoxr --enable-libfreetype --enable-fontconfig --enable-libfribidi --enable-libass --enable-libxml2 --enable-gnutls --disable-schannel --enable-gcrypt --enable-libssh --enable-libcdio --enable-libbluray --enable-opengl --enable-libmfx --enable-ffnvcodec --enable-cuda --enable-amf --toolchain=msvc libavutil 56. 29.100 / 56. 29.100 libavcodec 58. 53.100 / 58. 53.100 libavformat 58. 28.101 / 58. 28.101 libavdevice 58. 7.100 / 58. 7.100 libavfilter 7. 55.100 / 7. 55.100 libswscale 5. 4.101 / 5. 4.101 libswresample 3. 4.100 / 3. 4.100 libpostproc 55. 4.100 / 55. 4.100 D aac raw ADTS AAC (Advanced Audio Coding) E adts ADTS AAC (Advanced Audio Coding)

编解码宏定义

AV_CODEC_ID_AAC

注意:FFmpeg自带的AAC编解码宏定义都是AV_CODEC_ID_AAC

avcodec_find_encoder(AV_CODEC_ID_AAC)

avcodec_find_decoder(AV_CODEC_ID_AAC)


编码器结构体

libavcodec\aacenc.c

如何使用FFmpeg进行高效AAC音频编解码操作?

AVCodec ff_aac_encoder = { .name = "aac", .long_name = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_AAC, .priv_data_size = sizeof(AACEncContext), .init = aac_encode_init, .encode2 = aac_encode_frame, .close = aac_encode_end, .defaults = aac_encode_defaults, .supported_samplerates = mpeg4audio_sample_rates, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, .capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY, .sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .priv_class = &aacenc_class, };

采样格式

新版的ffmpeg 编码AAC只支持的AV_SAMPLE_FMT_FLTP,老版本的是AV_SAMPLE_FMT_S16,如果输入的PCM数据是AV_SAMPLE_FMT_S16的,avcodec_encode_audio2会返回-22错误.

我们可以在AVCodec结构体中的sample_fmts字段中判断编码器是否支持你的格式


解码器结构体

libavcodec\aacdec.c

AVCodec ff_aac_decoder = { .name = "aac", .long_name = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"), .type = AVMEDIA_TYPE_AUDIO, .id = AV_CODEC_ID_AAC, .priv_data_size = sizeof(AACContext), .init = aac_decode_init, .close = aac_decode_close, .decode = aac_decode_frame, .sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, .channel_layouts = aac_channel_layout, .flush = flush, .priv_class = &aac_decoder_class, .profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), };

单声道PCM编码为AAC格式写入MP4文件

int EncodeMONOPCMToAACAndWriteMP4File() { const char *pszPCMFileName = "F:/input.pcm"; const char *pszMP4FileName = "F:/outputAAC.mp4"; AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC); if (!codec) { //std::cout << "查找AV_CODEC_ID_AAC编码器失败" << std::endl; exit(1); } //设置音频编码参数 AVCodecContext *pAVCodecContext = avcodec_alloc_context3(codec); pAVCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP; pAVCodecContext->sample_rate = 8000; pAVCodecContext->codec_type = AVMEDIA_TYPE_AUDIO; pAVCodecContext->channel_layout = AV_CH_LAYOUT_MONO; pAVCodecContext->channels = av_get_channel_layout_nb_channels(pAVCodecContext->channel_layout); if (avcodec_open2(pAVCodecContext, codec, NULL) < 0) { //失败的原因通常是编码器不支持设置的采样格式,AAC只支持AV_SAMPLE_FMT_FLTP采样格式 //std::cout << "打开AV_CODEC_ID_AAC编码器失败" << std::endl; exit(1); } AVFormatContext *pAVFormatOutputContext = NULL; //设置MP4为输出容器 avformat_alloc_output_context2(&pAVFormatOutputContext, NULL, "mp4", pszMP4FileName); if (!pAVFormatOutputContext) { //std::cout << "分配输出上下文失败" << std::endl; } AVStream* pAudioStream = avformat_new_stream(pAVFormatOutputContext, NULL); //拷贝编码器的参数到音频流中 avcodec_parameters_from_context(pAudioStream->codecpar, pAVCodecContext); int nRet = avio_open(&pAVFormatOutputContext->pb, pszMP4FileName, AVIO_FLAG_WRITE); if (nRet < 0) { //std::cout << "打开输出文件失败" << std::endl; } nRet = avformat_write_header(pAVFormatOutputContext, NULL); AVFrame *pAudioFrame = av_frame_alloc(); pAudioFrame->format = AV_SAMPLE_FMT_FLTP; pAudioFrame->channel_layout = AV_CH_LAYOUT_MONO; pAudioFrame->channels = av_get_channel_layout_nb_channels(pAudioFrame->channel_layout); pAudioFrame->nb_samples = 1024; //开辟一块内存空间保存即将从文件中读取的音频数据 nRet = av_frame_get_buffer(pAudioFrame, 0); // 计算出每一帧的数据 单个采样点的字节 * 通道数目 * 每帧采样点数量 int nAudioFrameLen = av_get_bytes_per_sample((enum AVSampleFormat)pAudioFrame->format) \ * pAudioFrame->channels \ * pAudioFrame->nb_samples; FILE *pInputHandle = fopen(pszPCMFileName, "rb"); int64_t nPTS = 0; AVPacket* pAudioPacket = av_packet_alloc(); for (;;) { //如下的写法是一样的效果 //int nReadLen = fread(pAudioFrame->data[0], 1, nAudioFrameLen, pInputHandle); int nReadLen = fread(pAudioFrame->data[0], 1, pAudioFrame->linesize[0], pInputHandle); if (nReadLen <= 0) { break; } nPTS += pAudioFrame->nb_samples; //使用采样率作为pts的单位,具体换算成秒 pts*1/采样率 pAudioFrame->pts = nPTS; nRet = avcodec_send_frame(pAVCodecContext, pAudioFrame); if (nRet != 0) continue; nRet = avcodec_receive_packet(pAVCodecContext, pAudioPacket); if (nRet != 0) continue; pAudioPacket->stream_index = 0; nRet = av_interleaved_write_frame(pAVFormatOutputContext, pAudioPacket); } av_write_trailer(pAVFormatOutputContext); avio_close(pAVFormatOutputContext->pb); //对应上述的av_frame_alloc av_frame_free(&pAudioFrame); //对应上述的av_packet_alloc av_packet_free(pAudioPacket); avformat_free_context(pAVFormatOutputContext); avcodec_close(pAVCodecContext); avcodec_free_context(&pAVCodecContext); }

AAC相关参数设置

av_opt_set(pAudioCodecContext->priv_data, "profile", "aac_low", 0);


PCM转码为AAC

ffmpeg -f f32le -ar 8000 -ac 1 -i input.pcm output.aac


问题注意

[aac @ 000001718e41df40] Format aac detected only with low score of 1, misdetection possible!

读取PCM文件转码为AAC,读取的数据缓存不对,导致转码以后的数据不正确

// 循环读取数据 // 计算出每一帧的数据 单个采样点的字节 * 通道数目 * 每帧采样点数量 int frame_bytes = av_get_bytes_per_sample((AVSampleFormat)frame->format) \ * frame->channels \ * frame->nb_samples;