vue3接入大华摄像头

此代码拿走即用

目前只支持单个摄像头开发配置不支持动态获取摄像头列表ip配置

如需多个摄像头ip请修改vite.config.js文件代理改为对应摄像头ip

后续开发中.....

完成摄像头动态IP获取

本地测试直接在vite.config.js中修改对应摄像头IP即可

本次修改针对部署在nginx中

1.大华websdk包下载

https://support.dahuatech.com/sdkindex/sdkExploit/267?IsDpValue=%2BrSesyHc35SwoPzpUsL6AFdResJUzHZKcGdGR3d27us%3Dhttps://support.dahuatech.com/sdkindex/sdkExploit/267?IsDpValue=%2BrSesyHc35SwoPzpUsL6AFdResJUzHZKcGdGR3d27us%3D

2.项目结构图

📦dahuaDemo
 ┣ 📂public
 ┃ ┣ 📂module        //大华摄像头文件
 ┃ ┃ ┣ 📜audioTalkWorker.worker.js
 ┃ ┃ ┣ 📜audioWorker.worker.js
 ┃ ┃ ┣ 📜libDecodeSDK.js
 ┃ ┃ ┣ 📜libDecodeSDK.wasm
 ┃ ┃ ┣ 📜PlayerControl.js
 ┃ ┃ ┣ 📜videoWorker.worker.js
 ┃ ┃ ┗ 📜videoWorkerTrain.worker.js
 ┃ ┗ 📜vite.svg
 ┣ 📂src
 ┃ ┣ 📂assets
 ┃ ┃ ┗ 📜vue.svg
 ┃ ┣ 📂components
 ┃ ┃ ┣ 📜ControlPanel.vue      控制面板
 ┃ ┃ ┣ 📜DeviceList.vue        设备列表
 ┃ ┃ ┣ 📜PlaybackModal.vue     回放列表模态框
 ┃ ┃ ┗ 📜VideoGrid.vue         视频播放区域
 ┃ ┣ 📜App.vue
 ┃ ┣ 📜main.js
 ┃ ┗ 📜style.css
 ┣ 📜.env.development
 ┣ 📜.env.production
 ┣ 📜.gitignore
 ┣ 📜index.html
 ┣ 📜package.json
 ┣ 📜pnpm-lock.yaml
 ┣ 📜README.md
 ┗ 📜vite.config.js

3.代码文件

app.vue

<template>
  <div class="ipc-manager">
    <!-- 设备列表 -->
    <DeviceList
      :devices="devices"
      :current-device-index="currentDeviceIndex"
      @device-click="handleDeviceClick"
      @login-all="loginAllDevices"
      @logout-all="logoutAllDevices"
    />

    <!-- 视频播放区域 -->
    <VideoGrid
      :grid-type="gridType"
      :current-window-index="currentWindowIndex"
      :player-instances="playerInstances"
      @window-click="handleWindowClick"
    />

    <!-- 控制面板 -->
    <ControlPanel
      :current-device="currentDevice"
      :current-window-index="currentWindowIndex"
      :is-playing="isPlaying"
      :playback-speed="playbackSpeed"
      :is-recording="isRecording"
      :is-talking="isTalking"
      :volume="volume"
      :channel="channel"
      :stream-type="streamType"
      :playback-channel="playbackChannel"
      :playback-list="playbackList"
      :current-page="currentPage"
      :total-pages="totalPages"
      @change-grid="handleGridChange"
      @change-stream="handleStreamChange"
      @change-channel="handleChannelChange"
      @start-preview="startPreview"
      @stop-preview="stopPreview"
      @start-playback="startPlayback"
      @stop-playback="stopPlayback"
      @toggle-sound="toggleSound"
      @toggle-talk="toggleTalk"
      @toggle-record="toggleRecord"
      @snapshot="captureSnapshot"
      @zoom-in="zoomIn"
      @zoom-out="zoomOut"
      @toggle-fullscreen="toggleFullscreen"
      @ptz-control="handlePTZControl"
      @change-volume="handleVolumeChange"
      @change-speed="handleSpeedChange"
      @search-record="searchRecord"
      @playback-go="handlePlaybackGo"
      @download-record="downloadRecord"
    />

    <!-- 回放列表模态框 -->
    <PlaybackModal
      v-if="showPlaybackModal"
      :playback-list="playbackList"
      :current-page="currentPage"
      :total-pages="totalPages"
      @close="showPlaybackModal = false"
      @page-change="handlePageChange"
      @play="handlePlaybackItemClick"
      @download="handleDownloadRecord"
    />
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from "vue";
import DeviceList from "./components/DeviceList.vue";
import VideoGrid from "./components/VideoGrid.vue";
import ControlPanel from "./components/ControlPanel.vue";
import PlaybackModal from "./components/PlaybackModal.vue";

// 设备相关
const devices = ref([
  {
    ip: "192.168.0.206",
    port: "80",
    username: "admin",
    password: "bydl666888",
    name: "Camera 1",
    loggedIn: false,
    session: null,
  },
]);

const currentDeviceIndex = ref(0);
const currentDevice = computed(() => devices.value[currentDeviceIndex.value]);

// 视频播放相关
const gridType = ref(1); // 1, 2, 3, 4
const currentWindowIndex = ref(0);
const playerInstances = ref(new Array(16).fill(null));
const drawInstances = ref(new Array(16).fill(null));
const ivsInstances = ref(new Array(16).fill(null));
const talkInstances = ref(new Array(16).fill(null));
const recordInstances = ref(new Array(16).fill(null));

// 播放状态
const isPlaying = ref(false);
const isRecording = ref(false);
const isTalking = ref(false);
const playbackSpeed = ref(1);
const volume = ref(50);

// 配置
const channel = ref(0);
const streamType = ref(0);
const playbackChannel = ref(0);

// 回放相关
const playbackList = ref([]);
const currentPage = ref(1);
const totalPages = ref(1);
const playbackOptions = ref(null);
const showPlaybackModal = ref(false);

// 其他
const ChannelPTZCaps = ref([]);
const isStartAll = ref(false);

// 计算属性
const currentPlayer = computed(
  () => playerInstances.value[currentWindowIndex.value],
);

// 初始化
onMounted(() => {
  init();
  addFullscreenListeners();
});

// 清理
onUnmounted(() => {
  cleanup();
});

const init = () => {
  // 初始化数组
  playerInstances.value = new Array(16).fill(null);
  drawInstances.value = new Array(16).fill(null);
  ivsInstances.value = new Array(16).fill(null);
  talkInstances.value = new Array(16).fill(null);
  recordInstances.value = new Array(16).fill(null);
};

const addFullscreenListeners = () => {
  const events = [
    "fullscreenchange",
    "webkitfullscreenchange",
    "mozfullscreenchange",
    "msfullscreenchange",
  ];
  events.forEach((event) => {
    document.addEventListener(event, handleFullscreenChange);
  });
};

const handleFullscreenChange = () => {
  // 处理全屏变化
};

// 设备管理
const handleDeviceClick = async (index) => {
  await logoutCurrentDevice();
  currentDeviceIndex.value = index;
  await loginDevice();
};

const loginDevice = async () => {
  const device = currentDevice.value;

  try {
    //在此次调用大华内置方法设置请求头中的self-targetip字段为摄像头ip+端口
    setIP(device.ip + ":" + device.port);
    //---------------------------------------------------------------
    const res = await RPC.login(device.username, device.password, false);

    device.loggedIn = true;
    device.session = _getSession();

    // 保活
    RPC.keepAlive(
      300,
      60000,
      device.session,
      `${device.ip}:${device.port}`,
      currentWindowIndex.value,
    );

    // 获取设备能力
    await getDeviceCapabilities();

    // 开始预览
    await startPreview();
  } catch (err) {
    console.error("Login failed:", err);
    handleLoginError(err);
  }
};

const logoutCurrentDevice = async () => {
  const device = currentDevice.value;
  if (!device.session) return;

  _setSession(device.session);

  try {
    await RPC.Global.logout();

    // 停止相关播放器
    stopWindowPreview(currentWindowIndex.value);

    device.loggedIn = false;
    device.session = null;
  } catch (err) {
    console.error("Logout failed:", err);
  }
};

const loginAllDevices = async () => {
  isStartAll.value = true;
  await logoutAllDevices();
  currentDeviceIndex.value = 0;
  await loginDevice();
};

const logoutAllDevices = async () => {
  for (let i = 0; i < devices.value.length; i++) {
    if (devices.value[i].session) {
      _setSession(devices.value[i].session);
      await RPC.Global.logout();
      devices.value[i].loggedIn = false;
      devices.value[i].session = null;
    }
  }

  // 停止所有窗口的预览
  stopAllPreviews();
};

// 视频预览控制
const startPreview = async () => {
  const device = currentDevice.value;

  // 停止当前窗口的预览
  stopWindowPreview(currentWindowIndex.value);

  const options = {
    wsURL: `ws://${device.ip}:${device.port}/rtspoverwebsocket`,
    rtspURL: `rtsp://${device.ip}:${device.port}/cam/realmonitor?channel=${channel.value + 1}&subtype=${streamType.value}&proto=Private3`,
    username: device.username,
    password: device.password,
    lessRateCanvas: true,
    wndIndex: currentWindowIndex.value,
    h265AccelerationEnabled: true,
  };

  const player = new PlayerControl(options);
  console.log(player);

  // 绑定事件
  player.on("PlayStart", () => {
    isPlaying.value = true;
    handlePlayStart();
  });

  player.on("DecodeStart", (e) => {
    initCanvasTools();
  });

  player.on("Error", (err) => {
    console.error("Player error:", err);
    isPlaying.value = false;
  });

  player.on("IvsDraw", (data, index) => {
    handleIvsDraw(data, index);
  });
  player.on("WorkerReady", function () {
    player.connect();
  });

  // 初始化播放器
  const canvasElement = document.getElementById(
    `h5_canvas_${currentWindowIndex.value}`,
  );
  const videoElement = document.getElementById(
    `h5_video_${currentWindowIndex.value}`,
  );

  if (canvasElement && videoElement) {
    player.init(canvasElement, videoElement);
  }

  playerInstances.value[currentWindowIndex.value] = player;

  // 如果是批量登录,登录下一个设备
  if (
    isStartAll.value &&
    getLoggedInCount() < gridType.value * gridType.value
  ) {
    handleNextWindowLogin();
  }
};

const stopWindowPreview = (windowIndex) => {
  if (playerInstances.value[windowIndex]) {
    playerInstances.value[windowIndex].stop();
    playerInstances.value[windowIndex].close();
    playerInstances.value[windowIndex] = null;
  }

  if (talkInstances.val
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cq_run

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值