前端使用hls方式播放h265(HEVC)格式的.ts在線文件,附vue2.x封裝h265web.js代碼挤巡,附github項目地址

接上一篇h265web.js 同時播放多個視頻,修改官方example 中player.js并重新編譯,這里使用vue2.x封裝最新版本h265web.js v20220916 tag 【注意: 后面有新版本了酷麦,tag v20220916之前的版本內存溢出我只想到定時刷新頁面來解決矿卑,之后的版本未曾嘗試過,自行測試】
也就是這個:

image.png

其實h265web.js作者在demo中提供了vue demo的沃饶,如下:


image.png

分別是Vue Cli 和 Vite創(chuàng)建的母廷,這都不是重點,重點是demo里面的播放器很簡陋糊肤,可以看到里面都是簡單的播放暫停琴昆,而且沒有樣式。


image.png

這樣對于初學者來說不夠友好馆揉,有沒有一個能用的哦业舍,我只想復制粘貼。升酣。舷暮。


image.png

來來來,各位看官看這里噩茄,拿走不謝脚牍,也希望大家多多分享自己的學習心得,讓后來的同學們少走彎路巢墅。
這里提一下啊诸狭,大家寫文章的時候要確保正確而且要結合自己的感悟,有時候看別人的博客寫得沒頭沒尾的君纫,而且有的相互copy驯遇,點開都是一樣的內容,有的代碼拿來也跑不起來蓄髓,各種報錯叉庐。。会喝。真的很浪費時間啊喂陡叠。

扯遠了玩郊,本文這里其實是結合上篇文章【見文章開頭】的v20211204 tag 中的example,簡單將html和css拿過來包裝修改枉阵,改成vue中的版本译红,并帶上視頻底部controller。最后頁面效果如下:

image.png

image.png

代碼如下:

1.首先去官網(wǎng)下載最新h265web.js v20220916 tag 兴溜,下載后解壓將dist目錄中的文件復制到自己的項目中侦厚。

image.png

在index.html中引入missile.js
image.png

開始封裝,這是我的文件目錄:


image.png

2.組件封裝代碼如下拙徽,這里使用了elementui@2.7.2刨沦,vue@2.5.2
index.vue

<!--h265webjs 播放器,當前是下載官網(wǎng)的tag v20220916 版本膘怕,修改example中play.js后的版本
只是修改了dom盒子的獲取想诅,源碼是直接取的id定位,這樣不能渲染多個視頻盒子岛心,因此把這部分代碼改了
-->
<template>
  <div v-loading="loading"
       element-loading-text=""
       element-loading-spinner="el-icon-loading"
       element-loading-background="transparent"
       :class="['player-container',videoBoxClass]">
    <!--  video dom-->
    <div :id="config.player" class="glplayer"></div>
    <!-- controller-->
    <div class="controller">
      <!--      插件視頻時長獲取錯誤来破,導致計算的進度條有問題,這里隱藏-->
      <div class="progress-common progress-contaniner" style="display: none;">
        <div class="progress-common cachePts"></div>
        <div class="progress-common progressPts"></div>
      </div>

      <div class="operate-container">
        <li class="playBtn">
          <a href="javascript:void(0)" title="start">Start</a>
        </li>

        <span class="ptsLabel">00:00:00/00:00:00</span>
        <div class="voice-div">
                  <span>
                      <a class="muteBtn" href="javascript:void(0)">
                          <svg class="icon"
                               style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;"
                               viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="488">
                              <path
                                d="M153.6 665.6V358.4h204.8V256H153.6c-56.32 0-102.4 46.08-102.4 102.4v307.2c0 56.32 46.08 102.4 102.4 102.4h204.8v-102.4H153.6zM358.4 256v102.4l204.8-128v563.2L358.4 665.6v102.4l307.2 204.8V51.2zM768 261.12v107.52c61.44 20.48 102.4 76.8 102.4 143.36s-40.96 122.88-102.4 143.36v107.52c117.76-25.6 204.8-128 204.8-250.88s-87.04-225.28-204.8-250.88z"
                                p-id="489">
                              </path>
                          </svg>
                      </a>
                  </span>
          <progress class="progressVoice" value="50" max="100"></progress>
        </div>

        <a class="fullScreenBtn" href="javascript:void(0)">
          <svg class="icon" style="width: 1em;height: 1em;vertical-align: middle;fill: currentColor;overflow: hidden;"
               viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="403">
            <path
              d="M167.8 903.1c-11.5 0-22.9-4.4-31.7-13.1-17.5-17.5-17.5-45.8 0-63.3l221.1-221.1c17.5-17.5 45.9-17.5 63.3 0 17.5 17.5 17.5 45.8 0 63.3L199.4 890c-8.7 8.7-20.2 13.1-31.6 13.1zM638.5 432.4c-11.5 0-22.9-4.4-31.7-13.1-17.5-17.5-17.5-45.8 0-63.3l221.7-221.7c17.5-17.5 45.8-17.5 63.3 0s17.5 45.8 0 63.3L670.1 419.3c-8.7 8.7-20.2 13.1-31.6 13.1zM859.7 903.8c-11.5 0-23-4.4-31.7-13.1L606.7 668.8c-17.5-17.5-17.4-45.9 0.1-63.4s45.9-17.4 63.3 0.1l221.4 221.9c17.5 17.5 17.4 45.9-0.1 63.4-8.8 8.7-20.2 13-31.7 13zM389 432.1c-11.5 0-23-4.4-31.7-13.1L136.1 197.2c-17.5-17.5-17.4-45.9 0.1-63.4s45.9-17.4 63.3 0.1l221.2 221.7c17.5 17.5 17.4 45.9-0.1 63.4-8.7 8.7-20.2 13.1-31.6 13.1z"
              fill="#ffffff" p-id="404">
            </path>
            <path
              d="M145 370c-24.7 0-44.8-20.1-44.8-44.8V221.8C100.2 153.5 155.7 98 224 98h103.4c24.7 0 44.8 20.1 44.8 44.8s-20.1 44.8-44.8 44.8H224c-18.9 0-34.2 15.3-34.2 34.2v103.4c0 24.7-20.1 44.8-44.8 44.8zM883.3 370c-24.7 0-44.8-20.1-44.8-44.8V221.8c0-18.9-15.3-34.2-34.2-34.2H700.8c-24.7 0-44.8-20.1-44.8-44.8S676.1 98 700.8 98h103.5c68.2 0 123.8 55.5 123.8 123.8v103.5c0 24.7-20.1 44.7-44.8 44.7zM327.5 926.6H224c-68.2 0-123.8-55.5-123.8-123.8V699.4c0-24.7 20.1-44.8 44.8-44.8s44.8 20.1 44.8 44.8v103.5c0 18.9 15.3 34.2 34.2 34.2h103.5c24.7 0 44.8 20.1 44.8 44.8s-20.1 44.7-44.8 44.7zM804.3 926.6H700.8c-24.7 0-44.8-20.1-44.8-44.8s20.1-44.8 44.8-44.8h103.5c18.9 0 34.2-15.4 34.2-34.2V699.4c0-24.7 20.1-44.8 44.8-44.8 24.7 0 44.8 20.1 44.8 44.8v103.5c0 68.2-55.6 123.7-123.8 123.7z"
              fill="#ffffff" p-id="405">
            </path>
          </svg>
        </a>
        <span class="showLabel" style="display: none;"></span>
      </div>
    </div>
  </div>
</template>

<script>
// import {makeH265webjs} from './player'
import {makeH265webjs} from './player20220916'
const token = "base64:QXV0aG9yOmNoYW5neWFubG9uZ3xudW1iZXJ3b2xmLEdpdGh1YjpodHRwczovL2dpdGh1Yi5jb20vbnVtYmVyd29sZixFbWFpbDpwb3JzY2hlZ3QyM0Bmb3htYWlsLmNvbSxRUTo1MzEzNjU4NzIsSG9tZVBhZ2U6aHR0cDovL3h2aWRlby52aWRlbyxEaXNjb3JkOm51bWJlcndvbGYjODY5NCx3ZWNoYXI6bnVtYmVyd29sZjExLEJlaWppbmcsV29ya0luOkJhaWR1"
export default {
  name: 'h265webjs',
  props: {
    videoBoxClass: { // 播放器外部盒子class
      type: String,
      default: ''
    },
    config: {
      type: Object,
      default() {
        return {
          player: "glplayer", // video id
          width: 960,
          height: 540,
          token: token,
          extInfo: {
            moovStartFlag: true,
            coreProbePart: 0.1,
            ignoreAudio: 1, // 忽略音頻播放(只對H265視頻生效)
            // cacheLength: 25,
            // probeSize: 8192,
            // autoPlay: true
          },
          // 視頻播放源
          url: '', // 獨立參數(shù)
        }
      }
    }
  },
  data() {
    return {
      player: null,
      resizeTimer: null, // resize 定時器

      // 優(yōu)化
      loading: false,
      loadingTimer:null,
    }
  },
  // watch: {
  //   'config.url': {
  //     deep: true,
  //     handler(val) {
  //       this.initVideo()
  //     }
  //   }
  // },
  mounted() {
    this.initVideo()
  },
  beforeDestroy() {
    this.resizeTimer && clearTimeout(this.resizeTimer)
    this.loadingTimer && clearTimeout(this.loadingTimer)
    this.destoryVideo()
  },
  methods: {
    initVideo() {
      this.loading = true
      this.destoryVideo()
      this.player = makeH265webjs(this.config.url, {
        token,
        width: this.$el.clientWidth || 300,
        height: this.$el.clientHeight || 400,
        ...this.config,
        event:{
          onLoadFinish:()=>{
            this.loading = false
          }
        }
      }, `.${this.videoBoxClass}`); // 修改了打包后的源碼的

      window.addEventListener('resize', this.resizeFn)
    },
    // 釋放資源
    destoryVideo() {
      window.removeEventListener('resize', this.resizeFn)
      this.player && this.player.release && this.player.release();
      this.player = null
      let playerDom = document.getElementById(this.config.player)
      if (playerDom) { // 清空
        playerDom.innerHTML = ''
      }
    },
    getWH() {
      return {
        width: this.config.width || this.$el.clientWidth || 300,
        height: this.config.height || this.$el.clientHeight || 400,
      }
    },
    resizeFn() {
      if (this.resizeTimer) clearTimeout(this.resizeTimer)
      this.resizeTimer = setTimeout(() => { // 有的版本沒有resize方法....
        let {width, height} = this.getWH()
        // console.log(this.player.resize,this.player.resize, width, height, 1111)
        this.player.resize && this.player.resize(width, height)
      }, 200)
    }
  }
}
</script>

<style scoped lang="less">
//button.css
.controller li {
  position: relative;
  float: left;
  border: 15px solid #404040;
  color: #404040;
  height: 0;
  width: 0;
  -webkit-border-radius: 100%;
  -moz-border-radius: 100%;
  -o-border-radius: 100%;
  border-radius: 100%;
  margin: 0 5px;
}

.controller li a {
  border-style: solid;
  text-indent: -9999px;
  position: absolute;
  top: -8px;
  left: -5px;
}

.playBtn a {
  border-color: transparent transparent transparent #fff;
  border-width: 8px 0 8px 12px;
  width: 0;
  height: 0;
}

.pauseBtn a {
  border-color: transparent white;
  border-width: 0 3px;
  height: 15px;
  width: 6px;
  left: -6px;
}

.stopBtn a {
  border: 7px solid #fff;
  height: 0;
  width: 0;
  left: -7px;
  top: -7px;
}

.forwardBtn a {
  border-left-width: 8px;
  left: 1px;
}

.forwardBtn a:first-child {
  margin-left: -7px;
}

.rewindBtn a {
  border-width: 8px 8px 8px 0;
  border-color: transparent #fff transparent transparent;
  width: 0;
  height: 0;
}

.rewindBtn a:first-child {
  margin-left: -7px;
}

.ejectBtn a.arrow {
  border-width: 0 8px 8px 8px;
  border-color: transparent transparent #fff transparent;
  top: -26px;
  left: -8px;
}

.ejectBtn a.dash {
  border-width: 0 0 4px;
  border-color: transparent transparent #fff;
  height: 0;
  width: 16px;
  left: -8px;
  top: 4px;
}


//index.css

.player-container {
  position: relative;
  /*width: 960px;
  height: 540px;*/
  /*overflow: hidden;*/
}

.bottom-container {
  margin-top: 600px;
}

.controller {
  background-color: black;
  /*background-image: linear-gradient(
      to right, red 50px, yellow, blue, green);*/
  /*background-position: 100% 50%;*/
  width: 100%;
  min-height: 50px;
  position: absolute;
  /*float: bottom;*/
  z-index: 99999;
  left: 0;
  bottom: 0;
}

/*.controller:hover {
    background-color: white;
}*/

.operate-container {
  min-height: 20px;
  padding: 3px;
  /*position: relative;
    top: 50%;
    transform: translateY(-50%);*/
}

.muteBtn {
  color: white;
}

.progressVoice {
  float: left;
  width: 50%;
  margin-top: 0.2rem;
  /*border: 10px solid rgba(255,255,255,0);*/
  color: #d9d9d9;
  height: 10px;

  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  border: none;
}

.progressVoice::-moz-progress-bar {
  background-color: #d9d9d9;
}

.progressVoice::-webkit-progress-value {
  background-color: #d9d9d9;
}

.coverLayer {
  width: 100%;
  height: 100%;
  padding-top: 300px;
  z-index: 10000;
  position: absolute;
  top: 0px;
  left: 0px;
  background-color: rgba(0, 0, 0, 0.5);
}

.coverLayerBtn {
  width: 20%;
  height: 200px;
  border-radius: 50px;
}


.ptsLabel {
  font-size: 15px;
  color: white;
  background: rgb(0, 0, 0);
  /*border: 1px solid white;*/
  float: left;
  /*border-radius: 7px;*/
  padding: 1px;
  margin-top: 4px;
  margin-left: 5px;
}

.voice-div {
  color: white;
  /*background: rgb(0, 0, 0);
  border: 2px solid white;
  border-radius: 7px;
  padding-left: 20px;*/
  width: 18%;
  float: left;
  margin-top: 3px;
  margin-left: 10px;

  /*position: relative;*/
  /*margin-top: 50%;*/
  /*transform: translateY(-50%);*/
}

.voice-div > span {
  /*font-size: 15px;*/
  float: left;
  color: white;
  margin-right: 5px;
}

.voice-div > progress {
  margin-top: 8px;
}

.fullScreenBtn {
  /*font-size: 10px;*/
  float: right;
  margin-top: 3px;
  margin-right: 5px;
  color: white;
  /*background: #ffffff;*/
  /*border: 2px solid white;*/
  /*text-align: center;*/
  /*line-height: 20px;*/
  height: 24px;
  /*font-weight: bold;*/
  /*border-radius: 7px;*/
}

.showLabel {
  height: 18px;
  font-size: 8px;
  color: white;
  background: rgb(0, 0, 0);
  border-bottom: 1px solid #666666;
  /*border-radius: 7px;*/
  padding-top: 1px;
  padding-left: 5px;
  padding-right: 5px;
  float: right;
  margin-top: 5px;
  margin-right: 5px;
}

//progress.css
.progress-common {
  height: 10px; /** same as progress-contaniner **/
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px;
  border: none;
  margin-bottom: 2px;
}

.progress-contaniner {
  z-index: 1000;
  width: 100%;
  background-color: #666666;
}

.cachePts {
  position: absolute;
  z-index: 1001;
  width: 0px;
  /*margin-top: 0.125rem;*/
  background-color: #d9d9d9;
}

/*.cachePts::-moz-progress-bar {
    background-color: #e9e9e9;
}
.cachePts::-webkit-progress-value {
    background-color: #e9e9e9;
}*/

.progressPts {
  position: absolute;
  z-index: 1002;
  width: 0px;
  /*margin-top: 0.125rem;*/
  background-color: rgba(246, 79, 30, 255);
}

/*.progressPts::-moz-progress-bar {
    background-color: yellow;
}
.progressPts::-webkit-progress-value {
    background-color: yellow;
}*/

</style>
<style scoped lang="less">
// 自定義樣式
.player-container {
  background: black;

  /deep/ .el-loading-spinner {
    margin-top: 0;
    transform: translateY(-50%);

    i {
      font-size: 10em;
      color: #ffffff;
      font-weight: bold;
    }
  }
}

</style>

player20220916.js

import H265webjsModule from '../../../../static/h265webjs/index'

const SHOW_LOADING = "加載中...";
const SHOW_DONE = "已就緒";

function durationFormatSubVal(val) {
  let valStr = val.toString();
  if (valStr.length < 2) {
    return '0' + valStr;
  }
  return valStr;
}

function durationText(duration) {
  if (duration < 0) {
    return "Play";
  }
  let durationSecInt = Math.round(duration);
  return durationFormatSubVal(Math.floor(durationSecInt / 3600))
    + ":" + durationFormatSubVal(Math.floor((durationSecInt % 3600) / 60))
    + ":" + durationFormatSubVal(Math.floor(durationSecInt % 60));
}

const getMsTime = () => {
  return new Date().getTime();
};

/***************************************************
 *
 *
 *
 * 1. H.265/HEVC MP4/FLV/HLS/TS
 * Demo for create player(MP4/FLV/HLS/TS)
 * 點播/直播播放器創(chuàng)建Demo(MP4/FLV/HLS/TS)
 *
 *
 *
 ***************************************************/
// clear cache count
H265webjsModule.clear();
export function makeH265webjs(videoURL, config, wrapBox) {
  let configEventCallback = config.event // 官網(wǎng)事件回調
  let playerId = config.player;

  let playerObj = H265webjsModule.createPlayer(videoURL, config);

  let playerDom = document.querySelector('#' + playerId);
  let playerCont = document.querySelector(wrapBox);
  let controllerCont = document.querySelector(wrapBox + ' .controller');
  let progressCont = document.querySelector(wrapBox + ' .progress-contaniner');
  let progressContW = progressCont.offsetWidth;
  let cachePts = progressCont.querySelector(wrapBox + ' .cachePts');
  let progressPts = progressCont.querySelector(wrapBox + ' .progressPts');
  let progressVoice = document.querySelector(wrapBox + ' .progressVoice');
  let playBar = document.querySelector(wrapBox + ' .playBtn');
  let playBtn = playBar.getElementsByTagName('a')[0];
  let showLabel = document.querySelector(wrapBox + ' .showLabel');
  let ptsLabel = document.querySelector(wrapBox + ' .ptsLabel');
  // let coverToast = document.querySelector(wrapBox + ' .coverLayer');
  // let coverBtn = document.querySelector(wrapBox + ' .coverLayerBtn');
  let muteBtn = document.querySelector(wrapBox + ' .muteBtn');
  // let debugYUVBtn     = document.querySelector('#debugYUVBtn');
  // let debugYUVATag    = document.querySelector('#debugYUVUrl');
  let fullScreenBtn = document.querySelector(wrapBox + ' .fullScreenBtn');
  let mediaInfo = null;

  playBtn.disabled = true;
  // playBar.textContent = '>';
  showLabel.textContent = SHOW_LOADING;
  playerCont.style.width = config.width + 'px';
  playerCont.style.height = config.height + 'px';
  controllerCont.style.width = config.width + 'px';

  let muteState = false;

  // controllerCont.style.left = playerContainer.clientLeft;
  // controllerCont.style.bottom = playerContainer.clientBottom;
  // alert(playerContainer.clientLeft);

  let playAction = () => {
    console.log("is playing:", playerObj.isPlaying());
    if (playerObj.isPlaying()) {
      console.log("bar pause============>");
      // playBar.textContent = '>';
      playBar.setAttribute('class', 'playBtn');
      playerObj.pause();
    } else {
      // playBar.textContent = '||';
      playBar.setAttribute('class', 'pauseBtn');
      playerObj.play();
    }
  };

  playerCont.onmouseover = function () {
    controllerCont.hidden = false;
  };

  playerCont.onmouseout = function () {
    controllerCont.hidden = true;
  };

  playerDom.onmouseup = function () {
    playAction();
  };

  playBtn.onclick = () => {
    playAction();
  };

  // 喇叭點擊
  muteBtn.onclick = () => {
    // console.log(playerObj.getVolume());
    if (muteState === true) {
      playerObj.setVoice(1.0);
      progressVoice.value = 100;
    } else {
      playerObj.setVoice(0.0);
      progressVoice.value = 0;
    }
    muteState = !muteState;
  };

  fullScreenBtn.onclick = () => {
    playerObj.fullScreen();
    // setTimeout(() => {
    //     playerObj.closeFullScreen();
    // }, 2000);
  };

  // 進度條點擊
  // progressCont.addEventListener('click', (e) => {
  //   showLabel.textContent = SHOW_LOADING;
  //   let x = e.pageX - progressCont.getBoundingClientRect().left; // or e.offsetX (less support, though)
  //   let y = e.pageY - progressCont.getBoundingClientRect().top;  // or e.offsetY
  //   let clickedValue = x * progressCont.max / progressCont.offsetWidth;
  //   // alert(clickedValue);
  //   playerObj.seek(clickedValue);
  // });

  // 聲音進度條點擊
  progressVoice.addEventListener('click', (e) => {
    let x = e.pageX - progressVoice.getBoundingClientRect().left; // or e.offsetX (less support, though)
    let y = e.pageY - progressVoice.getBoundingClientRect().top;  // or e.offsetY
    let clickedValue = x * progressVoice.max / progressVoice.offsetWidth;
    progressVoice.value = clickedValue;
    let volume = clickedValue / 100;
    // alert(volume);
    // console.log(
    //     progressVoice.offsetLeft, // 209
    //     x, y, // 324 584
    //     progressVoice.max, progressVoice.offsetWidth);
    playerObj.setVoice(volume);
  });

  playerObj.onSeekStart = (pts) => {
    showLabel.textContent = SHOW_LOADING + " seek to:" + parseInt(pts);
  };

  playerObj.onSeekFinish = () => {
    showLabel.textContent = SHOW_DONE;
  };

  playerObj.onPlayFinish = () => {
    console.log("============= FINISHED ===============");
    // playBar.textContent = '>';
    playBar.setAttribute('class', 'playBtn');
    // playerObj.release();
    // console.log("=========> release ok");
  };

  playerObj.onRender = (width, height, imageBufferY, imageBufferB, imageBufferR) => {
    console.log("on render");
  };

  playerObj.onOpenFullScreen = () => {
    console.log("onOpenFullScreen");
  };

  playerObj.onCloseFullScreen = () => {
    console.log("onCloseFullScreen");
  };

  // Seek完成
  playerObj.onSeekFinish = () => {
    showLabel.textContent = SHOW_DONE;
  };

  // 當前正在緩存幀數(shù)據(jù)
  playerObj.onLoadCache = () => {
    showLabel.textContent = "Caching...";
  };

  // 幀數(shù)據(jù)緩存完成
  playerObj.onLoadCacheFinshed = () => {
    showLabel.textContent = SHOW_DONE;
  };

  // 播放器封面圖加載完成
  playerObj.onReadyShowDone = () => {
    console.log("onReadyShowDone");
    showLabel.textContent = "Cover Img OK";
  };

  // 媒體文件當前加載成功鹉梨,可以進行播放
  playerObj.onLoadFinish = () => {
    if(configEventCallback && configEventCallback.onLoadFinish){
      configEventCallback.onLoadFinish()
    }
    playerObj.setVoice(1.0);
    mediaInfo = playerObj.mediaInfo();
    console.log("mediaInfo===========>", mediaInfo);
    /*
    meta:
        durationMs: 144400
        fps: 25
        sampleRate: 44100
        size: {
            width: 864,
            height: 480
        },
        audioNone : false
    videoType: "vod"
    */
    if (mediaInfo.meta.isHEVC === false) {
      console.log("is not HEVC/H.265 media!");
      //coverToast.removeAttribute('hidden');
      //coverBtn.style.width = '100%';
      //coverBtn.style.fontSize = '50px';
      //coverBtn.innerHTML = 'is not HEVC/H.265 media!';
      //return;
    }
    //console.log("is HEVC/H.265 media.");

    playBtn.disabled = false;

    if (mediaInfo.meta.audioNone) {
      progressVoice.value = 0;
      progressVoice.style.display = 'none';
    } else {
      playerObj.setVoice(0.5);
    }

    if (mediaInfo.videoType == "vod") {
      cachePts.max = mediaInfo.meta.durationMs / 1000;
      progressCont.max = mediaInfo.meta.durationMs / 1000;
      ptsLabel.textContent = durationText(0) + '/' + durationText(progressCont.max);
    } else {
      cachePts.hidden = true;
      progressCont.hidden = true;
      ptsLabel.textContent = 'LIVE';

      if (mediaInfo.meta.audioNone === true) {
        // playBar.textContent = '||';
       // playerObj.play();
      } else {

        // coverToast.removeAttribute('hidden');
        // coverBtn.onclick = () => {
        //   // playBar.textContent = '||';
        //   playAction();
        //   coverToast.setAttribute('hidden', 'hidden');
        // };
      }

    }

    showLabel.textContent = SHOW_DONE;
  };

  // 播放器緩沖進度回調 -- no
  playerObj.onCacheProcess = (cPts) => {
    // 追幀設置
    // if(playerObj.isPlaying()){
    //   // setInterval(()=>{
    //   //   playerObj.seek(cPts);
    //   // }, 1000)
    // }


    // console.log("onCacheProcess => ", cPts, playerObj.mediaInfo().meta.durationMs);
    try {
      // cachePts.value = cPts;
      let precent = cPts / progressCont.max;
      let cacheWidth = precent * progressContW;
      // console.log(precent, precent * progressCont.offsetWidth);
      cachePts.style.width = cacheWidth + 'px';
    } catch (err) {
      console.log(err);
    }
  };

  // 播放器當前播放PTS時刻更新
  playerObj.onPlayTime = (videoPTS) => {

    // console.log(videoPTS, playerObj.mediaInfo().meta.durationMs, 111)

    if (mediaInfo.videoType == "vod") {
      // progressPts.value = videoPTS;
      let precent = videoPTS / progressCont.max;
      let progWidth = precent * progressContW;
      // console.log(precent, precent * progressCont.offsetWidth);
      progressPts.style.width = progWidth + 'px';

      ptsLabel.textContent = durationText(videoPTS) + '/' + durationText(progressCont.max);
    } else {
      // ptsLabel.textContent = durationText(videoPTS) + '/LIVE';
      ptsLabel.textContent = '/LIVE';
    }
  };

  playerObj.do();
  return playerObj;
};
// export function exposeAll(){
//   H265webjsModule.clear();
// }

注意這個路徑改成你本地的:


image.png

3.使用:
h265webjs.vue

<template>
  <div class="wrap">
    <div class="box-top">
 
    <div style="margin-top: 20px;margin-bottom: 20px;">
      <template v-for="(item,index) in configArr">
        切換視頻源{{ index + 1 }}:
        <el-select :value="item.url" placeholder="請選擇" :key="`select-${index}`" @change="(val)=>changeSrc(item,val)">
          <el-option
            v-for="item in options"
            :key="item.value"
            :label="item.label"
            :value="item.value">
          </el-option>
        </el-select>
      </template>
    </div>
    <div class="flex-box">
      <template v-for="(item,index) in configArr">
        <h265webjs :key="index"
                   v-if="item.canShow"
                   :videoBoxClass="item.videoBoxClass"
                   :config="item"
                   :style="index===1?'margin-left: 20px;':''"/>
      </template>
    </div>
  </div>
</template>

<script>
import h265webjs from './h265webjs/index'

export default {
  name: 'videoPlay3',
  components: {
    h265webjs
  },
  data() {
    let options = this.$videoTestSrcOption()
    return {
      options: options,

      configArr: [
        {
          player: "glplayer1",
          // width: 960,
          // height: 540,
          extInfo: {
            moovStartFlag: true,
            coreProbePart: 0.1,
            ignoreAudio: 0,
          },
          url: options[0].value,
          // url: './static/video/HEVC(H.265).m3u8',
          // url: './static/video/AVC(H.264).m3u8'
          // url: './static/video/test.mp4'
          // url: this.$host() + '/videoSrc/obj/media-fe/xgplayer_doc_video/hls/xgplayer-demo.m3u8',
          videoBoxClass: 'h265web-box1',
          canShow: true // 視頻源更改后讳癌,強制刷新播放器
        },
        {
          player: "glplayer2",
          extInfo: {
            moovStartFlag: true,
            coreProbePart: 0.1,
            ignoreAudio: 0,
          },
          url: options[0].value,
          videoBoxClass: 'h265web-box2',
          canShow: true // 視頻源更改后穿稳,強制刷新播放器
        }
      ],
    }
  },
  mounted() {
  },
  beforeDestroy() {
    // destoryPlayerServer(this.playerObject)
  },
  methods: {
    changeSrc(item, newUrl) {
      if (item.url !== newUrl) {
        item.url = newUrl
        // 視頻源更改后存皂,v-if強制刷新播放器
        item.canShow = false

        this.$nextTick(() => {
          item.canShow = true
        })
      }
    },
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.flex-box {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;

  > div {
    width: 100%;
    flex: auto;
  }
}

.wrap{
  padding: 20px;
  text-align: left;
  .box-top{
    background: #7cb1cc;
    padding: 20px;
    line-height: 30px;
  }
}
</style>

這個option的地址改成你的測試視頻源地址即可:


image.png

這里還有個注意點,切換視頻源是用的v-if + $nextTick的方式逢艘,為了避免不必要的報錯旦袋,其實我一開始是組件內部監(jiān)聽的方式,但后面因為報錯還是改成了這種它改。


image.png

4.參數(shù)說明:


image.png

image.png

附github demo 地址

因為不止一個小伙伴私下里找我了疤孕,這里還是貼上源碼,可以直接運行方便看到效果央拖。里面有兩個版本祭阀,如果遇到最新版播放卡頓或內存溢出問題可暫時切換為低版本【內存溢出依舊存在,作者后續(xù)看到issue并提交了一版鲜戒,但我還沒有機會驗證】专控,該版本全屏報錯問題已在代碼中修復。


image.png

啰嗦一句

我提供的demo中內存溢出問題依舊存在遏餐,只是低版本會好很多伦腐。當時我用的已經(jīng)是最新版本tag v20220916了,最后我的解決方式選擇低版本并“定時刷新頁面”釋放內存失都。然后我給作者提了issue柏蘑,不想定時刷新的小伙伴可以試下tag v20220916后面的版本幸冻,若問題依舊存在可在github上向 h265web.js作者闡明問題(我這邊因為項目結束原因沒機會再測試了)

image.png

若對你有幫助,請點個贊吧咳焚,謝謝支持洽损!
本文地址:http://www.reibang.com/p/1f98a9b5e316,轉載請注明出處黔攒,謝謝趁啸。

參考:
h265web.js github

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市督惰,隨后出現(xiàn)的幾起案子不傅,更是在濱河造成了極大的恐慌,老刑警劉巖赏胚,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件访娶,死亡現(xiàn)場離奇詭異,居然都是意外死亡觉阅,警方通過查閱死者的電腦和手機崖疤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來典勇,“玉大人劫哼,你說我怎么就攤上這事「铙希” “怎么了权烧?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長伤溉。 經(jīng)常有香客問我般码,道長,這世上最難降的妖魔是什么乱顾? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任板祝,我火速辦了婚禮,結果婚禮上走净,老公的妹妹穿的比我還像新娘券时。我一直安慰自己,他們只是感情好伏伯,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布橘洞。 她就那樣靜靜地躺著,像睡著了一般舵鳞。 火紅的嫁衣襯著肌膚如雪震檩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音抛虏,去河邊找鬼博其。 笑死,一個胖子當著我的面吹牛迂猴,可吹牛的內容都是我干的慕淡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼沸毁,長吁一口氣:“原來是場噩夢啊……” “哼峰髓!你這毒婦竟也來了?” 一聲冷哼從身側響起息尺,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤携兵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搂誉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體徐紧,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年炭懊,在試婚紗的時候發(fā)現(xiàn)自己被綠了并级。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡侮腹,死狀恐怖嘲碧,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情父阻,我是刑警寧澤愈涩,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站至非,受9級特大地震影響钠署,放射性物質發(fā)生泄漏糠聪。R本人自食惡果不足惜荒椭,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舰蟆。 院中可真熱鬧趣惠,春花似錦、人聲如沸身害。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽塌鸯。三九已至侍瑟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涨颜。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工费韭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庭瑰。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓星持,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弹灭。 傳聞我的和親對象是個殘疾皇子督暂,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內容