背景介绍
目前cocos自带mp4播放器,能实现基本的播放需求,但是没有支持直播功能,而且对于视频文件的播放也是先下载再播放,没有做到边下载变播放的分片下载,
h5这块由于受到浏览器的限制,目前支持直播的方案大致分为flv/ts流/ws等
视频本身可以理解为一个压缩过的二进制流,视频解码的目的就是解压缩,把视频数据还原成原始的像素,声音解码就是把 mp3/aac 等格式还原成原始的 PCM 格式。
FFmpeg是一套老牌的、跨平台音视频处理工具,历史悠久,功能强大,性能卓著,市场上有大量基于 FFmpeg 的编解码器和播放器。
本文参考手淘、花椒等FFMpeg实现的h5播放器,基于cocos实现了一套支持flv的直播播放器组件,支持2d的sprite和3d的meshrender,大体结构是这样的
wasm兼容性
从es6和wasm的对比看,目前的覆盖率已经可以商用了
cocos加载wasm的方式有3种
1.wasm和js胶水放在构建模版里面修改index.ejs
普通require就可以,需要注意跨域
2.合成一个文件做成ffmpeg.mjs,用cocos的lib导入方式使用
3.做出asset文件放在资源包里,让wasm和js胶水文件合成一个文件,后缀用.bin的方式加载
为什么选择flv
准确来说,flv是一种视频封装协议,能够支持文件形式和流形式。
FLV文件体积小巧,清晰的FLV视频1分钟在1MB左右,一部电影在100MB左右,是普通视频文件体积的三分之一。再加上CPU占有率低、视频质量良好等特点使其在网络上盛行,所以网上的几家著名视频共享网站均采用FLV格式文件提供视频。
具体区别如下:
1.文件格式不同:flv是流式文件,mp4是容器式文件。
2.解析方式不同:flv文件可以边传输边解析,而mp4文件则不行。
3.观看方式不同:flv文件可以一边下载一边观看,而mp4文件则需要全部下载完毕才能够观看。flv格式不用全部下载完毕,就支持解析观看等,这也是它使用越来越广泛的原因。
4.直播流支持: flv可以支持直播流,mp4不行
5.hls直播流对比: hls的ts切片流相比,延迟更小,延迟能做到1-2s,而hls直播流也是通过生成切片的方式传输,延迟会更高。
6.ws等对比: flv比较通用,各大cdn和直播网站都支持flv方式出流,ws等比较定制化。
视频播放流程
视频播放的流程包括:
1.下载视频流,为解码视频做准备
2.解封装,将视频流按照封装协议解开,分解信息头/音频流/视频流
3.解码,分离后的数据流解码成音频和视频元数据
4.播放,将分离的视频流进行图片渲染,音频流进行声音播放
播放器整体实现过程
下载阶段
视频文件分片下载
flv文件支持分片下载播放,下载分片之前需要获取视频文件的大小
监听onreadystatechange事件的头部返回信息获取长度
var len = xhr.getResponseHeader(“Content-Length”);
在浏览器模式下通过xmlhttprequest实现
var xhr = new XMLHttpRequest;
xhr.open(‘get’, url, true);
xhr.responseType = ‘arraybuffer’;
xhr.setRequestHeader(“Range”, “bytes=” + start + “-” + end);
直播通过fetch的流式chunk请求方式
xmlhttprequest的流式下载支持不太好,因此最终选择了fetch方式
解封装方式
解封装的过程其实就是解析文件协议的过程,flv协议的格式可以参考这里,【流媒体协议】图解 FLV 协议 快速入门 - 知乎
我们只需要解析头部type=18和分离视频type=8/视频type=9,因为协议比较简单,因此跳解性能非常好。
然后将分离的音视频数据传入ffmpeg的decoder
解码
音频解码: 浏览器有音频接口
const AudioContext = window.webkitAudioContext || window.AudioContext;
可以通过decodeAudioData来解码,也可以通过ffmpeg来解码,但是decodeAudioData的解码性能是浏览器内置的因此可能会采用硬接,看浏览器是否支持,这个可以自己选择,目前使用的是ffmpeg的软解方式,好处是可以做重采样,生成最适合浏览器的采样率,比方web是48000hz,ios是41000hz,兼容性更好。
视频解码:
目前对h264,h265的解码库都比较复杂,都是通过wasm的方式处理的,没有js的原生库,而且性能上wasm也比较好
我们的目标是渲染到sprite组件或者3d节点上,通过修改builtin-sprite的effect支持2d渲染和builtin-unlit的effect支持3d渲染
yuv成rgb的转化公式我参考了论坛videotexture的demo,用3.6.2的effect转化处理的最终结果a值强制设置为1.0
性能
视频解码方式:硬解和软解,硬解由于受到各种专利限制和浏览器限制,很难做到兼容性覆盖,因此软解是比较合适的方案。
而基于ffmpeg和wasm的解码是通过cpu运算实现的软解,并且由于浏览器对wasm多线程的兼容性问题,
ffmpeg编译wasm的时候关闭多线程,需要的cpu资源比较多,在我iphonex的设备上,播放单视频流占有大概在50%左右。
为什么只选择了flv,ffmpeg可以支持各种视频格式
ffmpeg确实支持很多的视频格式,而且最初我也是这么设计的,但是在测试过程中存在一些和预期不一致的地方,ffmpeg自带缓冲区,解码出来的流不是稳定的,有可能在某一帧返回10个音频帧或者视频帧,
解码过程是在worker里面处理的,postmessage到主线程的时机不可控制,尤其是碰巧视频和音频同时出来,因此有可能造成卡帧,因此最终还是选择了上层解封装,ffmpeg只负责解码,这种方式出包比较稳定
微信
微信自己封装了一套api,而且对wasm的支持不太友好,需要很大的改造,等完成了再分享
原生
原生不支持wasm和worker,音频播放的方式也不一样,需要用c++的jsb和多线程来重新处理,而且在原生端可以开启ffmpeg的多线程模式,性能提升会更大
demo和组件地址
感兴趣的童鞋可以请我喝几杯咖啡,给我补几根头发
https://store.cocos.com/app/detail/4471
注意:web的播放器都有跨域问题,需要配置出流地址支持跨域
音视频相关内容涉及的会比较多,只能简单介绍了,先到这里吧。
















有问题大家可以加群提供售后
