如何在音视频元素中使用未完成的fetch请求流实现边下边播
结论
完全可以实现边下边播,无需等待全量资源下载完成。你当前的写法无法实现该效果,核心是你将全量资源转成Blob后才赋值给播放器,且自定义的ReadableStream没有正确传递下载到的数据流块。
现有代码的问题
你当前写的ReadableStream逻辑存在无效问题:拿到下载的数据流块value后没有调用controller.enqueue(value)传递数据,等于下载到的内容全部被丢弃,最终生成的Blob实际没有有效内容。且你在拿到完整Response后才调用response.blob(),该操作会等待全量资源下载完成才会返回结果,必然会导致用户需要等全量下载完才能播放。
最优实现方案
方案1:直接使用原生播放器能力(推荐)
如果你的服务端对音视频资源的响应配置符合标准要求:
- 配置了正确的
Content-Length响应头 - 支持
Accept-Ranges: bytes范围请求 - 配置了正确的CORS跨域头
你完全不需要写自定义fetch逻辑,直接执行player.src = audioSRC即可,浏览器原生会自动处理缓冲、边下边播逻辑,性能和兼容性最好。
方案2:自定义fetch处理流(适合需要加解密、自定义鉴权等特殊场景)
如果有特殊逻辑需要自己处理下载的数据流,可以用MediaSource Extensions (MSE) API实现边下边播,示例代码如下:
// 先创建MediaSource实例,绑定到播放器 const mediaSource = new MediaSource(); player.src = URL.createObjectURL(mediaSource); mediaSource.addEventListener('sourceopen', async () => { // 注意:这里的mime类型要和你实际的音视频格式完全匹配,比如MP3是'audio/mpeg',MP4音频是'audio/mp4; codecs="mp4a.40.2"' const mime = 'audio/mpeg'; if (!MediaSource.isTypeSupported(mime)) { console.error('当前浏览器不支持该音视频格式'); return; } const sourceBuffer = mediaSource.addSourceBuffer(mime); try { const response = await fetch(audioSRC); if (!response.ok || !response.body) { throw new Error('资源请求失败'); } const reader = response.body.getReader(); async function readNextChunk() { const { done, value } = await reader.read(); if (done) { mediaSource.endOfStream(); return; } // 等sourceBuffer空闲后再写入新的数据流块 sourceBuffer.addEventListener('updateend', readNextChunk, { once: true }); sourceBuffer.appendBuffer(value); } readNextChunk(); } catch (err) { console.error('流处理出错', err); mediaSource.endOfStream('network'); } });
注意事项
- SourceBuffer的MIME类型必须和实际音视频格式完全匹配,否则会直接抛出播放错误
- 写入数据流块时必须等待上一次写入完成(监听
updateend事件),否则会触发缓冲区操作冲突 - 如果需要加密/解密数据流,可以在拿到
value之后、appendBuffer之前插入自定义解密逻辑
内容的提问来源于stack exchange,提问作者Rohin
相关产品推荐
相关产品推荐

