基于web assembly (WASM) 的H265 Web播放器
1 背景
目前这个时间点,原生支持H265(HEVC)播放的浏览器极少,可以说基本没有,主要原因一个是H265的解码有更高的性能要求,从而换取更高的压缩率,目前大多数机器CPU软解H265的超清视频还是有点吃力,硬解兼容性又不好,另外一个原因主要是H265的专利费问题。因此H265有被各大浏览器厂商放弃的趋势,转而去支持更加开放的AV1编码,但是AV1编码的商用和普及估计还有段时间。
H265与H264相比主要的好处在于相同分辨率下降低了几乎一倍的码率,对带宽压力比较大的网站来说,使用H265可以极大削减带宽消耗(尽管可能面临专利费麻烦),但是由于浏览器的支持问题,目前H265的播放主要在APP端实现,借助硬件解码,可以获得比较好的性能和体验。
本文相关的代码使用WASM、FFmpeg、WebGL、Web Audio等组件实现了一个简易的支持H265的Web播放器,作为探索、验证,just for fun。
2 代码
github地址: https://github.com/sonysuqin/WasmVideoPlayer.
3 依赖
3.1 WASM
WASM的介绍在这里,可以在浏览器里执行原生代码(例如C、C++),要开发可以在浏览器运行的原生代码,需要安装他的工具链,我使用的是当时最新的版本(1.38.21)。编译环境有Ubuntu、MacOS等,这里有介绍。
3.2 FFmpeg
主要使用FFmpeg来做解封装(demux)和解码(decoder),由于使用了FFmpeg(3.3),理论上可以播放绝大多数格式的视频,这里只针对H265编码、MP4封装,在编译时可以只按需编译最少的模块,从而得到比较小的库。
使用Emscripten编译FFmpeg主要参考下面这个网页,做了一些修改: https://blog.csdn.net/Jacob_job/article/details/79434207
3.3 WebGL
H5使用Canvas来绘图,但是默认的2d模式只能绘制RGB格式,使用FFmpeg解码出来的视频数据是YUV格式,想要渲染出来需要进行颜色空间转换,可以使用FFmpeg的libswscale模块进行转换,为了提升性能,这里使用了WebGL来硬件加速,主要参考了这个项目,做了一些修改: https://github.com/p4prasoon/YUV-Webgl-Video-Player
3.4 Web Audio
FFmpeg解码出来的音频数据是PCM格式,可以使用H5的Web Audio Api来播放,主要参考了这个项目,做了一些修改: https://github.com/samirkumardas/pcm-player
4 播放器实现
这里只是简单实现了播放器的部分功能,包括下载、解封装、解码、渲染、音视频同步等基本功能,每个环节还有很多细节可以优化。seek还没有做,因为涉及的东西比较多。
4.1 模块结构
4.2 线程模型
理论上来说,播放器应该使用这样的线程模型,各个模块在各自线程各司其职:
但是WASM目前对多线程(pthread)的支持不够好,各个浏览器的WASM多线程支持还处于试验阶段,因此现在最好不要在原生代码里编写pthread的代码。这里使用了Web Worker,把下载和对FFmpeg的调用放到单独的线程中去。
主要有三个线程:
- 主线程(Player):界面控制、播放控制、下载控制、音视频渲染、音视频同步;
- 解码线程(Decoder Worker):音视频数据的解封装、解码;
- 下载线程(Downloader Worker):下载某个chunk。 线程之间通过postMessage进行异步通信,在需要传输大量数据(例如视频帧)的地方,需要使用Transferable接口来传输,避免大数据的拷贝损耗性能。
4.3 Player
4.3.1 接口
- play:开始播放;
- pause:暂停播放;
- resume:恢复播放;
- stop:停止播放;
- fullscreen:全屏播放;
- seek:seek播放未实现。
4.3.2 下载控制
为防止播放器无限制地下载文件,在下载操作中占用过多的CPU,浪费过多带宽,这里在获取到文件码率之后,以码率一定倍数的速率下载文件。
4.3.3 缓冲控制
为防止播放器无限制的解码占用过多的CPU,设置一个已解码视频帧队列长度的阈值,超过阈值则停止解码,队列消耗到一定程度后重启解码。
4.3.4 音视频同步
音频数据直接喂给Web Audio,通过Web Audio的Api可以获得当前播放的音频的时间戳,以该时间戳为时间基准来同步视频帧,如果当前视频帧的时间已经落后则立刻渲染,如果比较早,则需要delay。 在H5里delay可以通过setTimeout实现(还未找到更好的方式),上面做缓冲控制的另外一个意义在于控制视频的渲染频率,如果调用setTimeout的视频帧太多,内存会暴涨。
4.3.5 渲染
简单地将PCM数据交给PCM Player,YUV数据交给WebGL Player。
4.4 Downloader
这个模块很简单,只是单纯为了不在主线程做太多事情而分离,功能主要有:
- 通过Content-Length字段获取文件的长度;
- 通过Range字段下载一个chunk。
如上面提到的,Player会进行速率控制,因此需要把文件分成chunk,按照chunk方式进行下载。下载的数据先发给Player,由Player转交给Decoder(理论上应该直接交给Decoder,但是Downloader无法直接与Decoder通信)。
4.5 Decoder
这个模块需要加载原生代码生成的胶水代码(glue code),胶水代码会加载wasm。
self.importScripts("libffmpeg.js");
4.5.1 接口
- initDecoder:初始化解码器,开辟文件缓存;
- uninitDecoder:反初始化解码器;
- openDecoder:打开解码器,获取文件信息;
- closeDecoder:关闭解码器;
- startDecoding:开始解码;
- pauseDecoding:暂停解码。
这些方法都由Player模块通过postMessage异步调用。
4.5.2 缓存
这里简单使用了WASM的MEMFS文件接口(WASM的文件系统参考),使用方式就是直接调用stdio的方法,然后在emcc的编译命令中加入编译选项:
-s FORCE_FILESYSTEM=1
MEMFS会在内存中虚拟一个文件系统,Decoder收到Player发过来的文件数据直接写入缓存,由解码任务读取缓存。
4.5.3 解码
- 播放开始后不能立刻打开解码器,因为FFmpeg探测数据格式需要一定的数据长度(例如MP4头的长度);
- 缓存的数据足够后Player打开解码器,会得到音频的参数(通道数、采样率、采样大小、数据格式),视频的参数(分辨率,duation、颜色空间),以这些参数来初始化渲染器、界面;
- Player调用startDecoding会启动一个定时器执行解码任务,以一定的速率开始解码;
- Player缓存满后会调用pauseDecoding暂停解码器。
4.5.4 数据交互
解码后的数据直接通过Transferable Objects postMessage给Player,这样传递的是引用,不需要拷贝数据,提高了性能。
Javascript与C的数据交互:
发送:
……
this.cacheBuffer = Module._malloc(chunkSize);
……
Decoder.prototype.sendData = function (data) {var typedArray = new Uint8Array(data);Module.HEAPU8.set(typedArray, this.cacheBuffer); //拷贝Module._sendData(this.cacheBuffer, typedArray.length); //传递
};接收:
this.videoCallback = Module.addFunction(function (buff, size, timestamp) {var outArray = Module.HEAPU8.subarray(buff, buff + size); //拷贝var data = new Uint8Array(outArray);var objData = {t: kVideoFrame,s: timestamp,d: data};self.postMessage(objData, [objData.d.buffer]); //发送给Player
});
需要把回调通过openDecoder方法传入C层,在C层调用。
5 编译
5.1 安装Emscripten
参考其官方文档。
5.2 下载FFmpeg
git clone https://git.ffmpeg.org/ffmpeg.git
这里切到了3.3分支。
5.3 下载本文的代码
保证FFmpeg目录和代码目录平级。
git clone https://github.com/sonysuqin/WasmVideoPlayer.git
5.4 编译
进入代码目录,执行:
./build_decoder.sh
6 测试
可以使用任意的Http Server(Apache、Nginx等),例如: 如果安装了node/npm/http-server,则在代码目录下执行:
http-server -p 8080 .
在浏览器输入即可:
http://127.0.0.1:8080
7 浏览器支持
目前(20190207)没有做太多严格的浏览器兼容性测试,主要在Chrome上开发,以下浏览器比较新的版本都可以运行:
- Chrome(360浏览器、搜狗浏览器等webkit内核也支持);
- Firefox;
- Edge。
基于web assembly (WASM) 的H265 Web播放器相关推荐
- 基于metaRTC嵌入式webrtc的H265网页播放器实现(我与metaRTC的缘分)完结
目前100ms延迟已达成,暂不进一步开始,过程中还是感觉有点力不从心,还是多点积累再出发!我与meta RTC的缘分开始于实现H265网页播放的理想,搜遍全网,也只发现metaRTC实现了webrtc ...
- webRTC H265浏览器播放器+metaRTC推流实现webRTC H265解决方案
概述 webRTC H265解决方案中metaRTC native端到端以及SFU等都已经有成熟解决方案,但这些解决方案中缺乏浏览器拉流播放,近期网友superxxd推出了webRTC H265浏览器 ...
- 基于WASM的H265 Web播放器
基于WASM的H265 Web播放器 1 背景 2 代码 3 依赖 3.1 WASM 3.2 FFmpeg 3.3 WebGL 3.4 Web Audio 4 播放器实现 4.1 模块结构 4.2 线 ...
- 最简单的基于Flash的流媒体示例:网页播放器(HTTP,RTMP,HLS)
音视频领域,再次搜索,依然是大神雷霄骅的杰作.再次致敬,一路走好. ===================================================== Flash流媒体文章列表 ...
- YUVPlayer: 基于Android平台的YUV视频原始数据播放器
基于Android平台的YUV视频原始数据播放器 编译环境 FFmpeg版本: 4.2.2 NDK版本:r17c 运行环境 x86(模拟器) arm64-v8a(64位手机) 功能点 从文件中读取YU ...
- linux(ubuntu)下基于java的在线音乐仿qq播放器,
linux下基于java的在线音乐仿qq播放器,界面挺漂亮,界面全都是本人自己用java来画的,主要是我自己喜欢用ubuntu,但是由于没有人去做它的播放器,就自己来了,可以在线,有专辑图片,播放列表 ...
- 一个基于Android开发的简单的音乐播放器
一个基于Android开发的简单的音乐播放器 记得当时老师让我们写因为播放器时,脑子一头雾水,网上杂七杂八的资料也很少有用,因此索性就自己写一篇,希望对有缘人有用. 因为有好多人问我要源码,所以附上g ...
- 基于MSP430G2553官方开发板的音乐播放器
基于MSP430G2553官方开发板的音乐播放器 实现目标 硬件资源 芯片资源使用情况 外接硬件 程序实现 开发环境配置 各部分硬件驱动 主循环功能实现 实现目标 实现以蜂鸣器为播放设备,能够对简谱乐 ...
- 基于Arduino Uno开发板制作音乐播放器
基于Arduino Uno开发板制作音乐播放器 本文将基于Arduino开发板实现一个音乐播放器. 利用Arduino Uno读取sd卡模块中内存卡的音乐,传输信号到扬声器进行播放. 一.项目软硬件简 ...
最新文章
- 机器学习常见算法及优缺点!
- String Manipulation
- handler消息机制
- (读书笔记).NET大局观-.NET框架类库概观
- 使用匿名内部类创建线程
- C语言 线性表的链式存储结构(链表实现)
- Cactoos中的面向对象的声明式输入/输出
- Spring Cloud构建微服务架构:服务网关(过滤器)【Dalston版】
- 八数码——路径寻找问题
- 线性回归用matlab怎么做,matlab中如何应用regress()函数进行线性回归分析?
- bzoj 5016: [Snoi2017]一个简单的询问(莫队)
- WPF学习:3.Border Brush
- 简单概述PHP的命名空间及其在自动载入上的应用
- 攻防世界-music-高手进阶区-miscmisc
- 各种redhat版本下载
- android gson解析json数据,Android中使用Gson解析JSON数据的两种方法
- 制作u盘winpe启动盘_微PE工具箱 (WePE),绿色纯净的WinPE启动盘/U盘重装系统工具...
- 轮播图左右按钮会被选中的问题
- jquery开关灯案例_jQuery实现开关灯效果
- 搜索引擎免费登录入口大全(转)
热门文章
- Opencv中的MSER特征
- cnn生成图像显著图_使用CNN生成图像先验实现场景的盲图像去模糊
- opencv上设置摄像头曝光参数的经验
- java导航栏母版页_母版页导航栏选中解决方案
- 抚州全国计算机二级报名入口,2021上半年抚州市计算机二级报名时间|网上报名入口【已开通】...
- RNN-BPTT 笔记
- 成像导引头半实物仿真测试系统ETest设计与开发
- “母亲节”谈竟争、讲创业_有人泪奔,有人笑
- Mina Codec Filter对应协议实现编解码处理
- 本贴为交换友情链接专用