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

使用Agora RTC SDK-NG开发Next.js视频通话重连时RTCPeerConnection创建失败求助

解决Agora RTC SDK-NG在Next.js中重新加入通话的PeerConnection数量超限问题

这个错误的核心原因是每次重新加入通话时,没有彻底销毁之前创建的Agora客户端实例、音视频轨道以及关联的RTCPeerConnection资源,导致浏览器中堆积了大量未释放的连接实例,超出了浏览器的最大限制。以下是具体的解决步骤:

1. 离开通话时执行完整的资源销毁流程

每次触发离开操作时,必须按顺序清理所有资源,不能遗漏:

  • 停止并关闭本地音视频轨道
  • 离开当前频道
  • 销毁Agora客户端实例
  • 重置相关变量,避免引用残留

示例代码:

const leaveAndCleanup = async (client, localAudioTrack, localVideoTrack) => {
  // 停止并关闭本地音频轨道
  if (localAudioTrack) {
    localAudioTrack.stop();
    localAudioTrack.close();
  }
  // 停止并关闭本地视频轨道
  if (localVideoTrack) {
    localVideoTrack.stop();
    localVideoTrack.close();
  }
  // 离开频道并销毁客户端
  if (client) {
    await client.leave();
    client.destroy();
  }
};

2. 在Next.js组件中用useRef存储实例,避免重复创建

Next.js的组件重新渲染或挂载可能导致多次创建客户端实例,用useRef可以保持实例的唯一性,同时在组件卸载时自动清理:

示例组件代码:

import { useEffect, useRef } from 'react';
import AgoraRTC from 'agora-rtc-sdk-ng';

const VideoCall = () => {
  // 用useRef存储客户端和轨道实例,避免每次渲染重新创建
  const clientRef = useRef(null);
  const localAudioTrackRef = useRef(null);
  const localVideoTrackRef = useRef(null);
  const remoteTracksRef = useRef({});

  // 组件卸载时自动清理资源
  useEffect(() => {
    return async () => {
      // 清理本地轨道
      if (localAudioTrackRef.current) {
        localAudioTrackRef.current.stop();
        localAudioTrackRef.current.close();
      }
      if (localVideoTrackRef.current) {
        localVideoTrackRef.current.stop();
        localVideoTrackRef.current.close();
      }
      // 清理远端轨道
      Object.values(remoteTracksRef.current).forEach(track => {
        track.stop();
        track.close();
      });
      // 销毁客户端
      if (clientRef.current) {
        await clientRef.current.leave();
        clientRef.current.destroy();
      }
    };
  }, []);

  // 加入通话逻辑
  const joinChannel = async (appId, channelId, token, uid) => {
    // 加入前先检查是否有残留实例,有则清理
    if (clientRef.current) {
      await leaveAndCleanup(
        clientRef.current,
        localAudioTrackRef.current,
        localVideoTrackRef.current
      );
      clientRef.current = null;
      localAudioTrackRef.current = null;
      localVideoTrackRef.current = null;
      remoteTracksRef.current = {};
    }

    // 创建新的客户端实例
    clientRef.current = AgoraRTC.createClient({ mode: 'rtc', codec: 'h264' });

    // 初始化客户端、加入频道、发布轨道的逻辑
    await clientRef.current.join(appId, channelId, token, uid);
    localAudioTrackRef.current = await AgoraRTC.createMicrophoneAudioTrack();
    localVideoTrackRef.current = await AgoraRTC.createCameraVideoTrack();
    await clientRef.current.publish([localAudioTrackRef.current, localVideoTrackRef.current]);

    // 处理远端用户加入的逻辑
    clientRef.current.on('user-published', async (user, mediaType) => {
      await clientRef.current.subscribe(user, mediaType);
      const track = mediaType === 'audio' ? user.audioTrack : user.videoTrack;
      remoteTracksRef.current[user.uid] = track;
      // 将轨道挂载到DOM的逻辑...
    });
  };

  // 手动离开通话
  const leaveChannel = async () => {
    await leaveAndCleanup(
      clientRef.current,
      localAudioTrackRef.current,
      localVideoTrackRef.current
    );
    clientRef.current = null;
    localAudioTrackRef.current = null;
    localVideoTrackRef.current = null;
    remoteTracksRef.current = {};
    // 移除DOM中的轨道元素逻辑...
  };

  return (
    <div>
      <button onClick={() => joinChannel('你的APPID', '频道ID', 'TOKEN', 0)}>加入通话</button>
      <button onClick={leaveChannel}>离开通话</button>
    </div>
  );
};

export default VideoCall;

3. 检查远端轨道的清理

如果订阅了远端用户的音视频轨道,在离开通话时必须同样停止并关闭这些轨道,避免资源泄漏。上面的示例中已经包含了远端轨道的清理逻辑,确保所有订阅的轨道都被正确释放。

内容的提问来源于stack exchange,提问作者MD Shakib Uddin Bhuiyan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:00:17