|
|
|
@ -9,6 +9,7 @@ import Foundation |
|
|
|
import AVFoundation |
|
|
|
import MediaPlayer |
|
|
|
import UIKit |
|
|
|
//import CallKit |
|
|
|
|
|
|
|
public class MusicService: NSObject { |
|
|
|
public static let shared: MusicService = MusicService() |
|
|
|
@ -35,11 +36,15 @@ public class MusicService: NSObject { |
|
|
|
// 音频中断处理相关 |
|
|
|
private var interruptedDuringPlayback: Bool = false |
|
|
|
|
|
|
|
// private let callObserver = CXCallObserver() |
|
|
|
|
|
|
|
private override init() { |
|
|
|
super.init() |
|
|
|
setupRemoteCommandCenter() |
|
|
|
setupAudioSession() |
|
|
|
setupNotificationObservers() |
|
|
|
// 监听通话状态(需主线程队列) |
|
|
|
// callObserver.setDelegate(self, queue: DispatchQueue.main) |
|
|
|
} |
|
|
|
|
|
|
|
public func initialize(serviceUrl: String, userToken: String) { |
|
|
|
@ -103,6 +108,9 @@ public class MusicService: NSObject { |
|
|
|
print("🎵 [MusicService] ✅ 已注册AudioSessionHub_ResumeMusicPlayback通知") |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: - 基础播放控制 |
|
|
|
func getCurrentUrlString() -> String? { |
|
|
|
guard let urlAsset = player?.currentItem?.asset as? AVURLAsset else { |
|
|
|
@ -1003,96 +1011,24 @@ public class MusicService: NSObject { |
|
|
|
@objc private func handleAudioSessionHubInterruption(_ notification: Notification) { |
|
|
|
interruptedDuringPlayback = isPlaying |
|
|
|
pause() |
|
|
|
print("🎵 [MusicService] 收到AudioSessionHub中断开始通知") |
|
|
|
print("🎵 [MusicService] 收到AudioSessionHub中断开始通知:\(interruptedDuringPlayback)") |
|
|
|
// AudioSessionHub已经处理了中断开始,我们这里可以做额外的处理 |
|
|
|
} |
|
|
|
|
|
|
|
// 处理AudioSessionHub发送的中断结束通知 |
|
|
|
@objc private func handleAudioSessionHubInterruptionEnded(_ notification: Notification) { |
|
|
|
print("🎵 [MusicService] 收到AudioSessionHub中断结束通知") |
|
|
|
|
|
|
|
guard let userInfo = notification.userInfo else { |
|
|
|
print("🎵 [MusicService] AudioSessionHub通知缺少userInfo") |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
let shouldResume = userInfo["shouldResume"] as? Bool ?? false |
|
|
|
let options = userInfo["options"] as? UInt ?? 0 |
|
|
|
|
|
|
|
print("🎵 [MusicService] AudioSessionHub建议恢复: \(shouldResume), 选项: \(options)") |
|
|
|
print("🎵 [MusicService] 中断前播放状态: \(interruptedDuringPlayback)") |
|
|
|
|
|
|
|
// 只有在中断前正在播放且系统建议恢复时才恢复播放 |
|
|
|
if interruptedDuringPlayback && shouldResume { |
|
|
|
print("🎵 [MusicService] 准备恢复音乐播放...") |
|
|
|
|
|
|
|
// 延迟恢复,给AudioSessionHub时间完成音频会话重新激活 |
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in |
|
|
|
guard let self = self else { return } |
|
|
|
|
|
|
|
do { |
|
|
|
// 设置音频会话为播放模式 |
|
|
|
// let audioSession = AVAudioSession.sharedInstance() |
|
|
|
// try audioSession.setCategory(.playback, mode: .default, options: [.mixWithOthers]) |
|
|
|
// try audioSession.setActive(true, options: []) |
|
|
|
print("🎵 [MusicService] 音频会话已设置为播放模式") |
|
|
|
|
|
|
|
// 恢复播放 |
|
|
|
self.resume() |
|
|
|
print("🎵 [MusicService] 已恢复音乐播放") |
|
|
|
self.interruptedDuringPlayback = false |
|
|
|
} catch { |
|
|
|
print("🎵 [MusicService] 设置音频会话失败: \(error)") |
|
|
|
|
|
|
|
// 即使设置失败也尝试恢复播放 |
|
|
|
self.resume() |
|
|
|
print("🎵 [MusicService] 已尝试恢复音乐播放(音频会话设置失败)") |
|
|
|
self.interruptedDuringPlayback = false |
|
|
|
} |
|
|
|
} |
|
|
|
} else if !shouldResume { |
|
|
|
print("🎵 [MusicService] AudioSessionHub不建议恢复,保持暂停状态") |
|
|
|
interruptedDuringPlayback = false |
|
|
|
} else { |
|
|
|
print("🎵 [MusicService] 中断前未在播放,不恢复") |
|
|
|
print("🎵 [MusicService] 收到AudioSessionHub中断结束通知:\(interruptedDuringPlayback)") |
|
|
|
if (interruptedDuringPlayback){ |
|
|
|
self.resume() |
|
|
|
interruptedDuringPlayback = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@objc private func handleAudioSessionHubResumeMusicPlayback(_ notification: Notification) { |
|
|
|
print("🎵 [MusicService] ✅ 收到AudioSessionHub恢复播放通知") |
|
|
|
print("🎵 [MusicService] 当前状态 - interruptedDuringPlayback: \(interruptedDuringPlayback), isPlaying: \(isPlaying)") |
|
|
|
|
|
|
|
// 简化逻辑:只要收到通知且之前被中断,就尝试恢复 |
|
|
|
if interruptedDuringPlayback { |
|
|
|
print("🎵 [MusicService] 开始恢复音乐播放...") |
|
|
|
|
|
|
|
// 延迟一点时间确保音频会话已经正确设置 |
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in |
|
|
|
guard let self = self else { return } |
|
|
|
|
|
|
|
do { |
|
|
|
// 设置音频会话为播放模式 |
|
|
|
// let audioSession = AVAudioSession.sharedInstance() |
|
|
|
// try audioSession.setCategory(.playback, mode: .default, options: [.mixWithOthers]) |
|
|
|
// try audioSession.setActive(true, options: []) |
|
|
|
print("🎵 [MusicService] 音频会话已设置为播放模式") |
|
|
|
|
|
|
|
// 恢复播放 |
|
|
|
self.resume() |
|
|
|
print("🎵 [MusicService] ✅ 已恢复音乐播放") |
|
|
|
self.interruptedDuringPlayback = false |
|
|
|
} catch { |
|
|
|
print("🎵 [MusicService] ❌ 设置音频会话失败: \(error)") |
|
|
|
|
|
|
|
// 即使设置失败也尝试恢复播放 |
|
|
|
self.resume() |
|
|
|
print("🎵 [MusicService] ⚠️ 已尝试恢复音乐播放(音频会话设置失败)") |
|
|
|
self.interruptedDuringPlayback = false |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
print("🎵 [MusicService] ℹ️ 不需要恢复播放 - interruptedDuringPlayback: \(interruptedDuringPlayback)") |
|
|
|
print("🎵 [MusicService] ✅ 收到AudioSessionHub恢复播放通知:\(interruptedDuringPlayback)") |
|
|
|
if (interruptedDuringPlayback){ |
|
|
|
self.resume() |
|
|
|
interruptedDuringPlayback = false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -1116,3 +1052,28 @@ public class MusicService: NSObject { |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 实现 CXCallObserverDelegate |
|
|
|
//extension MusicService: CXCallObserverDelegate { |
|
|
|
// public func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { |
|
|
|
// // 检测通话开始(来电被接听 或 呼出被接通) |
|
|
|
// if call.hasConnected && !call.hasEnded { |
|
|
|
// // 记录播放状态(避免重复标记,仅在通话刚连接时标记) |
|
|
|
// if !call.hasEnded { // 确保是通话开始而非结束 |
|
|
|
// self.interruptedDuringPlayback = isPlaying |
|
|
|
// if isPlaying { |
|
|
|
// print("检测到通话开始,暂停音乐") |
|
|
|
// pause() |
|
|
|
// } |
|
|
|
// } |
|
|
|
// } |
|
|
|
// // 通话结束且当前无其他活跃通话 |
|
|
|
// if call.hasEnded && call.hasConnected { |
|
|
|
// // 确保播放器处于暂停状态再恢复 |
|
|
|
// if self.interruptedDuringPlayback { |
|
|
|
// print("检测到通话结束,恢复音乐") |
|
|
|
// self.resume() |
|
|
|
// } |
|
|
|
// } |
|
|
|
// } |
|
|
|
//} |
|
|
|
|