音视频入门基础:FLV专题(23)——FFmpeg源码中,获取FLV文件音频信息的实现(下)

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

音视频入门基础:FLV专题系列文章:

音视频入门基础:FLV专题(1)——FLV官方文档下载

音视频入门基础:FLV专题(2)——使用FFmpeg命令生成flv文件

音视频入门基础:FLV专题(3)——FLV header简介

音视频入门基础:FLV专题(4)——使用flvAnalyser工具分析FLV文件

音视频入门基础:FLV专题(5)——FFmpeg源码中,判断某文件是否为FLV文件的实现

音视频入门基础:FLV专题(6)——FFmpeg源码中,解码FLV header的实现

音视频入门基础:FLV专题(7)——Tag header简介

音视频入门基础:FLV专题(8)——FFmpeg源码中,解码Tag header的实现

音视频入门基础:FLV专题(9)——Script Tag简介

音视频入门基础:FLV专题(10)——Script Tag实例分析

音视频入门基础:FLV专题(11)——FFmpeg源码中,解析SCRIPTDATASTRING类型的ScriptDataValue的实现

音视频入门基础:FLV专题(12)——FFmpeg源码中,解析DOUBLE类型的ScriptDataValue的实现

音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现

音视频入门基础:FLV专题(14)——FFmpeg源码中,解码Script Tag的实现

音视频入门基础:FLV专题(15)——Video Tag简介

音视频入门基础:FLV专题(16)——FFmpeg源码中,解码Video Tag的VideoTagHeader的实现

音视频入门基础:FLV专题(17)——FFmpeg源码中,提取Video Tag的VIDEODATA的实现

音视频入门基础:FLV专题(18)——Audio Tag简介

音视频入门基础:FLV专题(19)——FFmpeg源码中,解码Audio Tag的AudioTagHeader,并提取AUDIODATA的实现

音视频入门基础:FLV专题(20)——FFmpeg源码中,获取FLV文件major_brand、minor_version、compatible_brands、encoder、Duration的实现

音视频入门基础:FLV专题(21)——FFmpeg源码中,获取FLV文件音频信息的实现(上)

音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)

音视频入门基础:FLV专题(23)——FFmpeg源码中,获取FLV文件音频信息的实现(下)

音视频入门基础:FLV专题(24)——FFmpeg源码中,获取FLV文件视频信息的实现

音视频入门基础:FLV专题(25)——通过FFprobe显示FLV文件每个packet的信息

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

本文接着《音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)》,继续讲解FFmpeg获取FLV文件的音频信息到底是从哪个地方获取的。本文的一级标题从“七”开始。

七、Bit depth

FLV文件中每个Audio Tag的AudioTagHeader的SoundSize属性都包含Bit depth(又叫位深度、位元深度、采样深度、采样位数、采样格式)信息。但是如果FLV文件中的音频压缩编码格式为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;
//...
}

所以如果FLV文件中的音频压缩编码格式为AAC,通过“ffmpeg -i video1.flv命令”获取到的音频采样格式固定为fltp,该值没有意义:

八、音频码率

FFmpeg获取音频码率是通过解析名称为“onMetadata”的Script Tag获取的。onMetadata中存在一个audiodatarate属性表示音频码率,单位为kilobits per second,即Kbps:

由《音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现》、《音视频入门基础:FLV专题(14)——FFmpeg源码中,解码Script Tag的实现》可以知道,FFmpeg源码通过amf_parse_object函数中的下面代码块将名称为“onMetadata”的Script Tag中audiodatarate属性解析出来,乘以1024,得到单位为bps的音频码率,存到flv->audio_bit_rate中,即存到(FLVContext *)(s->priv_data)->audio_bit_rate中:

static int amf_parse_object(AVFormatContext *s, AVStream *astream,AVStream *vstream, const char *key,int64_t max_pos, int depth)
{
//...if (key) {//...if (depth == 1) {if (amf_type == AMF_DATA_TYPE_NUMBER ||amf_type == AMF_DATA_TYPE_BOOL) {//...else if (!strcmp(key, "audiodatarate") &&0 <= (int)(num_val * 1024.0))flv->audio_bit_rate = num_val * 1024.0;//...}//...}}
}

然后通过libavformat/flvdec.c中的create_stream函数,将flv->audio_bit_rate赋值给AVCodecParameters的bit_rate。st->codecpar为指向一个AVCodecParameters类型变量的指针:

static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
//...if (codec_type == AVMEDIA_TYPE_AUDIO) {st->codecpar->bit_rate = flv->audio_bit_rate;flv->missing_streams &= ~FLV_HEADER_FLAG_HASAUDIO;}
//...
}

通过avcodec_parameters_to_context函数将AVCodecParameters的bit_rate赋值给AVCodecContext的bit_rate:

int avcodec_parameters_to_context(AVCodecContext *codec,const AVCodecParameters *par)
{
//...codec->bit_rate              = par->bit_rate;
//...
}

然后在dump_stream_format函数中,通过avcodec_string函数中的语句:bitrate = get_bit_rate(enc)拿到AVCodecContext的bit_rate,该bit_rate以bps为单位。最后再把它除以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);
//...
}

所以通过flvAnalyser工具查看到的音频码率跟使用FFmpeg命令查看到的音频码率不一样,这是因为FFmpeg源码中获取onMetadata中的audiodatarate属性是先把它乘以1024,但是打印的时候再除以1000的(正常情况下打印的时候应该除以1024才对)。比如实际的音频码率为133.6865kb/s,乘以1024就是136895b/s,再除以1000就变成了136kb/s。所以FFmpeg(截止FFmpeg7.0.1)打印音频码率这部分代码应该是有bug的,导致显示的音频码率不准确:

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Python练习13

Python日常练习 题目&#xff1a; 请编写fun函数&#xff0c;其功能是打印杨辉三角形。杨辉三角行如图所示&#xff1a; 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 要求&#xff1a; 采用列表函数完成 -----------------------------------…

Redis - 持久化

Redis ⽀持RDB和AOF两种持久化机制&#xff0c;持久化功能有效地避免因进程退出造成数据丢失问题&#xff0c; 当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。本章内容&#xff1a; 介绍RDB、AOF的配置和运⾏流程&#xff0c;以及控制持久化的命令&#xff0c;如bgsave和…

Vivado+Vscode联合打造verilog环境

一、Vivado下载安装 详细参考我另一篇文章&#xff1a; Vivado2022.2下载安装_fpga vivado下载-CSDN博客https://blog.csdn.net/weixin_61081689/article/details/143460790?spm1001.2014.3001.5501 二、Vscode下载安装 详细参考我另一篇文章&#xff1a; VscodeAnacond…

项目实战使用gitee

1.创建本地仓库 2.进行提交到本地仓库 创建仓库后在idea中会显示图标&#xff0c;点击绿色的√进行快速提交 3.绑定远程仓库 4.番外篇-创建gitee仓库 注意不要勾选其他

Golang | Leetcode Golang题解之第551题学生出勤记录I

题目&#xff1a; 题解&#xff1a; func checkRecord(s string) bool {absents, lates : 0, 0for _, ch : range s {if ch A {absentsif absents > 2 {return false}}if ch L {latesif lates > 3 {return false}} else {lates 0}}return true }

派(分治法)

题目&#xff1a;蒜头君的生日要到了&#xff01;根据习俗&#xff0c;他需要将一些派分给大家。他有 N个不同口味、不同大小的派。 有 F个朋友会来参加我的派对&#xff0c;每个人会拿到一块派&#xff08;必须一个派的一块&#xff0c;不能由几个派的小块拼成&#xff1b;可以…

Docker + Python

文章目录 一、Docker Hub - 使用搜索 Python二、Python Image 使用1、如何使用此 Image在 Python 应用项目中 创建`Dockerfile`文件运行单个 Python 脚本镜像中的多个 Python 版本2、镜像变体1、`python:<version>`2、`python:<version>-slim`3、`python:<versi…

虚拟机linux7.9下安装mysql遇到的问题

1.提示文件权限不够 解决&#xff1a;chmod -R 777 /usr/local/mysql/ 2.提示硬盘空间不够&#xff0c;mysql初始化失败 解决&#xff1a;更改/etc/my.cnf&#xff0c;将日志文件大小减少&#xff08;innodb_log_file_size&#xff09;&#xff0c;删除/data/mysql目录下的文…

GPU集群上分布式训练大模型

总结一下如何在超算系统上进行预训练大模型的分布式训练 / 微调&#xff0c;文中代码已上传至 github 实验环境 集群1&#xff1a;国家广州超算 星逸A800智能AI集群 GPU&#xff1a;8 * Nvdia Tesla-A800 80G显存 CPU&#xff1a;2 * 28核 Intel Xeon Gold 6348 内存&#xff…

读取excel并且显示进度条

读取excel并且显示进度条 通过C#实现DataGridView加载EXCEL文件&#xff0c;但加载时不能阻塞UI刷新线程&#xff0c;且向UI显示加载进度条。 #region 左上角导入 private async void ToolStripMenuItem_ClickAsync(object sender, EventArgs e) { …

Java | Leetcode Java题解之第552题学生出勤记录II

题目&#xff1a; 题解&#xff1a; class Solution {static final int MOD 1000000007;public int checkRecord(int n) {long[][] mat {{1, 1, 0, 1, 0, 0},{1, 0, 1, 1, 0, 0},{1, 0, 0, 1, 0, 0},{0, 0, 0, 1, 1, 0},{0, 0, 0, 1, 0, 1},{0, 0, 0, 1, 0, 0}};long[][] re…

Oracle OCP认证考试考点详解082系列16

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 76. 第76题&#xff1a; 题目 解析及答案&#xff1a; 以下哪三项活动会被记录在数据库的警报日志中&#xff1f; A. 块损坏错误 数据库…

103、Python并发编程:使用信号量Semaphore实现资源有限的并发场景

引言 在前面几篇文章的基础上&#xff0c;应对并发编程中现成同步的需求场景&#xff1a; 我们可以使用锁&#xff0c;作为多线程同步的几个核心基础&#xff0c;实现对临界资源的保护&#xff0c;确保满足基本的互斥访问逻辑。 使用条件变量Condition&#xff0c;实现有固定…

蛋奶烙饼:美味与温暖的邂逅

食家巷蛋奶烙饼&#xff0c;那金黄的色泽、浓郁的奶香和蛋香&#xff0c;光是看着就让人垂涎欲滴。它的制作过程并不复杂&#xff0c;却充满了生活的烟火气。将面粉、鸡蛋、牛奶等简单的食材混合在一起&#xff0c;搅拌成细腻的面糊。在平底锅中倒入少许油&#xff0c;舀一勺面…

Linux内核中IRQ Domain的结构、操作及映射机制详解

往期内容 本专栏往期内容&#xff0c;interrtupr子系统&#xff1a; 深入解析Linux内核中断管理&#xff1a;从IRQ描述符到irq domain的设计与实现 pinctrl和gpio子系统专栏&#xff1a; 专栏地址&#xff1a;pinctrl和gpio子系统 编写虚拟的GPIO控制器的驱动程序&#xff1a;…

cocos creator 3.8.3物理组件分组的坑

坑&#xff0c;坑的不行的大坑 group用的二进制的左移获取十进制的数值 目前是这样判断的&#xff0c;也不知道对不对&#xff0c;什么get、set Group没找到

如何解决“在ANACONDA prompt可以使用‘conda activate‘,但是在pycharm终端没办法使用该指令“”

一、设置好环境变量 此电脑&#xff08;右键&#xff09;-属性-高级系统设置-环境变量-在系统变量那一筐的PATH双击添加 二、完成后再测试conda activate 笔者测试完后&#xff0c;conda是可以用了&#xff0c;但是conda activate用不了。 显示该错误CommandNotFoundError:…

三菱MR-J4-B系列伺服参数一览

要点 与伺服系统控制器连接后&#xff0c;同服系统控制器的伺服参数的值即被写入各参数中。根据伺服系统控制器的机种和伺服放大器软件版本及MRConfigurator2的软件版本&#xff0c;存在无法设定的参数或范围。详细内容请参照伺服系统控制器的用户手册。请使用MR Configurator2…

Pattern program MPAT 详解

本文为VIP文章,主要介绍Pattern中元素与格式、常用指令、地址&数据产生指令等。 目录 一、pattern概述 二:Pattern构成元素 1、pattern构成元素:MPAT、END 2、pattern构成元素:pattern file name 3、pattern构成元素:SDEF 4、Pattern构成元素:REGISETR 5、Pa…

Qt编译lua库并调用

参考博客&#xff1a; 编译lua库 参考下面文章编译lua库文件 QT5.9学习笔记之QT编译lua库_qtluaintf.h-CSDN博客 https://blog.csdn.net/qq_23345187/article/details/112710677 Qt代码引用lua库文件 打开pro项目文件&#xff0c;右键空白处&#xff0c;点击添加库&#xff…