文章目录
- WasmVideoPlayer:浏览器里直接播放H265视频
- 技术实现
- 线程模型
- 缓冲控制
- 音视频同步
- 编译和使用
- 已知问题
WasmVideoPlayer:浏览器里直接播放H265视频
这个项目在 GitHub 上拿到了 1390 Star。
sonysuqin 开源了 WasmVideoPlayer,做的事情很明确:用 WebAssembly 在浏览器里播放 H265 编码的视频。
目前原生支持 H265 播放的浏览器基本没有。H265 解码对性能要求高,硬解兼容性差,再加上专利费的问题,各大浏览器厂商都在转向支持更开放的 AV1 编码。但 AV1 的商用普及还需要时间。
现实情况是,H265 相比 H264,在相同分辨率下码率几乎降了一半。对带宽压力大的网站来说,用 H265 能省不少带宽成本。只是浏览器不支持,目前 H265 播放主要靠 APP 端的硬件解码。
这个项目用 WASM、FFmpeg、WebGL、Web Audio 组合出了一套浏览器端的 H265 播放方案。
技术实现
整个播放器的核心组件:
- FFmpeg 3.3 负责解封装和解码,理论上支持 FFmpeg 的各种内置 codec,包括 H264、H265,默认支持 MP4、FLV 文件播放和 HTTP-FLV 流播放
- WebGL 做 YUV 到 RGB 的颜色空间转换和渲染,硬件加速,性能比 CPU 转换好
- Web Audio 处理 PCM 音频数据的播放
- WASM 让 C/C++ 代码能在浏览器里跑
线程模型
WASM 对多线程的支持还不够成熟,浏览器端的 pthread 还在试验阶段。所以这个项目用了 Web Worker 来实现线程分离。
三个线程各管各的:
主线程负责界面控制、播放控制、下载控制、音视频渲染和同步。解码线程跑在 Decoder Worker 里,处理解封装和解码。下载线程单独一个 Downloader Worker,按 chunk 下载文件。
线程之间用 postMessage 异步通信,传视频帧这种大数据时用 Transferable 接口,避免拷贝损耗性能。
缓冲控制
播放器的缓冲控制比较关键。FFmpeg 的同步读数据接口必须保证能返回数据,但 WASM 现在没法用多线程同步机制。
项目做了两层缓存:获取文件元信息之前的数据缓存,和解码帧缓存。数据不足时停止解码进入 Buffer 状态,数据够了继续播放。这样 FFmpeg 就不会因为读不到数据而报错退出。
下载也有速率控制,拿到文件码率后以码率的一定倍数下载,不会无限制占用带宽和 CPU。
音视频同步
音频数据直接交给 Web Audio,通过 API 获取当前播放的音频时间戳,以此为基准同步视频帧。视频帧时间落后就立刻渲染,时间早就 delay。
delay 用 setTimeout 实现,缓冲控制同时也在控制渲染频率,避免 setTimeout 调用太多导致内存暴涨。
编译和使用
环境需要安装 Emscripten 工具链,下载 FFmpeg 3.3 分支,再 clone 项目代码,保证 FFmpeg 和代码目录平级。进入代码目录执行./build_decoder.sh编译。
测试用任意 HTTP Server 都行,比如http-server -p 8080 .,浏览器打开 localhost:8080 就能跑。
Chrome、Firefox、Edge 的较新版本都支持,360 浏览器、搜狗浏览器等 webkit 内核的也能用。
已知问题
H265 解码播放的 CPU 占用比较高。如果不及时传递音频数据,AudioContext 的 currentTime 不做控制可能导致音视频不同步。
这个项目定位是探索验证性质,功能实现上还有很多细节可以优化。但它确实验证了一件事:通过 WASM + FFmpeg 的组合,浏览器端播放 H265 是可行的。
现上还有很多细节可以优化。但它确实验证了一件事:通过 WASM + FFmpeg 的组合,浏览器端播放 H265 是可行的。