diff --git a/src/views/videoCommunication/realTimeConsultation.vue b/src/views/videoCommunication/realTimeConsultation.vue index 9a5c57a..2df6f03 100644 --- a/src/views/videoCommunication/realTimeConsultation.vue +++ b/src/views/videoCommunication/realTimeConsultation.vue @@ -69,7 +69,7 @@ class="share-preview" autoplay playsinline - muted + :muted="false" > @@ -623,7 +623,7 @@ export default { // ==================== 【4】采集卡设备 ==================== async switchCaptureCard(deviceId) { - // 清理旧采集卡流 + // 1. 彻底清理旧资源(防止残留) if (this.captureCheckTimer) clearTimeout(this.captureCheckTimer); this.closeCaptureCard(); @@ -635,65 +635,64 @@ export default { this.isSharing = true; await this.$nextTick(); + const videoEl = this.$refs.shareVideo; - const constraints = { - video: { - deviceId: { exact: deviceId }, - width: { ideal: 1920 }, - height: { ideal: 1080 }, - frameRate: { ideal: 30 }, - }, - audio: false, - }; + // 2. 强制重置 Video 标签状态(防止 CSS 缓存黑屏) + // 强制重置 Video 标签状态 + if (videoEl) { + videoEl.srcObject = null; + videoEl.load(); + videoEl.style.width = "100%"; + videoEl.style.height = "100%"; + videoEl.style.objectFit = "contain"; + videoEl.style.background = "#000"; + videoEl.muted = false; // ✅ 允许播放声音 + } - let captureStream = null; try { - captureStream = await navigator.mediaDevices.getUserMedia(constraints); - } catch (err) { - console.error("采集卡获取流失败:", err); - const lowConstraints = { - video: { deviceId: { exact: deviceId } }, - audio: false, - }; - try { - captureStream = await navigator.mediaDevices.getUserMedia( - lowConstraints - ); - } catch (e2) { - this.$message.error( - "无法打开采集卡,请检查USB口、权限、是否被其他软件占用" - ); - this.isSharing = false; - return; - } - } + console.log("正在执行最终修复方案:强制30帧 + 无分辨率限制..."); - const videoEl = this.$refs.shareVideo; - videoEl.srcObject = captureStream; - videoEl.muted = true; - videoEl.playsinline = true; - videoEl.autoplay = true; - - let hasValidFrame = false; - videoEl.onloadeddata = () => (hasValidFrame = true); - - // 3秒超时无信号判定失败 - this.captureCheckTimer = setTimeout(() => { - if (!hasValidFrame) { - this.$message.error("采集卡无信号,请检查设备、HDMI线、USB3.0接口"); - this.closeCaptureCard(); - this.isSharing = false; + // 3. 发起请求(核心修改点) + const stream = await navigator.mediaDevices.getUserMedia({ + video: { + deviceId: { exact: deviceId }, + // 核心修复 A:强制帧率 30,这是绿联 USB 2.0 采集卡的“生命线” + frameRate: { exact: 30 }, + // 核心修复 B:强制 MJPEG,绕过 USB 2.0 带宽瓶颈 + advanced: [{ chromegfx: "mjpeg" }], + // 注意:这里绝对不要写 width/height,让它默认输出 640x480 + }, + audio: true, + }); + + console.log( + "采集成功!实际设置:", + stream.getVideoTracks()[0].getSettings() + ); + + // 4. 绑定流 + if (videoEl) { + videoEl.srcObject = stream; + + // 核心修复 C:确保播放 + const playPromise = videoEl.play(); + if (playPromise !== undefined) { + playPromise + .then(() => { + console.log("视频播放已成功启动"); + this.$message.success("采集卡已连接 (强制30帧/MJPEG)"); + }) + .catch((error) => { + console.error("自动播放被拦截:", error); + this.$message.error("播放被拦截,请尝试点击页面任意位置"); + }); + } } - }, 3000); - - // 监听采集卡断开 - const videoTrack = captureStream.getVideoTracks()[0]; - if (videoTrack) - videoTrack.onended = () => { - this.$message.warning("采集卡信号已断开"); - this.closeCaptureCard(); - this.isSharing = false; - }; + } catch (err) { + console.error("最终方案失败:", err); + this.$message.error("连接失败: " + err.message); + this.isSharing = false; + } }, // 关闭采集卡并释放资源 @@ -702,14 +701,16 @@ export default { if (this.captureCheckTimer) clearTimeout(this.captureCheckTimer); const videoEl = this.$refs.shareVideo; if (!videoEl) return; + const stream = videoEl.srcObject; - if (stream) - stream.getTracks().forEach((t) => { - t.stop(); - t.onended = null; + if (stream) { + // 停止所有轨道 + stream.getTracks().forEach((track) => { + track.stop(); + track.onended = null; }); + } videoEl.srcObject = null; - videoEl.onloadeddata = null; } catch (e) { console.log("关闭采集卡异常:", e); } @@ -724,7 +725,7 @@ export default { try { const stream = await navigator.mediaDevices.getDisplayMedia({ video: { width: 1920, height: 1080, frameRate: 15 }, - audio: false, + audio: true, }); const track = stream.getVideoTracks()[0]; track.onended = () => this.stopShare(); @@ -994,20 +995,20 @@ export default { // ==================== 【11】网络状态监控 ==================== startNetworkMonitoring() { - setInterval(() => { - if (!window.hirtcwebsdk) return; - const s = window.hirtcwebsdk.getNetworkStats(); - if (s?.rtt) { - this.networkDelay = Math.round(s.rtt); - if (this.networkDelay < 100) - this.networkQuality = { text: "优秀", color: "#00e676" }; - else if (this.networkDelay < 300) - this.networkQuality = { text: "良好", color: "#00e676" }; - else if (this.networkDelay < 600) - this.networkQuality = { text: "一般", color: "#ffc107" }; - else this.networkQuality = { text: "差", color: "#f56c6c" }; - } - }, 2000); + // setInterval(() => { + // if (!window.hirtcwebsdk) return; + // const s = window.hirtcwebsdk.getNetworkStats(); + // if (s?.rtt) { + // this.networkDelay = Math.round(s.rtt); + // if (this.networkDelay < 100) + // this.networkQuality = { text: "优秀", color: "#00e676" }; + // else if (this.networkDelay < 300) + // this.networkQuality = { text: "良好", color: "#00e676" }; + // else if (this.networkDelay < 600) + // this.networkQuality = { text: "一般", color: "#ffc107" }; + // else this.networkQuality = { text: "差", color: "#f56c6c" }; + // } + // }, 2000); }, // ==================== 【12】设置模块 ==================== @@ -1065,7 +1066,7 @@ export default { this.inviteDialogVisible = true; }, handleInviteConfirm(selectedContacts) { - console.log('邀请的联系人:', selectedContacts); + console.log("邀请的联系人:", selectedContacts); this.$message.success(`已邀请 ${selectedContacts.length} 位联系人`); this.inviteDialogVisible = false; },