video.vue 9.42 KB
<script lang="ts" setup>
  import { isNumber } from 'lodash';
  // import videoJs, { VideoJsPlayer, VideoJsPlayerOptions } from 'video.js';
  import 'video.js/dist/video-js.css';
  import { onMounted, onUnmounted, ref, unref } from 'vue';
  import { useDesign } from '/@/hooks/web/useDesign';
  // import { getJwtToken, getShareJwtToken } from '/@/utils/auth';
  // import { isShareMode } from '/@/views/sys/share/hook';
  import 'videojs-flvjs-es6';
  import {
    CaretUpOutlined,
    CaretRightOutlined,
    PauseOutlined,
    CaretDownOutlined,
    CaretLeftOutlined,
    ZoomInOutlined,
    ZoomOutOutlined,
  } from '@ant-design/icons-vue';
  import { Button } from 'ant-design-vue';
  import { nextTick } from 'vue';
  import { useMessage } from '/@/hooks/web/useMessage';
  import Jessibuca from './types/jessibuca';
  // import JessibucaDemo from './components/JessibucaDemo.vue';

  const { prefixCls } = useDesign('basic-video-play');

  // const props = defineProps<{
  //   options?: VideoJsPlayerOptions;
  //   withToken?: boolean;
  // }>();

  // const emit = defineEmits<{
  //   (event: 'ready', instance?: Nullable<VideoJsPlayer>): void;
  //   (event: 'onUnmounted'): void;
  // }>();

  const JessibucaRef = ref();

  // const videoPlayInstance = ref<Nullable<VideoJsPlayer>>();

  const playUrl = ref('http://flv.bdplay.nodemedia.cn/live/bbb.flv');

  const { createMessage } = useMessage();

  let jessibuca: Jessibuca | null = null;
  const container = ref(null);
  const createJessibuca = () => {
    jessibuca = new (window as any).Jessibuca({
      decoder: '/jessibuca/decoder.js',
      container: container.value, //播放器容器
      videoBuffer: 0.2, // 缓存时长
      isResize: true,
      isFullResize: true,
      text: '',
      loadingText: '加载中',
      hasAudio: false,
      debug: true,
      showBandwidth: true, // 显示网速
      operateBtns: {
        fullscreen: true,
        screenshot: true,
        play: true,
        audio: true,
      },
      controlAutoHide: true, // 底部控制台是否自动隐藏
      ifFlv: false,
      forceNoOffscreen: true, //是否不使用离屏模式(提升渲染能力)
      isNotMute: false, // 是否开启声音,默认是关闭声音播放的。
    }) as Jessibuca;
  };

  const videoInfo = ref<{ width: string | number; height: string | number }>({
    width: 640,
    height: 360,
  });
  //播放/暂停
  const handleClick = () => {
    console.log('播放/暂停', unref(isPlay));
    if (unref(isPlay)) {
      jessibuca?.pause();
    } else {
      jessibuca?.play();
    }
  };

  const handleTopClick = async () => {
    console.log(unref(JessibucaRef), 'JessibucaRef');
    console.log('上');
    handleControl('up');
    setTimeout(() => {
      handleControl('stop');
    }, 500);
  };

  const handleRightClick = () => {
    console.log('右');
  };

  const handleBottomClick = () => {
    console.log('下');
  };

  const handleLeftClick = () => {
    console.log('左');
  };

  // type 1:放大    2:缩小
  const handleScale = (type: number) => {
    console.log(type);
  };

  const handleControl = (direction: string) => {
    console.log(direction);
  };

  const isPlay = ref<Boolean | null | undefined>(true);

  // // 关闭视频
  const destroy = () => {
    if (jessibuca) {
      jessibuca.destroy();
    }
    createJessibuca();
    isPlay.value = false;
  };

  onMounted(async () => {
    createJessibuca();
    await nextTick();
    jessibuca?.play(playUrl.value);
    isPlay.value = jessibuca?.isPlaying();

    // 是否播放
    jessibuca?.on('play', function () {
      isPlay.value = true;
    });

    // 是否暂停
    jessibuca?.on('pause', () => {
      isPlay.value = false;
    });

    jessibuca?.on('videoInfo', (data) => {
      let { width, height } = data || {};
      width = isNumber(width) ? (`${width}px` as unknown as number) : width;
      height = isNumber(height) ? (`${height}px` as unknown as number) : height;
      videoInfo.value = {
        width,
        height,
      };
    });

    // 播放报错事件
    jessibuca?.on('error', (error) => {
      error == 'playError' && createMessage.warning('播放错误');
      error == 'fetchError' && createMessage.warning('http请求失败');
      error == 'websocketError' && createMessage.warning('websocket 请求失败');
      error == 'webcodecsH265NotSupport' && createMessage.warning('webcodecs 解码失败');
      error == 'mediaSourceH265NotSupport' && createMessage.warning('mediaSource 解码失败');
      error == 'wasmDecodeError' && createMessage.warning('wasm 解码失败');
    });
  });

  onUnmounted(() => {
    jessibuca && jessibuca.destroy();
  });

  defineExpose({
    handleDestroy: destroy,
  });
</script>

<template>
  <div :class="prefixCls" class="!w-full h-full flex" :style="videoInfo">
    <!-- <video
      ref="videoPlayEl"
      class="video-js vjs-big-play-centered vjs-show-big-play-button-on-pause !w-8/10 !h-full"
      muted
    >
    </video> -->

    <div class="video-js vjs-big-play-centered vjs-show-big-play-button-on-pause !w-8/10 !h-full">
      <div
        class="root video-js vjs-big-play-centered vjs-show-big-play-button-on-pause !w-full !h-full"
      >
        <div class="container-shell">
          <div id="container" ref="container"></div>
        </div>
      </div>
    </div>
    <div class="!w-2/10 bg-white flex items-center flex-col">
      <h1>云台控制</h1>

      <div class="home mt-5">
        <Button class="front-sty-center child center" shape="circle" @click="handleClick()">
          <PauseOutlined v-if="isPlay" class="child-icon" style="color: #fffbfb" />
          <CaretRightOutlined v-else class="child-icon" style="color: #fffbfb" />
        </Button>

        <div class="box">
          <div>
            <Button class="left-top in-block" @click="handleTopClick">
              <CaretUpOutlined class="icon-rotate child-icon" />
            </Button>
            <Button class="right-top in-block" @click="handleRightClick">
              <CaretRightOutlined class="icon-rotate child-icon" />
            </Button>
          </div>
          <div>
            <Button class="left-bottom in-block" @click="handleBottomClick">
              <CaretLeftOutlined class="icon-rotate child-icon" />
            </Button>
            <Button class="right-bottom in-block" @click="handleLeftClick">
              <CaretDownOutlined class="icon-rotate child-icon" />
            </Button>
          </div>

          <Button class="circle" @click="handleClick" />
        </div>
      </div>
      <div class="flex justify-center mt-8">
        <Button shape="circle" class="button-icon" @click="handleScale(1)">
          <ZoomInOutlined style="color: #315a9c; font-size: 1.5rem" />
        </Button>
        <Button shape="circle" class="ml-10 button-icon" @click="handleScale(2)">
          <ZoomOutOutlined style="color: #315a9c; font-size: 1.5rem" />
        </Button>
      </div>
    </div>
  </div>
</template>

<style lang="less" scoped>
  @prefix-cls: ~'@{namespace}-basic-video-play';

  .@{prefix-cls} {
    .vjs-error-display {
      .vjs-modal-dialog-content::after {
        content: '无法加载视频,原因可能是服务器或网络故障,也可能是格式不支持.';
      }
    }
  }

  .child {
    position: absolute;
    width: 3rem;
    height: 3rem;
    display: flex;
    justify-content: center;
    background: #e2dede;
    align-items: center;
    border: none;
  }

  .child-icon {
    font-size: 1.5rem;
    color: #fffbfb;
  }

  .button-icon {
    width: 3rem;
    height: 3rem;
    background: #f5f5f5;
    border: none;
  }

  .center {
    top: 50%;
    left: 50%;
    width: 4rem;
    height: 4rem;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: #5586d4;
  }

  .home {
    position: relative;
    width: 10rem;
    height: 10rem;
  }

  .box {
    transform: rotateZ(45deg);
    width: 10rem;
    height: 10rem;
  }

  .icon-rotate {
    transform: rotate(315deg);
  }

  .front-sty-center {
    position: absolute;
    top: 50%;
    z-index: 9999;
    left: 50%;
    transform: translate(-50%, -50%);
  }

  .circle {
    display: inline-block;
    border-radius: 50%;
    background-color: #5586d4;
    width: 4rem;
    height: 4rem;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }

  .in-block {
    display: inline-block;
    position: relative;
  }

  .left-top {
    width: 5rem;
    height: 5rem;
    border-radius: 5rem 0 0 0;
    background-color: #e2dede;
  }

  .right-top {
    width: 5rem;
    height: 5rem;
    border-radius: 0 5rem 0 0;
    background-color: #e2dede;
  }

  .left-bottom {
    width: 5rem;
    height: 5rem;
    border-radius: 0 0 0 5rem;
    background-color: #e2dede;
  }

  .right-bottom {
    width: 5rem;
    height: 5rem;
    border-radius: 0 0 5rem 0;
    background-color: #e2dede;
  }

  .root {
    display: flex;
    place-content: center;
  }

  .container-shell {
    position: relative;
    width: auto;
  }

  #container {
    background: rgba(13, 14, 27, 0.7);
    width: 640px;
  }

  .err {
    position: absolute;
    top: 40px;
    left: 10px;
    color: red;
  }

  .option {
    position: absolute;
    top: 4px;
    right: 10px;
    display: flex;
    place-content: center;
    font-size: 12px;
  }

  .option span {
    color: white;
  }

  .page {
    background: url(/bg.jpg);
    background-repeat: no-repeat;
    background-position: top;
  }

  @media (max-width: 720px) {
    #container {
      width: 90vw;
      height: 52.7vw;
    }
  }
</style>