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

React Native中peerConnection.ontrack未触发问题求助

React Native WebRTC ontrack 未触发问题的修复方案

核心问题分析

  1. 事件绑定时机滞后:当前代码在完成SDP交换后才绑定ontrack,但Web端offer中可能已包含媒体轨道信息,晚绑定会直接错过事件触发。
  2. PeerConnection引用被回收:peerConnection是函数内局部变量,函数执行完成后可能被垃圾回收,导致后续ICE候选和媒体轨道事件无法触发。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 22:54:55