You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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} />
}

问题原因

  1. 播放触发冲突:video标签设置了autoPlay,同时代码里手动调用play(),当srcObject赋值时,浏览器自动播放流程和手动播放请求互相干扰,导致中断。
  2. 未处理状态切换:设置新的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 19:37:34