Überprüfen Sie den Wiedergabestatus von AVPlayer


Antworten:


57

So erhalten Sie eine Benachrichtigung zum Erreichen des Endes eines Artikels (über Apple ):

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

Und um das Spielen zu verfolgen, können Sie:

"Verfolgen Sie Änderungen an der Position des Abspielkopfs in einem AVPlayer-Objekt" mithilfe von addPeriodicTimeObserverForInterval: queue: usingBlock: oder addBoundaryTimeObserverForTimes: queue: usingBlock : .

Beispiel ist von 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];
}];

Sie können eine Boolesche Flagge einwerfen, um den Spielstatus zu überprüfen.
Moonman239

Benachrichtigungen können Probleme verursachen, wenn Sie den Gegenstand des Spielers ändern, z. B. mit -replaceCurrentItemWithPlayerItem:, und ihn nicht richtig handhaben. Eine zuverlässigere Möglichkeit für mich ist es AVPlayer, den Status mithilfe von KVO zu verfolgen . Siehe meine Antwort unten für weitere Details: stackoverflow.com/a/34321993/3160561
maxkonovalov

2
Benötigen Sie auch eine Fehlermeldung? AVPlayerItemFailedToPlayToEndTimeNotification
ToolmakerSteve

332

Sie können sagen, dass es spielt mit:

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

Swift 3-Erweiterung:

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

29
Nicht unbedingt, es werden keine Situationen behandelt, in denen der Player aufgrund eines Fehlers in der Datei blockiert. Etwas, das ich auf die harte Tour herausgefunden habe ...
Dermot

7
Wie Dermot sagte, wenn Sie versuchen, etwas im Flugzeugmodus zu spielen, ist die AVPlayer-Rate immer noch auf 1,0 eingestellt, da dies die Absicht impliziert, zu spielen.
Phi

4
Der AVPlayer hat eine Fehlereigenschaft, überprüfen Sie einfach, ob es nicht Null ist und überprüfen Sie, ob die Rate nicht 0 ist :)
James Campbell

23
Beachten Sie, dass die Antwort aktualisiert wurde, um das @ Dermot-Szenario zu verhindern. Also funktioniert dieser tatsächlich.
Jomafer

2
Funktioniert nicht, wenn Videomaterial umgekehrt abgespielt wird ( - [AVPlayer setRate:-1.0]), da -1.0es kleiner als 0 ist.
Julian F. Weinert

48

In iOS10 gibt es dafür jetzt eine integrierte Eigenschaft: timeControlStatus

Diese Funktion spielt beispielsweise den avPlayer basierend auf seinem Status ab oder hält ihn an und aktualisiert die Wiedergabe- / Pause-Taste entsprechend.

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

Was Ihre zweite Frage betrifft, um zu wissen, ob der avPlayer das Ende erreicht hat, ist es am einfachsten, eine Benachrichtigung einzurichten.

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

Wenn es zum Beispiel zu Ende ist, können Sie es zum Anfang des Videos zurückspulen lassen und die Pause-Taste auf Wiedergabe zurücksetzen.

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

Diese Beispiele sind nützlich, wenn Sie Ihre eigenen Steuerelemente erstellen. Wenn Sie jedoch einen AVPlayerViewController verwenden, sind die Steuerelemente integriert.


Einfach zu bedienen für iOS 10+
Technerd

2
@objc func didPlayToEnd () für Swift 4.2
Sean Stayns

21

rateAuf diese Weise können Sie NICHT überprüfen, ob ein Video abgespielt wird (es könnte ins Stocken geraten). Aus der Dokumentation von rate:

Zeigt die gewünschte Wiedergabegeschwindigkeit an; 0.0 bedeutet "pausiert", 1.0 zeigt den Wunsch an, mit der natürlichen Rate des aktuellen Gegenstands zu spielen.

Schlüsselwörter "Wunsch zu spielen" - eine Rate von 1.0bedeutet nicht, dass das Video abgespielt wird.

Die Lösung seit iOS 10.0 ist die Verwendung, AVPlayerTimeControlStatusdie auf dem AVPlayer timeControlStatusGrundstück beobachtet werden kann .

Die Lösung vor iOS 10.0 (9.0, 8.0 usw.) besteht darin, eine eigene Lösung zu erstellen. Eine Rate von 0.0bedeutet, dass das Video angehalten wird. Wenn rate != 0.0dies bedeutet, dass das Video entweder abgespielt wird oder blockiert ist.

Sie können den Unterschied herausfinden, indem Sie die Spielerzeit beobachten über: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

Der Block gibt die aktuelle Spielerzeit in zurück CMTime, sodass ein Vergleich von lastTime(der Zeit, die zuletzt vom Block empfangen wurde) und currentTime(der Zeit, die der Block gerade gemeldet hat) zeigt, ob der Spieler spielt oder blockiert ist. Wenn zum Beispiel lastTime == currentTimeund rate != 0.0, dann ist der Spieler ins Stocken geraten.

Wie von anderen angemerkt, wird durch angezeigt, ob die Wiedergabe beendet ist AVPlayerItemDidPlayToEndTimeNotification.


18

Eine zuverlässigere Alternative dazu NSNotificationbesteht darin, sich als Beobachter zum rateEigentum des Spielers hinzuzufügen .

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

Überprüfen Sie dann, ob der neue Wert für die beobachtete Rate Null ist. Dies bedeutet, dass die Wiedergabe aus irgendeinem Grund gestoppt wurde, z. B. zum Erreichen des Endes oder zum Stillstand aufgrund eines leeren Puffers.

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

Um rate == 0.0genau zu wissen, warum die Wiedergabe gestoppt wurde, können Sie die folgenden Überprüfungen durchführen:

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
}

Und vergessen Sie nicht, Ihren Spieler anzuhalten, wenn er das Ende erreicht:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;


Ich denke auch, muss benachrichtigt werden, wenn das Ende nicht erreicht wird AVPlayerItemFailedToPlayToEndTimeNotification. Wenn dieser Fehler auftritt, wird die Endzeit nie erreicht. Oder lesen Sie die Antwort von maz und fügen Sie Ihrem Code eine Prüfung hinzu player.error != nil. In diesem Fall wurde die Wiedergabe aufgrund eines Fehlers "beendet".
ToolmakerSteve

Übrigens, was fanden Sie "nicht zuverlässig" bei der Verwendung von NSNotification of AVPlayerItemDidPlayToEndTimeNotification?
ToolmakerSteve

ToolmakerSteve, danke für Ihren Hinweis, hat meiner Antwort eine Fehlerprüfung hinzugefügt. Für nicht zuverlässige Benachrichtigungen - Ich bin selbst auf das Problem gestoßen, als ich wiederholte und wiederverwendbare Spieleransichten in der Sammlungsansicht implementiert habe, und KVO hat die Dinge klarer gemacht, da alles lokaler gehalten werden konnte, ohne dass sich die Benachrichtigungen verschiedener Spieler gegenseitig störten.
Maxkonovalov

17

Für Swift :

AVPlayer :

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

Update :
player.rate > 0Bedingung geändert in, player.rate != 0da wenn Video in umgekehrter Reihenfolge abgespielt wird, es dank Julian für den Hinweis negativ sein kann.
Hinweis : Dies könnte wie die obige Antwort von (Maz) aussehen, aber in Swift gab '! Player.error' einen Compilerfehler aus, sodass Sie in Swift mit 'player.error == nil' nach Fehlern suchen müssen (wegen der Fehlereigenschaft) ist nicht vom Typ '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
   }
}

2
AVPlayer! = AVAudioPlayer
quemeful

2
Vorsichtsmaßnahmen: alle oben in der Antwort erwähnt, die zwei Jahre vor dieser auftauchte.
Dan Rosenstark

1
@Yar Ich weiß, dass die obige Antwort ebenfalls positiv bewertet wurde, aber dies ist für Swift und in Swift '! Player.error' hat bei mir nicht funktioniert. Es ist nur für Bool-Typen zulässig, daher habe ich die Antwort hinzugefügt diese Stapelüberlauf-App :)
Aks

Ich mache mir Sorgen, dass ich nicht weiß, wie gut der Spieler ist. Fehler, der nicht Null ist, funktioniert tatsächlich.
Dan Rosenstark

1
Funktioniert nicht, wenn Videomaterial umgekehrt abgespielt wird ( player.rate = -1.0), da -1,0 kleiner als 0 ist.
Julian F. Weinert

9

Schnelle Erweiterung basierend auf der Antwort von Maz

extension AVPlayer {

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

6

Die Swift-Version von maxkonovalovs Antwort lautet:

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

und

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

Vielen Dank, dass Sie maxkonovalov!


Hallo Herr Stanev, vielen Dank für Ihre Antwort. Dies funktioniert nicht für mich (auch bekannt als druckt nichts in der Konsole?)
Cesare

Egal, es funktioniert - mein Spieler war nil! Aber ich muss wissen, wann die Ansicht beginnt (nicht endet). Wie geht das?
Cesare

3

Derzeit ist mit Swift 5 der einfachste Weg, um zu überprüfen, ob der Player spielt oder angehalten ist, die Überprüfung der Variablen .timeControlStatus .

player.timeControlStatus == .paused
player.timeControlStatus == .playing

1

Antwort ist Ziel 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
}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.