// composables/useStreams.js import { nextTick } from 'vue' export function useStreams({ instanceList, videoElement, wslist, openStr, VideoConverter }) { // 用 Map 做延迟队列,避免固定 8 个的限制 const feedState = new Map() function ensureState(index) { if (!feedState.has(index)) { feedState.set(index, { processing: false, pending: null }) } return feedState.get(index) } function pushFrame(index, buf) { const st = ensureState(index) if (st.processing) { // 覆盖旧的等待帧,保留最新 st.pending = buf return } st.processing = true try { instanceList[index].converter.appendRawData(new Uint8Array(buf)) } finally { st.processing = false if (st.pending) { const next = st.pending st.pending = null queueMicrotask(() => pushFrame(index, next)) } } } function resetFeedState(index) { const st = feedState.get(index) if (!st) return st.processing = false st.pending = null } async function waitForVideoEl(udid, tries = 20, delay = 16) { for (let i = 0; i < tries; i++) { const el = videoElement.value?.[udid] if (el) return el await nextTick() await new Promise(r => setTimeout(r, delay)) } return null } function refreshStream(index, hard = true) { const devices = Object.keys(videoElement.value || {}) const udid = devices[index] const video = videoElement.value?.[udid] if (!video || !instanceList[index]) return resetFeedState(index) try { instanceList[index].converter?.destroy?.() } catch { } instanceList[index].converter = null if (hard) { try { video.pause?.() } catch { } try { video.removeAttribute?.('src') } catch { } try { video.load?.() } catch { } } // 重新挂新的 converter instanceList[index].converter = new VideoConverter(video, 60, 1) // 让后端尽快推关键帧 try { wslist[index]?.send?.(openStr) } catch { } } return { feedState, pushFrame, resetFeedState, waitForVideoEl, refreshStream, } }