diff --git a/Model/Player/Backends/AVPlayerBackend.swift b/Model/Player/Backends/AVPlayerBackend.swift index 9f8e9445..fd72500b 100644 --- a/Model/Player/Backends/AVPlayerBackend.swift +++ b/Model/Player/Backends/AVPlayerBackend.swift @@ -364,7 +364,11 @@ final class AVPlayerBackend: PlayerBackend { let startPlaying = { #if !os(macOS) - try? AVAudioSession.sharedInstance().setActive(true) + do { + try AVAudioSession.sharedInstance().setActive(true) + } catch { + self.logger.error("Error setting up audio session: \(error)") + } #endif self.setRate(self.model.currentRate) diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index daf2c51f..942589ba 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -248,13 +248,6 @@ final class MPVBackend: PlayerBackend { #if !os(macOS) do { try AVAudioSession.sharedInstance().setActive(true) - - NotificationCenter.default.addObserver( - self, - selector: #selector(self.handleAudioSessionInterruption(_:)), - name: AVAudioSession.interruptionNotification, - object: nil - ) } catch { self.logger.error("Error setting up audio session: \(error)") } @@ -649,33 +642,4 @@ final class MPVBackend: PlayerBackend { logger.info("MPV backend received unhandled property: \(name)") } } - - #if !os(macOS) - @objc func handleAudioSessionInterruption(_ notification: Notification) { - logger.info("Audio session interruption received.") - - guard let info = notification.userInfo, - let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt - else { - logger.info("AVAudioSessionInterruptionTypeKey is missing or not a UInt in userInfo.") - return - } - - let type = AVAudioSession.InterruptionType(rawValue: typeValue) - - logger.info("Interruption type received: \(String(describing: type))") - - switch type { - case .began: - pause() - logger.info("Audio session interrupted.") - default: - break - } - } - - deinit { - NotificationCenter.default.removeObserver(self, name: AVAudioSession.interruptionNotification, object: nil) - } - #endif } diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index 3f0c25c8..67412658 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -47,7 +47,7 @@ final class PlayerModel: ObservableObject { static var shared = PlayerModel() - let logger = Logger(label: "stream.yattee.app") + let logger = Logger(label: "stream.yattee.player.model") var playerItem: AVPlayerItem? @@ -204,6 +204,14 @@ final class PlayerModel: ObservableObject { #if !os(macOS) mpvBackend.controller = mpvController mpvBackend.client = mpvController.client + + // Register for audio session interruption notifications + NotificationCenter.default.addObserver( + self, + selector: #selector(handleAudioSessionInterruption(_:)), + name: AVAudioSession.interruptionNotification, + object: nil + ) #endif playbackMode = Defaults[.playbackMode] @@ -220,6 +228,10 @@ final class PlayerModel: ObservableObject { currentRate = playerRate } + deinit { + NotificationCenter.default.removeObserver(self, name: AVAudioSession.interruptionNotification, object: nil) + } + func show() { #if os(macOS) if presentingPlayer { @@ -1234,6 +1246,42 @@ final class PlayerModel: ObservableObject { return nil } + #if !os(macOS) + @objc func handleAudioSessionInterruption(_ notification: Notification) { + logger.info("Audio session interruption received.") + logger.info("Notification received: \(notification)") + + guard let info = notification.userInfo, + let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt, + let type = AVAudioSession.InterruptionType(rawValue: typeValue) + else { + logger.info("AVAudioSessionInterruptionTypeKey is missing or not a UInt in userInfo.") + return + } + + logger.info("Interruption type received: \(type)") + + switch type { + case .began: + logger.info("Audio session interrupted.") + // We need to call pause() to set all variables correctly, and play() + // directly afterwards, because the .began interrupt is sent after audio + // ducking ended and playback would pause. Audio ducking usually happens + // when using headphones. + pause() + play() + case .ended: + logger.info("Audio session interruption ended.") + // We need to call pause() to set all variables correctly. + // Otherwise, playback does not resume when the interruption ends. + pause() + play() + default: + break + } + } + #endif + #if os(macOS) private func assignKeyPressMonitor() { keyPressMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { keyEvent -> NSEvent? in