AVAudioSession setAggregatedIOPreference:error:Error Domain = NSOSStatusErrorDomain代码= 560030580“(null)”

时间:2019-09-02 21:42:05

标签: swift twilio avaudiosession

我一直收到此错误,并且已经花了几个小时没有成功。请帮忙。非常感谢。随附的代码如下。

错误消息:AVAudioSession setAggregatedIOPreference:错误:错误Domain = NSOSStatusErrorDomain代码= 560030580“(null)”

上次打印的消息为:通话已断开

class callTwilioViewController: UIViewController ,PKPushRegistryDelegate, TVONotificationDelegate, TVOCallDelegate, AVAudioPlayerDelegate, UITextFieldDelegate {

@IBOutlet weak var placeCallButton: UIButton!
@IBOutlet weak var iconView: UIImageView!
@IBOutlet weak var outgoingValue: UITextField!
@IBOutlet weak var callControlView: UIView!
@IBOutlet weak var muteSwitch: UISwitch!
@IBOutlet weak var speakerSwitch: UISwitch!

var deviceTokenString:String?

var voipRegistry:PKPushRegistry
var incomingPushCompletionCallback: (()->Swift.Void?)? = nil

var isSpinning: Bool
var incomingAlertController: UIAlertController?

var callInvite:TVOCallInvite?
var call:TVOCall?

var ringtonePlayer:AVAudioPlayer?
var ringtonePlaybackCallback: (() -> ())?

required init?(coder aDecoder: NSCoder) {
    isSpinning = false
    voipRegistry = PKPushRegistry.init(queue: DispatchQueue.main)

    super.init(coder: aDecoder)

    voipRegistry.delegate = self
    voipRegistry.desiredPushTypes = Set([PKPushType.voIP])
}

override func viewDidLoad() {
    super.viewDidLoad()

    toggleUIState(isEnabled: true, showCallControl: false)
    outgoingValue.delegate = self
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func fetchAccessToken() -> String? {
    let endpointWithIdentity = String(format: "%@?identity=%@", accessTokenEndpoint, identity)
    guard let accessTokenURL = URL(string: baseURLString + endpointWithIdentity) else {
        return nil
    }

    return try? String.init(contentsOf: accessTokenURL, encoding: .utf8)
}

func toggleUIState(isEnabled: Bool, showCallControl: Bool) {
    placeCallButton.isEnabled = isEnabled
    if (showCallControl) {
        callControlView.isHidden = false
        muteSwitch.isOn = false
        speakerSwitch.isOn = true
    } else {
        callControlView.isHidden = true
    }
}

@IBAction func placeCall(_ sender: UIButton) {
    if (self.call != nil) {
        self.call?.disconnect()
        self.toggleUIState(isEnabled: false, showCallControl: false)
    } else {
        playOutgoingRingtone(completion: { [weak self] in
            if let strongSelf = self {
                strongSelf.makeCall(strongSelf.outgoingValue.text!)
            }
        })

        self.toggleUIState(isEnabled: false, showCallControl: false)
        self.startSpin()
    }
}

func makeCall(_ to: String) {
    guard let accessToken = fetchAccessToken() else {
        return
    }

    let connectOptions: TVOConnectOptions = TVOConnectOptions(accessToken: accessToken) { (builder) in
        builder.params = [twimlParamTo : to]
    }

    self.checkRecordPermission { (permissionGranted) in
        if (!permissionGranted) {
            let alertController: UIAlertController = UIAlertController(title: "Voice Quick Start",
                                                                       message: "Microphone permission not granted",
                                                                       preferredStyle: .alert)

            let continueWithMic: UIAlertAction = UIAlertAction(title: "Continue without microphone",
                                                               style: .default,
                                                               handler: { (action) in
                                                                self.call = TwilioVoice.connect(with: connectOptions, delegate: self)
            })
            alertController.addAction(continueWithMic)

            let goToSettings: UIAlertAction = UIAlertAction(title: "Settings",
                                                            style: .default,
                                                            handler: { (action) in
                                                                UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!,
                                                                                          options: [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly: false],
                                                                                          completionHandler: nil)
            })
            alertController.addAction(goToSettings)

            let cancel: UIAlertAction = UIAlertAction(title: "Cancel",
                                                      style: .cancel,
                                                      handler: { (action) in
                                                        self.toggleUIState(isEnabled: true, showCallControl: false)
                                                        self.stopSpin()
            })
            alertController.addAction(cancel)

            self.present(alertController, animated: true, completion: nil)
        } else {
            self.call = TwilioVoice.connect(with: connectOptions, delegate: self)
        }
    }
}

func checkRecordPermission(completion: @escaping (_ permissionGranted: Bool) -> Void) {
    let permissionStatus: AVAudioSession.RecordPermission = AVAudioSession.sharedInstance().recordPermission

    switch permissionStatus {
    case AVAudioSessionRecordPermission.granted:
        // Record permission already granted.
        completion(true)
        break
    case AVAudioSessionRecordPermission.denied:
        // Record permission denied.
        completion(false)
        break
    case AVAudioSessionRecordPermission.undetermined:
        // Requesting record permission.
        // Optional: pop up app dialog to let the users know if they want to request.
        AVAudioSession.sharedInstance().requestRecordPermission({ (granted) in
            completion(granted)
        })
        break
    default:
        completion(false)
        break
    }
}

@IBAction func muteSwitchToggled(_ sender: UISwitch) {
    if let call = call {
        call.isMuted = sender.isOn
    } else {
        NSLog("No active call to be muted")
    }
}

@IBAction func speakerSwitchToggled(_ sender: UISwitch) {
    toggleAudioRoute(toSpeaker: sender.isOn)
}

// MARK: UITextFieldDelegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    outgoingValue.resignFirstResponder()
    return true
}

// MARK: PKPushRegistryDelegate
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
    NSLog("pushRegistry:didUpdatePushCredentials:forType:");

    if (type != .voIP) {
        return
    }

    guard let accessToken = fetchAccessToken() else {
        return
    }

    let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined()

    TwilioVoice.register(withAccessToken: accessToken, deviceToken: deviceToken) { (error) in
        if let error = error {
            NSLog("An error occurred while registering: \(error.localizedDescription)")
        }
        else {
            NSLog("Successfully registered for VoIP push notifications.")
        }
    }

    self.deviceTokenString = deviceToken
}

func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
    NSLog("pushRegistry:didInvalidatePushTokenForType:")

    if (type != .voIP) {
        return
    }

    guard let deviceToken = deviceTokenString, let accessToken = fetchAccessToken() else {
        return
    }

    TwilioVoice.unregister(withAccessToken: accessToken, deviceToken: deviceToken) { (error) in
        if let error = error {
            NSLog("An error occurred while unregistering: \(error.localizedDescription)")
        }
        else {
            NSLog("Successfully unregistered from VoIP push notifications.")
        }
    }

    self.deviceTokenString = nil
}

/**
 * Try using the `pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:` method if
 * your application is targeting iOS 11. According to the docs, this delegate method is deprecated by Apple.
 */
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
    NSLog("pushRegistry:didReceiveIncomingPushWithPayload:forType:")

    if (type == PKPushType.voIP) {
        TwilioVoice.handleNotification(payload.dictionaryPayload, delegate: self)
    }
}

/**
 * This delegate method is available in iOS 11 and above. Call the completion handler once the
 * notification payload is passed to the `TwilioVoice.handleNotification()` method.
 */
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
    NSLog("pushRegistry:didReceiveIncomingPushWithPayload:forType:completion:")
    // Save for later when the notification is properly handled.
    self.incomingPushCompletionCallback = completion

    if (type == PKPushType.voIP) {
        TwilioVoice.handleNotification(payload.dictionaryPayload, delegate: self)
    }


}

func incomingPushHandled() {
    if let completion = self.incomingPushCompletionCallback {
        completion()
        self.incomingPushCompletionCallback = nil
    }
}

// MARK: TVONotificaitonDelegate
func callInviteReceived(_ callInvite: TVOCallInvite) {

    NSLog("callInviteReceived:")

    if (self.callInvite != nil) {
        NSLog("A CallInvite is already in progress. Ignoring the incoming CallInvite from \(callInvite.from)")
        self.incomingPushHandled()
        return
    } else if (self.call != nil && self.call?.state == .connected) {
        NSLog("Already an active call. Ignoring incoming CallInvite from \(callInvite.from)");
        self.incomingPushHandled()
        return;
    }

    self.callInvite = callInvite;

    let from = callInvite.from
    let alertMessage = "From: \(from)"

    playIncomingRingtone()

    let incomingAlertController = UIAlertController(title: "Incoming",
                                                    message: alertMessage,
                                                    preferredStyle: .alert)

    let rejectAction = UIAlertAction(title: "Reject", style: .default) { [weak self] (action) in
        if let strongSelf = self {
            strongSelf.stopIncomingRingtone()
            callInvite.reject()
            strongSelf.callInvite = nil

            strongSelf.incomingAlertController = nil
            strongSelf.toggleUIState(isEnabled: true, showCallControl: false)
        }
    }
    incomingAlertController.addAction(rejectAction)

    let ignoreAction = UIAlertAction(title: "Ignore", style: .default) { [weak self] (action) in
        if let strongSelf = self {
            /* To ignore the CallInvite, you don't have to do anything but just literally ignore it */

            strongSelf.callInvite = nil
            strongSelf.stopIncomingRingtone()
            strongSelf.incomingAlertController = nil
            strongSelf.toggleUIState(isEnabled: true, showCallControl: false)
        }
    }
    incomingAlertController.addAction(ignoreAction)

    let acceptAction = UIAlertAction(title: "Accept", style: .default) { [weak self] (action) in
        if let strongSelf = self {
            strongSelf.stopIncomingRingtone()
            let acceptOptions: TVOAcceptOptions = TVOAcceptOptions(callInvite: callInvite) { (builder) in
                builder.uuid = strongSelf.callInvite?.uuid
            }
            strongSelf.call = callInvite.accept(with: acceptOptions, delegate: strongSelf)
            strongSelf.callInvite = nil

            strongSelf.incomingAlertController = nil
            strongSelf.startSpin()
        }
    }
    incomingAlertController.addAction(acceptAction)

    toggleUIState(isEnabled: false, showCallControl: false)
    present(incomingAlertController, animated: true, completion: nil)
    self.incomingAlertController = incomingAlertController

    // If the application is not in the foreground, post a local notification
    if (UIApplication.shared.applicationState != UIApplication.State.active) {
        let content = UNMutableNotificationContent()
        content.title = "Incoming Call"
        content.body = "Call Invite from \(callInvite.from)"
        content.sound = UNNotificationSound.default

        let request = UNNotificationRequest(identifier: "VoiceLocaNotification",
                                            content: content, trigger: nil)

        let center = UNUserNotificationCenter.current()
        center.add(request) { (error) in
            if (error != nil) {
                print("Failed to add notification reqeust: \(error!.localizedDescription)")
            }
        }
    }

    self.incomingPushHandled()
}

func cancelledCallInviteReceived(_ cancelledCallInvite: TVOCancelledCallInvite) {
    NSLog("cancelledCallInviteCanceled:")

    if (self.callInvite == nil ||
        self.callInvite!.callSid != cancelledCallInvite.callSid) {
        NSLog("No matching pending CallInvite. Ignoring the Cancelled CallInvite")
        return
    }

    self.stopIncomingRingtone()
    playDisconnectSound()

    if (incomingAlertController != nil) {
        dismiss(animated: true) { [weak self] in
            if let strongSelf = self {
                strongSelf.incomingAlertController = nil
                strongSelf.toggleUIState(isEnabled: true, showCallControl: false)
            }
        }
    }

    self.callInvite = nil

    UNUserNotificationCenter.current().removeAllPendingNotificationRequests()

    self.incomingPushHandled()
}

// MARK: TVOCallDelegate
func callDidStartRinging(_ call: TVOCall) {
    NSLog("callDidStartRinging:")

    self.placeCallButton.setTitle("Ringing", for: .normal)
}

func callDidConnect(_ call: TVOCall) {
    NSLog("callDidConnect:")

    self.call = call

    self.placeCallButton.setTitle("Hang Up", for: .normal)

    toggleUIState(isEnabled: true, showCallControl: true)
    stopSpin()
    toggleAudioRoute(toSpeaker: true)


}

func call(_ call: TVOCall, isReconnectingWithError error: Error) {





    NSLog("call:isReconnectingWithError:")

    self.placeCallButton.setTitle("Reconnecting", for: .normal)

    toggleUIState(isEnabled: false, showCallControl: false)
}

func callDidReconnect(_ call: TVOCall) {
    NSLog("callDidReconnect:")

    self.placeCallButton.setTitle("Hang Up", for: .normal)

    toggleUIState(isEnabled: true, showCallControl: true)
}

func call(_ call: TVOCall, didFailToConnectWithError error: Error) {
    NSLog("Call failed to connect: \(error.localizedDescription)")

    callDisconnected()
}

func call(_ call: TVOCall, didDisconnectWithError error: Error?) {
    if let error = error {
        NSLog("Call failed: \(error.localizedDescription)")
    } else {
        NSLog("Call disconnected")
    }

    callDisconnected()
}

func callDisconnected() {
    self.call = nil

    playDisconnectSound()
    toggleUIState(isEnabled: true, showCallControl: false)
    stopSpin()
    self.placeCallButton.setTitle("Call", for: .normal)
}


// MARK: AVAudioSession
func toggleAudioRoute(toSpeaker: Bool) {
    // The mode set by the Voice SDK is "VoiceChat" so the default audio route is the built-in receiver. Use port override to switch the route.
    let audioDevice: TVODefaultAudioDevice = TwilioVoice.audioDevice as! TVODefaultAudioDevice
    audioDevice.block = {
        kTVODefaultAVAudioSessionConfigurationBlock()


        do {
            if (toSpeaker) {
                try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)


            } else {
                try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
            }
        } catch {
            NSLog(error.localizedDescription)
        }
    }
    audioDevice.block()
}




// MARK: Ringtone player & AVAudioPlayerDelegate
func playOutgoingRingtone(completion: @escaping () -> ()) {
    self.ringtonePlaybackCallback = completion

    let ringtonePath = URL(fileURLWithPath: Bundle.main.path(forResource: "outgoing", ofType: "wav")!)
    do {
        self.ringtonePlayer = try AVAudioPlayer(contentsOf: ringtonePath)
        self.ringtonePlayer?.delegate = self

        playRingtone()
    } catch {
        NSLog("Failed to initialize audio player")
        self.ringtonePlaybackCallback?()
    }
}



func playIncomingRingtone() {
    let ringtonePath = URL(fileURLWithPath: Bundle.main.path(forResource: "incoming", ofType: "wav")!)
    do {
        self.ringtonePlayer = try AVAudioPlayer(contentsOf: ringtonePath)
        self.ringtonePlayer?.delegate = self
        self.ringtonePlayer?.numberOfLoops = -1

        playRingtone()
    } catch {
        NSLog("Failed to initialize audio player")
    }
}

func stopIncomingRingtone() {
    if (self.ringtonePlayer?.isPlaying == false) {
        return
    }

    self.ringtonePlayer?.stop()

    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord)
    } catch {
        NSLog(error.localizedDescription)
    }

    //Shit

    do {

        try AVAudioSession.sharedInstance().setAggregatedIOPreference(.aggregated)

    } catch {
        NSLog(error.localizedDescription)
    }
}

func playDisconnectSound() {
    let ringtonePath = URL(fileURLWithPath: Bundle.main.path(forResource: "disconnect", ofType: "wav")!)
    do {
        self.ringtonePlayer = try AVAudioPlayer(contentsOf: ringtonePath)
        self.ringtonePlayer?.delegate = self
        self.ringtonePlaybackCallback = nil

        playRingtone()
    } catch {
        NSLog("Failed to initialize audio player")
    }
}

func playRingtone() {
    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
    } catch {
        NSLog(error.localizedDescription)
    }

    self.ringtonePlayer?.volume = 1.0
    self.ringtonePlayer?.play()
}

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
    if (self.ringtonePlaybackCallback != nil) {
        DispatchQueue.main.async {
            self.ringtonePlaybackCallback!()
        }
    }

    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord)
    } catch {
        NSLog(error.localizedDescription)
    }
}


// MARK: Icon spinning
func startSpin() {
    if (isSpinning != true) {
        isSpinning = true
        spin(options: UIView.AnimationOptions.curveEaseIn)
    }
}

func stopSpin() {
    isSpinning = false
}

func spin(options: UIView.AnimationOptions) {
    UIView.animate(withDuration: 0.5,
                   delay: 0.0,
                   options: options,
                   animations: { [weak iconView] in
                    if let iconView = iconView {
                        iconView.transform = iconView.transform.rotated(by: CGFloat(Double.pi/2))
                    }
    }) { [weak self] (finished: Bool) in
        guard let strongSelf = self else {
            return
        }

        if (finished) {
            if (strongSelf.isSpinning) {
                strongSelf.spin(options: UIView.AnimationOptions.curveLinear)
            } else if (options != UIView.AnimationOptions.curveEaseOut) {
                strongSelf.spin(options: UIView.AnimationOptions.curveEaseOut)
            }
        }
    }
}

}

0 个答案:

没有答案