使用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
相关产品推荐
相关产品推荐

