MediaStream播放报错:play()请求被新加载请求中断问题排查
播放MediaStream视频轨道时出现DOMException错误
错误信息
Uncaught (in promise) DOMException: The play() request was interrupted by a new load request.
浏览器指向错误触发行:
videoRef.current.srcObject = new MediaStream([videoTrack])
问题代码
import { useEffect, useRef, useState } from "react" import { Link } from "react-router-dom"; import { Room } from "./Room"; export const Landing = () => { const [name, setName] = useState(""); const [localAudioTrack, setLocalAudioTrack] = useState(null); const [localVideoTrack, setlocalVideoTrack] = useState(null); const videoRef = useRef(null); const [joined, setJoined] = useState(false); const getCam = async () => { const stream = await window.navigator.mediaDevices.getUserMedia({ video: true, audio: true }) const audioTrack = stream.getAudioTracks()[0] const videoTrack = stream.getVideoTracks()[0] setLocalAudioTrack(audioTrack); setlocalVideoTrack(videoTrack); if (!videoRef.current) { return; } videoRef.current.srcObject = new MediaStream([videoTrack]) videoRef.current.play(); } useEffect(() => { if (videoRef && videoRef.current) { getCam() } }, [videoRef]); if (!joined) { return <div> <video autoPlay ref={videoRef}></video> <input type="text" onChange={(e) => { setName(e.target.value); }}> </input> <button onClick={() => { setJoined(true); }}>Join</button> </div> } return <Room name={name} localAudioTrack={localAudioTrack} localVideoTrack={localVideoTrack} /> }
问题原因
- 播放触发冲突:video标签设置了
autoPlay,同时代码里手动调用play(),当srcObject赋值时,浏览器自动播放流程和手动播放请求互相干扰,导致中断。 - 未处理状态切换:设置新的
srcObject前,没有暂停当前可能正在进行的播放,新媒体加载直接打断了原有播放请求。
解决方案
1. 移除重复播放触发
去掉video标签的autoPlay属性,保留手动play()调用;同时添加muted属性适配浏览器自动播放策略(非静音视频需用户交互才能播放):
<video ref={videoRef} muted></video>
2. 处理播放状态,避免冲突
设置新srcObject前,先暂停当前播放并清理原有资源,同时用try-catch捕获播放错误:
const getCam = async () => { try { const stream = await window.navigator.mediaDevices.getUserMedia({ video: true, audio: true }); const audioTrack = stream.getAudioTracks()[0]; const videoTrack = stream.getVideoTracks()[0]; setLocalAudioTrack(audioTrack); setlocalVideoTrack(videoTrack); if (!videoRef.current) return; // 清理原有播放资源 if (videoRef.current.srcObject) { videoRef.current.pause(); videoRef.current.srcObject.getTracks().forEach(track => track.stop()); } videoRef.current.srcObject = new MediaStream([videoTrack]); await videoRef.current.play(); } catch (err) { console.error("获取媒体或播放失败:", err); } }
3. 添加组件清理逻辑
在组件卸载时停止媒体轨道,避免内存泄漏:
useEffect(() => { if (videoRef.current) { getCam(); } return () => { if (localAudioTrack) localAudioTrack.stop(); if (localVideoTrack) localVideoTrack.stop(); if (videoRef.current?.srcObject) { videoRef.current.srcObject.getTracks().forEach(track => track.stop()); } }; }, [videoRef]);
最终修改后的代码
import { useEffect, useRef, useState } from "react" import { Link } from "react-router-dom"; import { Room } from "./Room"; export const Landing = () => { const [name, setName] = useState(""); const [localAudioTrack, setLocalAudioTrack] = useState(null); const [localVideoTrack, setlocalVideoTrack] = useState(null); const videoRef = useRef(null); const [joined, setJoined] = useState(false); const getCam = async () => { try { const stream = await window.navigator.mediaDevices.getUserMedia({ video: true, audio: true }); const audioTrack = stream.getAudioTracks()[0]; const videoTrack = stream.getVideoTracks()[0]; setLocalAudioTrack(audioTrack); setlocalVideoTrack(videoTrack); if (!videoRef.current) return; // 清理原有播放资源 if (videoRef.current.srcObject) { videoRef.current.pause(); videoRef.current.srcObject.getTracks().forEach(track => track.stop()); } videoRef.current.srcObject = new MediaStream([videoTrack]); await videoRef.current.play(); } catch (err) { console.error("获取媒体或播放失败:", err); } }; useEffect(() => { if (videoRef.current) { getCam(); } return () => { // 组件卸载时停止媒体轨道 if (localAudioTrack) localAudioTrack.stop(); if (localVideoTrack) localVideoTrack.stop(); if (videoRef.current?.srcObject) { videoRef.current.srcObject.getTracks().forEach(track => track.stop()); } }; }, [videoRef]); if (!joined) { return ( <div> <video ref={videoRef} muted></video> <input type="text" onChange={(e) => setName(e.target.value)} /> <button onClick={() => setJoined(true)}>Join</button> </div> ); } return <Room name={name} localAudioTrack={localAudioTrack} localVideoTrack={localVideoTrack} />; };
内容的提问来源于stack exchange,提问作者kushagra kartik
相关产品推荐
相关产品推荐

