当前位置: 首页 > 知识库问答 >
问题:

当手机锁定时,在接听电话时,iOS音频不工作。用于调用的WebRTC

潘修为
2023-03-14

我在使用带有WebRTC的Callkit进行VOIP呼叫时,在从锁屏接听呼叫时,遇到了音频问题。

一般功能

我的应用程序在启动时激活audio会话。对于进线量,SDP报价

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

如果应用程序在前台,它工作正常。

但是,当手机被锁定,用户从锁屏接听电话时,流被交换,但在用户自己进入应用程序之前,两端都没有音频。

当用户进入应用程序时,两端的音频都会激活。

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

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

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

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

但如果在显示呼叫之前(比如在向callkit报告呼叫之前)建立了对等连接,音频将停止工作。

共有3个答案

麻宾白
2023-03-14

请注意,我分享我的代码,它将满足我的需要,我分享以供参考。您需要根据需要进行更改。

当您收到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处理程序类中,配置媒体发送器和audiosession

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()
    }

请考虑,因为我工作的回调和委托代码包括委托和回调块。

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

齐乐
2023-03-14

@abhimanyu你是否仍然面临这个问题,还是你成功了。我面临着与CallKit相同的问题。

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

他们解决的问题与系统AudioSession有关,当ever CallKit显示来电用户界面并播放铃声时,CallKit控制AudioSession,在用户操作(接受/拒绝)后,它释放控制。在WebRTC M60版本中,现在他们为这个控制交换添加了观察员。这就是为什么如果应用程序在前台,它会工作的原因,但若手机被锁定且任何来电都被接听(我假设您正在使用CallKit UI进行通话,而不是将用户从锁定屏幕重定向到应用程序上),由于呼叫的本机UI,WebRTC无法激活其自己的AudioSession实例,因为呼叫正在通过CallKit屏幕进行。

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

如果您发现了此问题的任何解决方案,请让我知道。

仇浩旷
2023-03-14

我能够解决这个问题。

我遵循的步骤-

>

添加了RTCAdioSession头文件,它实际上是Webrtc的一个私有类。所以每次我从信令接收到呼叫事件时,我启用RTCAdioSession,在呼叫结束时,我禁用它。

我必须渲染传入流到一个虚拟视图(虽然它不显示时,调用和应用程序尚未打开,但它需要使音频工作)。

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

 类似资料:
  • 我使用Webrtc和Callkit打电话。当应用程序是前台时,一切正常工作,但如果屏幕被锁定,我回答呼叫音频只在我这边工作(我可以听到音频,但我的声音没有发送)。 当用户进入应用程序时,一切都是固定的。 所有后台设置和功能都设置正确。 我尝试使用RTCAdioSession和AVAudioSession配置音频,但在这两种情况下都是一样的。 解决方法:我将mediastream放入RTPeerCo

  • 在关于克里特岛方法的主要活动中,我补充说: 恳求我能给我一些帮助,想。

  • 我在这里尝试了许多解决方案,但没有人奏效。WebRTC工作正常,我在设备锁定时接听电话时处于连接状态,解锁后音频打开,视频启动。当屏幕保持锁定状态时,我如何只能获得音频? 我已启用RTCAdioSession,并在呼叫停止时将其禁用。 当设备第一次解锁时,它工作得很好,当我把它锁回去时,我得到了音频。但是第一次,当我接听来自CallKit的电话时,它不起作用。它只在设备解锁后才开始工作。

  • 我有申请。当我按下音量下降按钮时,计数器正在增加。但我想在我的设备被锁的时候做。 当我按下电源按钮时,计数器不工作。

  • 如何使android studio中的应用程序在手机锁定时工作。假设在任何紧急情况下,如果用户在手机上滑动或按下锁定按钮2次,手机锁定时,它会将其位置发送到保存的紧急号码。有什么想法吗?