Sprawdź stan odtwarzania AVPlayera

Odpowiedzi:

57

Aby otrzymać powiadomienie o osiągnięciu końca elementu (przez Apple ):

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

Aby śledzić odtwarzanie, możesz:

„śledzenia zmian w położeniu wskaźnika odtwarzania na przedmiocie AVPlayer” stosując addPeriodicTimeObserverForInterval: kolejki: usingBlock: lub addBoundaryTimeObserverForTimes: kolejki: usingBlock: .

Przykład pochodzi od 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];
}];
Todd Hopkinson
źródło
Możesz wrzucić flagę logiczną, aby sprawdzić stan gry.
moonman239
Powiadomienia mogą powodować problemy, jeśli zmienisz przedmiot gracza, na przykład za pomocą -replaceCurrentItemWithPlayerItem:, i nie obsłużysz go poprawnie. Bardziej niezawodnym sposobem jest dla mnie śledzenie AVPlayerstatusu za pomocą KVO. Zobacz moją odpowiedź poniżej, aby uzyskać więcej informacji: stackoverflow.com/a/34321993/3160561
maxkonovalov
2
Potrzebujesz również powiadomienia o błędzie? AVPlayerItemFailedToPlayToEndTimeNotification
ToolmakerSteve
332

Możesz powiedzieć, że gra za pomocą:

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

Rozszerzenie Swift 3:

extension AVPlayer {
    var isPlaying: Bool {
        return rate != 0 && error == nil
    }
}
maz
źródło
29
Niekoniecznie, nie obsługuje sytuacji, w których odtwarzacz zatrzymuje się z powodu błędu w pliku. Coś, co odkryłem
Dermot
7
Jak powiedział Dermot, jeśli spróbujesz zagrać w coś w trybie samolotowym, współczynnik AVPlayer jest nadal ustawiony na 1,0, ponieważ sugeruje zamiar gry.
phi
4
AVPlayer ma właściwość błędu, po prostu sprawdź, czy nie jest zerowa, a także sprawdź, czy kurs nie jest 0 :)
James Campbell
23
Zwróć uwagę, że odpowiedź została zaktualizowana, aby zapobiec scenariuszowi @Dermot. Więc ten faktycznie działa.
jomafer
2
Nie będzie działać podczas odtwarzania materiału wideo do tyłu ( - [AVPlayer setRate:-1.0]), ponieważ -1.0jest mniejsze niż 0.
Julian F. Weinert.
49

W iOS10 dostępna jest teraz wbudowana właściwość: timeControlStatus

Na przykład ta funkcja odtwarza lub wstrzymuje avPlayer na podstawie jego statusu i odpowiednio aktualizuje przycisk odtwarzania / pauzy.

@IBAction func btnPlayPauseTap(_ 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)
    }
}

Jeśli chodzi o drugie pytanie, aby wiedzieć, czy avPlayer dobiegł końca, najłatwiej byłoby skonfigurować powiadomienie.

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

Na przykład, gdy dojdzie do końca, możesz przewinąć go do początku wideo i zresetować przycisk Wstrzymaj do odtwarzania.

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

Te przykłady są przydatne, jeśli tworzysz własne kontrolki, ale jeśli używasz AVPlayerViewController, kontrolki są wbudowane.

Travis M.
źródło
Łatwy w użyciu na iOS 10+
technerd
2
@objc func didPlayToEnd () for Swift 4.2
Sean Stayns
21

rateNIE jest sposobem na sprawdzenie, czy wideo jest odtwarzane (może się zawiesić). Z dokumentacji rate:

Wskazuje żądaną szybkość odtwarzania; 0,0 oznacza „wstrzymano”, 1,0 oznacza chęć grania w naturalnym tempie bieżącej pozycji.

Słowa kluczowe „chęć zabawy” - stawka 1.0nie oznacza, że ​​wideo jest odtwarzane.

Rozwiązaniem od iOS 10.0 jest użycie, AVPlayerTimeControlStatusktóre można obserwować na AVPlayer timeControlStatuswłaściwości.

Rozwiązaniem wcześniejszym niż iOS 10.0 (9.0, 8.0 itd.) Jest wprowadzenie własnego rozwiązania. Stawka 0.0oznacza, że ​​wideo jest wstrzymane. Gdy rate != 0.0oznacza to, że wideo jest odtwarzane lub utknęło.

Możesz poznać różnicę, obserwując czas gracza poprzez: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

Blok zwraca aktualny czas gracza CMTime, więc porównanie lastTime(czasu, który został ostatnio odebrany z bloku) i currentTime(czasu, w którym właśnie został zgłoszony blok) pokaże, czy gracz gra, czy jest zatrzymany. Na przykład, jeśli lastTime == currentTimei rate != 0.0, to gracz utknął w martwym punkcie.

Jak zauważyli inni, ustalenie, czy odtwarzanie zostało zakończone, jest oznaczone ikoną AVPlayerItemDidPlayToEndTimeNotification.

kgaidis
źródło
18

Bardziej niezawodną alternatywą NSNotificationjest dodanie siebie jako obserwatora do ratewłasności gracza .

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

Następnie sprawdź, czy nowa wartość obserwowanej szybkości wynosi zero, co oznacza, że ​​odtwarzanie zostało zatrzymane z jakiegoś powodu, na przykład do końca lub zatrzymania z powodu pustego bufora.

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

Na rate == 0.0przykład, aby wiedzieć, co dokładnie spowodowało zatrzymanie odtwarzania, możesz wykonać następujące czynności:

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
}

I nie zapomnij zatrzymać odtwarzacza, gdy dotrze do końca:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;

maxkonovalov
źródło
Myślę, że potrzebuję również powiadomienia o nieosiągnięciu końca - AVPlayerItemFailedToPlayToEndTimeNotification. Jeśli wystąpi ten błąd, nigdy nie dotrze do czasu zakończenia. Lub czytając odpowiedź maz, dodaj do swojego kodu czek player.error != nil, w którym to przypadku odtwarzanie „zakończyło się” z powodu błędu.
ToolmakerSteve
Przy okazji, co według Ciebie jest „niewiarygodne” w używaniu NSNotification AVPlayerItemDidPlayToEndTimeNotification?
ToolmakerSteve
ToolmakerSteve, dziękuję za uwagę, dodał sprawdzanie błędów do mojej odpowiedzi. W przypadku niewiarygodnych powiadomień - sam natknąłem się na ten problem, wdrażając powtarzające się i wielokrotnego użytku widoki odtwarzacza w widoku kolekcji, a KVO wyjaśniło sprawę, ponieważ pozwoliło to zachować bardziej lokalnie bez interferencji powiadomień różnych graczy.
maxkonovalov
17

Dla Swift :

AVPlayer :

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

Aktualizacja :
player.rate > 0stan zmieniony na, player.rate != 0ponieważ jeśli wideo jest odtwarzane w odwrotnej kolejności, może to być negatywne dzięki Julianowi za wskazanie.
Uwaga : To może wyglądać tak samo, jak powyższa odpowiedź (Maz), ale w języku Swift '! Player.error' dawał mi błąd kompilatora, więc musisz sprawdzić błąd za pomocą 'player.error == nil' w Swift. (Ponieważ właściwość błędu nie jest typu „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
   }
}
Aks
źródło
2
AVPlayer! =
AVAudioPlayer
2
Ostrzeżenia: wszystkie wymienione powyżej w odpowiedzi, która pojawiła się dwa lata wcześniej.
Dan Rosenstark
1
@Yar Wiem również, że głosowałem powyżej odpowiedzi, ale to jest dla szybkiego i w Swift '! Player.error' nie działało dla mnie, jest dozwolone tylko dla typów bool, więc dodałem odpowiedź oops, przez pomyłkę zagłosowałem za Twój komentarz, aby udzielić odpowiedzi za pomocą ta aplikacja do przepełnienia stosu :)
Aks
Martwię się, że nie wiem, jak dobrze działa player.error, który nie jest zerowy.
Dan Rosenstark
1
Nie będzie działać podczas odtwarzania materiału wideo do tyłu ( player.rate = -1.0), ponieważ -1.0 jest mniejsze niż 0.
Julian F. Weinert
9

Szybkie rozszerzenie na podstawie odpowiedzi maz

extension AVPlayer {

    var isPlaying: Bool {
        return ((rate != 0) && (error == nil))
    }
}
Mark Bridges
źródło
6

Szybka wersja odpowiedzi maxkonovalova jest następująca:

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

i

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")
            }
        }
    }
}

Dziękuję maxkonovalov!

Panie Stanev
źródło
Witam panie Stanev, dziękuję za odpowiedź. To nie działa dla mnie (aka nie drukuje niczego w konsoli?)
Cesare
Nieważne, to działa - mój odtwarzacz był nil! Ale muszę wiedzieć, kiedy widok się zaczyna (a nie kończy). Jakiś sposób to zrobić?
Cesare
3

Obecnie w Swift 5 najłatwiejszym sposobem sprawdzenia, czy odtwarzacz jest odtwarzany lub wstrzymany, jest sprawdzenie zmiennej .timeControlStatus .

player.timeControlStatus == .paused
player.timeControlStatus == .playing
azemi
źródło
1

Odpowiedź brzmi: Cel C.

if (player.timeControlStatus == AVPlayerTimeControlStatusPlaying) {
    //player is playing
}
else if (player.timeControlStatus == AVPlayerTimeControlStatusPaused) {
    //player is pause
}
else if (player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
    //player is waiting to play
}
iOS Lifee
źródło
0
player.timeControlStatus == AVPlayer.TimeControlStatus.playing
Noda Hikaru
źródło
1
Ta udzielona odpowiedź może być prawidłowa, ale może skorzystać z wyjaśnienia . Odpowiedzi zawierające tylko kod nie są uważane za „dobre” odpowiedzi. Oto kilka wskazówek, jak napisać dobrą odpowiedź? . Z recenzji .
MyNameIsCaleb