OTT项目之H5原生播放器开发总结

发布于 2022-05-06  450 次阅读


在未接入第三方播放器之前,用的是H5自带属性Video标签来播放视频,其中有很多大坑,以下是次次开发的一些总结

1、播放器支持的视频格式及大小

video标签只支持三种格式:

  • MP4 = MPEG 4文件使用 H264 视频编解码器和AAC音频编解码器
  • WebM = WebM 文件使用 VP8 视频编解码器和 Vorbis 音频编解码器
  • Ogg = Ogg 文件使用 Theora 视频编解码器和 Vorbis音频编解码器

值得注意的是,需求格式包括视频本身格式和视频编码格式

视频转码配置要细致,不一定最高配置的视频会最清晰,控制输出码率和分辨率

不要让转出来的视频超出原视频的好几倍

1651805485218 可接受范围

1651805647079 体积提升四倍,不可接受

一般来说,视频平均每分钟的大小不应该超过50MB,平均每分钟超过50MB的视频可能会造成低网速的用户播放稍显卡顿,如果平均每分钟视频大小超过75MB,则可能对用户播放视频造成影响。

(公司是千兆网,不会太卡,但要考虑到家庭网络的速度)

2、VIDEO标签的一些属性

1、custom-cache属性

这个东西卡了我将近一个星期,因为安卓电视端浏览器和电脑端的缓冲机制是不一样的,电视端是视频全局缓冲,比如一个视频有500MB,网页可能会先缓冲头10秒,中间10秒,和末尾10秒。因此播放起来会播一点卡一点。这时候必须加一个属性:custom-cache="false",不用浏览器自带的缓冲机制,让缓冲进度集中到即将播放的那段时间去。

2、preload和autoplay属性

preload:播放前预加载

autoplay:得到视频地址后自动播放视频

值得注意的是这两个视频用在一起会起冲突:原理是autoplay比preload的优先级要高,如果加了autoplay的话,preload就会失效

所以说autoplay="autoplay"属性肯定是要有的,但对于很多集的视频来说,进入本路由加载第一个视频的同时,可能会加载其他请求,导致第一个视频出现卡顿的情况

解决方法:给视频标签带上preload的属性,然后当页面加载完之后,再给标签附上autoplay的属性。这样第一次播放视频的时候可以给视频预留缓冲的时间,且当你切换视频地址的时候,视频依旧会自动播放。

1651807358943

1651807394289

3、视频控件

标签controls="controls"是开启浏览器自带的视频控件,包括暂停播放,上一集下一集,进度条,时间等一些基础的功能。若我们要自己定义播放器就不加controls属性,自己写一个播控

首先界面:

刚进入:底部提示信息1651809513048

视频及时间段信息1651809595060

选集底部信息1651809622232

返回键窗口1651809725410

获取视频长度和时间:

//获取视频总时长,获取的时长格式是毫秒,需要转换为  00:00:00格式
this.videoMsg.totalTime = this.durationTrans(video.duration);
//获取当前时长
this.videoMsg.displayTime = this.durationTrans(video.currentTime);
//自动连续播放下一集
     if (this.videoMsg.displayTime >= this.videoMsg.totalTime){
         //连播下集
     }

//转换视频时间格式
   durationTrans(a) {
      var b = "";
      var h = parseInt(a / 3600),
        m = parseInt((a % 3600) / 60),
        s = parseInt((a % 3600) % 60);
      if (h > 0) {
        h = h < 10 ? "0" + h : h;
        b += h + ":";
      }
      m = m < 10 ? "0" + m : m;
      s = s < 10 ? "0" + s : s;
      b += m + ":" + s;
      return b;
    },

视频进度的前进和后退:

这里又有一个坑,

在电视端,点一下前进的瞬间再点一下,会被判定成点了两次前进,如果用户想跳过很多视频时间看后面,点了很多下前进键的话,就会有很多次判定,造成加载很卡的情况。

解决方案:,设置一个定时器,用户如果在短时间内一直点的话,在下一次点击之前,如果定时器还没结束的话就重置定时器,如果在下一次点击之前定时器到时间了的话就跳转视频播放位置。

当然,进度条时间要根据用户的前进后退优先进行更新

document.onkeydown = (event) => {
        var el = event || window.event || arguments.callee.caller.arguments[0];
                  //按右键前进x秒
          if (el.keyCode == 22 || el.keyCode == 39) {
            if (this.$route.name == "Video") {
              if (video.currentTime + this.changetime < video.duration) {
                //清除关闭控件的定时器
                this.controls = true;
                // this.qian = true;
                // this.qianCount += time;

                this.changetime += time;
                if (this.addTimer) {
                  clearTimeout(this.addTimer);
                  this.addTimer = null;
                }
                this.$refs.progress.style.width =
                  (((video.currentTime + this.changetime) / video.duration) *
                    10000) /
                    100.0 +
                  "%";

                this.addTimer = setTimeout(() => {
                  video.currentTime != 0
                    ? (video.currentTime += this.changetime)
                    : 1;
                  this.changetime = 0;
                  console.log("进行了一次前进");
                }, 800);
              }

              // video.currentTime = video.currentTime + 5;
            }
          }
}

关闭显示控件:

这里我用的watch监听,当控件显示后的4秒钟自动关闭

  watch: {
    // 监控进度条
    controls: {
      handler(newdata, olddata) {
        console.log(newdata, olddata);

        if (newdata == true) {
          console.log("----------------");

          let _this = this;
          setTimeout(() => {
            _this.controls = false;
          }, 4000);
        }
      },
      immediate: true,
    },
  },

4、视频切换集数与退出视频

切换视频的原理是切换视频的src,

  this.$refs.videoContent.src = "xxxxx";

如果视频的src是活的话,则只需动态修改参数就行,如:

      :src="videoList[videoIndex].video_mp4_url"
//这里切换视频只需要修改videoIndex的数值即可

退出视频页面又有一个大坑了,TV端的浏览器退出视频页面的时候,不会自动清除视频。所以退出一个视频的时候,这个视频任然会一直处于加载状态中,从而消耗程序性能,所以这样多看几个视频后,软件会直接卡死。所以在退出之前,我们需要做的事情有

  beforeDestroy() {
      //1、重置全局聚焦属性
    this.$tv.resetScrollEl();
    this.$tv.resetOffsetDistance();
      //2、暂停视频
    this.statu = "pase";
        //3、这个十分重要,清空视频链接!!否则tv端视频可能一直处于加载状态,导致程序卡死
    this.$refs.videoContent.src = "";

    this.$route.query.backPath = "video";
    clearTimeout(this.playloadTimer);
    this.playloadTimer = null;
    clearTimeout(this.lessTimer);
    this.lessTimer = null;
    clearTimeout(this.addTimer);
    this.addTimer = null;
    this.$refs.videoContent.pause();
  },

5、缓冲与继续播放

使用addEventListener监控视频缓存和加载完成

    //当视频因为缓存问题而暂停的时候,只判断一次,瞬间触发。需要完成的事
    video.addEventListener("waiting", () => {
      if (_this.videoloading == false) {
        console.log("加载中");
        video.pause();
        _this.statu = "pase";
        _this.videoloading = true;
      }
    });
    //监控视频缓存,视频可以无卡顿播放了的时候触发
//可用于视频加载隐藏,监控视频缓冲完的播放
    video.addEventListener("canplaythrough", function () {
      console.log("提示视频能够不停顿地一直播放");
      if (_this.videoloading == true) {
        console.log("加载中");
        if (_this.$route.name == "Video") {
          video.play();
        }

        _this.statu = "playing";
        _this.videoloading = false;
      }
    });

6、video的其他标签属性

属性 功能描述
controls controls 是否显示播放控件
autoplay autoplay 设置是否打开浏览器后自动播放
width Pilex(像素) 设置播放器的宽度
height Pilex(像素) 设置播放器的高度
loop loop 设置视频是否循环播放(即播放完后继续重新播放)
preload preload 设置是否等加载完再播放
src url 设置要播放视频的url地址
poster imgurl 设置播放器初始默认显示图片

7、Video的其他Api

API属性 事件说明
duration 返回媒体的播放总时长,单位秒
loop 是否循环播放
muted 是否静音
paused 是否暂停
currentTime 当前播放时间(单位:秒)
volume 音量值
networkState 返回当前网络状态
playbackRate 播放的倍速(加速、减速播放)
src 当前视频源的URL
ended 返回当前播放是否结束标志
error 返回当前播放的错误状态
initialTime 返回初始播放的位置
mediaGroup 当前音视频所属媒体组 (用来链接多个音视频标签)
played 当前播放部件已经播放的时间范围(TimeRanges对象)
preload 页面加载时是否同时加载音视频
readyState 返回当前的准备状态
seekable 返回当前可跳转部件的时间范围(TimeRanges对象)
audioTracks 返回可用的音轨列表(MultipleTrackList对象)
autoplay 媒体加载后自动播放
buffered 返回缓冲部件的时间范围(TimeRanges对象)
controller 返回当前的媒体控制器(MediaController对象)
controls 显示播控控件
crossOrigin CORS设置
currentSrc 返回当前媒体的URL
defaultMuted 缺省是否静音
defaultPlaybackRate 播控的缺省倍速
seeking 返回用户是否做了跳转操作
startOffsetTime 返回当前的时间偏移(Date对象)
textTracks 返回可用的文本轨迹(TextTrackList对象)
videoTracks 返回可用的视频轨迹(VideoTrackList对象)

8、Video的其他事件

// 1、loadstart:视频查找。当浏览器开始寻找指定的音频/视频时触发,也就是当加载过程开始时
    video.addEventListener('loadstart', function(e) {
      console.log('提示视频的元数据已加载')
      console.log(e)
      console.log(video.duration)            // NaN
    })

    // 2、durationchange:时长变化。当指定的音频/视频的时长数据发生变化时触发,加载后,时长由 NaN 变为音频/视频的实际时长
    video.addEventListener('durationchange', function(e) {
      console.log('提示视频的时长已改变')
      console.log(e)
      console.log(video.duration)           // 528.981333   视频的实际时长(单位:秒)
    })

    // 3、loadedmetadata :元数据加载。当指定的音频/视频的元数据已加载时触发,元数据包括:时长、尺寸(仅视频)以及文本轨道
    video.addEventListener('loadedmetadata', function(e) {
      console.log('提示视频的元数据已加载')
      console.log(e)
    })

    // 4、loadeddata:视频下载监听。当当前帧的数据已加载,但没有足够的数据来播放指定音频/视频的下一帧时触发
    video.addEventListener('loadeddata', function(e) {
      console.log('提示当前帧的数据是可用的')
      console.log(e)
    })

    // 5、progress:浏览器下载监听。当浏览器正在下载指定的音频/视频时触发
    video.addEventListener('progress', function(e) {
      console.log('提示视频正在下载中')
      console.log(e)
    })

    // 6、canplay:可播放监听。当浏览器能够开始播放指定的音频/视频时触发
    video.addEventListener('canplay', function(e) {
      console.log('提示该视频已准备好开始播放')
      console.log(e)
    })

    // 7、canplaythrough:可流畅播放。当浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时触发
    video.addEventListener('canplaythrough', function(e) {
      console.log('提示视频能够不停顿地一直播放')
      console.log(e)
    })

    // 8、play:播放监听
    video.addEventListener('play', function(e) {
      console.log('提示该视频正在播放中')
      console.log(e)
    })

    // 9、pause:暂停监听
    video.addEventListener('pause', function(e) {
      console.log('暂停播放')
      console.log(e)
    })

    // 10、seeking:查找开始。当用户开始移动/跳跃到音频/视频中新的位置时触发
    video.addEventListener('seeking', function(e) {
      console.log('开始移动进度条')
      console.log(e)
    })

    // 11、seeked:查找结束。当用户已经移动/跳跃到视频中新的位置时触发
    video.addEventListener('seeked', function(e) {
      console.log('进度条已经移动到了新的位置')
      console.log(e)
    })

    // 12、waiting:视频加载等待。当视频由于需要缓冲下一帧而停止,等待时触发
    video.addEventListener('waiting', function(e) {
      console.log('视频加载等待')
      console.log(e)
    })

    // 13、playing:当视频在已因缓冲而暂停或停止后已就绪时触发
    video.addEventListener('playing', function(e) {
      console.log('playing')
      console.log(e)
    })

    // 14、timeupdate:目前的播放位置已更改时,播放时间更新
    video.addEventListener('timeupdate', function(e) {
      console.log('timeupdate')
      console.log(e)
    })

    // 15、ended:播放结束
    video.addEventListener('ended', function(e) {
      console.log('视频播放完了')
      console.log(e)
    })

    // 16、error:播放错误
    video.addEventListener('error', function(e) {
      console.log('视频出错了')
      console.log(e)
    })

    // 17、volumechange:当音量更改时
    video.addEventListener('volumechange', function(e) {
      console.log('volumechange')
      console.log(e)
    })

    // 18、stalled:当浏览器尝试获取媒体数据,但数据不可用时
    video.addEventListener('stalled', function(e) {
      console.log('stalled')
      console.log(e)
    })

    // 19、ratechange:当视频的播放速度已更改时
    video.addEventListener('ratechange', function(e) {
      console.log('ratechange')
      console.log(e)
    })

腾讯课堂播放器仿写

https://gitee.com/crazy_lu/school-web-game/tree/master/


一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。