React+PeerJs视频通话构建报错:play()请求被新加载请求中断
React+PeerJs视频聊天应用Chrome播放错误解决方案
错误现象
使用React结合PeerJs开发视频聊天应用时,在Chrome浏览器发起或接收视频通话时,出现以下错误:
Uncaught (in promise) DOMException: The play() request was interrupted by a new load request
尝试过改用navigator.mediaDevices.getUserMedia的then链式调用、用getElementById替代useRef获取元素,问题仍未解决。
错误含义与产生原因
错误含义
该错误表示:视频元素的play()请求正在处理过程中,元素的数据源(如srcObject)被重新加载/替换,导致正在进行的播放请求被强制中断,从而抛出DOM异常。
产生原因
- 重复调用
play():代码中存在多次调用同一视频元素play()的情况(例如原代码中myVideoPromise.then回调里再次调用play()),前一次play请求还未完成,后一次又触发,导致冲突。 - 未等待视频就绪就播放:设置
srcObject后立即调用play(),此时视频元数据尚未加载完成,浏览器还未准备好播放,后续数据源的微小变更就会中断播放请求。 - DOM操作与React生命周期冲突:直接使用
getElementById操作DOM,脱离React虚拟DOM的管控,可能导致元素状态被意外覆盖,引发数据源重复设置。
修复方案
- 避免重复调用
play():每个视频元素仅调用一次play(),且要确保在数据源设置完成后执行。 - 使用React useRef管理视频元素:适配React组件生命周期,避免DOM操作与虚拟DOM的冲突。
- 等待视频元数据加载完成再播放:监听视频元素的
loadedmetadata事件,确保视频就绪后再触发播放。 - 正确处理play的Promise:捕获play可能抛出的异常,避免未处理的Promise错误。
修正后的代码示例
组件内定义useRef
import { useRef } from 'react'; // 在组件内部声明视频元素的ref const myVideoRef = useRef(null); const remoteVideoRef = useRef(null);
接收通话逻辑
peer.on('call', (call) => { if (navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({ audio: true, video: { width: 1280, height: 720 } }) .then((stream) => { // 设置本地视频流并监听就绪事件 if (myVideoRef.current) { myVideoRef.current.srcObject = stream; myVideoRef.current.addEventListener('loadedmetadata', () => { myVideoRef.current.play().catch(err => { console.error("本地视频播放失败:", err); }); }); } call.answer(stream); call.on('stream', (remoteStream) => { // 设置远程视频流并监听就绪事件 if (remoteVideoRef.current) { remoteVideoRef.current.srcObject = remoteStream; remoteVideoRef.current.addEventListener('loadedmetadata', () => { remoteVideoRef.current.play().catch(err => { console.error("远程视频播放失败:", err); }); }); } }); }) .catch(err => { console.error("获取媒体流失败:", err); }); } });
发起通话逻辑
const videoCall = () => { if (navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({ audio: true, video: { width: 1280, height: 720 } }) .then((stream) => { // 设置本地视频流并监听就绪事件 if (myVideoRef.current) { myVideoRef.current.srcObject = stream; myVideoRef.current.addEventListener('loadedmetadata', () => { myVideoRef.current.play().catch(err => { console.error("本地视频播放失败:", err); }); }); } const call = peer.call(myconnection.friendId, stream); call.on('stream', (remoteStream) => { // 设置远程视频流并监听就绪事件 if (remoteVideoRef.current) { remoteVideoRef.current.srcObject = remoteStream; remoteVideoRef.current.addEventListener('loadedmetadata', () => { remoteVideoRef.current.play().catch(err => { console.error("远程视频播放失败:", err); }); }); } }); }) .catch(err => { console.error("获取媒体流失败:", err); }); } };
页面中的视频元素
{/* 本地视频:添加muted规避自动播放限制,playsInline适配移动端 */} <video ref={myVideoRef} muted playsInline /> {/* 远程视频 */} <video ref={remoteVideoRef} playsInline />
内容的提问来源于stack exchange,提问作者Qih Se
相关产品推荐
相关产品推荐

