2

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!

Alexey Gorbel
  • 220
  • 3
  • 11
  • Does this answer your question? [AVAssetResourceLoaderDelegate implementation for an asset of unknown length](https://stackoverflow.com/questions/45786283/avassetresourceloaderdelegate-implementation-for-an-asset-of-unknown-length) – The Dreams Wind Jun 07 '22 at 22:28
  • @TheDreamsWind not really, but thanks for the link – Alexey Gorbel Jun 10 '22 at 10:31
  • what exactly doesn't work for you here? Provided the server side handles all http requests properly `AVAssetResourceLoaderDelegate` should be able to keep it up with use of `renewDate` property – The Dreams Wind Jun 10 '22 at 14:27

1 Answers1

0

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)
    }           
}
spacecash21
  • 1,331
  • 1
  • 19
  • 41
  • I've tried: it goes to "loaded" case, but asset.duration is CMTime(value: 0, timescale: 0, flags: __C.CMTimeFlags(rawValue: 17), epoch: 0). Thanks for reply anyway! – Alexey Gorbel May 03 '22 at 11:48