3

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

GoogleWebRTCsetRemoteDescription待命:

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 源,在发布配置中构建它们并包含在我的项目中。这无助于解决问题。

任何建议/意见将不胜感激。谢谢!

编辑1:

signaling_threadand are 都在worker_thread等待同一种锁中的东西。在锁定的那一刻,它们都没有执行我的任何代码。

我还尝试在 DEBUG build of 中运行GoogleWebRTC,在这种情况下不会发生任何锁定,但一切都会慢得多(这对于调试来说是可以的,但我们不能在生产环境中使用它)。

在此处输入图像描述

编辑2:

我试图包装额外DispatchQueue的 foroffersetLocalDescription回调,但这没有任何改变。问题仍然可以很好地重现(几乎 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)
                }
            })
        }
    }
4

2 回答 2

2

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

此外,回调/观察者喜欢SetLocalDescriptionObserverInterfaceRTCSetSessionDescriptionCompletionHandler从 WebRTC 在signaling thread.

看截图,好像信号线程当前被阻塞,不能再调用WebRTC API调用。

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

有关详细信息,请参阅 https://webrtc.googlesource.com/src/+/0a52ede821ba12ee6fff6260d69cddcca5b86a4e/api/g3doc/index.mdhttps://webrtc.googlesource.com/src/+/0a52ede821ba12ee6fff6260d69cddcca5b86a4e/api/g3doc/threading_design.md .

于 2021-05-26T03:45:51.650 回答
0

在 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)
    }
}
于 2021-05-31T11:11:18.303 回答