Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I register for when AVPlayer actually starts playing (from external source)

I'm having some trouble with registering WHEN the player is starting to play external videos (over internet) using AVPlayer. Please read the question before suggesting solutions. I initialize the player like this:

player = [[AVPlayer alloc] initWithURL:[[NSURL alloc] initWithString:@"http://example.com/video.mp4"]];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
[playerLayer setFrame:[videoView bounds]];
[videoView.layer addSublayer:playerLayer];

This adds the player to the view correctly. I have added the following two lines of code to keep track of when the player is ready, and what the status/rate is;

[player addObserver:self forKeyPath:@"rate" options:0 context:nil];
[player addObserver:self forKeyPath:@"status" options:0 context:nil];

These two line will call the method - (void)observeValueForKeyPath:.... when something changes with the status or the rate of the AVPlayer.

So far it looks like this:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{
    //To print out if it is 'rate' or 'status' that has changed:
    NSLog(@"Changed: %@", keyPath); 

    if ([keyPath isEqualToString:@"rate"]) //If rate has changed:
    { 
        if ([player rate] != 0) //If it started playing
        {
            NSLog(@"Total time: %f", CMTimeGetSeconds([[player currentItem] duration]));
            // This NSLog is supposed to print out the duration of the video.

            [self setControls];
            // This method (setControls) is supposed to set play/pause-buttons 
            // as well as labels for the current and total time of the current video.
        }
    }
    else if ([keyPath isEqualToString:@"status"]) // If the status changed
    {
        if(player.status == AVPlayerStatusReadyToPlay) //If "ReadyToPlay"
        {
            NSLog(@"ReadyToPlay");
            [player play]; //Start the video
        }
    }
}

The state of the AVPlayer changes to readyToPlay almost immediately after initializing it, and I then call [player play]. When this happens, the rate changes to 1.00000, meaning it's actually playing at that rate, but the video is now just starting to buffer, not playing. The screen is black, and it takes a couple of seconds, and then it starts playing. The rate, however, indicates it starts playing before it does. The rate stays at 1.00000, not going down to 0 when start-buffering, which makes it very difficult for me to know when the player has enough information to start setting the controls (I.E time stamps etc).

The NSLog() printing out the duration of the video above prints out nan (Not A Number), which leads me to think that the item isn't ready to be played, however, the rate stays at 1.0000 until it has buffered a while, then it will actually play, still with rate at 1.0000.

It does, however, get called twice. The rate "changes" to 1.0000 twice without being anything else in between. In neither calls, the duration of the video is an available variable.

My goal is to fetch the current and total timestamp of the video as fast as possible (I.E 0:00/3:52). This will also be used to register the scrubbing of a slider (for fast-forward etc.).

These values are not ready when the player notifies me it's playing at a rate of 1.0000, twice. If I manually click "play" after a second or so (and call [player play]), then it's working. How can I register to know when the video is ready, not just 'ready to get ready'?

like image 540
Sti Avatar asked Dec 22 '13 19:12

Sti


2 Answers

See addBoundaryTimeObserverForTimes:queue:usingBlock: on AVPlayer and this example from Apple.

AVPlayer *player = [AVPlayer playerWithURL:[NSURL URLWithString:@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"]];

[player play];

// Assumes a property: @property (strong) id playerObserver; 
// Cannot use kCMTimeZero so instead use a very small period of time
self.playerObserver = [player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:CMTimeMake(1, 1000)]] queue:NULL usingBlock:^{

        //Playback started

        [player removeTimeObserver:self.playerObserver];
}];
like image 148
Michael M. Myers Avatar answered Nov 15 '22 22:11

Michael M. Myers


I think the nearest you'll get to is to observe player.currentItem.playbackLikelyToKeepUp

like image 22
danh Avatar answered Nov 15 '22 23:11

danh