当前位置: 首页 > news >正文

音视频入门基础:AAC专题(7)——FFmpeg源码中计算AAC裸流每个packet的size值的实现

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

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

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

一、引言

通过FFprobe命令:

ffprobe -of json -show_packets XXX.aac

可以显示AAC裸流每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的size:

这个“size”实际是AVPacket结构体中的成员变量size,为AVPacket的成员变量data指向的缓冲区的大小,也就是AAC裸流中某个packet的大小(单位为字节)。该值通过fftools/ffprobe.c中的show_packet函数打印出来:

static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int packet_idx)
{
//...print_val("size",             pkt->size, unit_byte_str);
//...
}

本文讲述这个“size”值是怎样被计算出来的。如果想直接看结论,可以跳到本文的最后,直接看“总结”。对于AAC裸流,size值是通过adts_aac_read_packet函数计算出来的。

二、adts_aac_read_packet函数的定义

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

static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt)
{int ret, fsize;retry:ret = av_get_packet(s->pb, pkt, ADTS_HEADER_SIZE);if (ret < 0)return ret;if (ret < ADTS_HEADER_SIZE) {return AVERROR(EIO);}if ((AV_RB16(pkt->data) >> 4) != 0xfff) {// Parse all the ID3 headers between framesint append = ID3v2_HEADER_SIZE - ADTS_HEADER_SIZE;av_assert2(append > 0);ret = av_append_packet(s->pb, pkt, append);if (ret != append) {return AVERROR(EIO);}if (!ff_id3v2_match(pkt->data, ID3v2_DEFAULT_MAGIC)) {av_packet_unref(pkt);ret = adts_aac_resync(s);} elseret = handle_id3(s, pkt);if (ret < 0)return ret;goto retry;}fsize = (AV_RB32(pkt->data + 3) >> 13) & 0x1FFF;if (fsize < ADTS_HEADER_SIZE) {return AVERROR_INVALIDDATA;}ret = av_append_packet(s->pb, pkt, fsize - pkt->size);return ret;
}

该函数的作用是:获取一个ADTS音频帧的数据,赋值给形参pkt指向的packet。

形参s:既是输入型参数也是输出型参数,指向一个AVFormatContext对象。

形参pkt:输出型参数,指向一个AVPacket对象。执行adts_aac_read_packet函数后,pkt->data指向的的缓冲区会得到整个ADTS音频帧的数据,pkt->size会增至该ADTS音频帧的大小。

三、adts_aac_read_packet函数的内部实现分析

宏定义ADTS_HEADER_SIZE的值为7,表示不包含CRC校验的ADTS Header的长度:

#define ADTS_HEADER_SIZE 7

adts_aac_read_packet函数内部,首先通过av_get_packet函数从AVIOContext输入缓冲区或文件描述符中总共读取ADTS_HEADER_SIZE(7)个字节数据,也就是读取该音频帧的Header,追加到原来pkt->data指向的的缓冲区的尾部。关于av_get_packet函数的用法可以参考 《FFmpeg源码:append_packet_chunked、av_get_packet、av_append_packet函数分析》。如果实际读取到的大小小于7个字节,返回AVERROR(EIO)表示IO错误:

    ret = av_get_packet(s->pb, pkt, ADTS_HEADER_SIZE);if (ret < 0)return ret;if (ret < ADTS_HEADER_SIZE) {return AVERROR(EIO);}

由《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》可以知道,ADTS音频帧的adts_fixed_header中的syncword属性占12位,每个位都必须被设置为1。通过下面的if语句判断syncword属性的值是否正确,如果不正确,执行大括号内解析id3v2 header的流程。ID3v2是一系列元数据,里面存储了一些跟歌曲相关的信息(比如:演唱者、歌曲名、备注等)。关于AV_RB16宏定义的用法可以参考:《FFmpeg源码:AV_RB32、AV_RB16、AV_RB8宏定义分析》:

    if ((AV_RB16(pkt->data) >> 4) != 0xfff) {// Parse all the ID3 headers between frames//...}

获取adts_variable_header中的aac_frame_length属性,即该ADTS音频帧的总长度(包含ADTS Header、错误校验和AAC原始数据块,单位为字节),赋值给变量fsize。由《音视频入门基础:AAC专题(3)——AAC的ADTS格式简介》可以知道,ADTS Header至少占7个字节(当存在CRC校验时,ADTS Header占9字节;不存在CRC校验时,ADTS Header占7字节),所以如果从上面得到的该ADTS音频帧的总长度小于7,表示ADTS Header格式不正确,返回AVERROR_INVALIDDATA:

    fsize = (AV_RB32(pkt->data + 3) >> 13) & 0x1FFF;if (fsize < ADTS_HEADER_SIZE) {return AVERROR_INVALIDDATA;}

通过av_append_packet函数,增加形参pkt指向的packet的大小至aac_frame_length个字节,让pkt->data指向的的缓冲区得到整个ADTS音频帧的数据。关于av_append_packet函数的用法可以参考:《FFmpeg源码:append_packet_chunked、av_get_packet、av_append_packet函数分析》:

ret = av_append_packet(s->pb, pkt, fsize - pkt->size);

四、总结

1.AAC裸流每个packet的size值(该packet的大小,单位为字节)实际上是通过ADTS音频帧(adts音频压缩数据包)的adts_fixed_header中的aac_frame_length属性获取的。

2.如果仔细地观察,会发现AAC裸流每个packet的size值都不一样,这是因为ADTS音频帧存贮的是压缩后的音频数据。而WAV音频文件一般存贮的是PCM,也就是无压缩的原始音频数据,所以在存贮的是PCM数据的情况下,WAV音频文件每个packet的size值都是一样的。各位同学可以把本文跟《音视频入门基础:WAV专题(7)——FFmpeg源码中计算WAV音频文件每个packet的size值的实现》进行对比,以加深对音频帧size值的理解。

3.FFmpeg源码内部得到AAC裸流每个packet的size值是通过adts_aac_read_packet函数,但是解码ADTS Header获取里面信息(音频采样频率、声道数、采样位数等)是通过ff_adts_header_parse函数,关于ff_adts_header_parse函数可以参考:《音视频入门基础:AAC专题(6)——FFmpeg源码中解码ADTS格式的AAC的Header的实现》。可以看到这两个函数有部分功能是重复了,重复获取了ADTS音频帧的大小。FFmpeg对AAC裸流解封装时,会重复解码ADTS Header导致性能损失。也就是说:FFmpeg为了架构的通用性、扩展性、兼容性和代码的可读性造成了代码的冗余和重复,所以为了极致的性能,比如更快的解封装速度,很多开发者会选择不依赖任何第三方库自己写解析代码,或者改FFmpeg的源码实现优化。各位同学可以尝试改FFmpeg源码,使得对AAC裸流进行解复用时,只解码一次ADTS Header,从而提高解复用速度。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Python语言初识(二)】
  • 快速响应:提升前端页面加载速度技巧的必知策略方案
  • 【React】React18.2.0核心源码解读
  • 01-Mac OS系统如何下载安装Python解释器
  • AI大模型之旅--milvus向量库安装
  • 【Mysql-索引总结】
  • Centos 7 搭建Samba
  • 主流卷积神经网络CNN总结
  • MySQL5.7中增加的JSON特性的处理方法JSON_EXTRACT和JSON_ARRAY_APPEND以及MYSQL中JSON操作的方法大全
  • 小程序服务零工市场
  • 神经网络 归一化层
  • shell脚本(9.20)
  • 机器翻译之多头注意力(MultiAttentionn)在Seq2Seq的应用
  • 音视频入门基础:AAC专题(4)——ADTS格式的AAC裸流实例分析
  • 大健康裂变分销小程序开发
  • JavaScript-如何实现克隆(clone)函数
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Django 博客开发教程 16 - 统计文章阅读量
  • js
  • PHP 的 SAPI 是个什么东西
  • Spring框架之我见(三)——IOC、AOP
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Web Storage相关
  • 闭包--闭包作用之保存(一)
  • 对象管理器(defineProperty)学习笔记
  • 浮动相关
  • 欢迎参加第二届中国游戏开发者大会
  • 讲清楚之javascript作用域
  • 巧用 TypeScript (一)
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 项目实战-Api的解决方案
  • 小程序 setData 学问多
  • 数据库巡检项
  • ​Redis 实现计数器和限速器的
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #if和#ifdef区别
  • #include
  • #NOIP 2014# day.1 T2 联合权值
  • (31)对象的克隆
  • (solr系列:一)使用tomcat部署solr服务
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (黑马C++)L06 重载与继承
  • (亲测)设​置​m​y​e​c​l​i​p​s​e​打​开​默​认​工​作​空​间...
  • (三分钟)速览传统边缘检测算子
  • (一) springboot详细介绍
  • (转)jQuery 基础
  • (状压dp)uva 10817 Headmaster's Headache
  • (自用)网络编程
  • .apk 成为历史!
  • .NET Core SkiaSharp 替代 System.Drawing.Common 的一些用法
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .NET 分布式技术比较
  • .NET开源的一个小而快并且功能强大的 Windows 动态桌面软件 - DreamScene2