Home > Enterprise >  AVPlayer seek completionHandler returning false in iOS 15.4
AVPlayer seek completionHandler returning false in iOS 15.4

Time:03-30

We're trying to implement AVPlayer seek in our SwiftUI app, it worked prior to iOS 15.4 but not after the update.

let playerCurrentTime = CMTimeGetSeconds(player.currentTime())
let newTime = playerCurrentTime   45
let time2: CMTime = CMTimeMake(value: Int64(newTime * 1000 as Float64), timescale: 1000)
player.seek(to: time2, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) { success in
   print(success)
}

The completionHandler is called immediately with success = false. No other seek operations are running, and the AVPlayer status is readyToPlay.

We're streaming a MP3 file from an URL, using this initialisation code:

playerItem = AVPlayerItem(url: url)
        
player = AVPlayer(playerItem: playerItem)
player.addPeriodicTimeObserver(forInterval: CMTime(value: 1, timescale: 2), queue: DispatchQueue.main) { _ in
        if self.player.currentItem?.status == .readyToPlay {
            self.currentTimeInSeconds = CMTimeGetSeconds(self.player.currentTime())
            self.progressInPct = Double(self.currentTimeInSeconds) / Double(self.totalTimeInSeconds)
            self.nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = 1
            MPNowPlayingInfoCenter.default().nowPlayingInfo = self.nowPlayingInfo
                self.setupNowPlaying()
        } else {
            self.nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = 0
            MPNowPlayingInfoCenter.default().nowPlayingInfo = self.nowPlayingInfo
        }
}

We tried seeking on currentItem as well, but that didn't work either.

player.currentItem?.seek(to: time, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)

Anyone else experienced something like this, and have any pointers?

UPDATE: Tried doing a complete bare bones attempt, but still the same result:

struct testView: View {
    var player = AVPlayer()

    var body: some View {
        Button {
            self.startPlayer(url: episode.streamUrl!)
        }
        label: {
            Text("Test")
        }
    }
    func startPlayer(url: String) {
        let playerItem = AVPlayerItem(url: URL(string: url) !)
        self.player.replaceCurrentItem(with: playerItem)
        player.play()

        DispatchQueue.main.asyncAfter(deadline: .now()   5) {
            let time2: CMTime = CMTimeMake(value: Int64(45 * 1000 as Float64), timescale: 1000)
            player.seek(to: time2, toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero) {
                success in
                    print(success)
            }
        }
    }
}

Prints "false".

CodePudding user response:

For comparision here I attach piece of code that I use for time observation and 3 types of seeking functions. It is working fine so far on iOS 15.4, (although on UIKit and with .m3u8 playlist). Hopefully it might help you in some way.

  1. seeking functions:

    func seek1() {
     let videoDuration = (avPlayer.currentItem?.duration.seconds ?? 0)!
     let elapsedTime: Float64 = videoDuration * Float64(archiveControlsView.seekSlider.value)
     let toTime = CMTime.init(seconds: elapsedTime,
                              preferredTimescale: 100)
     avPlayer.seek(to: toTime,completionHandler: { (completed: Bool) -> Void in
         //do whatever you need
     })
    }
    
    func seek2() {
     let diff: TimeInterval = 60
     self.avPlayer.seek(to: CMTime.init(seconds: diff,   preferredTimescale: 100))
    }
    
    func seekToZero() {
     self.avPlayer.seek(to: kCMTimeZero)
    }
    
  2. time observing function

    var timeObserver: AnyObject!
    
    func createTimer() {
     let timeInterval: CMTime = CMTime.init(seconds: 1.0, preferredTimescale: 10)
    
     timeObserver = avPlayer.addPeriodicTimeObserver(forInterval: timeInterval,
                                                     queue: DispatchQueue.main) {
         (elapsedTime: CMTime) -> Void in
    
         guard self.status != .Seeking else { return }
         self.observeTime(elapsedTime)
         let duration = (self.avPlayer.currentItem?.duration.seconds ?? 0)!
         let elapsedTime = elapsedTime.seconds
         // do whatever you need with elapsed and duration
     } as AnyObject?
    }
    
  • Related