video 是如何实现兼容多协议,多容器,多编码的JS播放器

371 阅读3分钟

在web播放场景中,我们只能通过 video 标签进行播放。但 video 标签只支持几种特定的容器格式和解码器,容器格式支持mp4、webm、ogg,编码支持 h264。这就需要对其他协议和容器进行兼容。那么怎么兼容?当然是转换成 video 能够认识的格式。通过什么转换?通过媒体源扩展(MSE,Media Source Extensions)技术,来帮助浏览器识别并处理其他视频格式。MSEChromeSafariEdge等主流浏览器支持的一个新的Web API

B站的flv.js、社区的hls.js、video.js(社区最流行的H5播放器) 都是利用 MSE 来解决多协议、多容器格式的播放器库。

flv

官方GitHub:github.com/bilibili/fl…

flv.js是Bilibili网站开源的HTML5 flv播放器,基于HTTP-FLV流媒体协议,通过纯JS实现FLV转封装,使flv格式文件能在Web上进行播放。

但是flv.js也不是所有的flv格式视频都能播放,并且对浏览器环境也有一定的要求,以下是flv.js的使用限制:

  • 视频必须是AVC(H.264)编码,音频必须为AAC或者MP3编码
  • 浏览器环境必须支持MSE,查看支持列表:caniuse.com/#feat=media… ,值得注意的是,ie浏览器中,ie11以上才能正常使用,而ie11浏览器必须在win8系统以上才能运行;移动端上,ios仅支持在iPadOS 13以上系统,ios手机端完全不可运行;android则要求4.4以上系统
  • 浏览器必须支持video,fetch api、xhr和websocket支持其一便可
image.png

hls

hls.js是基于Http Live Stream协议开发,利用Media Source Extension,用于实现HLS在web上播放的一款js播放库。

由于HLS协议由苹果提出,并且在移动端设备上广泛支持,因此可以被广泛应用于直播场景。而hls.js在pc端只需要支持MSE便可以应用,移动端使用原生video标签设置src便可完成播放。hls.js会先请求m3u8文件,然后读取到文件的分片列表,以及视频的编码格式、时长等。随后会按照顺序去对TS分片进行请求,然后借助MSE将二进制buffer内容进行合流,组成一个可播的媒体资源文件。

graph TD
BrowserAPIcheck --> Load.m3u8文件
Load.m3u8文件 --> Read.ts文件
Read.ts文件  --> 设置VideoBuffer
Read.ts文件  --> 设置AudioBuffer
Read.ts文件  --> 设置SubtitleBuffer
设置VideoBuffer --> AttachMediaSource
设置AudioBuffer --> AttachMediaSource
设置SubtitleBuffer --> AttachMediaSource
AttachMediaSource --> Video
AttachMediaSource --> Audio

H265编码

flvhls 通过MSE只是解决了不支持的协议和容器问题,不支持的编码格式怎么解决?

新的视频编码标准H.265、AV1等比传统H.264拥有更高压缩率,但浏览器本身并不支持。解决的思路无非,通过FFmpeg + Webassembly来实现一个JS播放器,使用JS拉流、解封装,将FFmpeg的265解码能力编译成wasm模块供JS调用,视频经过FFmpeg解码出来YUV帧数据,通过WebGL绘制图像帧数据,而音频方面因为浏览器支持AAC、MP3等主流音频格式,音频数据直接通过Web Audio API进行播放, 最后通过PTS对齐保证音画同步。

image.png

其中 H.265 的视频因为硬解支持情况不完善,软解可能有性能风险,所以在 chrome 中被关闭了不支持,在 chromium 中可以通过参数打开。对于 H.265 视频可以依照这个思路,利用浏览器提供的接口来实现一个模拟的 video 标签,大体是通过 canvas 和 audio 来实现。稳定性不够,线上还是不太建议用。