YuKi
论坛版主
论坛版主
  • 最后登录2024-03-25
  • 发帖数20
  • 社区居民
阅读:7645回复:2

H5拉流设计方案

楼主#
更多 发布于:2021-06-24 21:24
这次主要讲讲在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拉流
pakhozou
论坛版主
论坛版主
  • 最后登录2023-03-16
  • 发帖数21
  • 社区居民
  • 忠实会员
沙发#
发布于:2021-06-27 22:20
6666666666666
doubleyong
管理员
管理员
  • 最后登录2026-05-25
  • 发帖数1198
  • 最爱沙发
  • 喜欢达人
  • 原创写手
  • 社区居民
  • 忠实会员
板凳#
发布于:2021-06-25 22:40
哇哦,超级干货文章哦,手动点赞
知识需要管理,知识需要分享
游客


返回顶部

公众号

公众号