How to get Duration from AVPlayer (Not AVAudioPlayer)?

Viewed 43570

I would like to make a UISlider(scrubber) for my AVPlayer. But since this is not an AVAudioPlayer, it doesn't have a built in duration. Any suggestion on how to create the Slider for fast forward, rewind and progress of the playback?

I read the doc on AVPlayer, it has a built in seekToTime or seekToTime:toleranceBefore:toleranceAfter:. I don't really understand it. Would this be the answer for my slider? AVPlayer also has addPeriodicTimeObserverForInterval:queue:usingBlock:, is this for getting the duration of my track? Can someone give me an example on how to implement this code? I am not a fan of Apple's documentation. It seems very hard to understand.

8 Answers

Swift 5

Put this code inside some function that you desired :

let duration = player.currentItem?.duration.seconds ?? 0
let playDuration = formatTime(seconds: duration) //Duration RESULT

Create a function called: formatTime(seconds: Double)

func formatTime(seconds: Double) -> String {
    let result = timeDivider(seconds: seconds)
    let hoursString = "\(result.hours)"
    var minutesString = "\(result.minutes)"
    var secondsString = "\(result.seconds)"

    if minutesString.count == 1 {
        minutesString = "0\(result.minutes)"
    }
    if secondsString.count == 1 {
        secondsString = "0\(result.seconds)"
    }

    var time = "\(hoursString):"
    if result.hours >= 1 {
        time.append("\(minutesString):\(secondsString)")
    }
    else {
        time = "\(minutesString):\(secondsString)"
    }
    return time
}

Then, another function to translate seconds to hour, minute and second. Since the seconds that by layerLayer.player?.currentItem?.duration.seconds will have long Double, so this is needed to become Human Readable.

func timeDivider(seconds: Double) -> (hours: Int, minutes: Int, seconds: Int) {
    guard !(seconds.isNaN || seconds.isInfinite) else {
        return (0,0,0)
    }
    let secs: Int = Int(seconds)
    let hours = secs / 3600
    let minutes = (secs % 3600) / 60
    let seconds = (secs % 3600) % 60
    return (hours, minutes, seconds)
}

Hope it's complete your answer.

Although the answers here are correct I believe is valuable to add that you can ask the AVAsset to load the duration (or any other property) and get a callback once it does so you can access the value. This is in response to many commenting that the value of player.currentItem.asset.duration returns 0.00 until the assets gets loaded.

It uses AVAsynchronousKeyValueLoading like this:

let asset = AVURLAsset(url: url)
asset.loadValuesAsynchronously(forKeys: ["duration"]) {
            
    var error: NSError? = nil
    let item: PlaylistItem
            
    // If the duration was loaded, construct a "normal" item,
    // otherwise construct an error item.
    switch asset.statusOfValue(forKey: "duration", error: &error) {
    case .loaded:
        item = PlaylistItem(url: url, title: title, artist: artist, duration: asset.duration)        
    case .failed where error != nil:
        item = PlaylistItem(title: title, artist: artist, error: error!) 
    default:
        let error = NSError(domain: NSCocoaErrorDomain, code: NSFileReadCorruptFileError)
        item = PlaylistItem(title: title, artist: artist, error: error)
    }           
}

This snippet is from Playing Custom Audio with Your Own player sample code by Apple.

Related