使用Web Audio API创建音频缓冲区时,可以使用decodeAudioData方法创建的缓冲区,这些缓冲区驻留在内存中,显然无法通过JavaScript访问。它们似乎在浏览器选项卡的整个生命周期中都存在,并且永远不会被垃圾收集。
我知道这些缓冲区与主线程分离,并设置在另一个线程上进行异步解码。我也知道API规范说不应该允许decodeAudioData对相同的输入缓冲区进行两次解码,我假设是为什么要保留解码缓冲区和/或编码输入缓冲区的副本。但是,在Chromecast等内存有限的设备上,这会导致大量内存累积,Chromecast崩溃。
在我的示例代码中,我使用Ajax获取mp3,然后将arraybuffer传递给decodeAudioData函数。通常在该函数内有一个onsuccess回调,它可以将解码的AudioBuffer作为参数。但是在我的代码中,我甚至没有通过它。因此我在解码后也没有对解码缓冲区做任何事情。在我的代码中没有引用它。它完全留在本机代码中。但是,每次调用此函数都会增加内存分配,并且永远不会释放。例如,在Firefox中:内存显示了Tab的生命周期中的音频缓存。非引用应该足以让垃圾收集器摆脱这些缓冲区。
我的主要问题是,是否有任何对这些解码音频缓冲区的引用,比如在audiocontext对象中,或者我可以尝试从内存中删除它们的其他地方?或者还有其他方法可以使这些存储和无法访问的缓冲区消失吗?
我的问题与目前关于decodeAudioData的所有其他问题不同,因为我表明即使没有用户存储任何引用甚至使用返回的解码音频缓冲区,也会发生内存泄漏。
function loadBuffer() {
// create an audio context
var context = new (window.AudioContext || window.webkitAudioContext)();
// fetch mp3 as an arraybuffer async
var url = "beep.mp3";
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function () {
context.decodeAudioData(
request.response,
function () {// not even passing buffer into this function as a parameter
console.log("just got tiny beep file and did nothing with it, and yet there are audio buffers in memory that never seem to be released or gc'd");
},
function (error) {
console.error('decodeAudioData error', error);
}
);
};
request.onerror = function () {
console.log('error loading mp3');
}
request.send();
}
当您将每个音频标签路由到Web Audio图表(通过使用MediaElementAudioSourceNode)时,您是否可以在Chromecast上使用多个音频标签?
我无法使用Web Audio API找到最终令人满意的Chromecast解决方案,同时播放四个mp3 - 用于四部分和声。第二代似乎没有足够的资源来保存音频缓冲器,同时使用decodeAudioData解码四个mp3文件,而不会留下太多的垃圾并最终崩溃。我决定使用基于Web Audio API构建的surikov的webaudiofont,并使用midi文件。我从未在桌面浏览器或其他具有更多资源的设备上遇到问题,但我必须让它在Chromecast上运行。我现在使用webaudiofont没有任何问题。
我找到了一种方法来解决Web Audio API音频播放器无限期地处理并崩溃Chromecast和其他移动平台的问题。 [[我没有在所有浏览器上测试过这个 - 你的里程可能会有所不同。 ]]
注意:您必须决定何时使用此清除方法(例如,在加载和播放了这么多缓冲区之后)。您可以在没有iframe的情况下执行此操作,但您可能需要重新加载页面一次或两次以启动垃圾收集。对于那些需要在Chromecast或其他移动设备等内存瘦平台上加载大量Web Audio API音频缓冲区的用户而言,这是一种实用的解决方法。
function hack_memory_management() {
var frame_player = document.getElementById("castFrame");
//sample is the object which holds an audio_context
frame_player.contentWindow.sample.clearBuffers();
setTimeout(function () {
frame_player.contentWindow.location.reload();
}, 1000);
}
CrossfadeSample.prototype.clearBuffers = function () {
console.log("CLEARING ALL BUFFERS -IT'S UP TO GC NOW'");
// I have four of each thing because I am doing four part harmony
// these are the decoded audiobuffers used to be passed to the source nodes
this.soprano = null;
this.alto = null;
this.tenor = null;
this.bass = null;
if (this.ctl1) {
//these are the control handles which hold a source node and gain node
var offName = 'stop';
this.ctl1.source[offName](0);
this.ctl2.source[offName](0);
this.ctl3.source[offName](0);
this.ctl4.source[offName](0);
// MAX GARGABE COLLECTION PARANOIA
//disconnect all source nodes
this.ctl1.source.disconnect();
this.ctl2.source.disconnect();
this.ctl3.source.disconnect();
this.ctl4.source.disconnect();
//disconnect all gain nodes
this.ctl1.gainNode.disconnect();
this.ctl2.gainNode.disconnect();
this.ctl3.gainNode.disconnect();
this.ctl4.gainNode.disconnect();
// null out all source and gain nodes
this.ctl1.source = null;
this.ctl2.source = null;
this.ctl3.source = null;
this.ctl4.source = null;
this.ctl1.gainNode = null;
this.ctl2.gainNode = null;
this.ctl3.gainNode = null;
this.ctl4.gainNode = null;
}
// null out the controls
this.ctl1 = null;
this.ctl2 = null;
this.ctl3 = null;
this.ctl4 = null;
// close the audio context
if (this.audio_context) {
this.audio_context.close();
}
// null the audio context
this.audio_context = null;
};
遗憾的是,即使这不能可靠地工作,Chromecast仍然会因为一些清晰的新mp3而崩溃。请参阅本页其他地方的“我现有的解决方案”。