Search code examples
iosobjective-cavfoundationavplayeravplayeritem

AVPlayerItem never ready to play


I started a new project with a single view and added this code to the view controller:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSURL *videoURL;
    videoURL = [NSURL URLWithString:@"https://s3.amazonaws.com/xxxxxx.mp4"];
    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];

    [playerItem addObserver:self
                      forKeyPath:@"status"
                         options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                         context:@"AVPlayerStatus"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context {

    if (context == @"AVPlayerStatus") {
        NSLog(@"AVPlayerStatus changed");

        if ([object isKindOfClass:[AVPlayerItem class]] && [keyPath isEqualToString:@"status"]) {
            NSLog(@"Ready to play");
        }
        else if (((AVPlayerItem *)object).status == AVPlayerStatusFailed) {
            NSLog(@"Failed to Ready");
        }
        else if (((AVPlayerItem *)object).status == AVPlayerStatusUnknown) {
            NSLog(@"Not known");
        }
    }
}

The output is:

2016-06-21 19:57:56.911 VideoTest[5740:1334235] AVPlayerStatus changed
2016-06-21 19:57:56.911 VideoTest[5740:1334235] Not known

Why doesn't the AVPlayerItem ever load? and how can I get it to load?


Solution

  • This is a remote asset (it's somewhere out there on the big wide Internet). It has no meaningful status until you try to play it; at that point, we begin loading the asset and exploring its nature. You have not tried to play it, so the status just sits there at Unknown.

    Why doesn't the AVPlayerItem ever load? and how can I get it to load?

    You can get the item to load by playing the asset.

    Your code is flawed in three respects:

    • Your first condition never tests whether the status is ReadyToPlay;

    • You never try to play the asset.

    • You're using the context wrong.

    I tested with your code, but edited like this, to fix all three of those things:

    @interface ViewController ()
    @property AVPlayer* player;
    @end
    
    @implementation ViewController
    
    - (IBAction)doLoad:(id)sender {
        NSURL *videoURL;
    
        videoURL = [NSURL URLWithString:@"https://s3.amazonaws.com/lookvideos.mp4/t/05093dabec6c9448f7058a4a08f998155b03cc41.mp4"];
        AVURLAsset *asset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
        AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
    
        [playerItem addObserver:self
                     forKeyPath:@"status"
                        options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                        context:nil];
    
    
        self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
        [self.player play];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                            change:(NSDictionary *)change context:(void *)context {
    
        NSLog(@"AVPlayerStatus changed");
    
        if (((AVPlayerItem *)object).status == AVPlayerStatusReadyToPlay) {
            NSLog(@"Ready to play");
        }
        else if (((AVPlayerItem *)object).status == AVPlayerStatusFailed) {
            NSLog(@"Failed to Ready");
        }
        else if (((AVPlayerItem *)object).status == AVPlayerStatusUnknown) {
            NSLog(@"Not known");
        }
    }
    
    @end
    

    At first I see "Not known" in the console, but as soon as I try to play the asset, it changes to "Ready to play".