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,实际代码中只用到了 sampleBuffnext3SampleBuff,不明白作者为什么这样写,也可能是忘了删,这里可以修改为

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() 函数中赋值,里面的数据来自于哈夫曼编码表,使用的码表为 book01book11,没有用到 book12,这11个码表的成员原型如下

typedef struct {const uint16_t len;const uint16_t data;
} hcode16_t;

因此可以把 int 改成 short,每声道可以节约6KB内存
备注:这样修改其实是错的,但是完全可以正常使用,具体原因见下文

内存统计如下,约 200 KB

1.6 禁用 TNS

修改 libfaac\coder.hlibfaac\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 修改数据类型

修改所有文件的 doublefloat
理论上来说这个修改会影响音质,实测没听出来,个人认为这个修改应该没问题
每声道内存再节省一半

内存统计如下,大约 90 KB

1.8 其他

以下无关紧要,能优化一点点,这里就不统计了
faacEncConfigurationint 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() 函数中赋值,里面的数据来也确实来自于哈夫曼编码表 book01book11,我最初在做优化时大概看了一眼这几个编码表,以为数值都在 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 为17
  • PutBit(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。注意!这里是重点!因为它是有符号数,高位全部被置为1
  • PutBit(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 这两位网友的留言!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习】了解 AUC - ROC 曲线

一、说明 在机器学习中&#xff0c;性能测量是一项基本任务。因此&#xff0c;当涉及到分类问题时&#xff0c;我们可以依靠AUC - ROC曲线。当我们需要检查或可视化多类分类问题的性能时&#xff0c;我们使用AUC&#xff08;曲线下面积&#xff09;ROC&#xff08;接收器工作特…

【TypeScript】类型声明及应用(二)

【TypeScript】类型声明及应用&#xff08;二&#xff09; 一、前言 TypeScript开发中需要对定义的变量指定类型&#xff0c;目前版本都支持哪些类型&#xff0c;每一个类型都有哪些含义&#xff0c;在这篇文章中&#xff0c;我们将会对其进行总结说明 二、JavaScript基本数据…

Quartz使用文档,使用Quartz实现动态任务,Spring集成Quartz,Quartz集群部署,Quartz源码分析

文章目录 一、Quartz 基本介绍二、Quartz Java 编程1、文档2、引入依赖3、入门案例4、默认配置文件 三、Quartz 重要组件1、Quartz架构体系2、JobDetail3、Trigger&#xff08;1&#xff09;代码实例&#xff08;2&#xff09;SimpleTrigger&#xff08;3&#xff09;CalendarI…

腾讯云从业者认证考试考点——云网络产品

文章目录 腾讯云网络产品功能网络产品概述负载均衡&#xff08;Cloud Load Balancer&#xff09;私有网络&#xff08;Virtual Private Cloud&#xff0c;VPC&#xff09;专线接入弹性网卡&#xff08;多网卡热插拔服务&#xff09;NAT网关&#xff08;NAT Gateway&#xff09;…

安全文件传输的重要性及其对企业的影响

在当今的信息时代&#xff0c;企业之间的文件传输已经成为日常工作的重要组成部分。无论是在商务合作、人力资源还是财务审计等方面&#xff0c;文件传输都发挥着关键的作用。然而&#xff0c;随着网络技术的发展&#xff0c;网络安全问题也日益突出&#xff0c;泄漏、篡改、丢…

用思维导图带你解读电子商务数据分析基本指标,产品、运营者必看

随着时代的发展&#xff0c;越来越多的人参与到电商之中。电商即电子商务&#xff0c;是依托现代信息网络技术&#xff0c;以商品交换为中心的新型商务贸易活动。电商可并不简单&#xff0c;做好电商又有哪些关键呢&#xff1f;别急&#xff0c;再此之前&#xff0c;需要先了解…

ViT-vision transformer

ViT-vision transformer 介绍 Transformer最早是在NLP领域提出的&#xff0c;受此启发&#xff0c;Google将其用于图像&#xff0c;并对分类流程作尽量少的修改。 起源&#xff1a;从机器翻译的角度来看&#xff0c;一个句子想要翻译好&#xff0c;必须考虑上下文的信息&…

leetcode每日一练-第102题-二叉树的层序遍历

一、思路 BFS 二、解题方法 通过广度优先搜索&#xff08;BFS&#xff09;的方式&#xff0c;按层遍历二叉树节点&#xff0c;并将每层的节点值保存在一个一维数组中&#xff0c;然后再将所有的一维数组存储在二维数组中&#xff0c;最后返回二维数组作为层序遍历的结果。 …

掌握无人机遥感数据预处理的全链条理论与实践流程、典型农林植被性状的估算理论与实践方法、利用MATLAB进行编程实践(脚本与GUI开发)以及期刊论文插图制作等

目录 专题一 认识主被动无人机遥感数据 专题二 预处理无人机遥感数据 专题三 定量估算农林植被关键性状 专题四 期刊论文插图精细制作与Appdesigner应用开发 近地面无人机植被定量遥感与生理参数反演 更多推荐 遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多…

【英杰送书第三期】Spring 解决依赖版本不一致报错 | 文末送书

Yan-英杰的主 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 问题描述 报错信息如下 报错描述 解决方法 总结 【粉丝福利】 【文末送书】 目录&#xff1a; 本书特色&#xff1a; 问题描述 报错信息如下 Description:An attempt…

重新理解 RocketMQ Commit Log 存储协议

最近突然感觉&#xff1a;很多软件、硬件在设计上是有 root reason 的&#xff0c;不是 by desgin 如此&#xff0c;而是解决了那时、那个场景的那个需求。一旦了解后&#xff0c;就会感觉在和设计者对话&#xff0c;了解他们的思路&#xff0c;学习他们的方法&#xff0c;思维…

【Hadoop 01】简介

目录 1 Hadoop 简介 2 下载并配置Hadoop 2.1 修改/etc/profile 2.2 修改hadoop-env.sh 2.3 修改core-site.xml 2.4 修改hdfs-site.xml 2.5 修改mapred-site.xml 2.6 修改yarn-site.xml 2.7 修改workers 2.8 修改start-dfs.sh、stop-dfs.sh 2.9 修改start-yarn.sh、s…

Spring MVC拦截器和跨域请求

一、拦截器简介 SpringMVC的拦截器&#xff08;Interceptor&#xff09;也是AOP思想的一种实现方式。它与Servlet的过滤器&#xff08;Filter&#xff09;功能类似&#xff0c;主要用于拦截用户的请求并做相应的处理&#xff0c;通常应用在权限验证、记录请求信息的日志、判断用…

小研究 - 微服务系统服务依赖发现技术综述(二)

微服务架构得到了广泛的部署与应用, 提升了软件系统开发的效率, 降低了系统更新与维护的成本, 提高了系统的可扩展性. 但微服务变更频繁、异构融合等特点使得微服务故障频发、其故障传播快且影响大, 同时微服务间复杂的调用依赖关系或逻辑依赖关系又使得其故障难以被及时、准确…

自监督去噪:Noise2Void原理和调用(Tensorflow)

文章原文: https://arxiv.org/abs/1811.10980 N2V源代码: https://github.com/juglab/n2v 参考博客&#xff1a; https://zhuanlan.zhihu.com/p/445840211https://zhuanlan.zhihu.com/p/133961768https://zhuanlan.zhihu.com/p/563746026 文章目录 1. 方法原理1.1 Noise2Noise回…

服务器数据恢复-raid5同步过程中又有一块磁盘报警的数据恢复案例

服务器数据恢复环境&#xff1a; 某研究院一台DELL存储&#xff0c;15块硬盘搭建的一组RAID5磁盘阵列。 该RAID5阵列只有一个卷组&#xff0c;该卷组占用了阵列的全部空间&#xff1b;该卷组只有一个起始位置为0扇区的XFS裸分区。 服务器故障&初检&分析&#xff1a; 该…

Spring Cloud Gateway - 新一代微服务API网关

Spring Cloud Gateway - 新一代微服务API网关 文章目录 Spring Cloud Gateway - 新一代微服务API网关1.网关介绍2.Spring Cloud Gateway介绍3.Spring Cloud Gateway的特性4.Spring Cloud Gateway的三大核心概念5.Gateway工作流程6.Gateway核心配置7.动态路由8.Predicate自定义P…

kafka第三课-可视化工具、生产环境问题总结以及性能优化

一、可视化工具 https://pan.baidu.com/s/1qYifoa4 密码&#xff1a;el4o 下载解压之后&#xff0c;编辑该文件&#xff0c;修改zookeeper地址&#xff0c;也就是kafka注册的zookeeper的地址&#xff0c;如果是zookeeper集群&#xff0c;以逗号分开 vi conf/application.conf 启…

Rust 数据类型 之 结构体(Struct)

目录 结构体&#xff08;Struct&#xff09; 定义与声明 结构体定义 结构体实例 结构体分类 单元结构体&#xff08;Unit Struct&#xff09; 元组结构体&#xff08;Tuple Struct&#xff09; 具名结构体&#xff08;Named Struct&#xff09; 结构体嵌套 结构体方法…

公网访问的Linux CentOS本地Web站点搭建指南

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 前言 在web项目中,部署的web站点需要被外部访问,则…