Apples documentation alludes to it, but how do you set up key-value observation for the loadedTimeRanges property of AVPlayerItem? That property is an NSArray that doesn't change, so you can't just use playerItem addObserver:self forKeyPath:@"loadedTimeRanges ...
Or is there another way to get notifications or updates whenever this changes?
Actually, I'm using KVO for loadedTimeRanges without any trouble. Maybe you're just not setting the right options? The following is a very slight modification of some of the code in Apple's AVPlayerDemo, and it's working quite nicely for me.
//somewhere near the top of the file
NSString * const kLoadedTimeRangesKey   = @"loadedTimeRanges";
static void *AudioControllerBufferingObservationContext = &AudioControllerBufferingObservationContext;
- (void)someFunction
{  
    // ...
    //somewhere after somePlayerItem has been initialized
    [somePlayerItem addObserver:self
                       forKeyPath:kLoadedTimeRangesKey
                          options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                          context:AudioControllerBufferingObservationContext];
    // ...
}
- (void)observeValueForKeyPath:(NSString*) path 
                  ofObject:(id)object 
                    change:(NSDictionary*)change 
                   context:(void*)context
{
    if (context == AudioControllerBufferingObservationContext)
    {
        NSLog(@"Buffering status: %@", [object loadedTimeRanges]);
    }
}
Right. loadedTimeRanges doesn't change but the objects inside of it change. You could setup a timer to run every second (or so) and inspect the values inside of loadedTimeRanges. Then you'll see the changes you are looking for.
dispatch_queue_t queue = dispatch_queue_create("playerQueue", NULL);
[player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
                                          queue:queue
                                     usingBlock:^(CMTime time) {  
                                         for (NSValue *time in player.currentItem.loadedTimeRanges) {
                                             CMTimeRange range;
                                             [time getValue:&range];
                                             NSLog(@"loadedTimeRanges: %f, %f", CMTimeGetSeconds(range.start), CMTimeGetSeconds(range.duration));
                                         }
                                     }];
Update to Swift 4.0 and later:
loadedTimeRangesObserver = player.observe(\AVPlayer.currentItem?.loadedTimeRanges, options: [.new, .initial]) { [unowned self] (player, change) in
    DispatchQueue.main.async {    
        guard let ranges = change.newValue as? [CMTimeRange] else { return }
        // update UI
    }
}     
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With