|
阅读:7645回复:2
H5拉流设计方案
这次主要讲讲在H5上拉流踩中的坑
一、首先我们要了解一些主流的视频流协议 1.RTMP RTMP的传输方式是TCP长链接,视频封装格式为FLV,每个时刻的数据收到后立刻转发,延迟较低,连续流,跨平台支持较差,需要flash支持 2.HTTP-FLV HTTP-FLV的传输方式是HTTP长链接,视频封装格式为FLV,每个时刻的数据收到后立刻转发,延迟较低,连续流,不支持多音频流、多视频流 3.HLS HLS的传输方式是HTTP短连接,视频封装格式的TS,集合一段时间数据生成TS切片文件,并且更新m3u8索引,延迟奇高,播放时需要多次请求,对于网络要求高 RTMP在H5端上面拉流需要flash的支持,但是flash在各大浏览器上几乎都不受待见,所以H5的拉流端一般不考虑这个协议,但是由于RTMP由于低延迟和长链接性,一般作为推流端 HTTP-FLV在H5端拉流延迟低,兼容性还不错,一般作为首选,但是在IOS上并不支持HTTP-FLV的视频流 HLS几乎是H5上面兼容性最好的协议,但是HLS的延迟奇高无比,在切片大小参数设置不当的情况下,可以达到30s的延迟 二、HTTP-FLV的拉流 说到HTTP-FLV的拉流就不得不提到flv.js,flv.js可以实现在H5端的flv视频格式播放。 var mediaMsg = {
type:"flv",
hasAudio:true,
hasVideo:true,
isLive:true,
cors:true,
url:res.flv,
}
var element = document.getElementsByName('videoElement')[0];
if (typeof player !== "undefined") {
if (player != null) {
player.unload();
player.detachMediaElement();
player.destroy();
player = null;
}
}
player = flvjs.createPlayer(mediaMsg, {
enableWorker: false,
lazyLoadMaxDuration: 3 * 60,
seekType: 'range',
});
player.attachMediaElement(element);
player.load();
player.play();这里就主要来说说针对flv.js拉流时的优化 1.加载动画的调用时机 一般来说,会在直播加载,直播缓冲的时候调用直播加载动画,flv.js里面提供监听解码帧的事件:statistics_info player.on("statistics_info", function (e) {
console.log(e.decodedFrames);
});将decodedFrames存为一个全局变量lastDecodedFrames,监听这两个值是否相同,如果相同则代表画面卡住不动了 player.on("statistics_info", function (res) {
console.log(lastDecodedFrame,res.decodedFrames);
if (lastDecodedFrame === 0) {
// loadingOver()
}
if (lastDecodedFrame !== res.decodedFrames) {
loadingOver();
if(onMessage){
console.log("发送消息");
top.postMessage({
playing:true,
msg:"It's playing",
},'*');
}
} else {
loadingShow();
}
lastDecodedFrame = res.decodedFrames
}); 上面的loadingOver()和loadingShow()就是加载动画控制方法除了监听当前解码帧与上一解码帧是否相同外还可以使用js自带的监听video的方法来监听视频是否正在加载 videoEle.onwaiting = function(){
console.log("监听到缓冲");
};用video元素对象调用onwaiting来监听是否在缓冲注意不要使用onplay事件来监听视频离开缓冲,要使用onplaying事件 2.追帧操作 一般来说,成功拉到视频流后,网络波动会让你的视频延迟越来越高,因为h5拉流的本质还是把视频流解析到video标签上播,h5的video标签可不会自动的去追上去 追帧也只能追到浏览器缓存下来的地方,可以通过videoEl.buffered.end(0)去获取当前视频缓存的大小,通过videoEl.currentTime获取当前视频播放的时间,用buffered减去cuurrentTime就是播放时间前缓存的大小,单位是秒。通过计时器去定时监听这个差值的大小来控制当前播放时间,可以通过两个方案来追帧。 第一个方案就是直接跳帧,当缓冲大于一定值后,跳过多余的缓冲 直接跳帧: videoEl.currentTime = videoEl.buffered.end(0) 这个方法会让当前视频的播放时间直接跳转到以及加载完成的时间,如果说跳转完成后,后续的时间并没有加载完成,会造成视频卡顿,所以较好的方案是跳转到缓冲区域后一点,如:videoEl.currentTime = videoEl.buffered.end(0)-0.5 第二个方案就是加速追帧,当缓冲大于一定值后,加速视频 加速追帧:videoEl.playbackRate=1.25 这个方法会让视频以1.25倍的速度播放,但是我们不能让他一直追到当前缓冲值,这样也会造成视频卡顿,所以追到当前缓冲值后一点就以正常速度播放,如: videoEl.buffered.end(0)-videoEl.currentTime <0.5 ? videoEl.playbackRate = 1:videoEl.playbackRate=1.25 注意:早期flv.js有内存泄漏的问题,在合并PR#354之前flv.js还存在音画不同步以及音频卡顿的问题,这里不建议找打包完成的flv.js文件引用,建议拉最新的代码自行打包! 这里说一下打包回遇到的问题 1.flv.js使用的gulp打包,在打包的时候会遇到如下报错 src\node_contextify.cc:628: Assertion `args[1]->IsString()' failed. 你的node版本大于10就会遇到这个报错,解决方案是:npm install natives 2.然后你再次打包,有可能遇到第二个错误 ReferenceError:primordials is not defined 这个是因为你的node版本大于12就会遇到这个报错,解决方案是降低node版本到12(不包含)以下 3.然后你继续打包,有可能就会遇到eslint报错,提示你语法不标准 解决方案就是绕过eslint的语法监测: 找到gulpfile.js文件打开,找到doLint方法,整个方法和调用这个方法的地方注释掉 function doLint(paths, exit) {
return gulp.src(paths)
.pipe(eslint())
.pipe(eslint.format())
.pipe(exit ? eslint.failAfterError() : eslint.result(function () {}));
}gulpWatcher.on('change', function (e) {
if (e.type === 'changed' || e.type === 'added') {
return doLint(e.path, false);
}
});gulp.task('lint', function () {
return doLint(['gulpfile.js', 'src/**/*.js'], true);
});将以上3个地方注释掉,就可以执行打包下次更新针对ios的h5拉流,使用hls拉流 |
|
|
板凳#
发布于:2021-06-25 22:40
哇哦,超级干货文章哦,手动点赞
|
|
|