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()
函数中申请的 buffe
r,经过以上优化已经可以做到单声道约 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…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
HTML中各种标签的作用
一、HTML文件主要标签结构及说明 1. <!DOCTYPE html> 作用:声明文档类型,告知浏览器这是 HTML5 文档。 必须:是。 2. <html lang“zh”>. </html> 作用:包裹整个网页内容,lang"z…...