这篇文章的目的是?
H.265播放器(Videox.js)在淘宝直播落地已经近两年了。之前的架构设计主要针对的是直播的场景,播放m3u8和flv的直播流,由于直播落地的场景是B端主播中控台,使用场景是可以预览画面即可,故而对帧率要求不高。但是今年的短视频业务面向的多是C端用户,需要在Web场景下播放1080P/720P的H.265视频,那么必须满足短视频主流分辨率+码率流畅播放的要求。同时业务上还要支持多视频格式如(mp4/fmp4)的需求,所以综合评估后对原有架构进行了升级。既然有了升级自然就需要沉淀下经验。按照一贯套路我就来水一篇文章了。当然这两年内业界也有大量H.265播放器的实践落地,我写这篇文章也是借这次重构的机会分享自己的一些经验,希望能帮助各位少踩些坑。
视频演示
如下将演示新版播放器播放 1分钟1080p/25fps/H.265 MP4视频,具体视频参数如下:
-
预加载1000000帧(即整个视频),完全解码不播放的内存占用、CPU占用、解码间隔时间
因为整个解码过程没有进行播放,所以解码间隔=单帧解码耗时。
从上面视频能看出来,一个几十M的文件完全解码能达到4.6G的内存占用,CPU占用高达300以上(4核)。当然,这是完全不做限制,火力全开解码。但也能得出结论:无干扰情况下平均解码一帧1080p仅需要13ms(基于mbp2015版)。
旧版直播播放器解码720p需要26ms(基于mbp2015版),而新版播放器播1080p目前的13ms还不是极限,后续将继续探索优化空间。
-
预加载10帧并解码,后续边播边解的相关数据
演示1太过极端不符合日常使用的场景,但因为极限情况平均解码只需要13ms,而视频帧率是25(即间隔40ms),所以可以隔一段时间喂几帧到解码器,这样平衡了播放和解码的速率之后,CPU占用降到120左右、内存占用降低到了300M。同时还能流畅播放。不过播放策略有很多种,各位有更好的方案也欢迎和我交流。
架构设计
整体架构设计
上图所示为新播放器基本骨架,包含了主要模块。模块间互相独立,各自接收通用协议的参数。比如Loader传递给Demuxer的数据为ArrayBuffer,经Demuxer统一解封装成Packet格式Buffer数据(Annex-B)喂给Renderer。上图用MP4举例(HVCC为H.265码流格式之一),替换成flv、ts格式也是遵循这个流程。Renderer负责decoder调度,音画同步、音视频播放等,可以说是播放器最核心的模块。UI View则主要用来绘制播放器控件UI,如进度条等。本文不打算详细介绍每个功能,仅对decoder做细节解构,其它有关联的模块仅简单说明和实现。
DEMO架构
因为没有Demuxer,所以直接用Loader读取Annex-B码流。
-
通过Loader读取到Annex-B码流的Uint8Array数据
-
通过postMessge将数据发送给Worker线程的WASM包解码
-
WASM通过回调函数传回YUV数据给Worker再通过postMessage传给主线程Canvas
实操步骤
如何将 FFmpeg 编译成 WASM 包
接下来就进入正题了,第一步,先编译FFmpeg做精简,为啥呢?因为FFmpeg不光是个C库,还是非常庞大的C库。我们要在Web上使用它就需要移除一些无用的模块,好在FFmpeg提供了相应配置的能力,使用根目录configure文件按如下步骤操作即可。
1. 准备
-
编译前我们需要去emscripten官网[7]下载最新版emsdk
emsdk就是用来把FFmpeg编译成wasm包的工具
-
官网FFmpeg[8] 下载源码版的FFmpeg(本文基于4.1)
2. 编译FFmpeg静态库
创建 make_decoder.sh
echo "Beginning Build:"
rm -r ./ffmpeg-lite
mkdir -p ./ffmpeg-lite # dist目录
cd ../ffmpeg # src目录,ffmpeg源码
make clean
emconfigure ./configure --cc="emcc" --cxx="em++" --ar="emar" --ranlib="emranlib" --prefix=$(pwd)/../ffmpeg-wasm/ffmpeg-lite --enable-cross-compile --target-os=none --arch=x86_32 --cpu=generic \
--enable-gpl --enable-version3 \
--disable-swresample --disable-postproc --disable-logging --disable-everything \
--disable-programs --disable-asm --disable-doc --disable-network --disable-debug \
--disable-iconv --disable-sdl2 \ # 三方库
--disable-avdevice \ # 设备
--disable-avformat \ # 格式
--disable-avfilter \ # 滤镜
--disable-decoders \ # 解码器
--disable-encoders \ # 编码器
--disable-hwaccels \ # 硬件加速
--disable-demuxers \ # 解封装
--disable-muxers \ # 封装
--disable-parsers \ # 解析器
--disable-protocols \ # 协议
--disable-bsfs \ # bit stream filter,码流转换
--disable-indevs \ # 输入设备
--disable-outdevs \ #输出设备
--disable-filters \ # 滤镜
--enable-decoder=hevc \
--enable-parser=hevc
make
make install
文章最后可免费领取,音视频学习资料包
因为wasm支持的能力还是比较有限,一些FFmpeg用来优化性能的模块都需要禁用(比如硬件加速、汇编等)。本文也仅介绍解码。所以播放涉及的功能只用到了hevc-decoder(hevc=h265),其它的通通禁掉。
执行make_decoder.sh在ffmpeg-lite文件夹内生成简化后的FFmpeg静态库和对应的.h声明文件。