Newer
Older
safe_production_front / src / views / bigScreen / jessibuca.vue
<script setup>
import { onMounted } from 'vue'
import { getDictByCode } from '@/api/system/dict'
const props = defineProps({
  id: {
    type: String,
  },
  showOperateBtns: {
    type: Boolean,
    default: false,
  },
})

// 定义变量
// const showOperateBtns = false // 是否显示按钮
const secretUserLevelList = ref([])
const forceNoOffscreen = true
let jessibuca = null
const isfullscreen = ref(false)
// 创建播放器的函数
function create() {
  console.log('执行jessibuca初始化', props.id)
  const $container = document.getElementById(props.id)
  if (!$container) {
    console.error('未找到播放器容器:', props.id)
    return
  }
  jessibuca = new Jessibuca({
    container: $container, // 播放器容器 若为 string ,则底层调用的是 document.getElementById('id')
    videoBuffer: 0.2, // 设置最大缓冲时长,单位秒,播放器会自动消除延迟
    isResize: false, //  1. 当为`true`的时候:视频画面做等比缩放后,高或宽对齐canvas区域,画面不被拉伸,但有黑边。 等同于 `setScaleMode(1)` 2. 当为`false`的时候:视频画面完全填充canvas区域,画面会被拉伸。等同于 `setScaleMode(0)`
    loadingText: '视频加载中', // 加载过程中文案
    useMSE: true, // 是否开启MediaSource硬解码 视频编码只支持H.264视频(Safari on iOS不支持)不支持 forceNoOffscreen 为 false (开启离屏渲染)
    useWCS: false, // 是否开启Webcodecs硬解码 视频编码只支持H.264视频 (需在chrome 94版本以上,需要https或者localhost环境) 支持 forceNoOffscreen 为 false (开启离屏渲染)
    debug: false, //  是否开启控制台调试
    showBandwidth: false, // 是否显示显示网速
    loadingTimeout: 60, // flv地址请求超时时间
    operateBtns: {
      // 配置操作按钮
      fullscreen: false, // 是否显示全屏按钮
      screenshot: false, // 是否显示截图按钮
      play: false, // 是否显示播放暂停按钮
      audio: false, // 是否显示声音按钮
      recorder: false, // 是否显示录制按
    },
    forceNoOffscreen, // 是否不使用离屏模式(提升渲染能力)
    isNotMute: false, // 是否开启声音,默认是关闭声音播放的
    hotKey: false, // 是否开启键盘快捷键 目前支持的键盘快捷键有:esc -> 退出全屏;arrowUp -> 声音增加;arrowDown -> 声音减少;
    keepScreenOn: false, // 开启屏幕常亮,在手机浏览器上, canvas标签渲染视频并不会像video标签那样保持屏幕常亮
    supportDblclickFullscreen: true, // 是否支持屏幕的双击事件,触发全屏,取消全屏事件

    hasAudio: false, // 如果不需要播放音频,可以设置hasAudio为false,这样就不会解码音频数据了,可以提升性能。
    autoWasm: true, // 在使用MSE或者Webcodecs 播放H265的时候,是否自动降级到wasm模式。
    controlAutoHide: false, // 底部控制台是否自动隐藏 只有鼠标聚焦到播放器内部才会显示,移除之后,会消失。
    wasmDecodeErrorReplay: true, // wasm解码报错之后,不再抛出异常,而是直接重新播放视频地址。
    wcsUseVideoRender: true, // webcodecs硬解码是否通过video标签渲染, 提升渲染性能。
  })

  jessibuca.on('videoInfo', (data) => {
    console.log('videoInfo:', 'width:', data.width, 'height:', data.width)
  })

  jessibuca.on('playFailedAndPaused', (error) => {
    console.log('playFailedAndPaused事件:监听流断掉', error)
  })

  jessibuca.on('timeout', (error) => {
    console.log('timeout当设定的超时时间内无数据返回,则回调:', error)
  })

  jessibuca.on('loadingTimeout', () => {
    console.log('loadingTimeout当play()的时候,如果没有数据返回,则回调: timeout')
  })

  jessibuca.on('pause', (flag) => {
    console.log('pause触发暂停事件')
  })

  jessibuca.on('fullscreen', (flag) => {
    console.log('当前是否全屏', flag)
    isfullscreen.value = flag
  })
  jessibuca.on('play', (flag) => {
    console.log('play触发播放事件', props.showOperateBtns)
    // 实现把插件原有截图提醒改成抓拍两字
    if (props.showOperateBtns) {
      const screenshotBtn = $container.querySelector('.jessibuca-screenshot')
      if (screenshotBtn) {
        screenshotBtn.title = '抓拍'

        // 修改按钮内的文本
        const textElement = screenshotBtn.querySelector('.icon-title')
        if (textElement) {
          textElement.textContent = '抓拍'
        }
      }
    }
  })
  jessibuca.on('playToRenderTimes', (times) => {
    console.log('监听调用play方法 经过 初始化-> 网络请求-> 解封装 -> 解码 -> 渲染 一系列过程的时间消耗:', times)
  })
}

// 获取视频流地址并播放视频
function play(playUrl = '') {
  console.log('播放视频路径: ', playUrl)
  console.log('jessibuca: ', jessibuca)
  // 播放视频
  playUrl && jessibuca.play(playUrl)
}

// 暂停
function pause() {
  jessibuca && jessibuca.pause().then(() => {
    console.log('pause success')
  })
}

// 恢复
function restore() {
  jessibuca && jessibuca.play().then(() => {
    console.log('restore success')
  })
}

// 切换播放源
function changeUrl(playUrl = '') {
  jessibuca && jessibuca.destroy()
  console.log('切换url', props.id)
  setTimeout(() => {
    create(props.id)
    jessibuca && jessibuca.play(playUrl)
  }, 100)
}

// 抓拍
function handleScreenshot() {
  const systemType = window.localStorage.getItem('systemType')
  const level = window.sessionStorage.getItem('secretUserLevel')
  const index = secretUserLevelList.value.findIndex(item => item.value === level)
  let title = '抓拍图片'
  if (index !== -1 && systemType === 'gm') {
    title = `【${secretUserLevelList.value[index].name}】${title}`
  }
  console.log(title);
  jessibuca && jessibuca.screenshot(title, 'png')
}

// 全屏
function handleFull() {
  console.log('操作全屏', isfullscreen.value)
  jessibuca.setFullscreen(!isfullscreen.value)
}

// 销毁视频
function destroy() {
  jessibuca && jessibuca.destroy()
  setTimeout(() => {
    create(props.id)
  }, 100)
}

onBeforeUnmount(() => {
  destroy()
})

// 组件挂载后执行的操作
onMounted(() => {
  getDictByCode('secretUserLevel').then((response) => {
    if (response.code === 200) {
      secretUserLevelList.value = response.data
    }
  })
  create(props.id)
})

// ----------------------- 以下是暴露的方法内容 ----------------------------
defineExpose({ play, destroy, pause, restore, changeUrl })
</script>

<template>
  <div :id="props.id" class="container">
    <div>
      <slot name="drawarea"></slot>
    </div>
    <!-- 识别关注要点 -->
    <div v-if="!isfullscreen">
      <slot name="identifyKey"></slot>
    </div>
    <!-- title -->
    <div>
      <slot name="titles"></slot>
    </div>
    <div v-if="props.showOperateBtns" class="button-area">
      <div class="screenshot-wrapper" style="margin-right: 6px;">
        <img src="../../assets/bigScreen/shot.png" alt="抓拍" @click="handleScreenshot">
      </div>
      <div class="screenshot-wrapper full-wrapper">
        <img src="../../assets/bigScreen/full.png" alt="全屏" @click="handleFull">
      </div>
    </div>
  </div>
  <slot name="draw" />
</template>

<style scoped>
.container {
  /* position: relative; */
  background: rgba(13, 14, 27, 0.7);
  height: 100%;
  width: 100%;
}
.button-area {
  position: absolute;
  bottom: 1%;
  right: 1%;
  z-index: 999999;
  background-color: rgba(81, 82, 92, 0.7);
  border-radius: 3px;
  padding: 1px 6px 0px 6px;
}

.screenshot-wrapper {
  position: relative;
  display: inline-block;
}

.screenshot-wrapper img {
  display: block;
  width: 26px;
  height: 26px;
  cursor: pointer;
}

.screenshot-wrapper::after {
  content: '抓拍';
  position: absolute;
  bottom: 100%; /* 修改为 bottom: 100% 使文字显示在图片上方 */
  left: 50%;
  transform: translateX(-50%);
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s, visibility 0.2s;
  white-space: nowrap;
}
.full-wrapper::after {
  content: '全屏';
  position: absolute;
  bottom: 100%; /* 修改为 bottom: 100% 使文字显示在图片上方 */
  left: 50%;
  transform: translateX(-50%);
  background-color: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s, visibility 0.2s;
  white-space: nowrap;
}

.screenshot-wrapper:hover::after {
  opacity: 1;
  visibility: visible;
}
</style>