Container 解析 → Demux → WebCodecs 解码 → Canvas / AudioWorklet 渲染,全部在浏览器里完成。零构建、纯 ESM、零第三方运行时依赖——不往页面塞一个几十 MB 的 wasm 运行时,只把你真正需要的容器模块 import 进来。

解码这件事,浏览器自己也已经会做了。把容器解析交回给 JS,把解码交回给浏览器,剩下的体积、启动速度和可控性都会回来。
ffmpeg 编译成 wasm,首屏得先等几十 MB 下载完
解码跑在 wasm 单线程里,长视频掉帧、发热明显
能力全量打包,只播一个 MP4 也要带上所有容器代码
零构建纯 ESM,按需 import 单个容器模块,用多少取多少
VideoDecoder 走浏览器原生解码通路,帧直送 Canvas,不抢主线程
解复用自己写,帧级可控:seek、切轨、时间戳全部整数 µs
每个模块都是同一个基类契约的实现:open() 拿 MediaInfo,readSample() 取帧,seek() 对齐关键帧。换容器不换调用方式。
任何字节源都能接:本地 File、带 Range 的 HTTP、WS 网关桥接、WebRTC 流。
自研容器解析器输出标准 EncodedChunk,时间戳统一为整数 µs,没有浮点漂移。
优先 WebCodecs 硬解;浏览器不支持或格式受限时,自动降级到 MSE 路线。
视频帧上 Canvas / WebGPU,音频交给 AudioWorklet 做采样级对齐。
解出来的帧直接交到你手上:Canvas 绘制、滤镜、截图、推理前处理都随你安排,不用再从 <video> 里反抽像素。
VideoDecoder / AudioDecoder 原生调用,帧回调可订阅
firstframe / error 等事件齐全,seek 关键帧对齐


一条 routePreference 声明优先级,WebCodecs 不可用或格式受限时自动切 MSE 走 <video>,业务代码一行不用改。
routePreference: ['webcodecs', 'mse'] 顺序可配
降级对上层透明,事件与 API 完全一致
没有 bundler、没有转译、没有 node_modules。浏览器原生 ESM 直接跑,改一行源码刷新即生效,调试栈里全是自己的代码。
纯 ESM 单包,16 个模块按子路径按需引入
npm / jsdelivr CDN / GitHub Pages 三种引入任选

每个模块都能单独引:import * as mp4Mod from 'pure-webcodecs-player/mp4'。同一套基类契约,换容器不换调用方式。
注册你要用的容器模块,剩下的探测、选路、缓冲、解码、渲染由 Player 统一调度。想更底层一点,也可以直接 new 一个 Demuxer 自己拉帧。
所有容器 / 传输模块 API 同构,形状与差异见 docs/CONTRACTS.md