cameraItem.vue 4.41 KB
<template>
  <div class="go-content-box" :style="{ width: w + 'px', height: h + 'px' }">
    <video crossOrigin="anonymous" :id="`my-player${index}`" ref="videoRef"
      class="video-js my-video vjs-theme-city vjs-big-play-centered">
    </video>
  </div>
</template>
<script setup lang="ts">
import { onMounted, ref, onUnmounted, watch, unref } from 'vue'
import videojs from 'video.js'
import 'videojs-flvjs-es6'
import type { VideoJsPlayerOptions } from 'video.js'
import 'video.js/dist/video-js.min.css'
import { getJwtToken, getShareJwtToken } from '@/utils/external/auth'
import { isShareMode } from '@/views/share/hook'
import { getOpenFlvPlayUrl, closeFlvPlay } from '@/api/external/flvPlay'
import { useFingerprint } from '@/utils/external/useFingerprint'
import { GetResult } from '@fingerprintjs/fingerprintjs'

const props = defineProps({
  sourceSrc: {
    type: String
  },
  name: {
    type: String
  },
  avatar: {
    type: String
  },
  w: {
    type: Number,
    default: 300
  },
  h: {
    type: Number,
    default: 300
  },
  index: {
    type: Number
  }
})

enum VideoPlayerType {
  m3u8 = 'application/x-mpegURL',
  mp4 = 'video/mp4',
  webm = 'video/webm',
  flv = 'video/x-flv',
}

const isRtspProtocol = (url: string) => {
  const reg = /^rtsp:\/\//g;
  return reg.test(url);
};

const getVideoTypeByUrl = (url = '') => {
  try {
    const { protocol, pathname } = new URL(url)

    if (protocol.startsWith('rtsp:')) return VideoPlayerType.flv

    const reg = /[^.]\w*$/
    const mathValue = pathname.match(reg) || []
    const ext = mathValue[0] as keyof typeof VideoPlayerType || 'webm'
    const type = VideoPlayerType[ext]
    return type ? type : VideoPlayerType.webm
  } catch (error) {
    console.error(error)
    return VideoPlayerType.webm
  }
};


// video标签
const videoRef = ref<HTMLElement | null>(null)

// video实例对象
let videoPlayer: videojs.Player | null = null

const fingerprintResult = ref<Nullable<GetResult>>(null)

//options配置
const options: VideoJsPlayerOptions & Recordable = {
  language: 'zh-CN', // 设置语言
  controls: true, // 是否显示控制条
  preload: 'auto', // 预加载
  autoplay: true, // 是否自动播放
  fluid: false, // 自适应宽高
  poster: props?.avatar || '',
  // src: getSource() || '', // 要嵌入的视频源的源 URL
  sources: [],
  muted: true,
  userActions: {
    hotkeys: true
  },
  techOrder: ['html5', 'flvjs'],
  flvjs: {
    mediaDataSource: {
      isLive: true,
      cors: true,
      withCredentials: false,
    },
  },
}


const { getResult } = useFingerprint()
async function getSource() {
  fingerprintResult.value = await getResult()
  let src = props.sourceSrc || ''

  if (isRtspProtocol(props.sourceSrc!)) {
    src = getOpenFlvPlayUrl(src, unref(fingerprintResult)?.visitorId || '')
  }

  return [
    {
      type: getVideoTypeByUrl(props.sourceSrc),
      src
    }
  ]
}

// 初始化videojs
const initVideo = async () => {
  if (videoRef.value) {
    // 创建 video 实例   
    options.sources = await getSource()
    if (options.sources && options.sources.length) {

      if (isRtspProtocol(props.sourceSrc || '')) {
        options.flvjs = {
          ...(options.flvjs || {}),
          config: {
            headers: {
              'X-Authorization': `Bearer ${isShareMode() ? getShareJwtToken() : getJwtToken()}`,
            }
          }
        }
      }
      videoPlayer = videojs(videoRef.value, options)
    }
  }
}

watch(
  () => props.sourceSrc,
  async (newData: any) => {
    const result = await getSource()
    // props.sourceSrc = newData
    videoPlayer?.src(result) as any
    videoPlayer?.play()
  },
  {
    immediate: true
  }
)

onMounted(() => {
  initVideo()
})

onUnmounted(() => {
  if (props.sourceSrc) {
    closeFlvPlay(props.sourceSrc, unref(fingerprintResult)!.visitorId!)
  }
  handleVideoDispose()
})

//播放
const handleVideoPlay = () => videoPlayer?.play()

const handleVideoDispose = () => videoPlayer?.dispose() && videoPlayer?.pause()
//暂停
defineExpose({
  handleVideoPlay,
  handleVideoDispose
})
</script>

<style lang="scss" scoped>
.go-content-box {
  display: flex;
  align-items: center;
  justify-content: center;

  .my-video {
    width: 100% !important;
    height: 100% !important;
    position: relative;
  }
}
</style>