GoogleWebRTC 挂起(冻结)swift 本机应用程序 (OpenVidu) 中的主线程

GoogleWebRTC hangs (freezes) the main thread in swift native app (OpenVidu)

我们的 iOS (swift) 本机应用程序与 OpenVidu 实现(这在后台使用 GoogleWebRTC)。需要的具体条件:需要加入现有房间,至少有 8 名参与者已经在直播。有 6 名参与者时,这种情况发生的频率较低,而少于 6 人时几乎不会发生。如果参与者一个接一个地加入,它不会挂起,只有当您加入房间并且所有其他参与者都已经流式传输时才会挂起。这表明问题的并发性质。

GoogleWebRTC 在 setRemoteDescription 通话中挂起:

func setRemoteDescription(sdpAnswer: String) {
    let sessionDescription: RTCSessionDescription = RTCSessionDescription(type: RTCSdpType.answer, sdp: sdpAnswer)
    self.peerConnection!.setRemoteDescription(sessionDescription, completionHandler: {(error) in
        print("Local Peer Remote Description set: " + error.debugDescription)
    })
}

正如您在上面的屏幕截图中看到的,主线程在 __psynch_cvwait 上挂起。似乎没有任何其他线程被锁定。 锁永远不会释放 使应用程序完全冻结。

为了解决这个问题,我尝试了以下方法:

  1. 我将OpenVidu信令服务器处理(RPC协议)从主线程移到了单独的线程中。这只会导致锁定现在发生在我创建的单独线程之一中。它现在不会阻止 UI,但会阻止 OV 信号。问题依旧。

  2. 我添加了锁来处理每个信号事件(参与者加入事件、发布视频等)同步(一个接一个)。这也无济于事(实际上使情况变得更糟)。

  3. 我没有使用来自 Cocoapods 的 GoogleWebRTC v. 1.1.31999,而是下载了最新的 GoogleWebRTC 源代码,在发布配置和 included into my project 中构建了它们。这对解决问题没有帮助。

任何 suggestions/comments 将不胜感激。 谢谢!

编辑 1:

signaling_threadworker_thread都在等待同一种锁。在锁定的那一刻,他们都没有执行我的任何代码。

我还尝试在 GoogleWebRTC 的 DEBUG 版本中 运行,在这种情况下没有发生锁定,但一切都运行得慢得多(这对于调试来说没问题,但我们不能使用它生产中)。

编辑 2:

我尝试为 offersetLocalDescription 回调包装额外的 DispatchQueue,但这没有任何改变。这个问题仍然可以很好地重现(几乎 100% 的时间,如果我有 8 个参与者使用流):

    self.peerConnection!.offer(for: constrains) { (sdp, error) in
        DispatchQueue.global(qos: .background).async {

            guard let sdp = sdp else {
                return
            }

            self.peerConnection!.setLocalDescription(sdp, completionHandler: { (error) in
                DispatchQueue.global(qos: .background).async {
                    completion(sdp)
                }
            })
        }
    }

可以从任何线程调用 WebRTC Obj-C API,但大多数方法调用都传递给 WebRTC 的内部线程 signalling thread

此外,callbacks/observers 像 SetLocalDescriptionObserverInterfaceRTCSetSessionDescriptionCompletionHandler 是从 signaling thread 上的 WebRTC 调用的。

看截图,似乎信令线程当前被阻塞,无法再调用 WebRTC API 调用。

因此,为避免死锁,创建您自己的线程/dispatch_queue 并处理回调是个好主意。

https://webrtc.googlesource.com/src/+/0a52ede821ba12ee6fff6260d69cddcca5b86a4e/api/g3doc/index.mdhttps://webrtc.googlesource.com/src/+/0a52ede821ba12ee6fff6260d69cddcca5b86a4e/api/g3doc/threading_design.md 了解详情。

经过 OpenVidu 团队的评论后,通过在添加已经在房间中的参与者之间增加 100 毫秒的延迟来解决问题。我认为这更像是一个 hack 而不是真正的解决方案,但我可以确认它在测试和生产环境中都有效:

DispatchQueue.global(qos: .background).async {
    for info in dict.values {
        let remoteParticipant = self.newRemoteParticipant(info: info)
        if let streamId = info.streamId {
            remoteParticipant.createOffer(completion: {(sdp) in
                self.receiveVideoFrom(sdp: sdp, remoteParticipant: remoteParticipant, streamId: streamId)
            })
        } else {
            print("No streamId")
        }
        Thread.sleep(forTimeInterval: 0.1)
    }
}