修复工作

This commit is contained in:
2025-10-02 15:24:58 +08:00
parent dfc0baff58
commit c63d6ffb3d

View File

@@ -0,0 +1,176 @@
<template>
<div style="position: absolute;height: 100%;width: 100%;display: flex;align-items: center;justify-content: center;">
<div v-show="isPlay" style="width: 100%; height: 100%; position: relative;">
<canvas ref="canvas" style="width: 100%; height: 100%; object-fit: contain;"></canvas>
<div class="stats" v-if="showStats">
FPS: {{ fps }} | 延迟: {{ latency }}ms | 帧数: {{ frameCount }}
</div>
</div>
<div v-show="!isPlay" style="color: #08979C;font-size: 25px;">暂无视频源</div>
</div>
</template>
<script>
export default {
name: 'JpegPlayer',
data() {
return {
isPlay: false,
ws: null,
canvas: null,
ctx: null,
frameCount: 0,
fps: 0,
latency: 0,
showStats: true,
lastFrameTime: 0,
fpsCounter: 0,
fpsTimer: null
};
},
mounted() {
this.canvas = this.$refs.canvas;
this.ctx = this.canvas.getContext('2d');
// 启动 FPS 计算定时器
this.fpsTimer = setInterval(() => {
this.fps = this.fpsCounter;
this.fpsCounter = 0;
}, 1000);
},
methods: {
createPlayer(url) {
if (!url) {
console.error('❌ 播放地址为空');
return;
}
// 检查是否是 WebSocket URL
if (!url.startsWith('ws://') && !url.startsWith('wss://')) {
console.error('❌ JpegPlayer 只支持 WebSocket URL');
return;
}
console.log('🎬 创建 JPEG WebSocket 播放器');
console.log('📺 WebSocket URL:', url);
// 关闭旧连接
if (this.ws) {
this.ws.close();
}
this.isPlay = true;
this.frameCount = 0;
// 创建 WebSocket 连接
this.ws = new WebSocket(url);
this.ws.binaryType = 'arraybuffer';
this.ws.onopen = () => {
console.log('✅ WebSocket 连接已建立');
this.lastFrameTime = Date.now();
};
this.ws.onmessage = (event) => {
this.handleFrame(event.data);
};
this.ws.onerror = (error) => {
console.error('❌ WebSocket 错误:', error);
};
this.ws.onclose = () => {
console.log('⚠️ WebSocket 连接已关闭');
this.isPlay = false;
};
},
handleFrame(data) {
try {
this.frameCount++;
this.fpsCounter++;
// 计算延迟
const now = Date.now();
this.latency = now - this.lastFrameTime;
this.lastFrameTime = now;
// 将 ArrayBuffer 转换为 Blob
const blob = new Blob([data], { type: 'image/jpeg' });
const url = URL.createObjectURL(blob);
// 创建图片对象
const img = new Image();
img.onload = () => {
// 调整 canvas 尺寸
if (this.canvas.width !== img.width || this.canvas.height !== img.height) {
this.canvas.width = img.width;
this.canvas.height = img.height;
console.log('📐 Canvas 尺寸:', img.width, 'x', img.height);
}
// 绘制图片
this.ctx.drawImage(img, 0, 0);
// 释放 URL
URL.revokeObjectURL(url);
// 只在前几帧输出日志
if (this.frameCount <= 3 || this.frameCount % 100 === 0) {
console.log(`📹 接收帧 #${this.frameCount}: ${data.byteLength} bytes, 分辨率: ${img.width}x${img.height}`);
}
};
img.onerror = () => {
console.error('❌ 图片解码失败');
URL.revokeObjectURL(url);
};
img.src = url;
} catch (error) {
console.error('❌ 处理帧数据失败:', error);
}
},
changeVideo(url) {
this.createPlayer(url);
},
closePlayer() {
this.isPlay = false;
if (this.ws) {
this.ws.close();
this.ws = null;
}
if (this.fpsTimer) {
clearInterval(this.fpsTimer);
this.fpsTimer = null;
}
// 清空 canvas
if (this.ctx) {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
},
beforeDestroy() {
this.closePlayer();
}
};
</script>
<style scoped>
.stats {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0, 0, 0, 0.7);
color: #00ff00;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
font-family: monospace;
z-index: 100;
}
</style>