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

基于WebRTC实现多人视频通话的多RTCPeerConnection管理问题

WebRTC多人通话多PeerConnection实现方案

核心实现思路

  • 每个远端参会用户对应独立的RTCPeerConnection实例,不能复用同一个实例,因为单个RTCPeerConnection只能维护一对一的媒体传输通道
  • 用字典结构存储所有PeerConnection实例,key用远端用户的唯一ID(可以用Firebase Auth的uid或者房间内分配的唯一用户标识),value就是对应的RTCPeerConnection对象
  • 本地媒体流只需要创建一次,主动添加到所有新建的PeerConnection实例中,不需要重复采集本地音视频
  • 每个PeerConnection对应独立的远端轨道渲染视图,收到远端流后绑定到对应UI控件上

关键代码实现

1. 存储结构改造

替换原有单PeerConnection变量,改用字典存储多实例:

// key为远端用户唯一ID,value为对应的PeerConnection实例
var peerConnections: [String: RTCPeerConnection] = [:]
// 存储每个远端用户对应的远程媒体轨道,方便渲染/销毁
var remoteStreams: [String: RTCMediaStream] = [:]
// 提前创建好的本地媒体流,只初始化一次
var localStream: RTCMediaStream!

2. 新用户加入时的PeerConnection处理

收到新远端用户的Offer信令时,优先判断是否存在对应用户的PeerConnection,不存在则新建:

// 收到远端用户offer信令时调用,remoteUid为远端用户唯一标识
func handleRemoteOffer(offer: RTCSessionDescription, remoteUid: String) {
    // 如果不存在对应用户的PeerConnection则新建
    if peerConnections[remoteUid] == nil {
        let config = RTCConfiguration()
        config.iceServers = [RTCIceServer(urlStrings: ["stun:stun.l.google.com:19302"])]
        config.sdpSemantics = .unifiedPlan
        // 初始化PeerConnection
        guard let newPeer = factory.peerConnection(with: config, constraints: defaultConstraints, delegate: self) else {
            LOG("创建PeerConnection失败,用户ID:\(remoteUid)")
            return
        }
        // 将本地流添加到新的PeerConnection中
        newPeer.add(localStream.videoTracks[0], streamIds: [localStream.streamId])
        newPeer.add(localStream.audioTracks[0], streamIds: [localStream.streamId])
        peerConnections[remoteUid] = newPeer
    }
    // 获取对应用户的PeerConnection设置远端描述
    guard let peer = peerConnections[remoteUid] else { return }
    peer.setRemoteDescription(offer) { error in
        if let error = error {
            LOG("setRemoteDescription失败,用户ID:\(remoteUid) 错误:\(error.localizedDescription)")
            return
        }
        LOG("setRemoteDescription成功,用户ID:\(remoteUid)")
        // 生成Answer回传给远端用户
        self.makeAnswer(for: remoteUid)
    }
}

3. Answer生成逻辑改造

指定对应远端用户的PeerConnection生成应答:

func makeAnswer(for remoteUid: String) {
    guard let peer = peerConnections[remoteUid] else { return }
    let answerConstraints = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "true"])
    peer.answer(for: answerConstraints) { sdp, error in
        if let error = error {
            LOG("生成Answer失败,用户ID:\(remoteUid) 错误:\(error.localizedDescription)")
            return
        }
        guard let sdp = sdp else { return }
        peer.setLocalDescription(sdp) { error in
            if let error = error {
                LOG("setLocalDescription失败,用户ID:\(remoteUid) 错误:\(error.localizedDescription)")
                return
            }
            // 把Answer通过Firebase信令发送给对应的远端用户
            self.sendAnswer(sdp: sdp, to: remoteUid)
        }
    }
}

4. 远端流回调处理

区分不同用户的PeerConnection回调,绑定对应渲染视图:

func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream) {
    // 找到对应当前PeerConnection的用户ID
    guard let remoteUid = peerConnections.first(where: { $0.value == peerConnection })?.key else { return }
    remoteStreams[remoteUid] = stream
    // 主线程更新UI,将远端流绑定到对应的渲染视图上
    DispatchQueue.main.async {
        let renderView = self.getRenderView(for: remoteUid) // 自行实现获取对应远端用户的渲染视图方法
        stream.videoTracks.first?.add(renderView)
    }
}

5. 用户退出销毁逻辑

用户离开房间时及时销毁对应资源避免内存泄漏:

func handleUserLeave(remoteUid: String) {
    // 销毁对应PeerConnection
    peerConnections[remoteUid]?.close()
    peerConnections.removeValue(forKey: remoteUid)
    // 移除远端流和对应的渲染视图
    remoteStreams.removeValue(forKey: remoteUid)
    removeRenderView(for: remoteUid) // 自行实现移除对应渲染视图的方法
}

注意事项

  • 信令收发必须携带发送方的用户唯一ID,才能正确匹配到对应的PeerConnection实例
  • 采用Unified Plan的SDP语义,兼容性更好,适配多轨道场景
  • 4人通话场景下本地会同时维护3个PeerConnection实例,设备性能消耗会比1对1场景高,建议做低性能设备的适配降级

内容的提问来源于stack exchange,提问作者Jatin Garg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 12:54:04