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

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;