我们的 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
上。似乎没有任何其他线程被锁定。 锁永远不会释放使应用程序完全冻结。
在尝试解决这个问题时,我尝试了以下方法:
我将 OpenVidu 信令服务器处理(RPC 协议)从主线程移动到单独的线程中。这只会导致锁定现在发生在我创建的单独线程之一中。现在它不会阻止 UI,但会阻止 OV 信号。问题依然存在。
我添加了锁来处理每个信令事件(参与者加入事件、发布视频等)同步(一个接一个)。这也没有帮助(实际上让情况变得更糟)。
我没有使用 Cocoapods 中的 GoogleWebRTC v. 1.1.31999,而是下载了最新的 GoogleWebRTC 源代码,在发布配置中构建它们并包含到我的项目中。这并没有帮助解决问题。
如有任何建议/意见,我们将不胜感激。 谢谢!
编辑1:
signaling_thread
和worker_thread
都在同一种锁中等待某些东西。在锁定时,它们都不会执行我的任何代码。
我还尝试在
GoogleWebRTC
的 DEBUG 版本中运行,在这种情况下不会发生锁定,但一切都会慢得多(这对于调试来说是可以的,但我们不能在生产中使用它)。
编辑2:
我尝试为
DispatchQueue
和 offer
回调添加额外的 setLocalDescription
,但这没有任何改变。这个问题仍然可以很好地重现(几乎 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 可以从任何线程调用,但大多数方法调用都会传递到名为
signalling thread
的 WebRTC 内部线程。
此外,像
SetLocalDescriptionObserverInterface
或RTCSetSessionDescriptionCompletionHandler
这样的回调/观察者是从signaling thread
上的WebRTC调用的。
从截图来看,似乎信令线程当前被阻塞,无法再调用WebRTC API调用。
因此,为了避免死锁,最好创建自己的线程 /
dispatch_queue
并处理回调。
见 https://webrtc.googlesource.com/src/+/0a52ede821ba12ee6fff6260d69cddcca5b86a4e/api/g3doc/index.md 和 https://webrtc.googlesource.com/src/+/0a52ede821ba12ee6fff6260d69cddcca5b86a4e/api/g3doc/threading_design.md 了解详情。
经过 OpenVidu 团队的评论,通过在添加已在房间中的参与者之间添加 100 毫秒的延迟来解决问题。我认为这更像是一个黑客而不是真正的解决方案,但我可以确认它在测试和生产环境中都有效:
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)
}
}
这个问题在最新版本的webrtc中修复了吗?有谁知道吗