diff --git a/src/components/call/CallWindow.vue b/src/components/call/CallWindow.vue index b35a634..67bc1f1 100644 --- a/src/components/call/CallWindow.vue +++ b/src/components/call/CallWindow.vue @@ -389,9 +389,16 @@ watchEffect(() => { // 监听小程序流,初始化 FLV 播放器 let flvPlayer: flvjs.Player | null = null let currentFlvUrl: string | null = null // 跟踪当前播放的 FLV URL +let chaseFrameTimer: number | null = null // 追帧定时器(解决累积延迟问题) // 清理 FLV 播放器的辅助函数 function cleanupFlvPlayer() { + // 清理追帧定时器 + if (chaseFrameTimer) { + clearInterval(chaseFrameTimer) + chaseFrameTimer = null + } + if (flvPlayer) { try { // @ts-ignore - flv.js Player 内部属性 @@ -448,6 +455,31 @@ watch( console.log('[CallWindow] 初始化 FLV 播放器:', newFlvUrl) flvPlayer = props.initFlvPlayer(flvVideoRef.value, 0) currentFlvUrl = newFlvUrl + + // 启动追帧定时器(解决累积延迟问题) + if (flvVideoRef.value) { + // 先清理可能存在的旧定时器 + if (chaseFrameTimer) { + clearInterval(chaseFrameTimer) + } + chaseFrameTimer = window.setInterval(() => { + if (!flvPlayer || !flvVideoRef.value) return + + const video = flvVideoRef.value + // 检查缓冲区是否有数据 + if (video.buffered.length > 0) { + const end = video.buffered.end(0) // 缓冲区末尾时间 + const current = video.currentTime // 当前播放时间 + const diff = end - current // 延迟时长 + + // 如果延迟超过 1.5 秒,执行追帧 + if (diff > 1.5) { + console.log(`[CallWindow] 检测到延迟 ${diff.toFixed(2)}s,执行追帧...`) + video.currentTime = end - 0.1 // 跳到缓冲区末尾附近 + } + } + }, 1500) // 每 1.5 秒检查一次 + } } }, { immediate: true, deep: true } diff --git a/src/composables/useWebRTC.ts b/src/composables/useWebRTC.ts index 2f8d5af..86b2f91 100644 --- a/src/composables/useWebRTC.ts +++ b/src/composables/useWebRTC.ts @@ -209,6 +209,15 @@ export function useWebRTC( } else { console.log('[WebRTC] RTMP 模式:等待 FLV 流') } + + // 延迟刷新房间流信息,确保小程序推流成功后能获取到 FLV 地址 + // 小程序推流成功需要时间(通常 2-3 秒),所以延迟刷新 + setTimeout(() => { + if (call.active && call.miniprogramStreams.length === 0 && currentRoomId) { + console.log('[WebRTC] 📡 通话建立后延迟刷新房间信息...') + refreshRoomStreams() + } + }, 2000) } else if (signal === 'offer') { // WebRTC 模式才处理 offer(RTMP 模式不使用 WebRTC 信令) @@ -251,27 +260,37 @@ export function useWebRTC( pull_url: content.pull_url }) - // 如果有 FLV 地址,添加或更新小程序流列表 - if (content.flv_url && content.user_id) { + // 获取 FLV 地址(优先使用 flv_url,兼容 pull_url 转换) + let flvUrl = content.flv_url || '' + + // 如果有 FLV 地址或用户 ID,添加或更新小程序流列表 + if (flvUrl && content.user_id) { const existingIndex = call.miniprogramStreams.findIndex(s => s.userId === content.user_id) if (existingIndex >= 0) { // 更新现有流的地址(重要:当用户重新加入时使用新的流地址) const oldFlvUrl = call.miniprogramStreams[existingIndex].flvUrl - call.miniprogramStreams[existingIndex].flvUrl = content.flv_url + call.miniprogramStreams[existingIndex].flvUrl = flvUrl console.log('[WebRTC] 🔄 更新小程序流地址:', content.user_id) console.log('[WebRTC] 旧地址:', oldFlvUrl) - console.log('[WebRTC] 新地址:', content.flv_url) + console.log('[WebRTC] 新地址:', flvUrl) } else { // 添加新的小程序流 call.miniprogramStreams.push({ userId: content.user_id, - flvUrl: content.flv_url + flvUrl: flvUrl }) - console.log('[WebRTC] ✅ 添加小程序流:', content.user_id, content.flv_url) + console.log('[WebRTC] ✅ 添加小程序流:', content.user_id, flvUrl) } console.log('[WebRTC] 📺 当前小程序流列表:', call.miniprogramStreams) - } else { - console.log('[WebRTC] ⚠️ participant_joined 没有 FLV 地址') + } else if (content.user_id && !flvUrl) { + // 兼容性处理:如果没有 FLV 地址,尝试延迟获取 + console.log('[WebRTC] ⚠️ participant_joined 没有 FLV 地址,1秒后尝试刷新房间信息') + setTimeout(() => { + if (call.active && currentRoomId && call.miniprogramStreams.length === 0) { + console.log('[WebRTC] 📡 延迟刷新房间信息...') + refreshRoomStreams() + } + }, 1000) } // 如果当前没有小程序流,尝试重新获取房间信息 @@ -615,6 +634,14 @@ export function useWebRTC( console.log('[WebRTC] 📤 发送 accepted 信令给:', currentReceiverUserId) sendSignal('accepted', undefined, currentReceiverUserId) + + // 延迟刷新房间流信息,确保小程序推流成功后能获取到 FLV 地址 + setTimeout(() => { + if (call.active && call.miniprogramStreams.length === 0 && currentRoomId) { + console.log('[WebRTC] 📡 接听后延迟刷新房间信息...') + refreshRoomStreams() + } + }, 2000) } catch (error) { console.error('[WebRTC] ❌ 接听失败:', error) endCall() @@ -1033,16 +1060,30 @@ export function useWebRTC( isLive: true, hasAudio: true, hasVideo: true, + cors: true, // 允许跨域 }, { enableWorker: false, - enableStashBuffer: false, + enableStashBuffer: false, // 关闭堆积缓存,减少延迟 stashInitialSize: 128, lazyLoad: false, + lazyLoadMaxDuration: 0.2, // 极短的懒加载时间 + autoCleanupSourceBuffer: true, // 自动清理缓存防止内存泄漏 }) player.attachMediaElement(videoElement) player.load() - player.play() + + // 自动播放失败时静音重试(兼容浏览器自动播放策略) + player.play().catch(async (e) => { + console.warn('[WebRTC] 自动播放失败,尝试静音播放:', e) + videoElement.muted = true + try { + await player.play() + console.log('[WebRTC] 静音播放成功') + } catch (e2) { + console.error('[WebRTC] 静音播放也失败:', e2) + } + }) // 监听错误 player.on(flvjs.Events.ERROR, (errorType, errorDetail, errorInfo) => {