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

一、引言

通过FFmpeg命令:

./ffmpeg -i XXX.aac

可以判断出某个文件是否为AAC裸流文件:

所以FFmpeg是怎样判断出某个文件是否为AAC裸流文件呢?它内部其实是通过adts_aac_probe函数来判断的。从《FFmpeg源码:av_probe_input_format3函数和AVInputFormat结构体分析(FFmpeg源码5.0.3版本)》和《7.0.1版本的FFmpeg源码中av_probe_input_format3函数和AVInputFormat结构体的改变》中我们可以知道:

FFmpeg源码中实现容器格式检测的函数是av_probe_input_format3函数,其内部通过循环while ((fmt1 = av_demuxer_iterate(&i))) 拿到所有容器格式对应的AVInputFormat结构,然后通过score = fmt1->read_probe(&lpd)语句执行不同容器格式对应的解析函数,根据是否能被解析,以及匹配程度,来判断出这是哪种容器格式。而AAC裸流文件对应的解析函数就是adts_aac_probe函数。

二、adts_aac_probe函数的定义

adts_aac_probe函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/aacdec.c中:

static int adts_aac_probe(const AVProbeData *p)
{int max_frames = 0, first_frames = 0;int fsize, frames;const uint8_t *buf0 = p->buf;const uint8_t *buf2;const uint8_t *buf;const uint8_t *end = buf0 + p->buf_size - 7;buf = buf0;for (; buf < end; buf = buf2 + 1) {buf2 = buf;for (frames = 0; buf2 < end; frames++) {uint32_t header = AV_RB16(buf2);if ((header & 0xFFF6) != 0xFFF0) {if (buf != buf0) {// Found something that isn't an ADTS header, starting// from a position other than the start of the buffer.// Discard the count we've accumulated so far since it// probably was a false positive.frames = 0;}break;}fsize = (AV_RB32(buf2 + 3) >> 13) & 0x1FFF;if (fsize < 7)break;fsize = FFMIN(fsize, end - buf2);buf2 += fsize;}max_frames = FFMAX(max_frames, frames);if (buf == buf0)first_frames = frames;}if (first_frames >= 3)return AVPROBE_SCORE_EXTENSION + 1;else if (max_frames > 100)return AVPROBE_SCORE_EXTENSION;else if (max_frames >= 3)return AVPROBE_SCORE_EXTENSION / 2;else if (first_frames >= 1)return 1;elsereturn 0;
}

其作用就是检测某个文件是否为AAC裸流文件。由于通过FFmpeg命令(通过《音视频入门基础:AAC专题(2)——使用FFmpeg命令生成AAC裸流文件》)生成的AAC裸流文件都是ADTS格式的,所以adts_aac_probe函数只能用于检测某个文件是否为ADTS格式的AAC裸流,不能用于检测是否为AAC的ADIF格式。

形参pd:输入型参数,为AVProbeData类型的指针。

AVProbeData结构体声明在libavformat/avformat.h中:

/*** This structure contains the data a format has to probe a file.*/
typedef struct AVProbeData {const char *filename;unsigned char *buf; /**< Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero. */int buf_size;       /**< Size of buf except extra allocated bytes */const char *mime_type; /**< mime_type, when known. */
} AVProbeData;

p->filename为:需要被推测格式的文件的路径。

p->buf:指向“存放从路径为p->filename的文件(AAC裸流文件)中读取出来的二进制数据”的缓冲区。

p->buf_size:缓冲区p->buf的大小,单位为字节。注:FFmpeg判断某个文件的格式时不会读取完整个文件,只会读取它前面的一部分,比如最开始的2048个字节。只要根据前面的这些字节就足够判断出它的格式了,所以p->buf_size的值一般就是2048。

p->mime_type:一般为NULL,可忽略。

返回值:返回一个类型为整形的分值。返回0表示该文件完全不符合AAC的ADTS格式。返回一个大于0的值表示该文件比较符合AAC的ADTS格式,但还需要在av_probe_input_format3函数中执行其它容器格式对应的解析函数来进行对比,最终通过最高分来确定到底是哪种容器格式。

三、adts_aac_probe函数的内部实现原理

adts_aac_probe函数内部,首先定义局部变量fsize来记录某个ADTS音频帧的长度;定义局部变量frames记录该AAC裸流文件前2048个字节(因为p->buf_size的值一般就是2048)中的有效音频帧的个数:

int fsize, frames;

让指针buf2指向“AAC裸流文件二进制数据”的开头,也就是第一个ADTS音频帧的adts_fixed_header:

    for (; buf < end; buf = buf2 + 1) {buf2 = buf;

按照大端模式读取第一个ADTS音频帧的前2个字节,赋值给变量header。关于AV_RB16宏定义的用法可以参考:《FFmpeg源码:AV_RB32、AV_RB16、AV_RB8宏定义分析》:

uint32_t header = AV_RB16(buf2);

由《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》可以知道,ADTS音频帧的adts_fixed_header中的syncword属性占12位,每个位都必须被设置为1;layer属性占2位,必须被设置为0。所以通过下面代码块判断syncword和layer属性的值是否正确。如果表达式:header & 0xFFF6) != 0xFFF0为真,表示这两个属性的值不正确,即表示ADTS Header格式不正确,让变量frames的值归0,表示有效音频帧的个数归0:

            if ((header & 0xFFF6) != 0xFFF0) {if (buf != buf0) {// Found something that isn't an ADTS header, starting// from a position other than the start of the buffer.// Discard the count we've accumulated so far since it// probably was a false positive.frames = 0;}break;}

获取adts_variable_header中的aac_frame_length属性,即该ADTS音频帧的总长度(包含ADTS Header、错误校验和AAC原始数据块,单位为字节)。赋值给变量fsize:

fsize = (AV_RB32(buf2 + 3) >> 13) & 0x1FFF;

由《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》可以知道,ADTS Header至少占7个字节(当存在CRC校验时,ADTS Header占9字节;不存在CRC校验时,ADTS Header占7字节),所以如果从上面得到的该ADTS音频帧的总长度小于7,表示ADTS Header格式不正确,通过break关键字跳出循环:

            if (fsize < 7)break;

让指针buf2指向下一个ADTS音频帧的adts_fixed_header:

buf2 += fsize;

如果该音频帧的ADTS Header格式正确,让frames的值(有效音频帧的个数)加1。执行for循环,继续判断下一个ADTS音频帧的Header的格式是否正确:

for (frames = 0; buf2 < end; frames++) {

buf等于buf0,意味着读取到ADTS音频帧的Header的格式都是正确的,让first_frames的值等于frames:

        max_frames = FFMAX(max_frames, frames);if (buf == buf0)first_frames = frames;

如果该AAC裸流文件前2048个字节中的有效音频帧的个数不小于3个,adts_aac_probe函数返回AVPROBE_SCORE_EXTENSION + 1(也就是返回51分),意味着该文件比较符合AAC的ADTS格式:

    if (first_frames >= 3)return AVPROBE_SCORE_EXTENSION + 1;

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

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

相关文章

TCP socket

TCP的socket和UDP大同小异&#xff0c;基本的代码结构都是相同的。一些相同的接口本文就不赘述了&#xff0c;例如&#xff0c;socket,bind&#xff0c;有需要看这篇文章UDP socket 服务端server 两步&#xff1a;初始化服务端&#xff0c;运行服务端 初始化服务端 创建soc…

物品识别——基于python语言

目录 1.物品识别 2.模型介绍 3.文件框架 4.代码示例 4.1 camera.py 4.2 interaction.py 4.3 object_detection.py 4.4 main.py 4.5 运行结果 5.总结 1.物品识别 该项目使用Python&#xff0c;OpenCV进行图像捕捉&#xff0c;进行物品识别。我们将使用YOLO&#xff08…

人工智能——猴子摘香蕉问题

一、实验目的 求解猴子摘香蕉问题&#xff0c;根据猴子不同的位置&#xff0c;求解猴子的移动范围&#xff0c;求解对应的过程&#xff0c;针对不同的目标状态进行求解。 二、实验内容 根据场景有猴子、箱子、香蕉&#xff0c;香蕉挂天花板上。定义多种谓词描述位置、状态等…

Vue生命周期;Vue路由配置;vue网络请求;vue跨域处理

一&#xff0c;Vue生命周期 <template><div > <h1 click"changeText">{{ info }}</h1></div> </template><script> export default {name: HelloWorld,data(){return{info:"介绍组件生命周期"}},methods:{chang…

CenterNet官方代码—目标检测模型推理部分

CenterNet模型推理部分解析 CenterNet官方代码环境部署 CenterNet作为2019年CVPR推出的论文&#xff0c;论文中给出了官方代码所在的github仓库地址。https://github.com/xingyizhou/CenterNet。 整个代码的代码量并不是特别大&#xff0c;但整个项目的难点在于使用了老版本的…

Djourney新手入门基础,AI摄影+AI设计+AI绘画-AIGC作图

人工智能技术的飞速发展&#xff0c;AI正逐渐渗透进创意领域&#xff0c;特别是在摄影、设计和绘画方面&#xff0c;AIGC&#xff08;Artificial Intelligence for Generative Content&#xff09;技术正在重塑我们的创作方式。本文将深入探讨Djourney这款创新工具&#xff0c;…

XML_Tomcat_HTTP

第四章 XML_Tomcat10_HTTP 一 XML XML是EXtensible Markup Language的缩写&#xff0c;翻译过来就是可扩展标记语言。所以很明显&#xff0c;XML和HTML一样都是标记语言&#xff0c;也就是说它们的基本语法都是标签。 可扩展 三个字表面上的意思是XML允许自定义格式。但这不代…

推荐这款神器:Perplexity

今天推荐是一款AI搜索引擎&#xff0c;还支持gpt-4模型的使用&#xff0c;虽然4小时只能使用5次&#xff0c;但是相比于常规的搜索引擎&#xff0c;在某些方面还是很强的&#xff0c;个人感觉优于newbing。 页面简洁&#xff0c;没有广告&#xff0c;内容丰富&#xff0c;功能…

LinkedHashMap 如何实现排序

目录 一、LinkedHashMap二、排序实现三、代码片段分析 一、LinkedHashMap LinkedHashMap 是 Java 中的一个集合类&#xff0c;它是 HashMap 的一个子类&#xff0c;继承了 HashMap 的所有特性&#xff0c;并且在此基础上增加了一个双向链表来维护元素的插入顺序或者访问顺序。L…

Day26_0.1基础学习MATLAB学习小技巧总结(26)——数据插值

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 参考书目&#xff1a; 1、《MATLAB基础教程 (第三版) (薛山)》 2、《MATL…

C++ STL中sort函数

STL的sort算法&#xff0c;数据量大时采用QuickSort快排算法&#xff0c;分段归并排序。一旦分段后的数据量小于某个门槛&#xff08;16&#xff09;&#xff0c;为避免QuickSort快排的递归调用带来过大的额外负荷&#xff0c;就改用Insertion Sort插入排序。如果递归层次过深&…

C++入门基础知识69(高级)——【关于C++ 动态内存】

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于C 动态内存的相关内容&#xff01; 目录…

[产品管理-19]:NPDP新产品开发 - 17 - 产品设计与开发工具 - 实体化设计工具:联合分析、功能分析、FAST技术图和逆向工程

目录 前言&#xff1a; 一、什么是实体化设计 1.1 什么是实体化设计 1、定义与概述 2、设计流程 3、关键要素 4、应用领域 5、举例说明 1.2 实体化设计与概念设计的区别 实体化设计 概念设计 区别归纳 1.3 实体化设计与初步设计、规格设计的区别 1、定义与目的 …

51单片机-LCD1602(液晶显示屏)- 写驱动

时间永远是检验真理唯一标准&#xff01;Whappy&#xff01; 主要简单写出几个驱动 初始化、显示字符、显示字符串、显示整形数据、有符号数据、十六进制、二进制&#xff01; void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); vo…

平安养老险阜阳中心支公司开展金融教育宣传专项活动

为全面深入开展“金融教育宣传月”的各项工作&#xff0c;不断完善金融惠民利民举措&#xff0c;提升金融服务质效&#xff0c;帮助基层群众增强维权意识、防非反诈的自我保护能力。近日&#xff0c;平安养老保险股份有限公司&#xff08;以下“平安养老险”&#xff09;阜阳中…

【Node.js】RabbitMQ 延时消息

概述 在 RabbitMQ 中实现延迟消息通常需要借助插件&#xff08;如 RabbitMQ 延迟队列插件&#xff09;&#xff0c;因为 RabbitMQ 本身不原生支持延迟消息。 延迟消息的一个典型场景是&#xff0c;当消息发布到队列后&#xff0c;等待一段时间再由消费者消费。这可以通过配置…

带你0到1之QT编程:十一、掌握Containers容器艺术,一网打尽开发利器

此为QT编程的第十一谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …

npm install报错,gyp verb `which` failed Error: not found: python

主要错误 gyp verb which failed Error: not found: python2 gyp ERR! configure error gyp ERR! stack Error: Cant find Python executable "python", you can set the PYTHON env variable. npm ERR! node-sass4.14.1 postinstall: node scripts/build.js 全部错…

几分钟学会搭建一个自己的外卖霸王餐系统

大家好&#xff0c;我是鲸天科技千千&#xff0c;大家都知道我是做小程序开发的&#xff0c;平时会给大家分享一些互联网相关的创业项目&#xff0c;感兴趣的可以跟我关注一下。 搭建一个首先就是要搭建一个自己的霸王餐小程序&#xff0c;我们自己的工作就是把这个小程序推广…

合肥鲸天科技的外卖会员卡系统有人做过吗?赚钱吗?

我们先来了解一下这个合肥鲸天科技&#xff0c;通过我在网上找到的资料和企业查询&#xff0c;这家公司还是很有实力的&#xff0c;合肥鲸天科技有限公司也是欢迎有合作的人到公司来进行一个考察和合作其他一些项目的。 外卖会员卡简介绍&#xff1a; 这个外卖会员卡&#xf…