如何在Vue.js中实现带环境变量的视频录制与回放?
实现带传感器数据同步的多摄像头视频录制与回放方案
针对你的需求,核心要解决视频与传感器数据的时间同步存储和Vue端的录制/回放联动两个问题,以下是具体实现方案:
一、数据与视频的同步存储设计
要实现类似CCTV的时间轴回放,关键是用精确时间戳作为视频片段和传感器数据的关联键:
- 视频存储:将视频按固定时长(比如5分钟)分段,每个片段命名包含
摄像头ID+起始时间戳(如cam_1_1699999200000.webm),视频文件存在对象存储或本地文件系统,同时在数据库中记录视频元数据:摄像头ID、起始/结束时间戳、文件路径。 - 传感器数据存储:用时序数据库(如InfluxDB、TimescaleDB)按固定频率(比如1秒/条)存储温度、CO₂等数据,每条数据必须包含精确时间戳和对应的视频片段ID(或直接用时间戳关联)。
二、Vue端录制功能实现
利用浏览器的MediaRecorder API实现摄像头录制,同时定时采集传感器数据并关联到当前视频片段:
核心代码示例
<template> <div class="recording-container"> <!-- 多摄像头切换可扩展为下拉选择器 --> <video ref="videoPlayer" autoplay width="640" height="480"></video> <button @click="toggleRecording" :disabled="isRecording"> {{ isRecording ? '停止录制' : '开始录制' }} </button> </div> </template> <script setup> import { ref } from 'vue' const videoPlayer = ref(null) const isRecording = ref(false) let mediaRecorder = null let sensorInterval = null let currentSegmentStartTime = null let currentCameraId = 'cam_1' // 枚举可用摄像头设备 async function getCameraDevices() { const devices = await navigator.mediaDevices.enumerateDevices() return devices.filter(d => d.kind === 'videoinput') } // 切换录制状态 async function toggleRecording() { isRecording.value ? stopRecording() : await startRecording() } // 启动录制 async function startRecording() { const cameras = await getCameraDevices() // 选择第一个摄像头,可扩展为用户选择 const stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: cameras[0].deviceId, width: 640, height: 480 }, audio: false // 花园场景无需音频 }) videoPlayer.value.srcObject = stream // 初始化MediaRecorder,选择webm格式(兼容性较好) mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' }) currentSegmentStartTime = Date.now() const segmentId = `${currentCameraId}_${currentSegmentStartTime}` // 定时采集传感器数据(每秒1次) sensorInterval = setInterval(async () => { const sensorData = await fetch('/api/sensors') // 后端接口获取实时数据 // 保存数据到时序数据库,关联视频片段ID和当前时间戳 await fetch('/api/sensors/save', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...sensorData, segmentId, timestamp: Date.now() }) }) }, 1000) // 每5分钟生成一个视频片段并上传后端 mediaRecorder.start(5 * 60 * 1000) mediaRecorder.ondataavailable = async (e) => { if (e.data.size > 0) { const formData = new FormData() formData.append('video', e.data, `${segmentId}.webm`) formData.append('cameraId', currentCameraId) formData.append('startTime', currentSegmentStartTime) await fetch('/api/videos/save', { method: 'POST', body: formData }) } } isRecording.value = true } // 停止录制 function stopRecording() { mediaRecorder.stop() clearInterval(sensorInterval) videoPlayer.value.srcObject.getTracks().forEach(track => track.stop()) isRecording.value = false } </script>
三、回放与仪表盘联动实现
通过时间选择器加载对应视频片段,并在视频播放时同步更新传感器仪表盘:
核心代码示例
<template> <div class="playback-container"> <video ref="playbackVideo" @timeupdate="syncSensorData" controls width="640" height="480"></video> <div class="dashboard"> <div class="metric">温度: {{ currentData.temperature }}°C</div> <div class="metric">CO₂: {{ currentData.co2 }}ppm</div> <div class="metric">树木湿度: {{ currentData.humidity }}%</div> <div class="metric">水箱液位: {{ currentData.waterLevel }}%</div> </div> <input type="datetime-local" v-model="selectedTime" @change="loadPlaybackData"> </div> </template> <script setup> import { ref } from 'vue' const playbackVideo = ref(null) const currentData = ref({ temperature: 0, co2: 0, humidity: 0, waterLevel: 0 }) const selectedTime = ref('') let sensorDataCache = [] // 根据选择的时间加载视频和对应时段的传感器数据 async function loadPlaybackData() { const targetTs = new Date(selectedTime.value).getTime() // 后端接口:根据时间戳获取对应视频片段和传感器数据 const res = await fetch(`/api/playback?timestamp=${targetTs}`) const data = await res.json() // 设置视频源 playbackVideo.value.src = `/api/videos/${data.videoSegmentId}.webm` // 缓存传感器数据避免频繁请求 sensorDataCache = data.sensorList } // 视频播放时同步更新传感器数据 function syncSensorData() { if (!sensorDataCache.length) return const playTimeMs = playbackVideo.value.currentTime * 1000 const videoStartTs = new Date(selectedTime.value).getTime() const currentTs = videoStartTs + playTimeMs // 找到最接近当前播放时间的传感器数据 const closestData = sensorDataCache.find(d => Math.abs(d.timestamp - currentTs) < 1000) if (closestData) { currentData.value = closestData } } </script> <style scoped> .dashboard { display: flex; gap: 2rem; margin: 1rem 0; padding: 1rem; background: #f5f5f5; border-radius: 8px; } .metric { font-size: 1.1rem; font-weight: 500; } </style>
四、关键注意事项
- 时间同步:所有时间戳必须用服务器时间,避免客户端本地时间偏差导致数据与视频错位。
- 性能优化:
- 视频分段不要太长(5-10分钟最优),减少加载压力;
- 回放时预加载相邻视频片段,避免切换卡顿;
- 传感器数据可按10秒聚合存储,减少前端渲染的数据量。
- 存储选型:传感器数据用时序数据库查询效率更高,视频用对象存储更节省成本且易于扩展。
内容的提问来源于stack exchange,提问作者laser
相关产品推荐
相关产品推荐

