3

I'm initializing an AVPlayerItem, and it takes a parameter automaticallyLoadedAssetKeys, which indicates which AVAsset keys need to be loaded before it is flagged as "ready to play".

I've seen examples of people passing in the duration property as one of these keys.

But then over in the documentation for AVPlayerItem, it talks about the duration property, and how to make sure that it's valid.

The value of this property will be reported as kCMTimeIndefinite until the duration of the underlying asset has been loaded. There are two ways to ensure that the value of duration is accessed only after it becomes available:

Wait until the status of the player item is readyToPlay

The other way is to register a KVO, which is fine. But this first comment would indicate to me that I do not need to ask that duration gets automatically loaded, because it should be already.(?)

If this is true, I can't figure out why anyone would ever pass in duration as a flag to be automatically loaded, because it looks like that's the case. Am I missing something here?

Dan Morrow
  • 4,433
  • 2
  • 31
  • 45

2 Answers2

2

Some people do this because there are iOS bugs (including in iOS 12.1.4) which sometimes cause AVPlayerItem.duration to be nan, even when status is readyToPlay. Examples here and here.

However, my experience is specifying "duration" in automaticallyLoadedAssetKeys does not solve the issue - despite Apple's docs for AVPlayerItem:

The value of each key in automaticallyLoadedAssetKeys will automatically be loaded by the underlying AVAsset before the player item achieves the status AVPlayerItem.Status.readyToPlay; i.e. when the item is ready to play, the value returned by invoking the asset property’s statusOfValue(forKey:error:) method will be one of the terminal status values

I can get AVPlayerItem.Status.readyToPlay, with statusOfValue(forKey: "duration") returning .loaded, and the duration nan.

The only solution I've found that works every time is to KVO both AVPlayerItem.status (for .readyToPlay) and AVPlayerItem.duration (for a valid number), and when either triggers, check both:

    if item.status != AVPlayerItem.Status.readyToPlay || !item.duration.isUsableNumber() {
        return // not ready yet (for addBoundaryTimeObserver, seek to end, etc.)
    }

Where isUsableNunber() is just:

 func isUsableNumber() -> Bool {
    return isValid && isNumeric && !isIndefinite && !isNegativeInfinity && !isPositiveInfinity
}
ozool
  • 94
  • 1
  • 9
1

Sorry, I know this is a little stale. But I recently found the answer to my problem.

So, I realized that I was getting the documentation confused, and it was probably right in the docs all along, if I had looked harder, I would've noticed.

AVPlayerItem(asset: asset) loads the duration property by default. It's just like calling AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: ["duration"])

It actually says this in the docs, but I completely missed it.

Dan Morrow
  • 4,433
  • 2
  • 31
  • 45