Воспроизвести аудио из частичного ответа и сохранить буфер после завершения
Фон
Я пишу сервис для обслуживания статических аудиофайлов в блоках, чтобы обеспечить свободный доступ для пользователей с меньшей пропускной способностью. Как только файл будет полностью передан (последовательность блоков является линейной, пользователи не смогут "перепрыгнуть" диапазон), я хочу сохранить файл в локальном кэше (используя localforage, но это не является частью этого вопроса), чтобы позже загрузите кэшированные файлы оттуда и сохраните пропускную способность.
проблема
С моим текущим состоянием знаний / кода / инструментов возможно только одно из следующих действий:
А) Потоковое аудио с HTMLAudioElement
const audio = new Audio()
audio.src = url
audio.preload = 'auto'
audio.load()
Аудио HTML5 внутренне заботится о частичном ответе, работает само по себе, но я не могу получить доступ к нижележащему буферу для сохранения файла после полной загрузки. Таким образом, я не смогу кешировать файл локально, не загрузив его отдельно (в другом запросе).
Б) Скачать / получить файл целиком, затем воспроизвести его
fetch(url, options) // set content header to array buffer
.then((response) => {
var blob = new Blob([response.value], { type: 'audio/mp3' })
var url = window.URL.createObjectURL(blob)
const audio = new Audio()
audio.src = url
audio.play()
})
Это дает мне доступ к данным, чтобы я мог их кэшировать для повторного использования в автономном режиме. Но я теряю опцию потоковой передачи, которая делает практически невозможным воспроизведение больших файлов без длительного ожидания.
C) Использование пользовательского загрузчика и воспроизведение каждого чанка с помощью API WebAudio
Так как A и B было недостаточно, я написал собственный загрузчик, который загружает чанки (который работает нормально) и отправляет событие с текущим чанком (как ArrayBuffer) в качестве данных. Он также отправляет другое событие подряд, которое возвращает все фрагменты, поэтому я могу создать из него большой двоичный объект:
const chunkSize = 2048
const audioContext = new AudioContext()
const loader = new StreamLoader(url, {step: chunkSize})
loader.on(StreamLoader.event.response, (data) => {
// data.response is of type ArrayBuffer
const floats = new Float32Array(data.response)
const audioBuffer = audioContext.createBuffer(1, chunkSize, 4410)
audioBuffer.getChannelData(0).set(floats)
const sourceNode = new AudioBufferSourceNode(audioContext, {buffer: audioBuffer})
sourceNode.connect(audioContext.destination)
sourceNode.start(0)
})
loader.once(StreamLoader.event.complete, (data) => {
const blob = new Blob(Object.values(data), {type: fileType})
const objectURL = URL.createObjectURL(blob)
const audio = new Audio();
audio.src = url;
audio.play();
})
loader.load()
Проблема здесь в том, что он производит только странный громкий шум. Тем не менее, игра с блоба, после завершения, работает нормально.
Мне известно о проблеме, что заголовок должен присутствовать для правильного декодирования аудио, поэтому мне любопытно, как HTMLAudioElement или MediaStream на самом деле внутренне разрешают это.
D) Использование MediaStream или MediaRecorder с пользовательским загрузчиком
Поскольку я не записываю с локального устройства, я не получил ни одного из них для работы с потоковыми чанками, загруженными с помощью xhr. Они ограничены для использования с местными источниками СМИ?
E) Использование таких инструментов, как Howler, SoundManager, SoundJS.
Я нашел вышеупомянутые инструменты, но, к сожалению, вернулся к выпуску A или B, используя их.
Ожидаемое решение
То, что я ожидаю найти, - это решение, которое позволяет мне воспроизводить аудио из потока (частичный ответ) и получать доступ к буферизованным данным для их кэширования.
Связанные ресурсы SO: