tutorial the play new examples abstracting ios audio memory-leaks mobile-safari web-audio

ios - the - web audio api play mp3



Fugas de memoria de Web Audio API en plataformas móviles (2)

Estoy trabajando en una aplicación que usará bastante el audio y estoy en la etapa de investigación para decidir si utilizar Web Audio API en los dispositivos que pueden admitirlo. He creado un banco de pruebas muy simple que carga un archivo sprite MP3 (~ 600kB de tamaño), tiene un botón reproducir y pausar y también un botón destruir, que en teoría debería permitir que GC recupere la memoria utilizada por la implementación Web Audio API . Sin embargo, después de cargar y destruir ~ 5 veces se bloquea iOS debido a una excepción de falta de memoria.

He perfilado MobileSafari en XCode Instruments y, de hecho, MobileSafari continuamente consume memoria. Además, el MP3 de 600kb usa ~ 80-90MB de memoria cuando se decodifica.

Mi pregunta es: cuando se decodifican datos de audio usando Web Audio API, ¿por qué el uso de la memoria es tan grande y también por qué la memoria nunca se reclama? Según tengo entendido, la decodificación es una operación asíncrona para el navegador y, por lo tanto, presumiblemente ocurre en un hilo separado. ¿Es posible que el hilo separado de los navegadores nunca libere la memoria utilizada durante la decodificación?

Mi código está debajo, cualquier ayuda / explicación es muy apreciada:

<!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>


Hice una publicación en el rastreador de problemas de SoundJS sobre esto, pero lo reitero aquí para cualquiera que esté buscando:

Parece que simplemente desconectar y desreferenciar el objeto AudioBufferSourceNode en iOS Safari no es suficiente; necesita borrar manualmente la referencia a su búfer, o el búfer en sí mismo tiene fugas. (Esto implica que el obj AudioBufferSourceNode tiene filtraciones, pero no consideramos esto como un límite práctico en nuestro proyecto).

Desafortunadamente para hacer esto, se necesita crear un buffer de scratch largo de 1 muestra, ya que la asignación a null causará una excepción. La declaración también debe estar en try-catch, ya que Chrome / FF lanzará cuando .buffer se reasigne en cualquier momento.

La solución que funcionó fue:

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; } } }

Espero que esto ayude a todos ustedes!


La memoria es grande porque la API de Web Audio decodifica su pequeño MP3 en LPCM de 32 bits, lo que le dará algo del orden de 10MB por minuto por canal.

Entonces, un MP3 estéreo de 4 minutos terminaría siendo algo así como 80MB.

Esta memoria no se puede reclamar mientras su aplicación se AudioBuffer al AudioBuffer decodificado. Por lo tanto, siempre que tenga una referencia (en su caso, soundBuffer ), esa memoria no se puede liberar. Si fuera así, no podrías reproducir el audio.