=================================================================
音视频入门基础:AAC专题系列文章:
音视频入门基础:AAC专题(1)------AAC官方文档下载
音视频入门基础:AAC专题(2)------使用FFmpeg命令生成AAC裸流文件
音视频入门基础:AAC专题(3)------AAC的ADTS格式简介
音视频入门基础:AAC专题(4)------ADTS格式的AAC裸流实例分析
音视频入门基础:AAC专题(5)------FFmpeg源码中,判断某文件是否为AAC裸流文件的实现
音视频入门基础:AAC专题(6)------FFmpeg源码中解码ADTS格式的AAC的Header的实现
音视频入门基础:AAC专题(7)------FFmpeg源码中计算AAC裸流每个packet的size值的实现
音视频入门基础:AAC专题(8)------FFmpeg源码中计算AAC裸流AVStream的time_base的实现
音视频入门基础:AAC专题(9)------FFmpeg源码中计算AAC裸流每个packet的duration和duration_time的实现
音视频入门基础:AAC专题(10)------FFmpeg源码中计算AAC裸流每个packet的pts、dts、pts_time、dts_time的实现
音视频入门基础:AAC专题(11)------AudioSpecificConfig简介
音视频入门基础:AAC专题(12)------FFmpeg源码中,解码AudioSpecificConfig的实现
音视频入门基础:AAC专题(13)------FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现
=================================================================
一、引言
对于携带Audio Specific Config的媒体文件,比如音频压缩编码格式为AAC的FLV文件,FFmpeg一般通过解码其Audio Tag中Audio Specific Config获取其音频信息。而通过《音视频入门基础:AAC专题(2)------使用FFmpeg命令生成AAC裸流文件》生成的AAC裸流文件和TS流中的AAC是没有Audio Specific Config的,只有ADTS Header,这时就得通过解码ADTS Header获取其音频信息(音频压缩编码格式的profile、音频采样率、音频声道数等):
本文讲述FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现。
二、音频压缩编码格式
具体获取方法可以参考:《音视频入门基础:AAC专题(5)------FFmpeg源码中,判断某文件是否为AAC裸流文件的实现》
三、音频压缩编码格式的profile
音频压缩编码格式还有附带的profile(规格)。比如,如果音频压缩编码格式为AAC,根据《ISO14496-3-2009.pdf》第124页,还有AAC Main、AAC LC、AAC SSR、AAC LTP这几种规格:
FFmpeg获取AAC裸流的音频压缩编码格式的profile,是根据ADTS Header中的profile_ObjectType属性获取的。由《音视频入门基础:AAC专题(3)------AAC的ADTS格式简介》可以知道,ADTS Header中存在一个占2位的profile_ObjectType属性,表示AAC的规格。
由《音视频入门基础:AAC专题(6)------FFmpeg源码中解码ADTS格式的AAC的Header的实现》可以知道,FFmpeg源码中通过ff_adts_header_parse函数解码ADTS格式的AAC的Header。而ff_adts_header_parse函数中,通过下面语句,将profile_ObjectType属性的值加1赋值给hdr->object_type:
cpp
hdr->object_type = aot + 1;
然后在parse_adts_frame_header函数中,将hdr->object_type赋值给ac->oc[1].m4ac.object_type:
cpp
static int parse_adts_frame_header(AACDecContext *ac, GetBitContext *gb)
{
//...
size = ff_adts_header_parse(gb, &hdr_info);
if (size > 0) {
//...
ac->oc[1].m4ac.object_type = hdr_info.object_type;
//...
}
//...
}
之后,通过aac_decode_frame_int函数将ac->oc[1].m4ac.object_type的值减1赋值给AVCodecContext的profile,这样AVCodecContext的profile就会得到原本的profile_ObjectType属性:
cpp
static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame,
int *got_frame_ptr, GetBitContext *gb,
const AVPacket *avpkt)
{
//...
// The AV_PROFILE_AAC_* defines are all object_type - 1
// This may lead to an undefined profile being signaled
ac->avctx->profile = ac->oc[1].m4ac.object_type - 1;
//...
}
然后在dump_stream_format函数中,通过avcodec_string函数中的语句:profile = avcodec_profile_name(enc->codec_id, enc->profile)拿到上一步中得到的AVCodecContext的profile。最后再在dump_stream_format函数中将profile打印出来:
cpp
void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
profile = avcodec_profile_name(enc->codec_id, enc->profile);
//...
}
所以FFmpeg获取AAC裸流文件的音频压缩编码格式的profile,获取的是ADTS Header中的profile_ObjectType属性:
四、音频采样率
FFmpeg获取AAC裸流的音频采样频率,是根据ADTS Header中的samplingFrequencyIndex属性获取的。 由《音视频入门基础:AAC专题(3)------AAC的ADTS格式简介》可以知道,ADTS Header中存在一个占4位的samplingFrequencyIndex属性,表示音频采样频率:
ff_adts_header_parse函数中,通过下面语句,将samplingFrequencyIndex属性的值赋值给hdr->sampling_index。将音频采样频率(单位为Hz)赋值给hdr->sample_rate:
cpp
hdr->sampling_index = sr;
hdr->sample_rate = ff_mpeg4audio_sample_rates[sr];
然后在parse_adts_frame_header函数中,将hdr->sample_rate赋值给ac->oc[1].m4ac.sample_rate:
cpp
static int parse_adts_frame_header(AACDecContext *ac, GetBitContext *gb)
{
//...
size = ff_adts_header_parse(gb, &hdr_info);
if (size > 0) {
//...
ac->oc[1].m4ac.sample_rate = hdr_info.sample_rate;
//...
}
//...
}
之后,通过aac_decode_frame_int函数将ac->oc[1].m4ac.sample_rate赋值给AVCodecContext的sample_rate:
cpp
static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame,
int *got_frame_ptr, GetBitContext *gb,
const AVPacket *avpkt)
{
//...
if (ac->oc[1].status && audio_found) {
avctx->sample_rate = ac->oc[1].m4ac.sample_rate << multiplier;
avctx->frame_size = samples;
ac->oc[1].status = OC_LOCKED;
}
//...
}
然后在dump_stream_format函数中,通过avcodec_string函数中的语句:av_bprintf(&bprint, "%d Hz, ", enc->sample_rate)拿到上一步中得到的AVCodecContext的sample_rate。最后再在dump_stream_format函数中将其打印出来:
cpp
void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
switch (enc->codec_type) {
case AVMEDIA_TYPE_AUDIO:
av_bprintf(&bprint, "%s", separator);
if (enc->sample_rate) {
av_bprintf(&bprint, "%d Hz, ", enc->sample_rate);
}
//...
}
//...
}
五、音频声道数
FFmpeg获取AAC裸流的音频声道数,是根据ADTS Header中的channel_configuration属性获取的。 由《音视频入门基础:AAC专题(3)------AAC的ADTS格式简介》可以知道,ADTS Header中存在一个占3位的channel_configuration属性,表示音频声道数:
ff_adts_header_parse函数中,通过下面语句,将音频声道数赋值给hdr->chan_config:
cpp
hdr->chan_config = ch;
然后在parse_adts_frame_header函数中,将hdr->chan_config赋值给AVCodecContext的ch_layout:
cpp
static int parse_adts_frame_header(AACDecContext *ac, GetBitContext *gb)
{
//...
size = ff_adts_header_parse(gb, &hdr_info);
if (size > 0) {
//...
if (hdr_info.chan_config) {
ac->oc[1].m4ac.chan_config = hdr_info.chan_config;
if ((ret = set_default_channel_config(ac, ac->avctx,
layout_map,
&layout_map_tags,
hdr_info.chan_config)) < 0)
return ret;
if ((ret = output_configure(ac, layout_map, layout_map_tags,
FFMAX(ac->oc[1].status,
OC_TRIAL_FRAME), 0)) < 0)
return ret;
}
//...
}
//...
}
然后在dump_stream_format函数中,通过avcodec_string函数中的语句:av_channel_layout_describe_bprint(&enc->ch_layout, &bprint)拿到AVCodecContext的ch_layout对应的音频声道数目。最后再在dump_stream_format函数中将音频声道数目打印出来:
cpp
void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...
switch (enc->codec_type) {
case AVMEDIA_TYPE_AUDIO:
av_channel_layout_describe_bprint(&enc->ch_layout, &bprint);
//...
break;
}
//...
}