vue3+easyplayer封装组件

该文章已生成可运行项目,

使用easyplayer.js版本为 https://github.com/EasyDarwin/EasyPlayer.js/tree/main

由于new easyplayer生成实例需要一段时间才能进行播放url,所以不断请求play方法,或者设置按钮手动点击开始播放

由于监听不到截图回调的事件,使用点击监听元素,手动触发截图功能并调用自身逻辑

自定义禁用右键弹窗信息

<template>
  <div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%">
    <slot name="header" v-if="!fullscreen"></slot>
    <div class="player-wrapper">
      <!-- 播放器容器 -->
      <div ref="playerContainer" class="player-box" @contextmenu="contextmenu">
        <div class="no-data" v-if="noData">{{ attrs.alt }}</div>
        <slot name="header" v-if="fullscreen"></slot>
        <slot></slot>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';

// 接收父组件的动态属性(包括所有配置项)
type easyPlayerProps = {
  alt?: string; // 无数据时显示的文本
  videoUrl: string; // 视频地址
  isLive?: boolean; // 是否直播
  hasAudio?: boolean; // 是否解析音频
  isMute?: boolean; // 是否静音
  stretch?: boolean; // 是否拉伸视频
  bufferTime?: number; // 缓冲时间
  loadTimeOut?: number; // 加载超时时间
  loadTimeReplay?: number; // 重连次数
  MSE?: boolean; // MSE 模式
  WCS?: boolean; // WCS 模式
  WASM?: boolean; // WASM 模式
  isReplay?: boolean; // 是否回放
  gpuDecoder?: boolean; // 硬解码
  watermark?: object | null; // 水印配置
  fullWatermark?: object | null; // 全屏水印
  debug?: boolean; // 是否打印日志
};

const attrs = withDefaults(defineProps<easyPlayerProps>(), {
  isLive: true,
  hasAudio: true,
  isMute: false,
  stretch: true,
  isReplay: false,
  bufferTime: 1,
  loadTimeOut: 30,
  loadTimeReplay: 3,
  MSE: false,
  WCS: true,
  WASM: false,
  gpuDecoder: false,
  watermark: null,
  fullWatermark: null,
  debug: false,
  videoUrl: ''
});

const emits = defineEmits<{
  (e: 'fullscreen', data: any): void;
  (e: 'snapInside', data: any): void;
  (e: 'customButtons', data: any): void;
  (e: 'videoInfo', data: any): void;
}>();

// 播放器 DOM 容器引用
const playerContainer = ref<HTMLDivElement | null>(null);
let playerInstance: any = null; // 播放器实例
const noData = ref(true); // 是否无数据

// 配置与状态
const config = reactive({
  isLive: attrs.isLive,
  hasAudio: attrs.hasAudio,
  isMute: attrs.isMute,
  stretch: attrs.stretch,
  bufferTime: attrs.bufferTime,
  loadTimeOut: attrs.loadTimeOut,
  loadTimeReplay: attrs.loadTimeReplay,
  MSE: attrs.MSE,
  WCS: attrs.WCS,
  WASM: attrs.WASM,
  gpuDecoder: attrs.gpuDecoder,
  watermark: attrs.watermark,
  fullWatermark: attrs.fullWatermark,
  debug: attrs.debug
});

const fullscreen = ref(false);
let isPlay = false;

// 初始化播放器
const initializePlayer = () => {
  if (playerContainer.value) {
    playerInstance = new (window as any).EasyPlayerPro(playerContainer.value, {
      isLive: config.isLive,
      hasAudio: config.hasAudio,
      isMute: config.isMute,
      stretch: config.stretch,
      bufferTime: config.bufferTime,
      loadTimeOut: config.loadTimeOut,
      loadTimeReplay: config.loadTimeReplay,
      MSE: config.MSE,
      WCS: config.WCS,
      WASM: config.WASM,
      gpuDecoder: config.gpuDecoder,
      watermark: config.watermark,
      fullWatermark: config.fullWatermark,
      debug: config.debug
    });

    console.log('播放器实例', playerInstance);
    // 事件监听
    playerInstance.on('fullscreen', (status: boolean) => {
      console.log('全屏', status);
      fullscreen.value = status;
      emits('fullscreen', status);
    });

    playerInstance.on('videoInfo', (data: any) => {
      isPlay = true;
      playerContainer.value?.querySelector('.easyplayer-icon-screenshot')?.addEventListener('click', screenshot);
      emits('videoInfo', data);

      if (!attrs.isReplay) {
        return;
      }
      // 获取子元素
      const childElement = document.querySelector('.easyplayer-record');
      if (childElement) {
        // 向上遍历两层父节点(根据实际HTML结构)
        const parentContainer = childElement.parentNode;
        // 确保父容器存在且是HTMLElement
        if (parentContainer instanceof HTMLElement) {
          parentContainer.style.display = 'none'; // 安全地设置样式
        }
      }
    });

    playerInstance.on('timeout', (error: any) => console.error('timeout:', error));

    playerInstance.on('recordStart', () => {
      emits('customButtons', '录像');
    });

    playerInstance.on('recordEnd', () => {
      emits('customButtons', '录像');
    });

    playerInstance.on('error', (error: any) => {
      console.error('播放错误:', error);
      destroyPlayer();
    });
  }
};

// 鼠标右键事件隐藏
const contextmenu = () => {
  nextTick(() => {
    let playBtn = playerContainer.value?.querySelector('.easyplayer-contextmenu-btn') as any;
    playBtn?.remove();
  });
};

const screenshot = () => {
  const base64 = playerInstance.screenshot('', 'png', 0.2, 'base64');
  emits('snapInside', base64);
};

let timeout: any = null;
// 销毁播放器
const destroyPlayer = () => {
  return new Promise(resolve => {
    if (playerInstance) {
      playerContainer.value?.querySelector('.easyplayer-icon-screenshot')?.removeEventListener('click', screenshot);
      playerInstance.destroy();
      playerInstance = null;
    }

    isPlay = false;
    clearTimeout(timeout);

    setTimeout(() => {
      resolve(null);
    }, 100);
  });
};

//重播
const onReplay = () => {
  destroyPlayer().then(() => {
    initializePlayer();
    play();
  });
};

// 播放功能
const play = () => {
  console.log('播放视频', attrs.videoUrl);
  noData.value = false;
  setTimeout(
    url => {
      playerInstance &&
        playerInstance
          .play(url)
          .then(() => {
            if (playerContainer.value) {
              let playBtn = playerContainer.value.querySelector('.easyplayer-play') as any;
              playBtn.style = 'display:block';
            }
            timeout = setTimeout(() => {
              console.log(isPlay);
              if (!isPlay) {
                play();
              }
            }, 1000);
          })
          .catch((error: any) => {
            console.error('播放失败:', error);
          });
    },
    100,
    attrs.videoUrl
  );
};

// 暂停功能
const pause = () => {
  playerInstance?.pause();
};

// 静音功能
const toggleMute = () => {
  const isMuted = playerInstance?.isMute();
  playerInstance?.setMute(!isMuted);
};

// 全屏功能
const toggleFullscreen = () => {
  playerInstance?.setFullscreen();
};

// 属性监听:当配置变化时重新初始化播放器
watch(
  () => attrs.videoUrl,
  newVal => {
    console.log(newVal, playerInstance);
    if (newVal) {
      onReplay();
    } else {
      noData.value = true;
      destroyPlayer();
    }
  },
  { deep: true }
);

// 生命周期管理
onMounted(() => {
  if (attrs.videoUrl) {
    initializePlayer();
    play();
  }
});

onBeforeUnmount(() => {
  destroyPlayer();
});

// 暴露方法供父组件调用
defineExpose({
  pause,
  onReplay,
  play,
  toggleMute,
  toggleFullscreen,
  stop,
  destroyPlayer
});
</script>

<style scoped>
.player-wrapper {
  position: absolute;
  width: 100%;
  height: 100%;
  margin: auto;
}
.player-box {
  position: relative;
  width: 100%;
  height: 100%;
  background-color: black;
}
.no-data {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 999;
  font-size: 16px;
  color: #ffffff;
  transform: translate(-50%, -50%);
}
</style>

本文章已经生成可运行项目
<think>好的,我现在需要帮助用户解决如何在Vue中使用海康威视SDK取流并通过EasyPlayer播放的问题。首先,我需要理清用户的需求。用户提到了使用海康的SDK取流,并在Vue项目中集成EasyPlayer进行播放,可能需要示例代码和步骤指导。 首先,我应该回忆海康威视的SDK使用流程。海康通常提供设备网络SDK,需要初始化SDK、登录设备、获取通道信息、获取实时流等步骤。然后,用户需要在Vue中展示视频流,这可能涉及到前端播放器的选择,比如EasyPlayer,它支持RTSP、RTMP等协议。 接下来,我需要考虑如何在Vue项目中集成EasyPlayer。可能需要引入EasyPlayer组件或库,并处理视频流的URL生成。同时,需要注意Vue的生命周期,比如在组件销毁时释放资源,避免内存泄漏。 然后,用户可能需要示例代码。我需要确保代码结构清晰,包含必要的步骤,比如初始化SDK、登录、取流、生成播放URL,并在Vue组件中嵌入播放器。还要注意错误处理和资源释放,比如在离开页面时停止流传输并注销登录。 另外,用户的问题中提到了参考引用中的内容,是关于Vue接入海康视频监控的教程,支持多种协议。需要确保我的回答与引用中的信息一致,可能涉及取流协议的选择,比如RTSP over WebSocket,以兼容浏览器环境。 还要注意用户可能的开发环境,比如Vue2或Vue3,是否需要不同的配置。此外,移动端H5的支持也是需要考虑的,可能需要调整播放器的配置以适应移动端。 最后,生成相关问题时要覆盖用户可能进一步询问的点,比如如何优化播放流畅度、处理多摄像头、进行录像回放等。确保问题有针对性,帮助用户深入理解整个流程。</think>### 海康威视 SDK 取流与 Vue 集成 EasyPlayer 教程 #### 1. 环境准备 - **海康设备网络SDK**:从官网下载最新版SDK(如`HCNetSDK.so`/`.dll`),包含设备登录、取流等接口[^1] - **Vue项目**:支持Vue2/Vue3,安装`npm install @easydarwin/easyplayer`或使用CDN引入EasyPlayer - **协议支持**:优先使用`RTSP over WebSocket`或`HLS`协议以适应浏览器环境 #### 2. SDK初始化与取流步骤 ```javascript // 初始化SDK(需通过WebAssembly或插件实现浏览器调用) const hikSDK = new HikVisionSDK({ wasmPath: &#39;/static/HCNetSDK.wasm&#39;, deviceIP: &#39;192.168.1.64&#39;, username: &#39;admin&#39;, password: &#39;your_password&#39; }); // 获取实时流地址 async function getRealUrl(channel=1) { await hikSDK.login(); const streamInfo = await hikSDK.getRealStream(channel, { protocol: &#39;rtsp&#39;, // 可选rtmp/hls streamType: 0 // 主码流 }); return streamInfo.url; // 示例: rtsp://192.168.1.64/Streaming/Channels/101 } ``` #### 3. Vue组件集成EasyPlayer ```vue <template> <div class="player-container"> <EasyPlayer :videoUrl="streamUrl" fluent autoplay :rtspTransport="&#39;websocket&#39;" @error="handleError" /> </div> </template> <script> import EasyPlayer from &#39;@easydarwin/easyplayer&#39; export default { components: { EasyPlayer }, data() { return { streamUrl: &#39;&#39; } }, async mounted() { this.streamUrl = await getRealUrl(); }, beforeDestroy() { hikSDK.stopStream(); // 停止取流 hikSDK.logout(); // 注销设备 } } </script> ``` #### 4. 关键配置说明 | 参数 | 作用 | 推荐值 | |------|------|--------| | `rtspTransport` | Web端强制使用websocket隧道 | `websocket` | | `decodeType` | 硬解码兼容性配置 | `auto` | | `stunServer` | NAT穿透地址 | `stun:stun.l.google.com:19302` | #### 5. 常见问题处理 - **跨域问题**:需在设备端配置`Access-Control-Allow-Origin` - **低延迟优化**:启用`TCP模式`+`I帧间隔`调整 - **移动端适配**:添加`playsinline`属性防止全屏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值