Skip to content

Commit

Permalink
Fix NowPlayingInfoCenter for a live voice broadcast
Browse files Browse the repository at this point in the history
  • Loading branch information
nimau committed Jan 12, 2023
1 parent 7a53a82 commit b2ae0e7
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import MediaPlayer
private var roomAvatarLoader: MXMediaLoader?
private let audioPlayers: NSMapTable<NSString, VoiceMessageAudioPlayer>
private let audioRecorders: NSHashTable<VoiceMessageAudioRecorder>
private let nowPlayingInfoProviders: NSMapTable<VoiceMessageAudioPlayer, VoiceMessageNowPlayingInfoProvider>
private let nowPlayingInfoDelegates: NSMapTable<VoiceMessageAudioPlayer, VoiceMessageNowPlayingInfoDelegate>

private var displayLink: CADisplayLink!

Expand Down Expand Up @@ -94,7 +94,7 @@ import MediaPlayer
private override init() {
audioPlayers = NSMapTable<NSString, VoiceMessageAudioPlayer>(valueOptions: .weakMemory)
audioRecorders = NSHashTable<VoiceMessageAudioRecorder>(options: .weakMemory)
nowPlayingInfoProviders = NSMapTable<VoiceMessageAudioPlayer, VoiceMessageNowPlayingInfoProvider>(valueOptions: .weakMemory)
nowPlayingInfoDelegates = NSMapTable<VoiceMessageAudioPlayer, VoiceMessageNowPlayingInfoDelegate>(valueOptions: .weakMemory)
activeAudioPlayers = Set<VoiceMessageAudioPlayer>()
super.init()

Expand Down Expand Up @@ -125,8 +125,12 @@ import MediaPlayer
pauseAllServicesExcept(nil)
}

func setNowPlayingInfoProvider(_ provider: VoiceMessageNowPlayingInfoProvider, forPlayer player: VoiceMessageAudioPlayer) {
nowPlayingInfoProviders.setObject(provider, forKey: player)
func registerNowPlayingInfoDelegate(_ delegate: VoiceMessageNowPlayingInfoDelegate, forPlayer player: VoiceMessageAudioPlayer) {
nowPlayingInfoDelegates.setObject(delegate, forKey: player)
}

func deregisterNowPlayingInfoDelegate(forPlayer player: VoiceMessageAudioPlayer) {
nowPlayingInfoDelegates.removeObject(forKey: player)
}

// MARK: - VoiceMessageAudioPlayerDelegate
Expand All @@ -140,16 +144,28 @@ import MediaPlayer

func audioPlayerDidStopPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
if currentlyPlayingAudioPlayer == audioPlayer {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter()
// If we have a NowPlayingInfoDelegate for this player
let nowPlayingInfoDelegate = nowPlayingInfoDelegates.object(forKey: audioPlayer)

// ask the delegate if we should disconnect from NowPlayingInfoCenter (if there's no delegate, we consider it safe to disconnect it)
if nowPlayingInfoDelegate?.shouldDisconnectFromNowPlayingInfoCenter(audioPlayer: audioPlayer) ?? true {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter(for: audioPlayer)
}
}
activeAudioPlayers.remove(audioPlayer)
}

func audioPlayerDidFinishPlaying(_ audioPlayer: VoiceMessageAudioPlayer) {
if currentlyPlayingAudioPlayer == audioPlayer {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter()
// If we have a NowPlayingInfoDelegate for this player
let nowPlayingInfoDelegate = nowPlayingInfoDelegates.object(forKey: audioPlayer)

// ask the delegate if we should disconnect from NowPlayingInfoCenter (if there's no delegate, we consider it safe to disconnect it)
if nowPlayingInfoDelegate?.shouldDisconnectFromNowPlayingInfoCenter(audioPlayer: audioPlayer) ?? true {
currentlyPlayingAudioPlayer = nil
tearDownRemoteCommandCenter(for: audioPlayer)
}
}
activeAudioPlayers.remove(audioPlayer)
}
Expand Down Expand Up @@ -248,7 +264,7 @@ import MediaPlayer
}
}

private func tearDownRemoteCommandCenter() {
private func tearDownRemoteCommandCenter(for audioPlayer: VoiceMessageAudioPlayer) {
displayLink.isPaused = true

UIApplication.shared.endReceivingRemoteControlEvents()
Expand All @@ -262,9 +278,9 @@ import MediaPlayer
return
}

// If we have a NowPlayingInfoProvider for this player
if let nowPlayingInfoProvider = nowPlayingInfoProviders.object(forKey: audioPlayer) {
nowPlayingInfoProvider.updatePlayingInfoCenter(forPlayer: audioPlayer)
// Checks if we have a delegate for this player, or if we should update the NowPlayingInfoCenter ourselves
if let nowPlayingInfoDelegate = nowPlayingInfoDelegates.object(forKey: audioPlayer) {
nowPlayingInfoDelegate.updateNowPlayingInfoCenter(forPlayer: audioPlayer)
} else {
let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: VectorL10n.voiceMessageLockScreenPlaceholder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

import Foundation

@objc protocol VoiceMessageNowPlayingInfoProvider {
func updatePlayingInfoCenter(forPlayer player: VoiceMessageAudioPlayer)
@objc protocol VoiceMessageNowPlayingInfoDelegate {

func updateNowPlayingInfoCenter(forPlayer player: VoiceMessageAudioPlayer)

func shouldDisconnectFromNowPlayingInfoCenter(audioPlayer: VoiceMessageAudioPlayer) -> Bool
}
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ class VoiceBroadcastPlaybackViewModel: VoiceBroadcastPlaybackViewModelType, Voic
// Init and start the player on the first chunk
let audioPlayer = self.mediaServiceProvider.audioPlayerForIdentifier(result.eventIdentifier)
audioPlayer.registerDelegate(self)
self.mediaServiceProvider.setNowPlayingInfoProvider(self, forPlayer: audioPlayer)
self.mediaServiceProvider.registerNowPlayingInfoDelegate(self, forPlayer: audioPlayer)

audioPlayer.loadContentFromURL(result.url, displayName: chunk.attachment.originalFileName)
self.audioPlayer = audioPlayer
Expand Down Expand Up @@ -472,6 +472,7 @@ extension VoiceBroadcastPlaybackViewModel: VoiceMessageAudioPlayerDelegate {
state.playbackState = .stopped
state.playingState.isLive = false
audioPlayer.deregisterDelegate(self)
self.mediaServiceProvider.deregisterNowPlayingInfoDelegate(forPlayer: audioPlayer)
self.audioPlayer = nil
}

Expand All @@ -485,18 +486,33 @@ extension VoiceBroadcastPlaybackViewModel: VoiceMessageAudioPlayerDelegate {
}
}

// MARK: - NowPlayingInfoProvider
// MARK: - VoiceMessageNowPlayingInfoDelegate

extension VoiceBroadcastPlaybackViewModel: VoiceMessageNowPlayingInfoProvider {
func updatePlayingInfoCenter(forPlayer player: VoiceMessageAudioPlayer) {
extension VoiceBroadcastPlaybackViewModel: VoiceMessageNowPlayingInfoDelegate {

func shouldDisconnectFromNowPlayingInfoCenter(audioPlayer player: VoiceMessageAudioPlayer) -> Bool {
guard BuildSettings.allowBackgroundAudioMessagePlayback, audioPlayer != nil, audioPlayer === player else {
return true
}

return state.broadcastState == .stopped
}

func updateNowPlayingInfoCenter(forPlayer player: VoiceMessageAudioPlayer) {
guard audioPlayer != nil, audioPlayer === player else {
return
}

let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
nowPlayingInfoCenter.nowPlayingInfo = [MPMediaItemPropertyTitle: VectorL10n.voiceBroadcastPlaybackLockScreenPlaceholder,
MPMediaItemPropertyPlaybackDuration: (state.playingState.duration / 1000.0) as Any,
MPNowPlayingInfoPropertyElapsedPlaybackTime: (state.bindings.progress / 1000.0) as Any,
MPNowPlayingInfoPropertyPlaybackRate: state.playbackState == .playing ? 1 : 0]
nowPlayingInfoCenter.nowPlayingInfo = [
// Title
MPMediaItemPropertyTitle: VectorL10n.voiceBroadcastPlaybackLockScreenPlaceholder,
// Buffering status (using the "artist" property to display it under the title)
MPMediaItemPropertyArtist: state.playbackState == .buffering ? VectorL10n.voiceBroadcastBuffering : "",
// Duration
MPMediaItemPropertyPlaybackDuration: (state.playingState.duration / 1000.0) as Any,
// Elapsed time
MPNowPlayingInfoPropertyElapsedPlaybackTime: (state.bindings.progress / 1000.0) as Any,
]
}
}

0 comments on commit b2ae0e7

Please sign in to comment.