当手机锁定时,iOS 音频在应答呼叫期间无法正常工作.用于呼叫的WebRTC



我在从锁定屏幕接听呼叫时,将Callkit与WebRTC一起使用进行VOIP呼叫时遇到音频问题。

一般功能 :

我的应用程序在启动音频会话时激活它。对于来电,将生成并交换SDP Offer和Answer。已设置对等连接。无论是音频通话还是视频通话,都会生成音频和视频流。然后使用以下代码将调用报告给调用工具包:

callProvider.reportNewIncomingCall(with: currentCallUUID!, update: update) { error in }

如果应用程序位于前台,则工作正常。

但是,当手机被锁定并且用户从锁定屏幕应答呼叫时,流将被交换,但在用户自己进入应用程序之前,两端都不会出现音频。

当用户进入应用程序时,音频在两端都处于活动状态。

所有后台设置和功能都已正确设置。

我还提到了苹果员工提供的以下解决方法。但即使它不起作用。

https://forums.developer.apple.com/thread/64544

正如我所提到的,我正在使用WebRTC进行调用。如果我在用户接听电话后交换媒体流(仍在锁定屏幕上(并且当时设置了对等连接。它工作正常(但它增加了进行呼叫连接的延迟(。

但是,如果在显示呼叫之前(例如在向呼叫kit报告呼叫之前(建立对等连接,则音频将停止工作。

我能够解决这个问题。

我遵循的步骤 -

  • 我在这里检查了与WebRTC相关的代码

  • 我添加了RTCAudioSession头文件,它实际上是Webrtc的私有类。因此,每次我收到来自信令的呼叫事件时,我都会启用 RTCAudiosession,并在呼叫结束时禁用它。

  • 我必须将传入的流渲染到虚拟视图(尽管在通话进行且应用程序尚未打开时不显示它,但需要使音频正常工作(。

如果有人面临同样的问题,我希望这会有所帮助。

@abhimanyu您仍然面临问题还是您使其正常工作。我在通话套件方面面临同样的问题。

根据我在WebRTC M60版本中的理解,他们已经修复了与CallKit相关的问题,我认为这会产生副作用并导致此问题。

他们修复的问题与系统音频会话有关,当 CallKit 呈现来电 UI 并播放铃声时,CallKit 会控制 AudioSession,并在用户操作(接受/拒绝(后释放控制权。在WebRTC M60版本中,现在他们为此控制交换添加了观察器。这就是为什么如果应用程序位于前台,它可以工作的原因,但如果手机被锁定并且任何来电被接受,那么(我假设您使用 CallKit UI 进行通话,而不是将用户重定向到应用程序从锁定屏幕接受(由于调用的本机 UI,WebRTC 无法激活自己的 AudioSession 实例,因为呼叫正在通过 CallKit 屏幕。

已在WebRTC M60上修复的错误链接:https://bugs.chromium.org/p/webrtc/issues/detail?id=7446

如果您找到此问题的任何解决方法,请告诉我。

请注意,我分享我的代码及其关于我的需求,我分享以供参考。 您需要根据需要进行更改。

当您收到VoIP通知时,创建WebRTC处理类的新事件,并且 将这两行添加到代码块,因为从VoIP通知启用音频会话失败

RTCAudioSession.sharedInstance().useManualAudio = true
RTCAudioSession.sharedInstance().isAudioEnabled = false 

确实接收方法;

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
let state = UIApplication.shared.applicationState



if(payload.dictionaryPayload["hangup"] == nil && state != .active
){


Globals.voipPayload = payload.dictionaryPayload as! [String:Any] // I pass parameters to Webrtc handler via Global singleton to create answer according to sdp sent by payload.

RTCAudioSession.sharedInstance().useManualAudio = true
RTCAudioSession.sharedInstance().isAudioEnabled = false



Globals.sipGateway = SipGateway() // my Webrtc and Janus gateway handler class


Globals.sipGateway?.configureCredentials(true) // I check janus gateway credentials stored in Shared preferences and initiate websocket connection and create peerconnection 
to my janus gateway which is signaling server for my environment


initProvider() //Crating callkit provider

self.update.remoteHandle = CXHandle(type: .generic, value:String(describing: payload.dictionaryPayload["caller_id"]!))
Globals.callId = UUID()

let state = UIApplication.shared.applicationState


Globals.provider.reportNewIncomingCall(with:Globals.callId , update: self.update, completion: { error in


})


}

}


func  initProvider(){
let config = CXProviderConfiguration(localizedName: "ulakBEL")
config.iconTemplateImageData = UIImage(named: "ulakbel")!.pngData()
config.ringtoneSound = "ringtone.caf"
// config.includesCallsInRecents = false;
config.supportsVideo = false

Globals.provider = CXProvider(configuration:config )
Globals.provider.setDelegate(self, queue: nil)
update = CXCallUpdate()
update.hasVideo = false
update.supportsDTMF = true

}

修改您的 didActivate 和 didDeActive 委托函数,如下所示,

func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
print("CallManager didActivate")
RTCAudioSession.sharedInstance().audioSessionDidActivate(audioSession)
RTCAudioSession.sharedInstance().isAudioEnabled = true
// self.callDelegate?.callIsAnswered()


}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
print("CallManager didDeactivate")
RTCAudioSession.sharedInstance().audioSessionDidDeactivate(audioSession)
RTCAudioSession.sharedInstance().isAudioEnabled = false


}

在 Webrtc 处理程序类中配置媒体发送器和音频会话

private func createPeerConnection(webRTCCallbacks:PluginHandleWebRTCCallbacksDelegate) {

let rtcConfig =  RTCConfiguration.init()
rtcConfig.iceServers = server.iceServers
rtcConfig.bundlePolicy = RTCBundlePolicy.maxBundle
rtcConfig.rtcpMuxPolicy = RTCRtcpMuxPolicy.require
rtcConfig.continualGatheringPolicy = .gatherContinually
rtcConfig.sdpSemantics = .planB

let constraints = RTCMediaConstraints(mandatoryConstraints: nil,
optionalConstraints: ["DtlsSrtpKeyAgreement":kRTCMediaConstraintsValueTrue])

pc = sessionFactory.peerConnection(with: rtcConfig, constraints: constraints, delegate: nil)
self.createMediaSenders()
self.configureAudioSession()



if webRTCCallbacks.getJsep() != nil{
handleRemoteJsep(webrtcCallbacks: webRTCCallbacks)
}

}

媒体发送者;

private func createMediaSenders() {
let streamId = "stream"

// Audio
let audioTrack = self.createAudioTrack()
self.pc.add(audioTrack, streamIds: [streamId])

// Video
/*  let videoTrack = self.createVideoTrack()
self.localVideoTrack = videoTrack
self.peerConnection.add(videoTrack, streamIds: [streamId])
self.remoteVideoTrack = self.peerConnection.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack

// Data
if let dataChannel = createDataChannel() {
dataChannel.delegate = self
self.localDataChannel = dataChannel
}*/
}
private func createAudioTrack() -> RTCAudioTrack {
let audioConstrains = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil)
let audioSource = sessionFactory.audioSource(with: audioConstrains)
let audioTrack = sessionFactory.audioTrack(with: audioSource, trackId: "audio0")
return audioTrack
}

音频会话 ;

private func configureAudioSession() {
self.rtcAudioSession.lockForConfiguration()
do {
try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue)
try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue)
} catch let error {
debugPrint("Error changeing AVAudioSession category: (error)")
}
self.rtcAudioSession.unlockForConfiguration()
}

请考虑这一点,因为我使用回调和委托代码包括委托和回调块。 您可以相应地忽略它们!!

供参考 您也可以查看此链接中的示例

相关内容

  • 没有找到相关文章

最新更新