背景
我正在编写一项服务,以分块方式提供静态音频文件,以为带宽较低的用户提供流畅的体验。文件完全流化后(块顺序是线性的,用户将无法“跳转”范围),我想将文件保存到本地缓存中(使用localforage,但这不是此问题的一部分)稍后从那里加载缓存的文件并节省带宽。
问题
根据我当前的知识/代码/工具,只能执行以下操作之一:
A)使用HTMLAudioElement
流式传输音频const audio = new Audio()
audio.src = url
audio.preload = 'auto'
audio.load()
HTML5音频在内部负责部分响应,本身可以正常工作,但是一旦完全加载,我将无法访问基础缓冲区来保存文件。因此,如果不单独下载(在另一个请求中),我将无法在本地缓存文件。
B)下载/ fetch整个文件,然后播放
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)使用自定义加载程序,并使用WebAudio API播放每个块
由于A和B不够用,所以我写了custom loader,它加载了块(工作正常),并以当前块(作为ArrayBuffer)调度一个事件作为数据。它还在末尾调度另一个事件,该事件返回所有块,因此我可以从中创建一个blob:
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()
这里的问题是,它只会产生奇怪的响亮噪音。但是,一旦完成,从Blob播放就可以了。
我知道这个问题,为了正确解码音频,需要提供标头,所以我很好奇HTMLAudioElement或MediaStream实际上是如何在内部解决此问题的。
D)在自定义加载程序中使用MediaStream或MediaRecorder
由于我不是从本地设备进行录制的,因此我没有使用这些文件来处理使用xhr加载的块。是否仅限与本地媒体源一起使用?
E)使用诸如Howler,SoundManager,SoundJS之类的工具。
我找到了上面提到的工具,但是很遗憾再次使用它们来发行A或B。
期望的解决方案
我希望找到一种解决方案,该解决方案使我可以播放流中的音频(部分响应)并访问缓冲的数据以对其进行缓存。
相关的SO资源: