音视频入门基础:AAC专题(13)——FFmpeg源码中,获取ADTS格式的AAC裸流音频信息的实现

=================================================================

音视频入门基础: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;
    }
//...
}
相关推荐
cuijiecheng20185 小时前
音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
ffmpeg·音视频
γ..5 小时前
基于MATLAB的图像增强
开发语言·深度学习·神经网络·学习·机器学习·matlab·音视频
悟纤9 小时前
Suno Api V4模型无水印开发「高清音频WAV下载」 —— 「Suno Api系列」第6篇
音视频·suno·suno v4·suno ai
gomogomono15 小时前
HDR视频技术之八:色域映射
音视频·hdr·yuv
流氓也是种气质 _Cookie17 小时前
uniapp blob格式转换为video .mp4文件使用ffmpeg工具
ffmpeg·uni-app
野蛮的大西瓜19 小时前
BigBlueButton视频会议 vs 华为云会议的详细对比
人工智能·自动化·音视频·实时音视频·信息与通信·视频编解码
网络安全queen20 小时前
网络安全-企业环境渗透2-wordpress任意文件读&&FFmpeg任意文件读
安全·web安全·ffmpeg
野蛮的大西瓜20 小时前
文心一言对接FreeSWITCH实现大模型呼叫中心
人工智能·机器人·自动化·音视频·实时音视频·文心一言·信息与通信
野蛮的大西瓜21 小时前
BigBlueButton视频会议 vs 钉钉视频会议系统的详细对比
人工智能·自然语言处理·自动化·音视频·实时音视频·信息与通信·视频编解码