index.vue 10 KB
<template>
  <div ref="container"
    :data-player-type="this.playerType"
    :class="containerClassName"
		v-loading="playLoading"
		element-loading-text="加载中..."
		element-loading-spinner="el-icon-loading"
		element-loading-background="rgba(0, 0, 0, 0.8)"
  >
    <div v-if="previewStyles" class="vion-player-preview" :style="previewStyles"></div>
  </div>
	
</template>

<script>
import { merge, throttle } from 'lodash';
// checkSupportMSEHevc checkSupportSIMD, checkSupportWCSHevc
import { getWatermarkCanvasImg } from './utils';
import { getPlayerConfig, getRotateConfig, getMirrorRotate, getPlaybackConfig } from './config';

export default {
  name: 'VionPlayer',
  props: {
    playUrl: {
      type: String,
      default: '', // 若配置,则播放器加载后,自动播放
    },
    watermarkText: {
      type: String,
      default: '',
    },
    hideControls: {
      type: Boolean,
      default: false,
    },
    showPtz: {
      type: Boolean,
      default: true,
    },
    // 是否回放。true 表示直播回放
    isPlayback: {
      type: Boolean,
      default: false,
    },
    // 是否是小程序
		isXCXPage: {
		  type: Boolean,
		  default: false,
		},
    // 预览图片地址
    previewUrl: {
      type: String,
      default: '',
    },
		// 预览图片地址
		watermarkOpacity: {
		  type: String,
		  default: '0.3',
		},
  },
  data() {
    return {
			playLoading:true,
      // eslint-disable-next-line
      _jessibuca: null,
      playOriginUrl: '',
      rotateNum: 0,
      mirrorIndex: 0,
      playerType: 'live', // 播放器类型:live playback

      player: {
        height: 0,
        width: 0,
      },
    }
  },
  computed: {
    containerClassName() {
      // 样式不支持响应式,因为会覆盖掉播放器自身添加的样式
      const list = ['vion-player'];
      if (this.hideControls) list.push('hide-controls');
      // if (this.playerType === 'playback') list.push('playback-status');
      return list;
    },
    previewStyles() {
      if (this.previewUrl) {
        return `background-image: url(${this.previewUrl});`
      } else {
        return null;
      }
    },
  },
  mounted() {
    this.init(this.playUrl);
		setTimeout(()=>{
			this.playLoading = false;
		},10000)
    // console.log('mounted', window.JessibucaPro.EVENTS);
  },
  beforeDestroy() {
    this.destroy();
  },
  methods:{
    init(url, params = {}) {
      const options = merge(getPlayerConfig(), {
        container: this.$refs.container,
        useWebFullScreen: this.isXCXPage,
        fullscreenWatermarkConfig: {
					opacity:this.watermarkOpacity*1||0.3,
          text: this.watermarkText || '',
        },
        operateBtns: {
          play: !this.isXCXPage,
          audio: true,
          ptz: this.showPtz, // 云台
          zoom: true,// 电子放大
          performance: !this.isXCXPage, // 视频流信息展示
          record: !this.isXCXPage, // 录制
          scale: !this.isXCXPage, // 显示模式:拉伸、缩放、正常
        },
        extendOperateBtns: [getRotateConfig.call(this), getMirrorRotate.call(this)],
      });

      console.log('init-options', options);
      this._jessibuca = new window.JessibucaPro(options);
      console.log('_jessibuca', this._jessibuca);

      this.registerEvent(this._jessibuca);
      if (url) {
        // 示例上有延迟写法
        this.delayPlay(url);
      }
    },
    registerEvent(playerIns) {
      playerIns.on(JessibucaPro.EVENTS.videoInfo, (data) => {
        console.log('width:', data.width, 'height:', data.height);
        this.player.width = data.width;
        this.player.height = data.height;
      });

      playerIns.on('play', (flag) => {
        // console.log('on-play', flag);
        // this.playing = true;
      });
      playerIns.on('pause', (flag) => {
        // console.log('on-pause', flag);
        // this.playing = false;
      });

      // 断线重连
      playerIns.on(JessibucaPro.EVENTS.playFailedAndPaused, (e) => {
        console.log('playFailedAndPaused', e);
				let isOnlyBack = window.localStorage.getItem('isOnlyBack')
				if(isOnlyBack&&isOnlyBack>0) {
					let isLoad = window.localStorage.getItem('isLoad')
					if(isLoad&&isLoad>0) {
						window.localStorage.setItem('isLoad',0)
					} else {
						window.localStorage.setItem('isLoad',1)
						window.location.reload();
					}
				}
        // this.replay();
      });

      playerIns.on(JessibucaPro.EVENTS.ptz, (arrow) => {
        // console.log('ptz', arrow);
        this.$emit('ptz-control', arrow);
      });

      // 改变倍速事件
      playerIns.on('playbackPreRateChange', (rate) => {
        // console.log('playbackPreRateChange', rate);
        this.$emit('rate-change', rate, this.setForward);
      });
      // 点击时间轴跳转事件
      playerIns.on('playbackSeek', (data) => {
        console.log('playbackSeek', data);
        // jessibuca.setPlaybackStartTime(data.ts);
      });

      // 电子放大
      this.$refs.container.addEventListener('wheel', throttle(this.handleWheel, 200));
    },
    setForward(rate) {
      this._jessibuca.forward(rate);
    },
    getWatermarkBase64() {
      const data = getWatermarkCanvasImg(150, 48, '文安智能');
      console.log('getWatermarkBase64', data);
    },
    delayPlay(url, isPlayback = false) {
      setTimeout(() => {
        if (isPlayback) {
          this._jessibuca.playback(url, getPlaybackConfig());
          this.playerType = 'playback';
        } else {
          this._jessibuca.play(url);
          this.playerType = 'live';
        }
        this.playOriginUrl = url;
				this.playLoading = false;
      }, 150);
    },
    // 获取是否是回放状态
    getPlaybackStatus(status) {
      // 传入参数 优先级 大于属性
      return status || this.isPlayback;
    },
    // true 放大,false 缩小
    zoom(direction = true) {
      // temp1.player.zoom.expandPrecision()
      const targetPlayer = this._jessibuca.player;
      // zooming 表示处于电子放大状态
      if (targetPlayer && targetPlayer.zooming && targetPlayer.zoom) {
        if (direction) targetPlayer.zoom.expandPrecision();
        else targetPlayer.zoom.narrowPrecision();
      }
    },
    handleWheel(event) {
      console.log('handleWheel', event);
      event.stopPropagation();
      const direction = event.deltaY < 0;
      this.zoom(direction);
    },

    // 暴露方法
    play(url, isPlayback = false) {
      if (url) {
        const playbackStatus = this.getPlaybackStatus(isPlayback);
        // 播放地址不同,则先停止再播放。地址相同,相当于暂停后,继续播放
        // 若是回放,则直接播放
        if (!playbackStatus && this.playOriginUrl && this.playOriginUrl !== url) {
          // 切换视频流地址,先销毁当前实例,再创建新实例,避免内存溢出
          this.replay(url);
        } else {
          this.delayPlay(url, playbackStatus);
        }
      } else {
        // console.error('The url is not valid');
      }
    },
    replay(url) {
      this.destroy().then(() => {
        this.init(url);
      });
    },
    pause() {
      this._jessibuca.pause();
    },
    stop() {
      this._jessibuca.close();
    },
    destroy() {
      if (this._jessibuca) {
        return this._jessibuca.destroy().then(() => {
          // this._jessibuca = null;
        }).catch(() => {
          return Promise.reject(new Error('请稍后重试'));
        });
      } else {
        return Promise.resolve(true);
      }
    },
    rotate(num) {
      let targetRotate = 0;
      if (Number.isInteger(num)) {
        targetRotate = num;
      } else {
        this.rotateNum++;
        targetRotate = (this.rotateNum % 4) * 90;
      }
      this._jessibuca.setRotate(targetRotate);
    },
    mirrorRotate() {
      // , 'vertical'
      const typeList = ['', 'level'];
      this.mirrorIndex++;
      const targetType = typeList[this.mirrorIndex % 2];
      this._jessibuca.setMirrorRotate(targetType);
    },
    screenshot(filename = '', format = 'jpeg', quality = 1, type = 'base64') {
      return new Promise((resolve, reject) => {
        // 如果是播放状态
        const isPlaying = this._jessibuca.isPlaying();
        console.log('screenshot', isPlaying);
        if (!this.player.height || !this.player.width || !isPlaying) {
          reject(new Error('播放失败,请重试!'));
        }

        // 创建一个新的图片对象
        const image = new Image();

        // 当图像加载完成后执行
        image.onload = () => {
          // 创建一个 Canvas 元素
          const canvas = getWatermarkCanvasImg(this.player.width, this.player.height, this.watermarkText, image);;

          // 将带有水印的 Canvas 转换回 base64
          const watermarkedBase64 = canvas.toDataURL('image/jpeg',0.5);

          // 现在,watermarkedBase64 包含了带有水印的 base64 图像数据
          // console.log(watermarkedBase64);
          resolve(watermarkedBase64);
        };
        image.error = function() {
          reject(new Error('图片加载失败!'));
        }

        // 设置图像的 src 属性,加载图像
        image.src = this._jessibuca.screenshot(filename, format, quality, type);
      });
    },
    screenshotOrigin(filename = '', format = 'jpeg', quality = 1, type = 'base64') {
      return this._jessibuca.screenshot(filename, format, quality, type);
    }
  },
}
</script>

<style lang="scss" scoped>
.vion-player {
  height: 100%;
  width: 100%;
  background-color: rgba(13, 14, 27, 0.7);
  // 时间轴
  ::v-deep(.jessibuca-control-progress-simple) {
    display: none;
  }
  // 电子放大
  ::v-deep(.jessibuca-zoom-controls) {
    // display: none !important;
  }
	::v-deep(.jessibuca-playback-control-time) {
	  display: none !important;
	}
	
  // 云台控制
  &[data-player-type="playback"] {
    ::v-deep(.jessibuca-ptz) {
      display: none !important;
    }
  }
}
.hide-controls {
  ::v-deep(.jessibuca-controls) {
    display: none !important;
  }
}
.vion-player-preview {
  height: 100%;
  width: 100%;
  background-size: cover;
  background-repeat: no-repeat;
}
</style>