基于Laravel9+React的流媒体应用观众端音视频连接失败问题
问题:观众端无法获取主播音视频,Simple Peer连接失败
使用Laravel 9、React Js、Laravel Echo、Pusher和Simple Peer搭建流媒体应用,当前在观众端无法获取主播的音视频,出现以下错误:
Uncaught Error: Connection failed. at p._onConnectionStateChange (simplepeer.min.js:6:91542) at _pc.onconnectionstatechange (simplepeer.min.js:6:81452)
观众端React代码
import React,{useEffect, useRef, useState} from 'react' import ReactDOM from 'react-dom'; import axios from 'axios' import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; import Peer from 'simple-peer/simplepeer.min.js'; export default function Viewer() { const [broadcasterId, setBroadcasterId] = useState(null) const vidRef = useRef(); window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_PUSHER_APP_KEY, wsHost: import.meta.env.VITE_PUSHER_HOST ?? `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', enabledTransports: ['ws', 'wss'], cluster:import.meta.env.VITE_PUSHER_APP_CLUSTER }); const handleViewer =()=>{ initializeStreamingChannel() initializeSignalOfferChannel() } function initializeStreamingChannel(){ let streamingPresenceChannel = window.Echo.join(`streaming-channel.${streamId}`) } function initializeSignalOfferChannel(){ window.Echo.private(`stream-signal-channel.${myid}`).listen('StreamOffer', ({data}) => { setBroadcasterId(data.broadcaster); createViewerPeer(data.offer, data.broadcaster); } ) } function createViewerPeer(offer, broadcaster){ console.log(offer) let peer = new Peer({ initiator: false, trickle: false, config: { iceServers: [ { urls:['stun:stun1.1.google.com:19302', 'stun:stun2.1.google.com:19302'] }, // { // urls: this.turn_url, // username: this.turn_username, // credential: this.turn_credential, // }, ], }, }); peer.addTransceiver("video", { direction: "recvonly" }); peer.addTransceiver("audio", { direction: "recvonly" }); handlePeerEvents(peer, offer, broadcaster) } function handlePeerEvents(peer, offer, broadcaster){ peer.on("signal", (data) => { let formData = new FormData(); formData.append("broadcaster", broadcaster) formData.append("answer", data) axios.post("http://127.0.0.1:8000/stream-answer", formData) .then((res) => { console.log(res); }) .catch((err) => { console.log(err); }); }); peer.on("stream", (stream) => { console.log(stream) // display remote stream vidRef.current.srcObject = stream }); peer.on("track", (track, stream) => { console.log("onTrack"); }); peer.on("connect", () => { console.log("Viewer Peer connected"); }); peer.on("close", () => { console.log("Viewer Peer closed"); peer.destroy(); }); const updatedOffer = { ...offer, sdp: `${offer.sdp}\n`, }; peer.signal(updatedOffer); } return ( <div> Viewer <video style={{width:'500px', height:'420px' }} autoPlay={true} ref={vidRef} > </video> <button onClick={handleViewer}>join stream</button> </div> ) } if (document.getElementById('view')) { ReactDOM.render(<Viewer/>, document.getElementById('view')); }
排查与修复方案
- 移除SDP手动修改:代码中手动给SDP添加换行符可能破坏格式,直接使用原始offer:
// 删掉这部分 // const updatedOffer = { // ...offer, // sdp: `${offer.sdp}\n`, // }; peer.signal(offer); - 添加错误监听:给Peer实例添加error事件,定位具体失败原因:
peer.on("error", (err) => { console.error("Peer连接错误详情:", err); }); - 管理Peer实例生命周期:将Peer实例存入useRef,避免局部变量被回收:
const peerRef = useRef(null); function createViewerPeer(offer, broadcaster){ peerRef.current = new Peer({...}); // 后续操作使用peerRef.current } - 配置TURN服务器:如果测试环境不在同一局域网,仅STUN无法穿透NAT,需启用TURN服务,填写有效的TURN地址、用户名和凭证:
iceServers: [ { urls:['stun:stun1.1.google.com:19302', 'stun:stun2.1.google.com:19302'] }, { urls: 'your-turn-server-url', username: 'turn-username', credential: 'turn-password', }, ] - 检查跨域与请求有效性:确认Laravel后端已配置CORS允许前端域名访问,且
/stream-answer接口能正确接收并转发answer给主播端。 - 优化视频标签属性:添加
playsInline属性,适配移动端和部分浏览器的播放规则:<video style={{width:'500px', height:'420px' }} autoPlay playsInline ref={vidRef} /> - 验证Pusher事件数据:确保后端触发的
StreamOffer事件中,offer对象包含完整的type和sdp字段,无格式错误。
内容的提问来源于stack exchange,提问作者stephen jason
相关产品推荐
相关产品推荐

