faac内存开销较大,为方便嵌入式设备使用进行优化(valgrind使用)
faac内存开销较大,为方便嵌入式设备使用进行优化,在github上提了issues但是没人理我,所以就搞一份代码自己玩吧。 基于faac_1_30版本,原工程https://github.com/knik0/faac
faac内存优化: faac内存开销较大,为方便嵌入式设备使用进行优化,在github上提了issues但是没人理我,所以就搞一份代码自己玩吧。基于faac_1_30版本,原工程https://github.com/knik0/faac
https://gitee.com/dma/faac-memory-optimization?_from=gitee_search
说明
faac内存开销较大,为方便嵌入式设备使用进行优化,在github上提了issues但是没人理我,所以就搞一份代码自己玩吧。
基于faac_1_30版本,原工程 https://github.com/knik0/faac
文件说明
- faac-1_30.zip 为 faac 源码
- 为了方便我个人使用,删除了 faac 源码中我用不到的文件,只保留 libfaac 目录下的必要文件和 inlcude 目录
- 增加 CMakeLists.txt 编译脚本
内存优化的内容
内存优化测试文件的格式为 PCM int16 小端 双声道 44100Hz
1.1 优化前
使用 valgrind 检查内存,结果如下,可以看到默认配置的内存开销约为 11.5 MB

1.2 修改默认最大声道数
修改 libfaac\coder.h
一般来说,双声道就够用了,如果有5.1声道之类的特殊需求可以自行修改
修改前#define MAX_CHANNELS 64
修改后#define MAX_CHANNELS 2
内存统计如下,约 560 KB

1.3 删除 bwpInfo
修改 libfaac\coder.h
在 faacEncOpen() 中会创建 faacEncStruct* hEncoder; 这个句柄
在 faacEncStruct.coderInfo 中的 bwpInfo 代码中没有用到,不知道作者为什么没有删除,意义不明。
修改前BwpInfo bwpInfo;
修改后// BwpInfo bwpInfo;
内存统计如下,约 240 KB

1.4 删除无效代码
修改 libfaac\frame.c faacEncEncode() 的代码中有这样一段
hEncoder->sampleBuff[channel] = hEncoder->nextSampleBuff[channel]; hEncoder->nextSampleBuff[channel] = hEncoder->next2SampleBuff[channel]; hEncoder->next2SampleBuff[channel] = hEncoder->next3SampleBuff[channel]; hEncoder->next3SampleBuff[channel] = tmp;
申请了4个 sampleBuff,这个函数每调用一次会依次交换这4个 buffer,实际代码中只用到了 sampleBuff 和 next3SampleBuff,不明白作者为什么这样写,也可能是忘了删,这里可以修改为
hEncoder->sampleBuff[channel] = hEncoder->next3SampleBuff[channel]; hEncoder->next3SampleBuff[channel] = tmp;
以下几处记得也要一起修改
faacEncOpen() 中修改为
// hEncoder->nextSampleBuff[channel] = NULL; // hEncoder->next2SampleBuff[channel] = NULL;
faacEncClose() 中修改为
// if (hEncoder->nextSampleBuff[channel]) // FreeMemory(hEncoder->nextSampleBuff[channel]); // if (hEncoder->next2SampleBuff[channel]) // FreeMemory (hEncoder->next2SampleBuff[channel]);
faacEncStruct 中修改为
// double *nextSampleBuff[MAX_CHANNELS]; // double *next2SampleBuff[MAX_CHANNELS];
这样每声道可以节约 16KB 内存
内存统计如下,约 210 KB

1.5 优化数据结构
修改 libfaac\coder.h
这有个前提条件,就是不启用 DRM,这个宏在 libfaac\coder.h,默认就是关闭的//#define DRM
接下来再看代码,CoderInfo 中有这样一个成员
struct {int data;int len;
} s[DATASIZE];
它用来进行哈夫曼编码,查看源码可知,这个结构体只在 huffcode() 函数中赋值,里面的数据来自于哈夫曼编码表,使用的码表为 book01 到 book11,没有用到 book12,这11个码表的成员原型如下
typedef struct {const uint16_t len;const uint16_t data;
} hcode16_t;
因此可以把 int 改成 short,每声道可以节约6KB内存
备注:这样修改其实是错的,但是完全可以正常使用,具体原因见下文
内存统计如下,约 200 KB

1.6 禁用 TNS
修改 libfaac\coder.h, libfaac\bitstream.c
这个修改会影响音质,实测影响很小,音质上的差异要仔细听才能察觉,个人认为这一点音质上的损失完全可以接受。
把 CoderInfo.tnsInfo 成员也去掉,并删除 faacEncEncode() 中 TnsEncode() 的调用以及两处 TnsInit() 调用。
删除 faacEncConfiguration 中的 unsigned int useTns
编译时删除 tns.c
直接将 WriteTNSData() 函数修改如下
static int WriteTNSData(CoderInfo *coderInfo,BitStream *bitStream,int writeFlag)
{int bits = 0;#ifndef DRMif (writeFlag) {PutBit(bitStream, 0, LEN_TNS_PRES);}bits += LEN_TNS_PRES;
#endifreturn bits;
}
内存统计如下,约 170 KB

1.7 修改数据类型
修改所有文件的 double 为 float
理论上来说这个修改会影响音质,实测没听出来,个人认为这个修改应该没问题
每声道内存再节省一半
内存统计如下,大约 90 KB

1.8 其他
以下无关紧要,能优化一点点,这里就不统计了faacEncConfiguration 中 int channel_map[64]; 可以改为 int channel_map[MAX_CHANNELS];faacEncStruct 去掉 double *msSpectrum[MAX_CHANNELS];
优化总结
除去 main() 函数中申请的 buffer,经过以上优化已经可以做到单声道约 70 KB,双声道约 90 KB,这样的内存开销即使放到stm32的部分中高端型号上都能运行,还要啥自行车?
单声道内存统计如下,大约 70 KB

大家如果有更好的优化方法欢迎留言分享
其他问题
Q&A
Q: windows编译报错 #include "win32_ver.h"
A: 这个文件是由configure生成的,目前看似乎没有太大影响,先去掉
关于《1.5 优化数据结构》章节的优化问题
这确实是一个不合理,但碰巧能够正常运行的优化。下面来详细解释一下这个问题。
首先引用一下CSDN上 asd451006071 和 weixin_43957341 这两位网友的留言
asd451006071 2022.10.04 并不是,音频会卡顿,不连续,通过QQ音乐等软件都能听得出来。这是因为huff编码哪里出了问题。我也是查了源码确实huffcode是16位的。但是就是这样。把uint16_t改成int16_t就行了。至于为什么这样就行了。我也感到很好奇。很惊讶。。weixin_43957341 2021.11.06 第六点,所有 double 转 float 这个有点小坑,虽然看起来能播,单独放在 苹果设备 上也能播,但是封装到 MP4 里,在 苹果设备 上就会播放异常,出现如卡视频,音频只有前几秒声音的情况,搞得我一度怀疑是时间戳或者 MP4 库本身的兼容问题asd451006071 2022.10.04 huffcode那个s数组,uint16_t改int16_t就好了。你试试看。。
不知道这两位网友是不是看错了,我前文写的很清楚,把这个结构体中的int改成short,结果他俩都改成unsigned short
struct {int data;int len;
} s[DATASIZE];
于是就会出现音频卡顿的问题,这个和播放器无关,因为就是编码出错了。这份代码我自己也一直再在用,没有任何异常,最近闲下来了,正好研究一下这个问题。
下文都用uint16代替unsigned short,其他数据类似
首先我要承认,这是我的错,这个结构体确实只在 huffcode() 函数中赋值,里面的数据来也确实来自于哈夫曼编码表 book01 到 book11,我最初在做优化时大概看了一眼这几个编码表,以为数值都在 int16 范围内不会溢出,所以大胆地将 int 改成 short 而且也没出问题。但不巧遗漏了 book03 的倒数第7项 {16,65534},这是唯一一个超出 int16 范围的数据,这也是我在重新研究这个问题时才发现的。但这并不是唯一会导致bug的值,但为什么改成int16正常,改成uint16反而异常?下面会结合代码进行分析。
先以 uint16 的情况为例,来看实际在 huffcode() 函数中用到 book03 的这段代码
case 3:case 4:for(ofs = 0; ofs < len; ofs += 4){// 此处省略若干代码else{data = book[idx].data;// add sign bitsfor(cnt = 0; cnt < 4; cnt++){if(qp[cnt]){blen++;data <<= 1;if (qp[cnt] < 0)data |= 1;}}coder->s[datacnt].data = data;coder->s[datacnt++].len = blen;DRMDATA;}bits += blen;}break;
- 假设在
data = book[idx].data;这里读取的是{16,65534},此时data为 65534 - 假设4次循环中只有一次
if(qp[cnt])条件成立,执行blen++; data <<= 1这两句以后,此时data为 131068(0x0001 fffc),blen为 17,这里暂不考虑if (qp[cnt] < 0) - 因为
uint16溢出,这时coder->s[datacnt].data = data;使s[datacnt].data被赋值为 65532(0xfffc)
最终编码时在 WriteSpectralData() 函数中
static int WriteSpectralData(CoderInfo *coderInfo,BitStream *bitStream,int writeFlag)
{int i, bits = 0;if (writeFlag) {for(i = 0; i < coderInfo->datacnt; i++) {int data = coderInfo->s[i].data;int len = coderInfo->s[i].len;if (len > 0) {PutBit(bitStream, data, len);bits += len;}}} else {for(i = 0; i < coderInfo->datacnt; i++) {bits += coderInfo->s[i].len;}}return bits;
}
int data = coderInfo->s[i].data;读取的data为 65532(0x0000 fffc),len为17PutBit(bitStream, data, len);将 17 位数据写入文件,即写入的二进制数据为 0 1111 1111 1111 1100,注意这里写入的最高位是0- 而实际上应该写入的二进制数据为 1 1111 1111 1111 1100,即131068(0x1fffc),也就是由于溢出的原因是的最高位从1变成了0,进而导致音频播放出错
接下来以 int16 的情况再看一遍这些代码的执行结果
- 假设在
data = book[idx].data;这里读取的是{16,65534},此时data为 65534 - 假设4次循环中只有一次
if(qp[cnt])条件成立,执行blen++; data <<= 1这两句以后,此时data为 131068(0x0001 fffc),blen为 17,这里暂不考虑if (qp[cnt] < 0) - 因为
int16溢出,这时coder->s[datacnt].data = data;使s[datacnt].data被赋值为 -4(0xfffc)。 - 在
WriteSpectralData()函数中 int data = coderInfo->s[i].data;读取的data为 -4(0xffff fffc),len为17。注意!这里是重点!因为它是有符号数,高位全部被置为1PutBit(bitStream, data, len);将 17 位数据写入文件,即写入的二进制数据为 1 1111 1111 1111 1100,正好将正确的数值写了进去!
所以,真正会出问题的哈夫曼编码不止 {16,65534},假设循环执行了4次,也就是放大了16倍,那么凡是大于4096的像 {13,8188} 这样的编码都会出错。另外一个可以使它正常工作的巧合在于查看 huffdata.c 中的编码表会发现,所有数值都是接近2的n次方的数值,对于大于4096的数来说这些数的高4位都是1,使得它即使左移4位,超出16位以上的部分仍然是1,进而在之后转换为有符号数时不会出现该是0的位被补为1,保证了数值的正确。例如8188(0x1ffc),左移4位得131068(0x1fffc),int16溢出后为-4(0xfffc),再赋值给int32为-4(0xffff fffc),丝毫不影响。假设出现 4097(0x1001)这样的数,左移4位得65552(0x10010),int16溢出后为16(0x0010),再赋值给int32为16(0x0000 0010),数据又会出现错误!
到此为止整个问题分析完毕。一个不合理的优化在两种巧合的共同作用下让它完美运行。
最后再次感谢 asd451006071 和 weixin_43957341 这两位网友的留言!
相关文章:
faac内存开销较大,为方便嵌入式设备使用进行优化(valgrind使用)
faac内存开销较大,为方便嵌入式设备使用进行优化,在github上提了issues但是没人理我,所以就搞一份代码自己玩吧。 基于faac_1_30版本,原工程https://github.com/knik0/faac faac内存优化: faac内存开销较大,为方便嵌入…...
分数线划定(c++题解)
题目描述 世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A 市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的 150% 划定,即如果计划录取 m 名志愿者…...
React 在 html 中 CDN 引入(包含 antd、axios ....)
一、简介 cdn 获取推荐 https://unpkg.com,unpkg 是一个快速的全球内容交付网络,适用于 npm 上所有内容。 【必备】react 相关 cdn。附:github 官方文档获取、现阶段官方文档 CDN 网址。 <script crossorigin src"https://unpkg.com…...
数据结构----异或
数据结构----异或 一.何处用到了异或 1. 运算符 //判断是否相同 用到了异或,看异或结果如果是0就是相同,不是0就是不同//注意: 不能给小数用,小数没有相等的概念,所以小数判断是否相同都是进行相减判断2.找一堆数中…...
PHP Smarty模板的语法规则是怎样的?
首先,你要知道Smarty模板是以模板格式来编写的。模板格式类似于HTML,但它的语法更加简洁明了。 以下是PHP Smarty模板的语法规则和代码例子: 变量:在Smarty模板中,你可以使用变量来显示动态内容。变量通常以“{$”符…...
Socks IP轮换:为什么是数据挖掘和Web爬取的最佳选择?
在数据挖掘和Web爬取的过程中,IP轮换是一个非常重要的概念。数据挖掘和Web爬取需要从多个网站或来源获取数据,而这些网站通常会对来自同一IP地址的请求进行限制或封锁。为了避免这些问题,数据挖掘和Web爬取过程中需要使用Socks IP轮换技术。在…...
优化|当机器学习上运筹学:PyEPO与端对端预测后优化
分享者:唐博 编者按: 这篇文章我想要写已经很久了,毕竟“端对端预测后优化”(End-to-End Predict-then-Optimize)正是我读博期间的主要研究方向,但我又一直迟迟没能下笔。想说自己杂事缠身(实…...
Cocos Creator的 Cannot read property ‘applyForce‘ of undefined报错
序: 1、博主是看了这个教程操作的时候出的bug>游戏开发 | 17节课学会如何用Cocos Creator制作3D跑酷游戏 | P9 代码控制对象移动_哔哩哔哩_bilibili 2、其实问题不是出在代码上,但是发现物体就是不平移 3、node全栈的资料》node全栈框架 正文…...
纯css实现九宫格图片
本篇文章所分享的内容主要涉及到结构伪类选择器,不熟悉的小伙伴可以了解一下,在常用的css选择器中我也有分享相关内容。 话不多说,接下来我们直接上代码: <!DOCTYPE html> <html lang"en"><head>&l…...
【MySQL】数据库的增删查改+备份与恢复
文章目录 一、创建数据库create二、数据库所使用的编码2.1 查询字符集和校验集2.2 指定编码创建数据库2.3 不同的校验集对比 三、删除数据库drop四、查看数据库show五、修改数据库alter六、数据库的备份与恢复6.1 备份 mysqldump6.2 恢复source6.3 仅备份几张表或备份多个数据库…...
Docker 部署 redis 举例
1、搜索镜像,也可以访问 https://hub.docker.com/ 搜索镜像,查看所有版本。 $ docker search redis2、拉取镜像 $ docker pull redis:5.03、启动镜像,并配置相关映射与绑定(附:Docker 常用命令与指令参数)…...
通过HandlerMethodArgumentResolver实现统一添加接口入参参数
背景:项目中有些接口的入参需要用户id信息,最简单的做法在每个Controller方法调用的时候获取登录信息然后给入参设置用户id,但是这样就会有很多重复性的工作。另一个可行的也更好的方案可以使用HandlerMethodArgumentResolver来实现。 部分示…...
JAVA-spring boot 2.4.X报错Unable to find GatewayFilterFactory with name Hystrix
网关升级spring boot项目后,启动网关报错,具体报错信息如下: 2021-12-06 09:06:25.335 ERROR 45102 --- [oundedElastic-3] reactor.core.publisher.Operators : Operator called default onErrorDropped reactor.core.Exceptions$ErrorCallback…...
运输层---UDP协议
目录 一. 无连接运输:UDP1.1 定义1.2 特点1.3 应用 二. UDP报文段结构三. UDP检验和3.1 定义3.2 检验和计算实例3.2 UDP检验和的局限 一. 无连接运输:UDP 1.1 定义 UDP(User Datagram Protocol)用户数据报协议:由 [RF…...
【LeetCode】剑指 Offer Ⅱ 第3章:字符串(7道题) -- Java Version
题库链接:https://leetcode.cn/problem-list/e8X3pBZi/ 题目解决方案剑指 Offer II 014. 字符串中的变位词双指针 数组模拟哈希表 ⭐剑指 Offer II 015. 找到字符串中所有字母异位词双指针 数组模拟哈希表 ⭐剑指 Offer II 016. 不含重复字符的最长子字符串双指针…...
【python】绘图代码模板
【python】绘图代码模板 pandas.DataFrame.plot( )画图函数Seaborn绘图 -数据可视化必备主题样式导入数据集可视化统计关系散点图抖动图箱线图小提琴图Pointplot群图 可视化数据集的分布绘制单变量分布柱状图直方图 绘制双变量分布Hex图KDE 图可视化数据集中的成对关系 好看的图…...
RTT学习笔记12-KConfig 语法学习
KConfig 语法学习 RTT 官方教程 https://www.rt-thread.org/document/site/#/development-tools/build-config-system/Kconfig 我自己写的IIC配置 menuconfig BSP_USING_I2C # I2C 菜单bool "Enable I2C BUS" # 提示I2C 菜单default n # 默认不使能I2C 菜单…...
基于Mediapipe的姿势识别并同步到Unity人体模型中
如题,由于是商业项目,无法公开源码,这里主要说一下实现此功能的思路。 人体关节点识别 基于Mediapipe Unity插件进行开发,性能比较低的CPU主机,无法流畅地运行Mediapipe,这个要注意一下。 Mediapipe33个人体…...
Linux下进程的特点与环境变量
目录 进程的特点 进程特点的介绍 进程时如何实现并发性的 进程间如何切换 概念铺设 PC指针 上下文 环境变量 PATH 修改PATH HOME SHELL env 命令行参数 什么是命令行参数? 打印命令行参数 通过函数获得环境变量 getenv 命令行参数 env 修改环境变…...
以Llama-2为例,在生成模型中使用自定义LogitsProcessor
以Llama-2为例,在生成模型中使用自定义LogitsProcessor 1. 前言2. 场景介绍3. 解决方法4. 结语 1. 前言 在上一篇文章 以Llama-2为例,在生成模型中使用自定义StoppingCriteria中,介绍了怎样在生成的过程中,使用stopping criteria…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
