移动平台上的Web Audio API内存泄漏

She*_*ess 13 audio memory-leaks mobile-safari ios web-audio-api

我正在开发一个将大量使用Audio的应用程序,我正处于决定是否在可支持它的设备上使用Web Audio API的研究阶段.我已经组装了一个非常简单的测试床,可以加载一个MP3精灵文件(大小约600kB),有一个播放和暂停按钮以及一个销毁按钮,理论上它应该允许GC回收Web Audio API实现使用的内存.但是,由于内存不足异常加载和销毁~5次iOS崩溃后.

我在XCode Instruments中描述了MobileSafari,事实上MobileSafari不断耗尽内存.此外,600kb MP3在解码时最终使用~80-90MB的内存.

我的问题是 - 当使用Web Audio API解码音频数据时,为什么内存使用量如此之大以及为什么内存永远不会被回收?根据我的理解,解码是浏览器的异步操作,所以可能发生在一个单独的线程上?浏览器单独的线程是否可能永远不会释放解码过程中使用的内存?

我的代码如下,非常感谢任何帮助/解释:

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Web Audio Playground</title>
</head>
<body>
<button id="load">
    Load
</button>
<button id="play">
    Play
</button>
<button id="pause">
    Pause
</button>
<button id="destroy">
    Destroy
</button>
<script type="application/javascript">
    (function () {
        window.AudioContext = window.AudioContext || window.webkitAudioContext;

        var loadButton = document.getElementById('load'),
                playButton = document.getElementById('play'),
                pauseButton = document.getElementById('pause'),
                destroyButton = document.getElementById('destroy'),
                audioContext = new window.AudioContext(),
                soundBuffer = null,
                soundSource = null;

        loadButton.addEventListener('click', function () {
            var request = new XMLHttpRequest();
            request.open('GET', 'live-sprite.mp3', true);
            request.responseType = 'arraybuffer';

            // Decode asynchronously
            request.onload = function () {
                audioContext.decodeAudioData(request.response, function (buffer) {
                    soundBuffer = buffer;
                });
            };
            request.send();
        });

        playButton.addEventListener('click', function () {
            soundSource = audioContext.createBufferSource();
            soundSource.buffer = soundBuffer;
            soundSource.connect(audioContext.destination);
            soundSource.start(0);
        });

        pauseButton.addEventListener('click', function () {
            if (soundSource) {
                soundSource.stop(0);
            }
        });

        destroyButton.addEventListener('click', function () {
            if (soundSource) {
                soundSource.disconnect(0);
                soundSource = null;
                soundBuffer = null;
                alert('destroyed');
            }
        });
    })();

</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

小智 5

我在SoundJS 问题跟踪器上发表了关于此问题的帖子,但我会在这里为任何寻找的人重申这一点:

似乎在 iOS Safari 上简单地断开连接并取消引用 AudioBufferSourceNode 对象是不够的;您需要手动清除对其缓冲区的引用,否则缓冲区本身会泄漏。(这意味着 AudioBufferSourceNode obj 本身泄漏,但我们并没有将其视为我们项目中的实际限制。)

不幸的是,要执行此操作,需要创建 1 个样本的长暂存缓冲区,因为分配给 null 会导致异常。该语句也必须用 try-catch 包装,因为在任何时候重新分配 .buffer 时 Chrome/FF 都会抛出异常。

有效的解决方案是:

var ctx = new AudioContext(),
    scratchBuffer = ctx.createBuffer(1, 1, 22050);

class WebAudioAdapter extends AudioAdapter {
    close() {
        if( this.__src ) {
            this.__src.onended = null;
            this.__src.disconnect(0);
            try { this.__src.buffer = scratchBuffer; } catch(e) {}
            this.__src = null;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这对大家也有帮助!


Kev*_*nis 3

内存很大,因为 Web Audio API 将您的小 MP3 解码为 32 位 LPCM \xe2\x80\x93,这将为您提供每个通道每分钟 10MB 的数据。

\n\n

因此,4 分钟的立体声 MP3 最终大约为 80MB。

\n\n

只要您的应用程序保留已解码AudioBuffer. 因此,只要您有对它的引用(在您的情况下为soundBuffer),该内存就无法被释放。如果是,则无法播放音频。

\n