Files
2025-08-08 10:49:36 +08:00

201 lines
6.6 KiB
Swift

//
// CallingBellFeature.swift
// TUICallKit
//
// Created by vincepzhang on 2022/12/30.
//
import Foundation
import AVFAudio
import TUICallEngine
#if canImport(TXLiteAVSDK_TRTC)
import TXLiteAVSDK_TRTC
#elseif canImport(TXLiteAVSDK_Professional)
import TXLiteAVSDK_Professional
#endif
let CALLKIT_AUDIO_DIAL_ID: Int32 = 48
class CallingBellFeature: NSObject, AVAudioPlayerDelegate {
enum CallingBellType {
case CallingBellTypeHangup
case CallingBellTypeCalled
case CallingBellTypeDial
}
var player: AVAudioPlayer?
var loop: Bool = true
private var needPlayRingtone: Bool = false
let selfUserCallStatusObserver = Observer()
override init() {
super.init()
registerNotifications()
registerObserveState()
}
deinit {
NotificationCenter.default.removeObserver(self)
TUICallState.instance.selfUser.value.callStatus.removeObserver(selfUserCallStatusObserver)
}
func registerObserveState() {
TUICallState.instance.selfUser.value.callStatus.addObserver(selfUserCallStatusObserver, closure: { newValue, _ in
let callStatus = TUICallState.instance.selfUser.value.callStatus.value
let callRole = TUICallState.instance.selfUser.value.callRole.value
if callStatus == TUICallStatus.waiting {
if callRole == TUICallRole.called {
self.startMusicBasedOnAppState(type: .CallingBellTypeCalled)
} else if callRole == TUICallRole.call {
self.startPlayMusic(type: .CallingBellTypeDial)
}
} else {
self.stopPlayMusic()
}
})
}
func startMusicBasedOnAppState(type: CallingBellType) {
if UIApplication.shared.applicationState != .background {
self.setAudioSessionWith(category: .soloAmbient)
self.startPlayMusic(type: type)
} else {
self.needPlayRingtone = true;
}
}
func registerNotifications() {
if #available(iOS 13.0, *) {
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive),
name: UIScene.didActivateNotification,
object: nil)
} else {
NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive),
name: UIApplication.didBecomeActiveNotification,
object: nil)
}
}
@objc func appDidBecomeActive() {
if needPlayRingtone {
let _ = startPlayMusic(type: .CallingBellTypeCalled)
needPlayRingtone = false;
}
}
func startPlayMusic(type: CallingBellType) {
guard let bundle = TUICallKitCommon.getTUICallKitBundle() else { return }
switch type {
case .CallingBellTypeHangup:
let path = bundle.bundlePath + "/AudioFile" + "/phone_hangup.mp3"
let url = URL(fileURLWithPath: path)
return startPlayMusicBySystemPlayer(url: url, loop: false)
case .CallingBellTypeCalled:
if TUICallState.instance.enableMuteMode {
return
}
var path = bundle.bundlePath + "/AudioFile" + "/phone_ringing.mp3"
if let value = UserDefaults.standard.object(forKey: TUI_CALLING_BELL_KEY) as? String {
path = value
}
let url = URL(fileURLWithPath: path)
return startPlayMusicBySystemPlayer(url: url)
case .CallingBellTypeDial:
let path = bundle.bundlePath + "/AudioFile" + "/phone_dialing.m4a"
startPlayMusicByTRTCPlayer(path: path, id: CALLKIT_AUDIO_DIAL_ID)
return
}
}
func stopPlayMusic() {
setAudioSessionWith(category: .playAndRecord)
if TUICallState.instance.selfUser.value.callRole.value == .call {
stopPlayMusicByTRTCPlayer(id: CALLKIT_AUDIO_DIAL_ID)
return
}
stopPlayMusicBySystemPlayer()
needPlayRingtone = false;
}
// MARK: TRTC Audio Player
private func startPlayMusicByTRTCPlayer(path: String, id: Int32) {
let audioDevice: TUIAudioPlaybackDevice = TUICallState.instance.mediaType.value == .audio ? .earpiece : .speakerphone
CallEngineManager.instance.setAudioPlaybackDevice(device: audioDevice)
let param = TXAudioMusicParam()
param.id = id
param.isShortFile = true
param.path = path
let audioEffectManager = TUICallEngine.createInstance().getTRTCCloudInstance().getAudioEffectManager()
audioEffectManager.startPlayMusic(param, onStart: nil, onProgress: nil)
audioEffectManager.setMusicPlayoutVolume(id, volume: 100)
}
private func stopPlayMusicByTRTCPlayer(id: Int32) {
let audioEffectManager = TUICallEngine.createInstance().getTRTCCloudInstance().getAudioEffectManager()
audioEffectManager.stopPlayMusic(id)
}
// MARK: System AVAudio Player
private func startPlayMusicBySystemPlayer(url: URL, loop: Bool = true) {
self.loop = loop
if player != nil {
stopPlayMusicBySystemPlayer()
}
do {
player = try AVAudioPlayer(contentsOf: url)
} catch let error as NSError {
print("Error: \(error.localizedDescription)")
return
}
guard let prepare = player?.prepareToPlay(), prepare else {
return
}
player?.delegate = self
player?.play()
}
private func stopPlayMusicBySystemPlayer() {
player?.stop()
player = nil
}
// MARK: AVAudioPlayerDelegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if loop {
player.play()
} else {
stopPlayMusicBySystemPlayer()
}
}
func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) {
if error != nil {
stopPlayMusicBySystemPlayer()
}
}
private func setAudioSessionWith(category: AVAudioSession.Category) {
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(category, options: [.allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
try audioSession.setActive(true)
} catch {
print("Error setting up audio session: \(error)")
}
}
}