Sam Fischer
Sam Fischer

Reputation: 1472

AVPlayer's addPeriodicTimeObserver function is not being executed properly

Background

I am trying to get a very basic audio player set up in a macOS application. I am using Xcode 9.3 on macOS 10.13.4.


Setup

I have a MediaManager class to control the AVPlayer object:

let EPISODE_SELECTED_KEY = "EPISODE_SELECTED"

class MediaManager {
    
    static var shared: MediaManager = MediaManager()
    
    var player: AVPlayer?
    
    var currentFile: URL? {
        didSet {
            if (currentFile != nil) {
                play(URL: currentFile!)
                NotificationCenter.default.post(name: NSNotification.Name(EPISODE_SELECTED_KEY), object: nil)
            }
        }
    }

    
    func play(URL: URL) {
        let item = AVPlayerItem(url: URL)
        player = AVPlayer(playerItem: item)
        player?.play()
    }

}

To visually track the progress of the audio item being played, I am subclassing NSSlider as so:

class PlayerSlider: NSSlider {
    
    var observerToken: Any?
    var mediaManager = MediaManager.shared
    
    override var isEnabled: Bool {
        didSet {
            // Register for PlayerManager notification.
            NotificationCenter.default.addObserver(self, selector: #selector(setupPlayerObserver), name: Notification.Name(EPISODE_SELECTED_KEY), object: nil)
        }
    }
    
    override var doubleValue: Double {
        didSet {
            // Make sure the player has an active item.
            guard mediaManager.currentEpisode != nil || mediaManager.currentFile != nil else {
                return
            }
            // Update player with slider's position.
            let seekTime = CMTime(seconds: (mediaManager.player?.currentTime().seconds)! * (doubleValue / 100), preferredTimescale: CMTimeScale(NSEC_PER_SEC))
            mediaManager.player?.seek(to: seekTime)
        }
    }
    
    @objc func setupPlayerObserver() {
        // Setup observer to update UI every 0.5 seconds.
        let interval = CMTime(seconds: 0.5, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        self.observerToken = mediaManager.player!.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { (time) in
            // Update slider value to audio item progress.
            let duration = self.mediaManager.player!.currentItem!.duration.seconds
            let seekTime = self.mediaManager.player!.currentTime().seconds
            self.doubleValue = seekTime / duration * 100
        })
        
    }
}

For testing purposes, I'm just set the currentFile property of the MediaManager singleton to a local file.


Problem

In the block that I pass to the addPeriodicTimeObserver function, if I comment out everything the file plays fine. However, if there's even a print statement in there, nothing happens. The file never plays and the app just hangs.


Questions

  1. What is going on inside that block? Even a single statement stops the AVPlayer from playing.
  2. Is this a good design for an audio file player? To have the MediaManager singleton and the listener inside the PlayerSlider.

Upvotes: 2

Views: 4265

Answers (0)

Related Questions