如何解决PCM音频录制后保存延迟问题,实现快速回放
如何解决PCM音频录制后保存延迟问题,实现快速回放
兄弟我太懂你这种急着回放鸟叫的心情了——刚录完鸟就飞了,等PCM存完黄花菜都凉了!结合你的场景,给你几个实用的解决方案,按见效快慢排序:
1. 边录边缓存,直接回放内存里的PCM数据(最快!不用等文件保存)
你现在是把录制和文件保存分开线程,但其实完全可以再加一个内存缓存(比如环形队列/LinkedBlockingQueue),录制线程每读取一段PCM数据,同时做两件事:
- 把数据写到文件里(继续你原来的逻辑)
- 把数据同步放到内存缓存里
等你停止录制的瞬间,立刻用AudioTrack从内存缓存里读取PCM数据开始播放——这时候完全不用等文件写完,缓存里已经有大部分录制好的音频了,剩下的还在从录制线程往缓存里送,播放可以无缝衔接。
举个代码思路(结合你的片段):
// 全局/类级别的环形缓存,比如用LinkedBlockingQueue<short[]> LinkedBlockingQueue<short[]> audioCache = new LinkedBlockingQueue<>(); // 录制线程里的逻辑 int readSize; short[] sData = new short[BufferSize]; while (isRecording) { readSize = recorder.read(sData, 0, BufferSize); if (readSize > 0) { // 1. 处理数据(你的for循环逻辑) for (int i = 0; i < readSize; ++i) {...} // 2. 复制一份数据存到缓存(避免原数组被覆盖) short[] cacheData = Arrays.copyOf(sData, readSize); audioCache.offer(cacheData); // 3. 写到文件(建议用缓冲流优化,后面会说) outputStream.write(...); } } // 停止录制后,立刻启动播放线程 new Thread(() -> { AudioTrack track = new AudioTrack(...); // 参数要和AudioRecord完全匹配 track.play(); short[] playData; while ((playData = audioCache.poll()) != null) { track.write(playData, 0, playData.length); } // 如果缓存读完了,但文件还没写完,可以继续读文件剩下的部分 // 这里可以加逻辑读取文件中缓存未覆盖的部分继续播放 track.stop(); track.release(); }).start();
2. 优化PCM文件的写入速度,减少保存延迟
你说60秒的录音要15秒才能存完,大概率是磁盘IO太频繁导致的。试试这两个优化点:
- 用
BufferedOutputStream包装你的文件输出流,它会把小批量的写入攒成大块再写到磁盘,减少IO次数:FileOutputStream fos = new FileOutputStream(pcmFile); BufferedOutputStream bos = new BufferedOutputStream(fos, 1024 * 32); // 32KB缓冲,可调整 // 之后用bos.write()来写数据 - 增大单次写入的块大小:不要每次读
BufferSize就写一次,比如攒个3-5次的BufferSize再写,也能减少IO操作。
3. 并行处理PCM转WAV,边写边播
其实WAV文件就是PCM数据+一个文件头,而文件头的信息(采样率、位深、声道数、总数据长度)里,除了总长度,其他在录制前就知道了。你可以:
- 录制停止时,先预估一个总数据长度(或者先写一个占位的长度,最后再修正),快速生成WAV的文件头写入到文件。
- 然后一边继续把剩余的PCM数据写入WAV文件,一边用
MediaPlayer或者AudioTrack播放这个WAV文件——播放器会先读取文件头,然后开始播放已经写入的PCM部分,后面的数据写完后会自动继续播放,完全不用等整个文件写完。
这个方法的好处是最终生成的是标准WAV文件,回放的时候不用额外处理PCM参数,但比第一种缓存方案稍慢一点,因为还是要等头写完,但比等整个PCM存完快太多了。
备注:内容来源于stack exchange,提问作者Luis A. Florit
相关产品推荐
相关产品推荐

