iosswiftavqueueplayeravplayeritemcmtime

Playing AVPlayerItem CurrentTime in Seconds Return Inconsistent Values


I am retooling my audio player application to use AVQueuePlayer instead of AVAudioPlayer, as I now appreciate that I need to be able to queue up audio files for playback. Since AVQueuePlayer is a subclass of AVPlayer, which uses CMTime to keep track of AVPlayerItem time properties (duration, currentTime, etc...) I had to rework my animation function which updates the slider and UILabels of my AudioPlayer view.

I am finding that, now that I am using CMTime structs, instead of TimeIntervals, my currentSeconds variable returns some odd, inconsistent values as the audio file plays. You can see the printed results below the code I've included.

This also results in my track slider hiccuping to different spots and the duration label jumping to odd values, including 00:00 while half way through playback.

I'm new to a lot of what's going on here and I've tried to change the interval values in the addTimeObserver function, thinking that but it hasn't changed anything. This might be an issue with a Timer object that I've scheduled to fire the animateFooterView() every 0.1 seconds... but I'm not sure how to address it.

func animateFooterView(){

    let track = audioQueuePlayer?.currentItem
    let duration: CMTime = (track?.asset.duration)!
    let durationSeconds: Float64 = CMTimeGetSeconds(duration)
    let currentSeconds: Float64 = CMTimeGetSeconds(track!.currentTime())

    audioQueuePlayer?.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, 10), queue: DispatchQueue.main, using:
        { (CMTime) -> Void in
            if track?.status == .readyToPlay{

                let trackProgress: Float = Float(currentSeconds / durationSeconds) //* (self.footerView.trackSlider.maximumValue)

                print("Progress in seconds: " + String(currentSeconds))

                self.footerView.trackSlider.value = trackProgress

                let minutesProgress = Int(currentSeconds) / 60 % 60
                let secondsProgress = Int(currentSeconds) % 60

                let minutesDuration = Int(durationSeconds) / 60 % 60
                let secondsDuration = Int(durationSeconds) % 60

                self.footerView.durationLabel.text = String(format: "%02d:%02d | %02d:%02d", minutesProgress, secondsProgress, minutesDuration, secondsDuration)
            }
        })
}

Print() output of currentSeconds:

Progress in seconds: 0.959150266
Progress in seconds: 0.459939791
Progress in seconds: 0.739364115
Progress in seconds: 0.0
Progress in seconds: 0.0
Progress in seconds: 0.253904687
Progress in seconds: 0.0
Progress in seconds: 0.15722927
Progress in seconds: 0.346783126
Progress in seconds: 0.050562017
Progress in seconds: 1.158813019
Progress in seconds: 1.252108811
Progress in seconds: 1.356793865
Progress in seconds: 1.458337661
Progress in seconds: 1.554249846
Progress in seconds: 1.615820093
Progress in seconds: 0.0
Progress in seconds: 0.0
Progress in seconds: 0.0
Progress in seconds: 0.050562017
Progress in seconds: 0.15722927
Progress in seconds: 0.253904687
Progress in seconds: 0.346783126
Progress in seconds: 0.459939791
Progress in seconds: 0.558436703
Progress in seconds: 0.656613436
Progress in seconds: 0.739364115
Progress in seconds: 0.854647401
Progress in seconds: 0.959150266
Progress in seconds: 1.057932049
Progress in seconds: 1.158813019

Here is a glimpse as at the timer I was mentioning, which calls animateFooterView every 0.1 seconds whenever the footerView plays.

func play() {

   ...
   timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(animateFooterView), userInfo: nil, repeats: true)      
   ...
}

Actually, I think I just realized what's going wrong here... I'm adding a periodicTimeObserver every 0.1 seconds due to the timer firing. I think I just need to add that observer once, and then do away with the timer all together?


Solution

  • I was correct in my assumption that the timer continually adding an observer to the AVPlayer was messing up the returned current time. I removed the timer and instead had the observer function update the current time. It works perfect now.