vue 項目進行直播視頻 vue-video-player

vue 項目進行直播視頻

4-30 更新 一個人的力量總是有限的,所以提供一些參考文檔,大佬博客
官方的詳細文檔:https://docs.videojs.com/docs/api/player.html#MethodscurrentTime
video-js 的使用技巧:https://www.awaimai.com/2053.html#5

最近電商直播非常火,於是接到需求搞搞!
看了很多相關的文章,沒看出有啥不同,最後直接採用 vue-video-player 來開發。直播流返回的是m3u8格式的,flv格式的播放不了,可能是我配置問題。然後關於這個插件可以用 flash 。我也沒去深究,畢竟谷歌都快放棄flash了。

安裝插件

npm install vue-video-player videojs-contrib-hls --save

注意這裏安裝了 2 個插件 vue-video-playervideojs-contrib-hls(這個後面會介紹到)

vue-video-player 使用

github 文檔也寫的很清楚,這裏也做個筆記把:

方式 1:全局引用

// main.js 文件

import Vue from 'vue'
import VueVideoPlayer from 'vue-video-player'

import 'video.js/dist/video-js.css'
// import 'vue-video-player/src/custom-theme.css'

Vue.use(
  VueVideoPlayer /* {
  options: global default options,
  events: global videojs events
} */
)

不太建議使用這種,畢竟不是每個頁面都需要用到,沒必要加重首頁加載的負擔

方式二:當作組件引入

import 'video.js/dist/video-js.css'
import { videoPlayer } from 'vue-video-player'
export default {
  components: {
    videoPlayer
  },
  data() {
    return {}
  }
}

引入了之後,就是 vue-video-player 的配置

vue-video-player 的配置

<template>
  <video-player class="video-player-box" ref="videoPlayer" :options="playerOptions" :playsinline="true">
  </video-player>

  <!-- 這裏有對應的操作事件,這裏就不一一寫了 -->
  <!--      
    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" 
    -->
</template>

<script>
  import 'video.js/dist/video-js.css'
  import { videoPlayer } from 'vue-video-player'
  export default {
    data() {
      return {
        // 很多參數其實沒必要的,也還有很多參數沒列出來,只是把我看到的所有文章做一個統計
        playerOptions: {
          autoplay: false, //如果true,瀏覽器準備好時開始回放。(好像微信瀏覽器不太行)
          techOrder: ['html5'], // 需要加載的插件,如果是要兼容flash的話,必須先加載flash。順序不能錯:['flash','html5']
          flash: {
            hls: { withCredentials: false }
          },
          html5: { hls: { withCredentials: false } },
          muted: true, // 默認情況下將會消除任何音頻。
          loop: false, // 導致視頻一結束就重新開始。
          language: 'en', // 提示的語言 中文的話是 zh-CN
          fluid: true, // 當true時,Video.js player將擁有流體大小。換句話說,它將按比例縮放以適應其容器。
          preload: 'auto', // 建議瀏覽器在<video>加載元素後是否應該開始下載視頻數據。auto瀏覽器選擇最佳行爲,立即開始加載視頻(如果瀏覽器支持)
          aspectRatio: '16:9', // 將播放器置於流暢模式,並在計算播放器的動態大小時使用該值。值應該代表一個比例 - 用冒號分隔的兩個數字(例如"16:9"或"4:3")
          playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度 0.7倍...
          sources: [
            // 播放的資源列表,雖然不知爲何是個數組,可能是,輪流播放?
            {
              type: 'video/mp4', // 類型。
              src: 'https://cdn.theguardian.tv/webM/2015/07/20/150716YesMen_synd_768k_vp8.webm' // 視頻流路徑
            },
            {
              withCredentials: false, // 直播是否有播放令牌,反正我是沒有
              type: 'application/x-mpegURL', // m3u8/Hls 的視頻流類型
              src: 'https://us-2.wl-cdn.com/hls/20200308/a9e9c7b577d4bb05d2a66508e63f5a17/index.m3u8' // 對應播放的m3u8 路徑
            }
          ],
          poster: '/static/images/author.jpg', //你的封面地址
          notSupportedMessage: '此視頻暫無法播放,請稍後再試', //允許覆蓋Video.js無法播放媒體源時顯示的默認信息。
          controls: true, // 是否顯示操作條
          controlBar: {
            // 播放的操作
            timeDivider: true, // 時間分割線
            durationDisplay: true, // 總時間
            remainingTimeDisplay: false,
            fullscreenToggle: true //全屏按鈕
          }
        }
      }
    },
    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)
      },
      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)
        // you can use it to do something...
        // player.[methods]
      }
    }
  }
</script>

第一個坑 No compatible source was found for this media.

進過上面的一大段配置,還是不能播放的!

video.cjs.js?3d33:440 VIDEOJS: ERROR: (CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) No compatible source was found for this media.

在這裏插入圖片描述

要解決這個很簡單,就是之前提到的 videojs-contrib-hls
如果採用的是方式 1,那就在 main.js 在全局引入這個文件
如果是方式 2,那麼就在對應的組件頁面引入就可以了

// 如果用的是比較早的版本 比如 5.14.1 版本,則用這句引入
import 'videojs-contrib-hls/dist/videojs-contrib-hls'
// 最新的版本
import 'videojs-contrib-hls'

坑 2:EventTarget’ of undefined

引入了 videojs-contrib-hls 。可是報錯 'EventTarget' of undefined

上面有說到,這個關於 videojs-contrib-hls 版本的問題。不同版本引入的方法也不一樣。只需要改一下引入的方式就可以了。詳情可以看下這條 issuss:
https://github.com/surmon-china/vue-video-player/issues/127

在 vue 中使用 m3u8 的視頻流播放到這裏就算是可以了。我的項目是可以正常播放了

坑 2:爲啥還是跑不了(這個坑我只看到文章提及,沒有遇到)

說 HLS 兼容性較好,主要是指可以通過 JS 讓用戶免配置(不必安裝 flash),可以在 caniuse 看下 HLS 的支持程度:http://caniuse.com/#search=HLS

只有 Edge 和 Safari 提供原生支持,其他瀏覽器都需要 JS 插件支持。那是不是隻要引了 videojs-contrib-hls 就 ok 了呢?❌,這裏面還有個坑。這個坑在該插件的官方文檔有說明:

Unlike a native HLS implementation, the HLS tech has to comply with the browser’s security policies. That means that all the files that make up the stream must be served from the same domain as the page hosting the video player or from a server that has appropriate CORS headers configured. Easy instructions are available for popular webservers and most CDNs should have no trouble turning CORS on for your account.

簡單的意思就是:除了原生支持 HLS 的瀏覽器,其他瀏覽器要想播 HLS,需要直播流服務端開啓 CORS。

一個最簡陋可是可以跑的 demo

貼一份最簡單的 demo 把。只要把對應組件都引入了這段 demo 是可以跑起來的.很多變量都沒來得及調整,隨意看看把~

<template>
  <div class="liveView">
    <video-player
      class="vjs-custom-skin"
      ref="videoPlayer"
      :options="playerOptions"
      @ready="onPlayerReadied"
      @timeupdate="onTimeupdate"
    >
    </video-player>

    <div class="inputWrapper">
      <div class="form-group row">
        <label for="" class="col-sm-4 col-form-label">HLS: </label>
        <div class="col-sm-8">
          <input class="form-control" type="text" placeholder="HLS url here" v-model="streams.hls" />
        </div>
      </div>
    </div>
    <div class="buttonWrapper">
      <button class="btn btn-primary" type="button" @click="enterStream">Apply</button>
    </div>
  </div>
</template>

<script>
  import { videoPlayer } from 'vue-video-player'
  import 'video.js/dist/video-js.css'
  import 'vue-video-player/src/custom-theme.css'
  // import 'videojs-contrib-hls/dist/videojs-contrib-hls'
  import 'videojs-contrib-hls'

  export default {
    name: 'live',
    components: {
      videoPlayer
    },
    data() {
      return {
        initialized: false,
        currentTech: '',
        streams: {
          rtmp: '',
          hls: ''
        },
        playerOptions: {
          overNative: true,
          autoplay: false,
          controls: true,
          techOrder: ['html5'],
          sourceOrder: true,
          flash: {
            hls: { withCredentials: false }
          },
          html5: { hls: { withCredentials: false } },
          sources: [
            {
              withCredentials: false,
              type: 'application/x-mpegURL',
              src: 'https://us-2.wl-cdn.com/hls/20200308/a9e9c7b577d4bb05d2a66508e63f5a17/index.m3u8'
            }
          ]
          // controlBar: {
          //   timeDivider: false, // 時間分割線
          //   durationDisplay: false, // 總時間
          //   progressControl: true, // 進度條
          //   customControlSpacer: true, // 未知
          //   fullscreenToggle: true // 全屏
          // },
        }
      }
    },
    computed: {
      player() {
        return this.$refs.videoPlayer.player
      },
      currentStream() {
        return this.currentTech === 'Flash' ? 'RTMP' : 'HLS'
      }
    },
    methods: {
      onPlayerReadied() {
        if (!this.initialized) {
          this.initialized = true
          this.currentTech = this.player.techName_
        }
      },
      // record current time
      onTimeupdate(e) {
        console.log('currentTime', e.cache_.currentTime)
      },
      enterStream() {
        this.playerOptions.sources[0].src = this.streams.hls
        this.playerOptions.autoplay = true
      },
      changeTech() {
        if (this.currentTech === 'Html5') {
          this.playerOptions.techOrder = ['html5']
        } else if (this.currentTech === 'Flash') {
          this.playerOptions.techOrder = ['flash']
        }
        this.playerOptions.autoplay = true
      }
    }
  }
</script>

<style scoped>
  .liveView {
    position: relative;
  }

  .selectWrapper {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 20px;
    line-height: 30px;
    margin: 20px;
    vertical-align: baseline;
  }

  .inputWrapper {
    width: 500px;
    margin: 0 auto;
  }

  .buttonWrapper {
    text-align: center;
  }
</style>

關於 flash 的組件使用

  • 需要安裝 vue-video-player 還有 videojs-flash
  • 如果 flash 和 hls 要一起使用,在配置中的 techOrder 必須配置爲 :
{
  "techOrder": ["flash", "html5"] //而且必須flash在前面
}

其他的吭就沒去多踩了。下次踩到了在記錄把!

一路走來坑不是很多,畢竟前端直播也是老生常談的問題了。很多坑都有解決的方法,耐心配置,總會播放起來的!

video.js移動端播放直接全屏

之前沒注意這個問題,還用了z-index作爲層級,在PC端模擬一直都是沒問題的,一到移動端發現!哦豁!

當然video.js已經提供瞭解決方法,在標籤上添加屬性 :playsinline="true" 即可。

然後深入的看了下源碼:

        // ios fullscreen
        if (this.playsinline) {
          this.$refs.video.setAttribute('playsinline', this.playsinline)
          this.$refs.video.setAttribute('webkit-playsinline', this.playsinline)
          this.$refs.video.setAttribute('x5-playsinline', this.playsinline)
          this.$refs.video.setAttribute('x5-video-player-type', 'h5')
          this.$refs.video.setAttribute('x5-video-player-fullscreen', false)
        }

有空在對這個全屏播放做一個深入的研究~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章