JAVA代码MP3音频合成问题

文章讲述了项目中遇到的MP3音频拼接bug,即合并大量音频时出现时间配置文件误差。作者尝试了JAVA原生库和ffmpeg等解决方案,最终选择手动计算标头占用时长以减小误差。过程中提到ffmpeg的优点和JAVA代码执行效率问题。
摘要由CSDN通过智能技术生成

MP3音频合成问题

bug修复

背景:项目有多个MP3音频拼接合成为一个总的MP3音频的需求;
问题:如果有太多MP3音频合成,在生成MP3音频时,与我们根据音频生成的时间配置文件有误差,音频文件越多误差越大;
问题的产生:原代码在拼接MP3音频文件时没有对音频文件做对应处理,只是单纯的转为字节存放在一个流里面,参考MP3文件的格式发现,音频文件有部分存放音频信息的内容也同样被存放进流里面,最后生成的音频文件与我们想要的音频文件会多出一部分字节,从而导致音频时间有误差。

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {
            //缓冲字节输出流(true表示可以在流的后面追加数据,而不是覆盖!!)
            for (String path : pathList) {
                bos.write(FileCopyUtils.copyToByteArray(new File(path)));
                bos.flush();
            }
//音频配置文件生成所用的方法
audioHeader.getPreciseTrackLength()
//通过这个方法获取音频文件的精确时间

修复方案一 :使用JAVA原生库处理

尝试了几种库,要不就是不支持MP3格式的文件,要不就是提供的方法里没有能直接避免标头影响的方法,故放弃

修复方案二:嵌入ffmpeg第三方软件合并音频

ffmpeg是音频合成的有力工具,他能够自动的避免标头文件的存在,单独合成音频内容部分,而且还可以根据不同音频的比特率、通道等多种属性做相对应的操作,可选指令也多;
需要下载的可以看看这位老哥的

链接: [link](https://zhuanlan.zhihu.com/p/649728220?utm_id=0)

本人尝试了下,在本机下载安装后本地运行,在合成多个音频的时候解决了这音频合成的问题,所以高兴的去代码上实现调用,然后又是一盆冷水;
代码调用的时候是用生成命令行的形式,将音频合成的指令通过以下代码实现

ProcessBuilder builder = new ProcessBuilder(command.toString().split(" "));
Process process = builder.start();
process.waitFor(); 

因为项目目前的需求是要合并多个,大概需要将几十上百个MP3音频格式合并到一个音频里,所以在执行上面代码的时候,程序半天都没有响应。
后续去查了下ffmpeg这个软件是否无法处理过多的音频发现并没有这方面的限制,然后又去计算了下合成几十个音频的时间,主要和服务器/计算机的配置有关,要是一般的服务器合并几十个音频响应时间应该在100毫秒以内,所以排除是该软件的影响。
其次,考虑JAVA代码生成指令并传输到ffmpeg的时候是否有什么耗时操作导致程序太久未响应,打了几个点,排查到为builder.start()这部分执行时间过长,更换另一种方式也是一样,后续问了下gpt,可能因素为生成的指令太长,无法解析,command指令可容纳的长度也和服务器/计算机配置有关,而我目前的需求跟现有配置能容纳的长度差别太大无法满足。
最后,考虑通过ffmpeg对应的JAVA库jffmpeg-core、Xuggle-Xuggler是否满足我的需求,但看了下这两个库都是很久没有维护,而且内网的maven中没有这个包,需要从github下载,更骚的是其中一个连github上都没有了,最后还是放弃了

转格式实现合并

由于历史原因(屎山代码),如果更换格式需要将这一整块的代码都重新改,动用的地方太多,而且对现在已经存在的系统资源无法做处理,只能重新上传,这部分业务方不会同意;
若只在合成的时候转为JAVA可接受的格式再转回来,这种也考虑过,不过在转完格式之后标头文件的大小也会因为格式的转变而转变,读取不到正确的标头字节。
如果是转格式然后只合并音频内容,由于MP3格式本身就是种被压缩过的格式,就算转为无损格式然后再转为MP3格式,也会再次被压缩导致音频变化太大,这种也无法接受,所以也没有接受这种方案。

最终解决方案:手动计算出标头占用时长

由于audioHeader.getPreciseTrackLength()这个方法是只读取音频文件的音频内容时长,没有提供标头所占用的时长,所以用手动的方式自己计算出来

标头占用时长计算公式:
long byteCount = 1000; // 文件的标头字节数
long bitRate = 128; // 比特率(单位:kbps)
float headerTime = (float) byteCount / (bitRate * 1000);
System.out.println(“标头时间:” + headerTime + “秒”);

标头字节计算公式:
总字节 - 音频字节

音频字节计算公式:
long bitRate = 128; // 比特率(单位:kbps)
long duration = 300; // 音频时长(单位:秒)
long byteCount = (bitRate * duration * 1000) / 8;
System.out.println(“音频内容的字节数:” + byteCount);

这种方法的计算也有一定的误差,误差在千分之一秒左右,比之前直接加入标头文件的误差少了一个数量级,这种方法的误差在于获取的比特率总是一个规范值,而不是一个精确的数值,但查看了下这个误差还能够接受,所以在写入音频文件配置信息的时候都会计算这个误差值并加入进去。

求教

如果还有大佬懂其他方式的话可以私信我,这块业务刚接触,想多了解一点

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值