web直播探索
引子
这是个忧伤的故事,最开始朋友问我如何接收裸流H264,并播放,我随手Google了下,答曰wfs,朋友曰就决定是你了。 从最开始用wfs库来处理裸流H264,到解码H264用video标签输出、再到使用flv.js,最终确定用hls来搞,我几经放弃因为XX坚持下来了……
wfs.js处理h264裸流
<video ref="video" id="video" controls></video></div>
this.$nextTick(() => {
if (Wfs.isSupported()) {
this.wfs = new Wfs()
this.wfs.attachMedia(this.$refs.video,'ch1')
}
})
// 断开
this.wfs.destory()
在前一篇博文也写到了该库的用法,以及注意事项,但我仅仅站在了第一层。
在写演示demo以及开发的时候,都是在chrome的模拟器来做的,没有测试过真机,交付的时候发现竟然不支持在ios播放……
Wfs.isSupported() 抄写栗子的时候完全没考虑过这玩意是干嘛的
function isSupported() {
return window.MediaSource && typeof window.MediaSource.isTypeSupported === 'function' && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42c01f,mp4a.40.2"');
}
源码中它判断的是window.MediaSource这个对象是否存在。
MSE(Media Source Extensions):W3C 标准API,解决 HTML5 的流问题(HTML5 原生仅支持播放 mp4/webm 非流格式,不支持 FLV),允许JavaScript动态构建 <video>
和 <audio>
的媒体流。可以用MediaSource.isTypeSupported() 判断是否支持某种MINE类型。
很重要一点在iOS的不支持Safari。
所以这个方案基本嗝屁了。
解码H264
调研了几个解码方式,最后选择了比较简单的Broadway.js来做个demo
使用Broadway.js
从https://github.com/mbebenita/Broadway下载其源码,其中最主要的代码在文件夹Player下,在其中添加一个任意名称的.html文件,并将如下代码写入此文件
<script type="text/javascript">
var player = new Player({size: {
width: 640,
height: 320
}});
document.body.appendChild(player.canvas);
var strhost = "ws://" + window.location.host + "/test";
// Setup the WebSocket connection and start the player
var client = new WebSocket( strhost );
client.binaryType = 'arraybuffer';
client.onmessage = function(evt) {
onMessage(evt)
};
function onMessage(evt) {
var messageData = new Uint8Array(evt.data);
player.decode(messageData);
}
function onError(evt) {
alert("error");
}
</script>
跟github栗子不太一样的是得用Uint8Array转一下流才能输出。
问题
该库尴尬点在于解码不支持分流,我测试的时候是传输整个h264文件,没什么问题,但是如果切割分流传输就会卡住不动了。
还有点就是他的上线竟然是1024x1024,大小超过就会报超过边界错误。
flv.js
该库的问题也是MSE问题,这里简单介绍一下吧。
- FLV里所包含的视频编码必须是
H.264
,音频编码必须是AAC
或MP3
, IE11和Edge浏览器不支持MP3音频编码,所以FLV里采用的编码最好是H.264+AAC,这个让音视频服务兼容不是问题。 - 对于录播,依赖
原生HTML5 Video标签
和 Media Source Extensions API - 对于直播,依赖录播所需要的播放技术,同时依赖
HTTP FLV
或者WebSocket
中的一种协议来传输FLV。其中HTTP FLV
需通过流式IO去拉取数据,支持流式IO的有fetch或者stream flv.min.js
文件大小 164Kb,gzip后 35.5Kb,flash播放器gzip后差不多也是这么大。- 由于依赖
Media Source Extensions
,目前所有iOS和Android4.4.4以下里的浏览器都不支持,也就是说目前对于移动端flv.js基本是不能用的。
HLS的一个直播的技术方案
最后的解决就是采用http-hls的方式来做视频直播:
- 采集视频流,并完成分片
- 前端请求获取m3u8文件
- 解析m3u8文件,获取ts文件
- ts文件转码放入ArrayBuffer
- MediaSource API进行合流,用video标签输出直播
如果天然支持HLS的浏览器,其实就是自己完成了3 4 5三个流程。
The Play() request was interrupted by a new load request
在调试过程中有几率出现上述问题,出现后会卡住,所以简单处理就是对错误回调进行处理
if (Hls.isSupported()) {
console.log('请求')
this.hls = new Hls({
// debug: true
});
this.hls.loadSource(source);
this.hls.attachMedia(this.$refs.video);
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
console.log("加载成功");
this.$refs.video.play();
});
this.hls.on(Hls.Events.ERROR, (event, data) => {
console.log(event, data);
// 监听出错事件
console.log("加载失败");
if (data.fatal) {
switch (data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
// try to recover network error
console.log('fatal network error encountered, try to recover');
this.hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log('fatal media error encountered, try to recover');
this.hls.recoverMediaError();
break;
default:
// cannot recover
this.hls.destroy();
break;
}
}
});
}
缓存问题
在开发环境对于请求打勾了 Diasble cache,所以没发现该问题,他们测试的时候一直有请求,抓包却抓不多,一看都是from disk cache。
解决方案还是后端接口无缓存状态no-cache, no-store, max-age=0, must-revalidate
IOS自动全屏问题
<video ref="video" x5-video-player-type='h5' x5-video-player-fullscreen='true' playsinline webkit-playsinline autoplay preload="auto" id="video" type="application/x-mpegURL"></video>
网上大部分帖子都是加playsinline,但是无效,还需要再加上webkit-playsinline