React Native中peerConnection.ontrack未触发问题求助
React Native WebRTC
ontrack 未触发问题的修复方案 核心问题分析
- 事件绑定时机滞后:当前代码在完成SDP交换后才绑定
ontrack,但Web端offer中可能已包含媒体轨道信息,晚绑定会直接错过事件触发。 - PeerConnection引用被回收:
peerConnection是函数内局部变量,函数执行完成后可能被垃圾回收,导致后续ICE候选和媒体轨道事件无法触发。 - Firebase监听未清理:未移除
child_added监听,会引发内存泄漏,重复连接时还会触发多次处理逻辑。
修复方案及代码修改
1. 保存PeerConnection到组件状态
在组件状态中维护peerConnection引用,避免被垃圾回收:
const [remoteStream, setRemoteStream] = useState<MediaStream | null>(null); const [connected, setConnected] = useState(false); const [peerConnection, setPeerConnection] = useState<RTCPeerConnection | null>(null); // 新增状态 const [currentOfferCode, setCurrentOfferCode] = useState<string | null>(null); const [candidateListener, setCandidateListener] = useState<any>(null);
2. 提前绑定所有PeerConnection事件
创建peerConnection后立即绑定ontrack、onicecandidate等事件,确保不会错过触发时机:
// 创建PeerConnection后立刻绑定事件 const pc = new RTCPeerConnection(rtcConfig); setPeerConnection(pc); // 绑定ontrack事件 pc.ontrack = (event: RTCTrackEvent) => { console.log('ontrack triggered'); const [stream] = event.streams; console.log('Stream Data: ', stream); setRemoteStream(stream); setConnected(true); }; // 绑定ICE候选事件 pc.onicecandidate = (event) => { if (event.candidate) { console.log('onicecandidate', event.candidate); database() .ref(`candidates/${offerCode}/mobileToWeb`) .push(event.candidate.toJSON()); } }; // 后续再处理SDP交换逻辑 await pc.setRemoteDescription(new RTCSessionDescription(offerData));
3. 添加资源清理逻辑
在组件卸载或重新连接时,清理PeerConnection和Firebase监听:
useEffect(() => { return () => { // 关闭PeerConnection if (peerConnection) { peerConnection.close(); setPeerConnection(null); } // 移除Firebase监听 if (currentOfferCode && candidateListener) { database().ref(`candidates/${currentOfferCode}/webToMobile`).off('child_added', candidateListener); } }; }, [peerConnection, currentOfferCode, candidateListener]);
4. 优化ICE候选监听逻辑
保存Firebase监听引用,确保在切换连接时能正确清理旧监听:
// 监听Web端ICE候选 const ref = database().ref(`candidates/${offerCode}/webToMobile`); const listener = ref.on('child_added', async snapshot => { const candidateData = snapshot.val(); if (candidateData && pc) { const candidate = new RTCIceCandidate(candidateData); await pc.addIceCandidate(candidate); } }); setCandidateListener(listener);
完整关键代码片段
const App = () => { const [remoteStream, setRemoteStream] = useState<MediaStream | null>(null); const [connected, setConnected] = useState(false); const [peerConnection, setPeerConnection] = useState<RTCPeerConnection | null>(null); const [currentOfferCode, setCurrentOfferCode] = useState<string | null>(null); const [candidateListener, setCandidateListener] = useState<any>(null); useEffect(() => { return () => { if (peerConnection) { peerConnection.close(); setPeerConnection(null); } if (currentOfferCode && candidateListener) { database().ref(`candidates/${currentOfferCode}/webToMobile`).off('child_added', candidateListener); } }; }, [peerConnection, currentOfferCode, candidateListener]); const connectWithOfferCode = async (offerCode: string) => { try { // 清理旧连接资源 if (peerConnection) { peerConnection.close(); setPeerConnection(null); } if (currentOfferCode && candidateListener) { database().ref(`candidates/${currentOfferCode}/webToMobile`).off('child_added', candidateListener); } setCurrentOfferCode(offerCode); // 获取Web端offer const offerSnapshot = await database() .ref(`offers/${offerCode}`) .once('value'); const offerData = offerSnapshot.val(); if (!offerData) { console.error('No offer found for this code.'); return; } // 创建PeerConnection并绑定事件 const pc = new RTCPeerConnection(rtcConfig); setPeerConnection(pc); pc.ontrack = (event: RTCTrackEvent) => { console.log('ontrack triggered'); const [stream] = event.streams; console.log('Stream Data: ', stream); setRemoteStream(stream); setConnected(true); }; pc.onicecandidate = (event) => { if (event.candidate) { console.log('onicecandidate', event.candidate); database() .ref(`candidates/${offerCode}/mobileToWeb`) .push(event.candidate.toJSON()); } }; // 处理SDP交换 await pc.setRemoteDescription(new RTCSessionDescription(offerData)); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); await database().ref(`answers/${offerCode}`).set({ type: answer.type, sdp: answer.sdp, }); // 监听Web端ICE候选 const ref = database().ref(`candidates/${offerCode}/webToMobile`); const listener = ref.on('child_added', async snapshot => { const candidateData = snapshot.val(); if (candidateData && pc) { const candidate = new RTCIceCandidate(candidateData); await pc.addIceCandidate(candidate); } }); setCandidateListener(listener); console.log('Connected with offer code:', offerCode); } catch (err) { console.error('Error connecting:', err); } }; // 其余渲染代码不变 };
额外检查点
- 确认Web端创建offer时已将媒体轨道(如屏幕共享流)添加到PeerConnection。
- 检查Firebase中offer、answer、candidates数据格式是否正确,无缺失或损坏。
- 确保React Native WebRTC库版本与Web端WebRTC API兼容。
内容的提问来源于stack exchange,提问作者dheeraj kumar
相关产品推荐
相关产品推荐

