视频会议

This commit is contained in:
duoaohui
2026-05-23 21:35:46 +08:00
parent c118ac43f5
commit 9df92977fd

View File

@@ -117,49 +117,74 @@ const resolveDom = (refValue) => {
return null
}
const applyVideo = () => {
// 当前已经贴到 video / audio 元素上的 track用于幂等判断避免重复 setSrcObject 触发闪烁
let boundVideoTrack = null
let boundAudioTrack = null
const applyVideo = (retry = 0) => {
// #ifdef H5
if (props.videoTrack && props.videoEnabled) {
const el = ensureMediaEl('video')
if (el) {
el.muted = props.isLocal
el.srcObject = new MediaStream([props.videoTrack])
const playPromise = el.play && el.play()
if (playPromise && typeof playPromise.catch === 'function') {
playPromise.catch(() => {})
const parent = resolveDom(mediaBox.value)
if (!parent) {
// DOM 尚未挂载,最多重试 10 次≈500ms
if (retry < 10) {
setTimeout(() => applyVideo(retry + 1), 50)
}
return
}
const el = ensureMediaEl('video')
if (!el) return
el.muted = props.isLocal
if (boundVideoTrack !== props.videoTrack) {
el.srcObject = new MediaStream([props.videoTrack])
boundVideoTrack = props.videoTrack
}
const playPromise = el.play && el.play()
if (playPromise && typeof playPromise.catch === 'function') {
playPromise.catch(() => {})
}
} else {
removeMediaEl('video')
boundVideoTrack = null
}
// #endif
}
const applyAudio = () => {
const applyAudio = (retry = 0) => {
// #ifdef H5
// 本地 tile 不挂载 audio避免回声
if (!props.isLocal && props.audioTrack && props.audioEnabled) {
const el = ensureMediaEl('audio')
if (el) {
el.srcObject = new MediaStream([props.audioTrack])
const playPromise = el.play && el.play()
if (playPromise && typeof playPromise.catch === 'function') {
playPromise.catch(() => {})
const parent = resolveDom(mediaBox.value)
if (!parent) {
if (retry < 10) {
setTimeout(() => applyAudio(retry + 1), 50)
}
return
}
const el = ensureMediaEl('audio')
if (!el) return
if (boundAudioTrack !== props.audioTrack) {
el.srcObject = new MediaStream([props.audioTrack])
boundAudioTrack = props.audioTrack
}
const playPromise = el.play && el.play()
if (playPromise && typeof playPromise.catch === 'function') {
playPromise.catch(() => {})
}
} else {
removeMediaEl('audio')
boundAudioTrack = null
}
// #endif
}
watch(() => [props.videoTrack, props.videoEnabled], () => nextTick(applyVideo), { immediate: true })
watch(() => [props.audioTrack, props.audioEnabled, props.isLocal], () => nextTick(applyAudio), { immediate: true })
watch(() => [props.videoTrack, props.videoEnabled], () => nextTick(() => applyVideo()), { immediate: true })
watch(() => [props.audioTrack, props.audioEnabled, props.isLocal], () => nextTick(() => applyAudio()), { immediate: true })
// 修复刚进入会议时画面不显示问题:
// watch 的 immediate:true 在 setup 阶段就触发 applyVideo此时 mediaBox ref 尚未绑定真实 DOM
// 即便包了 nextTick在 uni-h5 偶发条件下仍可能命中 ref 未就绪,导致 srcObject 没贴上。
// 这里在 onMounted 里再补一次绑定,确保 DOM ready 后必然挂上 track
// 修复刚进入会议时画面不显示、必须点切换才出图的问题:
// 1) watch 的 immediate:true 在 setup 阶段就触发 applyVideo此时 mediaBox ref 尚未绑定真实 DOM
// 2) 即便包了 nextTick在 uni-h5 ref 偶发未就绪
// 现在 applyVideo/applyAudio 在 DOM 未就绪时会自动重试,并在 onMounted 再强制兜底一次
onMounted(() => {
nextTick(() => {
applyVideo()