Есть ли способ остановить утечку памяти методом decodeAudioData API Web Audio? - PullRequest
0 голосов
/ 31 января 2019

Проблема

При создании аудио-буферов с использованием Web Audio API, есть буферы, созданные методом decodeAudioData, которые находятся в памяти и, по-видимому, недоступны через JavaScript.Кажется, они всю жизнь тянутся на вкладке браузера и никогда не собирают мусор.

Возможная причина проблемы

Я знаю, что эти буферы отделены от основного потока и установлены в другом потоке для асинхронного декодирования.Я также знаю, что спецификация API говорит, что decodeAudioData не должно быть позволено декодировать один и тот же входной буфер дважды, что я предполагаю, поэтому копия декодированного буфера и / или закодированный входной буфер хранятся вокруг.Однако на устройствах с ограниченной памятью, таких как Chromecast, это приводит к накоплению огромных объемов памяти и сбоям Chromecast.

Воспроизводимость

В моем примере кода я получаю mp3, используя Ajax, а затем передаю буфер массивав функцию decodeAudioData.Обычно внутри этой функции существует обратный вызов onsuccess, который может принимать декодированный AudioBuffer в качестве параметра.Но здесь, в моем коде, я даже не передаю это. Поэтому я также ничего не делаю с декодированным буфером после его декодирования.На него не ссылаются нигде в моем коде.Это полностью оставлено в нативном коде.Однако каждый вызов этой функции увеличивает выделение памяти и никогда не освобождается.Например, в Firefox about: memory показывает аудиобуферы там для жизни Tab.Без ссылки должно быть достаточно, чтобы сборщик мусора избавился от этих буферов.

Мой главный вопрос в том, есть ли какая-либо ссылка на эти декодированные аудиобуферы, скажем, в объекте аудиоконтекста или где-то еще?иначе что я могу попытаться удалить их из памяти?Или есть какой-то другой способ, которым я могу заставить эти сохраненные и недоступные буферы исчезнуть?

Мой вопрос отличается от всех других в настоящее время на SO относительно 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();
}

Чтобы предвидеть некоторые возможные ответы.

  1. Я должен использовать API Web Audio, потому что я играю гармонию четырех партий из четырех аудиофайлов в Chromecast, а элемент html audio не поддерживает одновременное воспроизведение нескольких файлов в Chromecast.
  2. Вероятно, любая библиотека JS, на которую вы можете сослаться [например, Howler.js, Tone.js, Amplitude.js и т. Д.], Построена на Web Audio API, и поэтому все они будут иметь общую проблему утечки памяти.
  3. Я знаю, что WAA зависит от реализации для каждого браузера.На данный момент я больше всего беспокоюсь о Chromecast, но проблема существует для каждого браузера, который я пробовал.
  4. Поэтому, я думаю, что это проблема, связанная со спецификацией, когда спецификации требуется правило недопущения кодирования, и поэтому разработчики хранят копии буфера в потоке уровня браузера, чтобы они могли проверять их по новым входным данным xhr.,Если писатель спецификаций случайно прочитал мой вопрос, не существует ли способа, чтобы пользователь мог иметь опцию для этого поведения и отказаться от нее, если он хочет, чтобы предотвратить внутреннее буферное хранилище на мобильных платформах и платформах с тонкой памятью?
  5. Мне не удалось найти ссылки на эти буферы ни в одном объекте JS.
  6. Я знаю, что могу выполнить audio_context.close (), а затем надеюсь на сборку мусора всех ресурсов, содержащихся в audio_context, и затем надеюсь, что смогу заново создать экземпляр audio_context с новым, но это не было эмпирическибыло достаточно своевременно для моего заявления.Сбой Chromecast до того, как GC вывезет мусор.

Ответы [ 3 ]

0 голосов
/ 14 февраля 2019

Можно ли использовать несколько аудио-тегов в Chromecast при маршрутизации каждого из них в граф Web Audio (с помощью MediaElementAudioSourceNode)?

0 голосов
/ 14 февраля 2019

Мое настоящее решение

Я не смог найти окончательного удовлетворительного решения для Chromecast с использованием API Web Audio и одновременного воспроизведения четырех mp3-файлов - используемых для гармонии четырех частей.Кажется, что во втором поколении просто не хватает ресурсов для хранения аудиобуферов и одновременного декодирования четырех mp3-файлов с использованием decodeAudioData, не оставляя слишком много мусора и в конечном итоге вылетая.Я решил пойти с webaudiofont Сурикова, который построен поверх API Web Audio, и использовать MIDI-файлы.У меня никогда не было проблем с настольными браузерами или другими устройствами с большим количеством ресурсов, но я должен заставить его работать на Chromecast.Теперь у меня нет проблем с использованием webaudiofont.

0 голосов
/ 02 февраля 2019

Прагматический обходной путь

Я нашел способ решения проблемы, связанной с бесконечным переносом аудиобуферов API Web Audio и падением Chromecast и других мобильных платформ.[[Я не проверял это во всех браузерах - ваш пробег может отличаться.]]

ЭТАП ЗАГРУЗКИ

  1. Загрузите документ с помощью API Web Audio внутри iFrame.
  2. Загрузите ваши звуковые буферы и делайте все, что вы делаете для их воспроизведения.

ЭТАП ОЧИСТКИ

Вызовите sourceNode.stop на всех воспроизводимых узлах, на которые вы ссылаетесь. Вызовите source.disconnect ();на всех исходных узлах. Вызовите gainNode.disconnect ();на всех узлах усиления эти исходные узлы связаны (и с любыми другими типами узлов WAA, которые вы можете использовать, у которых есть метод отключения) Установите для всех ссылочных gainNodes и sourceNodes значение null; Nullлюбые буферы, на которые вы ссылались, как декодированные, так и ваши кодированные аудиобуферы, извлеченные из xhr; KEY: на странице WAA вызовите audio_context.close ();затем установите audio_context = null;(это можно сделать из родительского элемента iFrame с помощью contentWindow). Примечание. Некоторые из этих шагов обнуления могут не быть абсолютно необходимыми, однако для меня этот подход сработал.

ЭТАП ПЕРЕЗАГРУЗКИ

Перезагрузите iframe с родительской страницы.Это приведет к тому, что все аудиобуферы будут собраны сборщиком мусора в следующем раунде GC, включая те, что находятся в скрытых (не JS) областях памяти. Ваш iframe должен будет заново создать контекст веб-аудио и загрузить его.буферизует, создает узлы и т. д., как вы делали это при первой загрузке.

Примечания: Вы должны решить, когда собираетесь использовать этот метод очистки (например, после столькихбуферы были загружены и воспроизведены).Вы можете сделать это без iframe, но вам может потребоваться перезагрузить страницу один или два раза, чтобы запустить сборку мусора.Это прагматичный обходной путь для тех, кому нужно загружать множество аудио-буферов Web Audio API на тонких платформах памяти, таких как Chromecast или других мобильных устройствах.

ОТ РОДИТЕЛЯ

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

ВНУТРЕННЯЯ РАМКА WAA

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.См. «Мое настоящее решение» в другом месте на этой странице.

...