Проверьте состояние воспроизведения AVPlayer


есть ли способ узнать, является ли AVPlayer воспроизведение остановилось или дошло до конца?

8 93

8 ответов:

чтобы получить уведомление о достижении конца элемента (через Яблоко):

[[NSNotificationCenter defaultCenter] 
      addObserver:<self>
      selector:@selector(<#The selector name#>)
      name:AVPlayerItemDidPlayToEndTimeNotification 
      object:<#A player item#>];

и отслеживать воспроизведение вы можете:

"отслеживать изменения положения головки воспроизведения в объекте AVPlayer" с помощью addPeriodicTimeObserverForInterval: queue: usingBlock: или addBoundaryTimeObserverForTimes: queue: usingBlock:.

пример из Apple:

// Assume a property: @property (retain) id playerObserver;

Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 1);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);
NSArray *times = [NSArray arrayWithObjects:[NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird], nil];

self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
    // Passing NULL for the queue specifies the main queue.

    NSString *timeDescription = (NSString *)CMTimeCopyDescription(NULL, [self.player currentTime]);
    NSLog(@"Passed a boundary at %@", timeDescription);
    [timeDescription release];
}];

Вы можете сказать, что он играет с помощью:

AVPlayer *player = ...
if ((player.rate != 0) && (player.error == nil)) {
    // player is playing
}

Swift 3 расширение:

extension AVPlayer {
    var isPlaying: Bool {
        return rate != 0 && error == nil
    }
}

В iOS10, есть встроенный в свойство для этого сейчас: timeControlStatus

например, эта функция воспроизводит или приостанавливает avPlayer на основе его статуса и соответствующим образом обновляет кнопку воспроизведения/паузы.

@IBAction func btnPlayTap(_ sender: Any) {
    if aPlayer.timeControlStatus == .playing {
        aPlayer.pause()
        btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
    } else if aPlayer.timeControlStatus == .paused {
        aPlayer.play()
        btnPlay.setImage(UIImage(named: "control-pause"), for: .normal)
    }
}

Что касается вашего второго вопроса, чтобы узнать, достиг ли avPlayer конца, проще всего было бы настроить уведомление.

NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)

когда он дойдет до конца, например, вы можете перемотать его на начало видео и сброс кнопки паузы для воспроизведения.

func didPlayToEnd() {
    aPlayer.seek(to: CMTimeMakeWithSeconds(0, 1))
    btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
}

эти примеры полезны, если вы создаете свои собственные элементы управления, но если вы используете AVPlayerViewController, то элементы управления встроены.

более надежная альтернатива NSNotification это добавить себя в качестве наблюдателя к игроку rate собственность.

[self.player addObserver:self
              forKeyPath:@"rate"
                 options:NSKeyValueObservingOptionNew
                 context:NULL];

затем проверьте, если новое значение для наблюдаемой скорости равно нулю, что означает, что воспроизведение остановилось по какой-то причине, например, достигнув конца или остановившись из-за пустого буфера.

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"rate"]) {
        float rate = [change[NSKeyValueChangeNewKey] floatValue];
        if (rate == 0.0) {
            // Playback stopped
        } else if (rate == 1.0) {
            // Normal playback
        } else if (rate == -1.0) {
            // Reverse playback
        }
    }
}

на rate == 0.0 case, чтобы узнать, что именно вызвало остановку воспроизведения, вы можете выполнить следующие проверки:

if (self.player.error != nil) {
    // Playback failed
}
if (CMTimeGetSeconds(self.player.currentTime) >=
    CMTimeGetSeconds(self.player.currentItem.duration)) {
    // Playback reached end
} else if (!self.player.currentItem.playbackLikelyToKeepUp) {
    // Not ready to play, wait until enough data is loaded
}

и не забудьте сделать ваши игрок останавливается, когда он достигает конца:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;

на Свифт:

AVPlayer:

let player = AVPlayer(URL: NSURL(string: "http://www.sample.com/movie.mov"))
if (player.rate != 0 && player.error == nil) {
   println("playing")
}

обновление:
player.rate > 0 условие изменено на player.rate != 0 потому что, если видео воспроизводится в обратном, он может быть отрицательным, благодаря Юлиан для указывая.
Примечание: это может выглядеть так же, как выше(МАЗ) ответ, но в Swift '!игрок.ошибка "давала мне ошибку компилятора, поэтому вы должны проверить ошибку с помощью" player.error = = nil ' in Быстрый.(потому что свойство error не имеет типа' Bool')

AVAudioPlayer:

if let theAudioPlayer =  appDelegate.audioPlayer {
   if (theAudioPlayer.playing) {
       // playing
   }
}

AVQueuePlayer:

if let theAudioQueuePlayer =  appDelegate.audioPlayerQueue {
   if (theAudioQueuePlayer.rate != 0 && theAudioQueuePlayer.error == nil) {
       // playing
   }
}

rate и не способ проверить, является ли видео игры (это может заглохнуть). Из документации rate:

указывает на желаемую скорость воспроизведения; 0.0 означает "приостановлено", 1.0 указывает на желание играть с естественной скоростью текущего элемента.

ключевые слова "желание играть" - в размере 1.0 не означает, что видео воспроизводится.

решение с iOS 10.0-это использовать AVPlayerTimeControlStatus что можно наблюдать на AVPlayertimeControlStatus собственность.

решение до iOS 10.0 (9.0, 8.0 и т. д.) свернуть свои собственные решения. в размере 0.0 означает, что видео приостановлено. Когда rate != 0.0 это означает, что видео либо играть или заглох.

вы можете узнать разницу, наблюдая за временем игрока через:func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

блок возвращает текущее время игрока в CMTime, так что сравнение lastTime (время последнего получения от Блока) и currentTime (время, которое блок только что сообщил) покажет, играет ли игрок или заглох. Например, если lastTime == currentTime и rate != 0.0, то игрок заглох.

как отметили другие, выяснение того, закончено ли воспроизведение, обозначается AVPlayerItemDidPlayToEndTimeNotification.

расширение Swift на основе ответа от maz

extension AVPlayer {

    var isPlaying: Bool {
        return ((rate != 0) && (error == nil))
    }
}

быстрая версия ответа максконовалова такова:

player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.New, context: nil)

и

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if keyPath == "rate" {
        if let rate = change?[NSKeyValueChangeNewKey] as? Float {
            if rate == 0.0 {
                print("playback stopped")
            }
            if rate == 1.0 {
                print("normal playback")
            }
            if rate == -1.0 {
                print("reverse playback")
            }
        }
    }
}

спасибо maxkonovalov!