HTML5 play()无法直接播放加密文件,必须通过MediaSource+WebCrypto实现边下载、边解密、边appendBuffer;需确保MSE状态就绪、密钥正确导入、Content-Type匹配且使用arrayBuffer()获取二进制数据。
play() 不能直接播放加密文件浏览器的 play() 方法只是触发媒体元素( 或 )开始播放,它不负责解密、不处理密钥、也不理解“加密文件”这个概念。如果文件本身是 AES 加密的 MP4 或自定义加密格式,直接丢给 ,浏览器会报 DOMException: The element has no supported sources 或静默失败。
想播加密内容,必须绕过原生 src 加载,改用 MediaSource + SourceBuffer,边下载、边解密、边喂给播放器。核心路径是:fetch

WebCrypto API(如 decrypt())解密 → appendBuffer() 推入 SourceBuffer。
MediaSource 对象绑定到 的 src,不能写死 src 属性moof+mdat 片段,不是整文件;整文件加密会导致无法分片加载和 seekingWebCrypto 要求密钥通过 importKey() 导入,且需指定正确算法(如 "AES-CBC")、格式("raw")和用途(["decrypt"])appendBuffer() 前要确保 SourceBuffer.updating === false,否则会抛 InvalidStateError
blob: URL有人把加密文件 base64 后塞进 src="data:video/mp4;base64,...",这毫无意义——base64 只是编码,不是解密,浏览器照样打不开。也有人生成 blob: URL 指向加密 Blob,结果还是报错,因为 Blob 内容仍是密文。
blob: URL 不触发任何解密行为,它只是本地引用,内容是什么就是什么video/mp4),不能写 application/octet-stream,否则 MSE 可能拒绝初始化fetch 获取加密数据,记得设 response.arrayBuffer(),别用 text() 或 json(),二进制乱码会导致解密失败MSE 在 Chrome/Firefox/Edge 没问题,但 Safari 对非标准加密(如非 Common Encryption)支持极弱;iOS Safari 甚至不支持某些 WebCrypto 算法(如 AES-GCM)。另外,纯 JS 解密大视频帧容易卡顿,尤其低端安卓机。
EXT-X-KEY),比前端全链路解密更稳libsodium-wrappers),而非纯 JS 的 WebCrypto
play() 调用后立刻 appendBuffer(),先等 mediaSource.readyState === "open",否则会失败play(),而在整个 MSE 生命周期控制和解密时机。很多人卡在第一个 appendBuffer() 就报错,往往是因为没等 sourceopen 事件,或者解密输出的 ArrayBuffer 长度为 0 —— 这种细节不打日志根本看不出。