📺关于 Video 视频的初步研究

2,035 阅读6分钟

前言:

由于自己的项目中使用了基于 video.js 的 vue-video-player 插件;所以研究了不仅限于 vue-video-player 的行为;还包括视频基础, 插件 video.js 以及 原生 标签在移动端(android及ios)的表现;

1.视频基础:

视频.jpg

2.项目中应用:

两种方式:

2.1 在HTML中用 data-setup

<video data-setup='{ "autoplay": false, "preload": "auto" }'...>

2.2 video.js 中用 options 传参

// videojs options
  options: {
    // muted: true, // 是否静音播放
    autoplay: false, // 播放器准备好之后是否自动播放
    loop: false, // 循环播放
    liveui: true,
    preload: 'metautoadata',
    language: 'en',
    playbackRates: [0.5, 1.0, 1.5, 2.0], // 播放速度
    aspectRatio: '16:9', // 视频适应比率
    fluid: true, // 播放器缩放以适合其容器
    bigPlayButton: true, // 视频中央大播放按钮
    // flash: {
    //   swf: 'video-js-fixed.swf'
    // },
    controlBar:{ //设置是否显示该组件
      'currentTimeDisplay': true, // 当前时间
      'timeDivider':true, // 时分器
      'durationDisplay':true, // 持续时间显示
      'remainingTimeDisplay':true, // 剩余时间显示
      'volumeMenuButton': {
        inline: true,//设置音量bar为水平
        vertical: false//设置音量bar为竖直
      }
    },
    // 视频流资源集
    sources: [{
      type: "video/mp4",
      src: 'https://xxx.com/video/工伤保险广告.mp4'
    }],
    notSupportedMessage: '此视频暂无法播放,请稍后再试' // 允许覆盖Video.js无法播放媒体源时显示的默认信息
    // 播放之前的视频画面
    poster: 'https://xxx.com/upload/82.png',
    // 控制按钮显示顺序(组件顺序)
    // children: ['playToggle', 'volumeMenuButton', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'subtitlesButton', 'captionsButton', 'fullscreenToggle']
      },

2.3 vue 中的支持:

vue中引用基于 video.js 的 vue-video-player 插件

// 局部注册使用的方式
<template>
  <video-player  class="video-player-box"
     ref="videoPlayer"
     :options="playerOptions"
     :playsinline="true"
     customEventName="customstatechangedeventname"
     @play="onPlayerPlay($event)"
     @pause="onPlayerPause($event)"
     @ended="onPlayerEnded($event)"
     @waiting="onPlayerWaiting($event)"
     @playing="onPlayerPlaying($event)"
     @loadeddata="onPlayerLoadeddata($event)"
     @timeupdate="onPlayerTimeupdate($event)"
     @canplay="onPlayerCanplay($event)"
     @canplaythrough="onPlayerCanplaythrough($event)"
     @statechanged="playerStateChanged($event)"
     @ready="playerReadied">
  </video-player>
</template>

<script>
  import 'video.js/dist/video-js.css'
  import { videoPlayer } from 'vue-video-player'

  export default {
   components: { videoPlayer },
    data() {
      return {
        playerOptions: {
          // videojs options
          muted: true,
          language: 'en',
          playbackRates: [0.5, 1.0, 1.5, 2.0],
          sources: [{
            type: "video/mp4",
            src: "http://cdn.moji.com/websrc/video/spring20200311.mp4"
          }],
          poster: "/static/images/author.jpg",
        }
      }
    },
    mounted() {
      console.log('this is current player instance object', this.player)
    },
    computed: {
      player() {
        return this.$refs.videoPlayer.player
      }
    },
    methods: {
      // listen event
      onPlayerPlay(player) {
        // console.log('player play!', player)
        this.$refs.videoPlayer.player.play()
      },
      onPlayerPause(player) {
        // console.log('player pause!', player)
      },
      // ...player event

      // or listen state event
      playerStateChanged(playerCurrentState) {
        // console.log('player current update state', playerCurrentState)
      },

      // player is ready
      playerReadied(player) {
        console.log('the player is readied', player)
        player.play()
        // you can use it to do something...
        // player.[methods]
      }
    }
  }
</script>

2.4 vue-video-player 的插件

Player
    PosterImage   //默认封面
    TextTrackDisplay
    LoadingSpinner
    BigPlayButton    //大播放按钮
    ControlBar    // 控制条
        PlayToggle   //播放暂停
        FullscreenToggle   //全屏
        CurrentTimeDisplay   //当前播放时间
        TimeDivider
        DurationDisplay
        RemainingTimeDisplay   //剩余播放时间
        ProgressControl   //时间轴
               SeekBar
               LoadProgressBar
               PlayProgressBar
               SeekHandle
        VolumeControl   //音量控制
              VolumeBar
              VolumeLevel
              VolumeHandle 
       PlaybackRateMenuButton  //播放速率

》》》更详细的介绍可以查看文末的4.0参考文档中给出的官方资料;

3. 开始各种踩坑:

3.1 The play() request was interrupted by a call to pause()

播放器在执行了play()方法后立即执行pause()所导致;

解决方法:在执行play()的时候加定时器;

setTimeout(() => {
  $video.play();
}, 10 );


// 此方法本人亲测了没有啥效果,但还是记录在此吧....
// 最后我是改变了调用play()的时机,采用了 在 @ready 回调中去调用已经实例化了的 player 实例来执行 player.play() 就不报错了。

3.2 autoPlay 自动播放

方案:

blog.videojs.com/autoplay-be…

要解决iOS上的自动播放问题,请不要使用videojs 选项自动播放视频。

不起作用的方式:

(1)

<video id="my-video-id" autoplay></video>

(2)

videojs('my-video-id', {
  "autoplay": true
});

而是等待视频对象加载,然后触发播放操作:

videojs('my-video-id').ready(function() {
  this.play();
});
或者:
SPA中设置
// player is ready 初始化
  playerReadied(player) {
    // 主动调用视频播放 API
    console.log('---readied----')
    player.play() // 主动调用播放api
    console.log('---执行播放----')
},

A、video 视频播放触发条件:

1、touch自chromeM55版本之后从手势事件中移除;视频首次播放无法在touch start中触发;

2、touch end跟click手势事件存在一定的时间重叠,视频的首次播放可能被触发,但不可靠;

3、视频的首次播放可以在click中触发;

videoBtn3.addEventListener('click', function (evt) {
    video.play();
})

4、视频的首次播放无法在timer响应中触发;

videoBtn4.addEventListener('touchstart', function (evt) {
    setTimeout(function(){
        video.play();
    },1000); 
})

5、视频的首次播放无法在promise回调中触发;

videoBtn5.addEventListener('touchstart', function (evt) {
  fetch('./video_gesture.html')
    .then(function(response) {
        video.play();
    })
    .catch(function(myBlob) {
        video.play();
    });
})

B、其他导致的因素:

1、chrome6以及更高的版本不允许媒体自动播放;

2、safari 阻止自动播放视频。opera 阻止autoplay;

3、出于用户体验,节省流量的考虑,移动端禁止自动播放;

4、视频文件太大,加载时间过长或错误;

C、总结:

1.经过以上所有策略的调整之后,IOS 基本可以实现自动播放,但是要设置静音,即:没音频轨道,或者设置了muted=true属性。

2.安卓的话,只有部分机型可以自动播放。而且不能模拟自动播放,一定要有用户行为(点击播放按钮)才可以触发播放,也就有用户的主动行为才是稳定的。

3.3 多端播放行为一致性处理

<video id="theVideo" 
  class="video-player" 
  preload="auto" 
  src="http://xxx" 
  type="video/mp4" 
  width="100%" 
  webkit-playsinline="true" 
  playsinline="true" 
  x5-video-player-type="h5" 
  x5-video-player-fullscreen="portraint"
  x-webkit-airplay="true"
  x5-playsinline=""></video>

为了适配 iOS 与安卓系统,最后四个属性起到关键作用;

webkit-playsinline="true";playsinline="true" 作用与 iOS 系统,使用 playsinline 让用户无法全屏播放视频,不带 control 属性,去掉了系统自带的控件,方便为用户提供自己编写的播放器控件,使得不同系统表现一致。

x5- 开头的两个属性,作用于安卓系统,因为安卓系统的微信浏览器内核为 X5,

x5-playsinline="";x5-video-player-type="h5";x5-video-player-fullscreen="landspace" 是两个比较通用的配置,让视频以横屏的同层方式播放。

3.4 视频编码(不是格式)导致的部分浏览器播放有声音无画面的问题

问题描述:

进度条能移动有声音但是没有图像。百度了下,了解相关知识点;

原理:

目前video标签只支持MP4,WebMail,Ogg格式的视频;

各大浏览器对三种视频格式的兼容性:

格式IEFirefoxOperaChromeSafari
MPEG 49.0+NoNo5.0+3.0+
WebMNo4.0+10.6+6.0+No
OggNo3.5+10.5+5.0+No
MP4 = MPEG4 文件使用 H264 视频编解码器和AAC音频编解码器
WebM = WebM 文件使用 VP8 视频编解码器和 Vorbis 音频编解码器
Ogg = Ogg   文件使用 Theora 视频编解码器和 Vorbis音频编解码器

其中 MP4 有3种编码,MPEG4(DivX)、MPEG4(Xvid)、AVC(H264);但只有H264才是公认的MP4标准编码。

解决方案:

下载 “格式工厂”,将输出配置里面的编码改为 AVC(H264) ,导出视频,重新上传服务端,搞定!!!;

3.5 视频资源无法正常加载

问题:

The media could not be loaded, either because the server or network failed or because the format is not supported

排查:

在排查这个问题的时候可以按照以下步骤依次排除原因:

1、核实video的url地址是否有误;

2、核实用户上网地区(有部分地区可能有限制,比如新疆、西藏);

3、核实用户的上网环境,是家庭网络还是公司网络,如果是家庭网络,让用户重启路由器试试。如果是公司网络,让用户问下公司网管是否禁掉相关协议;

4、如无法确定所连网络是否禁止相关协议,可以建议用户用电脑连接手机热点后再尝试播放,如果连接手机热点后能播放,那一定是相关视频协议被禁掉了;

5、MP4视频格式编码问题导致的,见 2.4。

3.6 minio中的视频无法在ios Safari 中播放

场景:

前端上传 MP4(H264(AVC编码)视频文件,服务端采用分布式存储 minio,并用 Nginx 代理转发服务接口,iOS客户端通过 URL 无法播放该视频文件,安卓端、PC端各浏览器均播放正常;

原因分析:

  • safari 不支持整个文件流,服务器请求必须支持分段请求(也就是断点续传);
  • safari 对于文件流的请求需要包含一个请求头(Request) Range, 和一个响应头(Response)Content-Range,通过我们的 Nginx 代理后没有返回 Range 的相关信息。

解决方案:

(1)首先配置 Nginx 支持 Range 标签返回,添加 add_header Accept-Ranges bytes这一行即可:

server {
  listen 80;
  location ~xxx{
  add_header Accept-Ranges bytes;
}

(2)再将请求的 Response 里设置 Content-Range 返回。

image.png

4.参考文档:

vue-video-player文档: https://github.com/surmon-china/vue-video-player

video.js 文档: docs.videojs.com/tutorial-op…

腾讯X5内核视频行为官方解释: x5.tencent.com/tbs/guide/v…

video.js中遇到的问题: www.cnblogs.com/loveamyfore…

关于 MediaSource: https://developer.mozilla.org/zh-CN/docs/Web/API/MediaSource