Как я могу зарегистрироваться, когда AVPlayer фактически начинает играть (из внешнего источника)

У меня возникли проблемы с регистрацией КОГДА игрок начинает проигрывать внешние видеоролики (через Интернет) с помощью AVPlayer. Пожалуйста, прочитайте вопрос, прежде чем предлагать решения. Я инициализирую плеер следующим образом:

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

Это правильно добавляет плеер в представление. Я добавил следующие две строки кода, чтобы отслеживать, когда плеер готов, и каков статус/скорость:

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

Эти две строки вызовут метод - (void)observeValueForKeyPath:...., когда что-то изменится со статусом или скоростью AVPlayer.

Пока это выглядит так:

- (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
        }
    }
}

state AVPlayer почти сразу после инициализации изменяется на readyToPlay, а затем я вызываю [player play]. Когда это произойдет, rate изменится на 1.00000, что означает, что он действительно играет с такой скоростью, но видео теперь только начинает буферировать, а не воспроизводиться. Экран черный, и он занимает пару секунд, а затем начинает воспроизведение. Тем не менее, скорость указывает, что он начинает играть до того, как он это сделает. Скорость остается на уровне 1.00000, а при начале буферизации не уменьшается до 0, что затрудняет мне знать, когда у игрока достаточно информации, чтобы начать настройку элементов управления (метки времени IE и т.д.).

NSLog() печать продолжительности видео выше выводит nan (Not A Number), что заставляет меня думать, что элемент не готов к воспроизведению, однако скорость остается на уровне 1.0000 пока он не забуферизуется некоторое время, тогда он будет играть, все еще со скоростью в 1.0000.

Однако он вызывает вызов дважды. rate "изменяется" на 1.0000 дважды, не находясь между ними. В обоих вызовах продолжительность видео является доступной переменной.

Моя цель - как можно быстрее получить текущую и общую временную метку видео (I.E 0:00/3:52). Это также будет использоваться для регистрации очистки ползунка (для быстрой перемотки вперед и т.д.).

Эти значения не готовы, когда игрок уведомляет меня, что он играет со скоростью 1.0000, дважды. Если я вручную нажму "play" через секунду или так (и вызовите [player play]), тогда он будет работать. Как я могу зарегистрироваться, когда видео готово, а не только "готово готовиться"?

Ответ 1

Смотрите addBoundaryTimeObserverForTimes:queue:usingBlock: на AVPlayer и в этом примере от 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];
}];

Ответ 2

Я думаю, что ближайший к вам - это наблюдать player.currentItem.playbackLikelyToKeepUp