AVPlayer show loaded progress for live MP3 stream

Viewed 119

I'm using AVPLayer and AVPlayerItem to play remote MP3 from URL. The tricky part here that this MP3 has unknown content-length. So this is a kind of live stream, but it has finite length(at some point all bytes are received).

How can I get already downloaded duration for such item?

That's how I initialize AVPlayerItem with AVURLAsset:

    let asset = AVURLAsset(url: url, options: ["AVURLAssetHTTPHeaderFieldsKey": headers])
    let item = AVPlayerItem(asset: asset)

What I've tried:

  1. Tried to get the duration directly from item or asset propery. It is NaN all the time in my case

     let duration = player.currentItem?.duration
     let assetDuration = player.currentItem?.asset.duration
    
  2. Observe loadedTimeRanges. But the value returned are very anaccurate. For example if real duration is 80 sec, from loadedTimeRanges returns to my sometimes 60, sometimes 70. If real duration is too small, e.g. 5 sec, then loadedTimeRanges might return even nothing

         item.addObserver(self,
                      forKeyPath: #keyPath(AVPlayerItem.loadedTimeRanges),
                      options: [.new],
                      context: &playerItemContext)
    

        if keyPath == #keyPath(AVPlayerItem.loadedTimeRanges) {
        if let ranges = change?[.newKey] as? [NSValue], let duration = ranges.first?.timeRangeValue.duration {
            print("Duration from timeRangeValue: \(duration.seconds)")
        }
    }

3.Implement my own AVAssetResourceLoaderDelegate. But I faced some other issues here..this is another topic

So, please share any ideas/thougths how can I achive that. Maybe there is another way do get from AVPlayer/AVPlayerItem all loaded bytes/seconds?

Thanks in advance!

1 Answers

Have you tried this?

How to get Duration from AVPlayer (Not AVAudioPlayer)?

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)
    }           
}
Related