Compruebe el estado de reproducción de AVPlayer


Respuestas:


57

Para recibir una notificación por llegar al final de un artículo (a través de Apple ):

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

Y para rastrear la reproducción puedes:

"seguir los cambios en la posición de la cabeza lectora en un objeto AVPlayer" mediante el uso de addPeriodicTimeObserverForInterval: cola: usingBlock: o addBoundaryTimeObserverForTimes: cola: usingBlock: .

El ejemplo es de 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];
}];

Podría lanzar una bandera booleana para verificar el estado de la reproducción.
— moonman239

Las notificaciones pueden causar problemas si cambia el elemento del jugador, por ejemplo, con -replaceCurrentItemWithPlayerItem:, y no lo maneja correctamente. Una forma más confiable para mí es rastrear AVPlayerel estado usando KVO. Vea mi respuesta a continuación para obtener más detalles: stackoverflow.com/a/34321993/3160561
— maxkonovalov

2
¿También necesita notificación de error? AVPlayerItemFailedToPlayToEndTimeNotification
— ToolmakerSteve

332

Puedes saber que está jugando usando:

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

Extensión Swift 3:

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

29
No necesariamente, no maneja situaciones en las que el jugador se detiene debido a un error en el archivo. Algo que descubrí por las malas ...
— Dermot

7
Como dijo Dermot, si intentas jugar algo mientras estás en modo avión, la tasa de AVPlayer aún se establece en 1.0, ya que implica la intención de jugar.
— phi

4
El AVPlayer tiene una propiedad de error, solo verifique que no sea nulo y que la tasa no sea 0 :)
— James Campbell

23
Tenga en cuenta que la respuesta se ha actualizado para evitar el escenario @Dermot. Entonces este realmente funciona.
— jomafer

2
No funcionará cuando se reproduzcan secuencias de video al revés ( - [AVPlayer setRate:-1.0]), porque -1.0es menor que 0.
— Julian F. Weinert

49

En iOS10, hay una propiedad incorporada para esto ahora: timeControlStatus

Por ejemplo, esta función reproduce o pausa el avPlayer según su estado y actualiza el botón de reproducción / pausa de manera apropiada.

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

En cuanto a su segunda pregunta, para saber si avPlayer llegó al final, lo más fácil sería configurar una notificación.

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

Cuando llega al final, por ejemplo, puede rebobinar hasta el principio del video y restablecer el botón Pausa a Reproducir.

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

Estos ejemplos son útiles si está creando sus propios controles, pero si usa un AVPlayerViewController, los controles vienen integrados.


Fácil de usar para iOS 10+
— technerd

2
@objc func didPlayToEnd () para Swift 4.2
— Sean Stayns

21

rateNO es la forma de comprobar si se está reproduciendo un vídeo (podría bloquearse). De documentación de rate:

Indica la velocidad de reproducción deseada; 0.0 significa "en pausa", 1.0 indica el deseo de jugar al ritmo natural del elemento actual.

Palabras clave "deseo de reproducir": una tasa de 1.0no significa que el video se esté reproduciendo.

La solución desde iOS 10.0 es usar lo AVPlayerTimeControlStatusque se puede observar en la AVPlayer timeControlStatuspropiedad.

La solución anterior a iOS 10.0 (9.0, 8.0, etc.) es desarrollar su propia solución. Una tasa de 0.0significa que el video está en pausa. Cuando rate != 0.0significa que el video se está reproduciendo o está bloqueado.

Puede averiguar la diferencia observando el tiempo del jugador a través de: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

El bloque devuelve el tiempo del jugador actual CMTime, por lo que una comparación de lastTime(el tiempo que se recibió por última vez del bloque) y currentTime(el tiempo que el bloque acaba de informar) dirá si el jugador está jugando o está estancado. Por ejemplo, si lastTime == currentTimey rate != 0.0, entonces el jugador se ha estancado.

Como han señalado otros, averiguar si la reproducción ha finalizado se indica mediante AVPlayerItemDidPlayToEndTimeNotification.


18

Una alternativa más confiable NSNotificationes agregarse como observador a la ratepropiedad del jugador .

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

Luego, verifique si el nuevo valor para la tasa observada es cero, lo que significa que la reproducción se ha detenido por algún motivo, como llegar al final o estancarse debido a que el búfer está vacío.

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

Por rate == 0.0ejemplo, para saber qué causó exactamente la detención de la reproducción, puede realizar las siguientes comprobaciones:

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
}

Y no olvide hacer que su reproductor se detenga cuando llegue al final:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;


Creo que también necesito notificación de no llegar al final - AVPlayerItemFailedToPlayToEndTimeNotification. Si ocurre este error, nunca llegará a la hora de finalización. O leyendo la respuesta de maz, agregue a su código una marca de verificación player.error != nil, en cuyo caso la reproducción ha "terminado" debido a un error.
— ToolmakerSteve

Por cierto, ¿qué le pareció "no confiable" sobre el uso de NSNotification AVPlayerItemDidPlayToEndTimeNotification?
— ToolmakerSteve

ToolmakerSteve, gracias por su nota, agregó la verificación de errores a mi respuesta. Para notificaciones no confiables: me encontré con el problema mientras implementaba vistas de reproductor repetidas y reutilizables en la vista de colección, y KVO dejó las cosas más claras, ya que permitió mantenerlo todo más local sin que las notificaciones de los diferentes jugadores interfirieran entre sí.
— maxkonovalov

17

Para Swift :

AVPlayer :

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

Actualización : la
player.rate > 0condición cambió a player.rate != 0porque si el video se reproduce al revés puede ser negativo gracias a Julian por señalarlo.
Nota : Esto puede verse igual que la respuesta anterior (Maz) pero en Swift '! Player.error' me estaba dando un error de compilador, por lo que debe verificar el error usando 'player.error == nil' en Swift. (Porque la propiedad de error no es de tipo '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
Advertencias: todo lo mencionado anteriormente en la respuesta que apareció dos años antes de esta.
— Dan Rosenstark

1
@Yar, sé que también voté a favor de la respuesta anterior, pero esto es para Swift y en Swift '! Player.error' no funcionaba para mí, solo está permitido para tipos bool, así que agregué la respuesta, voté a favor de tu comentario por error para responder usando esta aplicación de desbordamiento de pila :)
— Aks

Mi preocupación es que no sé qué tan bien funciona realmente el error player.error que no es nil.
— Dan Rosenstark

1
No funcionará cuando se reproduzcan secuencias de video al revés ( player.rate = -1.0), porque -1.0 es menor que 0.
— Julian F. Weinert

9

Extensión rápida basada en la respuesta de maz

extension AVPlayer {

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

6

La versión Swift de la respuesta de maxkonovalov es esta:

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

y

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

¡Gracias maxkonovalov!


Hola Sr. Stanev, gracias por su respuesta. Esto no me funciona (¿no imprime nada en la consola?)
— Cesare

No importa, funciona, ¡mi jugador lo era nil! Pero necesito saber cuándo comienza la vista (no termina). ¿Alguna forma de hacer esto?
— Cesare

3

Actualmente, con swift 5, la forma más fácil de comprobar si el reproductor está reproduciendo o en pausa es comprobar la variable .timeControlStatus .

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

1

La respuesta es el objetivo 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
}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.