音视频入门基础: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:

hdr->object_type    = aot + 1;

然后在parse_adts_frame_header函数中,将hdr->object_type赋值给ac->oc[1].m4ac.object_type:

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属性:

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 signaledac->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打印出来:

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:

    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:

​
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:

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函数中将其打印出来:

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中的samplingFrequencyIndex属性:

五、音频声道数

FFmpeg获取AAC裸流的音频声道数,是根据ADTS Header中的channel_configuration属性获取的。 由《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》可以知道,ADTS Header中存在一个占3位的channel_configuration属性,表示音频声道数:

ff_adts_header_parse函数中,通过下面语句,将音频声道数赋值给hdr->chan_config:

hdr->chan_config    = ch;

然后在parse_adts_frame_header函数中,将hdr->chan_config赋值给AVCodecContext的ch_layout:

​
​
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函数中将音频声道数目打印出来:

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;}
//...
}

所以FFmpeg获取AAC裸流文件的音频声道数,获取的是ADTS Header中的channel_configuration属性:

六、Bit depth

FFmpeg获取AAC裸流的Bit depth(又叫位深度、位元深度、采样深度、采样位数、采样格式),获取到的值是没有意义的。当音频压缩编码格式为AAC时,FFmpeg会强制把Bit depth设置为fltp。这是因为对于有损压缩编解码器(如MP3和AAC),Bit depth是在编码期间计算的,并且可以因采样而异,Bit depth只对PCM数字信号有意义。具体可以参考:《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》。

可以看到在aac_decode_init函数中(该函数定义在libavcodec/aacdec_template.c),强制把音频采样格式设置成了AV_SAMPLE_FMT_FLTP:

static av_cold int aac_decode_init(AVCodecContext *avctx)
{
//...avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
//...
}

所以如果音频压缩编码格式为AAC,通过FFmpeg获取到的音频采样格式固定为fltp,该值没有意义:

七、音频码率

通过解码ADTS Header无法直接获得音频码率,但是可以通过里面的属性间接计算出音频码率。

ff_adts_header_parse函数中,将该ADTS音频帧中原始数据块的个数乘以1024,得到的结果赋值给hdr->samples。FFmpeg源码内部强制默认AAC(AAC Main、AAC LC、AAC SSR、AAC LTP)的samples是1024。hdr->samples为该ADTS音频帧中采样的次数:

hdr->samples        = (rdb + 1) * 1024;

通过公式得到该ADTS音频帧的码率,单位为bits/s,赋值给hdr->bit_rate:

hdr->bit_rate       = size * 8 * hdr->sample_rate / hdr->samples;

然后ff_aac_ac3_parse函数中,通过下面代码得到实际的以bps为单位的音频码率,赋值给AVCodecContext的bit_rate:

int ff_aac_ac3_parse(AVCodecParserContext *s1,AVCodecContext *avctx,const uint8_t **poutbuf, int *poutbuf_size,const uint8_t *buf, int buf_size)
{
//...if (got_frame) {//...int bit_rate;if (avctx->codec_id != AV_CODEC_ID_AAC) {//...}else{AACADTSHeaderInfo hdr, *phrd = &hdr;int ret = avpriv_adts_header_parse(&phrd, buf, buf_size);if (ret < 0)return i;bit_rate = hdr.bit_rate;}/* Calculate the average bit rate */s->frame_number++;if (!CONFIG_EAC3_DECODER || avctx->codec_id != AV_CODEC_ID_EAC3) {avctx->bit_rate +=(bit_rate - avctx->bit_rate) / s->frame_number;}}
//...
}

然后在dump_stream_format函数中,通过avcodec_string函数中的语句:bitrate = get_bit_rate(enc)拿到AVCodecContext的bit_rate。最后再把它除以1000,得到以kb/s为单位的音频码率,打印出来:

void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)
{
//...bitrate = get_bit_rate(enc);if (bitrate != 0) {av_bprintf(&bprint, ", %"PRId64" kb/s", bitrate / 1000);
//...
}

所以FFmpeg获取AAC裸流文件的音频码率,是根据ADTS Header中的属性计算出来的:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/65156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

英文学术会议海报poster模板【可编辑】

英文学术会议海报poster模板【可编辑】 下载链接&#xff1a;学术会议海报poster模板【可编辑】 横版海报 竖版海报 下载链接&#xff1a;学术会议海报poster模板【可编辑】 提供了一套学术海报的PPT模板&#xff0c;适用于学术会议、研讨会等场合。 竖版&#xff0c;包含11…

机器学习之KNN算法预测数据和数据可视化

机器学习及KNN算法 目录 机器学习及KNN算法机器学习基本概念概念理解步骤为什么要学习机器学习需要准备的库 KNN算法概念算法导入常用距离公式算法优缺点优点&#xff1a;缺点︰ 数据可视化二维界面三维界面 KNeighborsClassifier 和KNeighborsRegressor理解查看KNeighborsRegr…

Jmeter自学【8】- 使用JMeter模拟设备通过MQTT发送数据

今天使用jmeter推送数据到MQTT&#xff0c;给大家分享一下操作流程。 一、安装JMeter 参考文档&#xff1a;Jmeter自学【1】- Jmeter安装、配置 二、安装MQTT插件 1、下载插件 我的Jmeter版本是5.6.3&#xff0c;用到的插件是&#xff1a;mqtt-xmeter-2.0.2-jar-with-depe…

若依框架中的上传图片后如何实现回显到页面的

在日常开发中&#xff0c;总会遇到上传文件、图片等功能&#xff0c;然后本地开发的话&#xff0c;又没有像OSS、七牛等网络存储&#xff0c;这个时候通常将文件上传到本地&#xff0c;那么上传之后拿到的是本地的路径&#xff0c;存储到数据库中&#xff0c;查询的时候如何将本…

Linux 文件 I/O 基础

目录 前言 一、文件描述符&#xff08;File Descriptor&#xff09; 二、打开文件&#xff08;open 函数&#xff09; 三、读取文件&#xff08;read 函数&#xff09; 四、写入文件&#xff08;write 函数&#xff09; 五、关闭文件&#xff08;close 函数&#xff09; …

【CSS in Depth 2 精译_091】15.4:让 CSS 高度值过渡到自动高度 + 15.5:自定义属性的过渡设置(全新)+ 15.6:本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 15 章 过渡】 ✔️ 15.1 状态间的由此及彼15.2 定时函数 15.2.1 定制贝塞尔曲线15.2.2 阶跃 15.3 非动画属性 15.3.1 不可添加动画效果的属性15.3.2 淡入与淡出 15.4 过…

路由器的原理

✍作者&#xff1a;柒烨带你飞 &#x1f4aa;格言&#xff1a;生活的情况越艰难&#xff0c;我越感到自己更坚强&#xff1b;我这个人走得很慢&#xff0c;但我从不后退。 &#x1f4dc;系列专栏&#xff1a;网路安全入门系列 目录 路由器的原理一&#xff0c;路由器基础及相关…

spring专题笔记(七):spring如何引入外部属性文件?spring在xml配置bean时如何引入外部的properties属性文件内容?

目录 1、spring在xml配置bean时引入外部的properties属性文件内容作用是什么&#xff1f; 2、引入配置文件步骤 2.1、首先创建一个java类MyDataSource&#xff0c;主要包含四个属性。 2.2、准备一个myDataConfig.properties属性文件&#xff0c;里面配置MyDataSource类中需…

梳理你的思路(从OOP到架构设计)_认识框架(Framework) 01

目录 1、 是框架的核心要素​编辑&i> 范例1&#xff1a; 范例2&#xff1a; 范例3&#xff1a; 1、 <E&I>是框架的核心要素 在特定领域(Domain)里&#xff0c;将EIT造形的<E&I>部份有意义地组合起来&#xff0c;就成为框架(Framework)了。基本…

邮件白名单是什么?

邮件白名单是一种电子邮件过滤规则&#xff0c;用于指定哪些发件人、域名或IP地址的邮件被允许通过过滤系统&#xff0c;不受任何限制地进入收件人的邮箱。与黑名单&#xff08;用于阻止特定发件人的邮件&#xff09;相反&#xff0c;白名单确保了来自受信任来源的邮件能够畅通…

Maven项目中不修改 pom.xml 状况下直接运行OpenRewrite的配方

在Java 的Maven项目中&#xff0c;可以在pom.xml 中配置插件用来运行OpenRewrite的Recipe&#xff0c;但是有一些场景是希望不修改pom.xml 文件就可以运行Recipe&#xff0c;比如&#xff1a; 因为不需要经常运行 OpenRewrite&#xff0c;所以不想在pom.xml 加入不常使用的插件…

JVM实战—2.JVM内存设置与对象分配流转

大纲 1.JVM内存划分的原理细节 2.对象在JVM内存中如何分配如何流转 3.部署线上系统时如何设置JVM内存大小 4.如何设置JVM堆内存大小 5.如何设置JVM栈内存与永久代大小 6.问题汇总 1.JVM内存划分的原理细节 (1)背景引入 (2)大部分对象的存活周期都是极短的 (3)少数对象…

kong网关使用pre-function插件,改写接口的返回数据

一、背景 kong作为api网关&#xff0c;除了反向代理后端服务外&#xff0c;还可对接口进行预处理。 比如本文提及的一个小功能&#xff0c;根据http header某个字段的值&#xff0c;等于多少的时候&#xff0c;返回一个固定的报文。 使用到的kong插件是pre-function。 除了上…

计算机网络:IP地址相关知识总结

目录 一、IP地址的表现形式 1.1 十进制表示形式 1.2 二进制表示形式 1.3 转换示例介绍 二、IP地址的组成 2.1 网络ID 2.2 主机ID 2.3 示例 三、IP地址的分类 3.1 A类地址 3.2 B类地址 3.3 C类地址 3.4 D类地址 3.5 E类地址 四、常见的特殊IP地址 五、IP地址二进…

群晖Cloud Sync一键同步让数据管理变得简单

前言&#xff1a;在这个数字化爆炸的时代&#xff0c;数据管理和备份已经变得不可或缺。无论是个人用户还是企业&#xff0c;都需要一种既高效又可靠的方式来管理和备份分散在各种设备和云存储中的文件。而群晖的 **Cloud Sync** 套件正是为了解决这个问题而生。 Cloud Sync 是…

前缀树介绍

数风流人物&#xff0c;还看今朝&#xff01; 前缀树 Trie&#xff08;发音类似 "try"&#xff09;或者说 前缀树 是一种树形数据结构&#xff0c;用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景&#xff0c;例如自动补全和拼写检查。 前…

kkfileview代理配置,Vue对接kkfileview实现图片word、excel、pdf预览

kkfileview部署 官网&#xff1a;https://kkfileview.keking.cn/zh-cn/docs/production.html 这个是官网部署网址&#xff0c;这里推荐大家使用docker镜像部署&#xff0c;因为我是直接找运维部署的&#xff0c;所以这里我就不多说明了&#xff0c;主要说下nginx代理配置&am…

【数据可视化案列】白葡萄酒质量数据的EDA可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

数据结构之栈,队列,树

目录 一.栈 1.栈的概念及结构 2.栈的实现 3.实现讲解 1.初始化栈 2.销毁栈 3.压栈 4.出栈 5.返回栈顶元素 6.返回栈内元素个数 7.判断栈内是否为空 二.队列 1.队列的概念及结构 2.队列的实现 3.实现讲解 1.初始化队列 2.销毁队列 3.单个成员入队列 4.单个成员…