Comprobar el estado de reproducción de AVPlayer

¿Hay alguna manera de saber si la reproducción de AVPlayer ha estancado o ha llegado al final?

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

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

Y para seguir jugando puedes:

“rastrear cambios en la posición de la cabeza lectora en un objeto AVPlayer” utilizando addPeriodicTimeObserverForInterval: queue: usingBlock: o addBoundaryTimeObserverForTimes: queue: 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]; }]; 

Puedes ver que está jugando usando:

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

Swift 3 extensión:

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

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

Por ejemplo, esta función reproduce o hace una pausa en avPlayer en función de su estado y actualiza el botón Reproducir / Pausa de forma adecuada.

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

En cuanto a su segunda pregunta, para saber si el jugador 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 hacer que se rebobine hasta el comienzo del video y restablecer el botón Pausa para Reproducir.

 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.

Una alternativa más confiable a NSNotification es agregarse como observador a la propiedad de la rate 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 alguna razón, como llegar al final o atascarse debido a un buffer vacío.

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)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 } } } 

Para la rate == 0.0 caso, para saber qué causó exactamente la detención de la reproducción, puede hacer 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 olvides parar tu reproductor cuando llegue al final:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;

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 :
player.rate > 0 condición player.rate > 0 cambió a player.rate != 0 porque si el video se reproduce en reversa, puede ser negativo gracias a Julian por señalar.
Nota : Esto podría parecerse a la respuesta de arriba (Maz) pero en Swift ‘! Player.error’ me estaba dando un error de comstackción, por lo que debe verificar el error usando ‘player.error == nil’ en Swift. (Porque la propiedad de error no es del 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 } } 

rate NO es la forma de verificar si un video se está reproduciendo (podría estar bloqueado). De la documentación de la rate :

Indica la tasa de reproducción deseada; 0.0 significa “en pausa”, 1.0 indica un deseo de jugar a la velocidad natural del elemento actual.

Palabras clave “deseo de jugar”: una tasa de 1.0 no significa que el video se está reproduciendo.

La solución desde iOS 10.0 es usar AVPlayerTimeControlStatus que se puede observar en la propiedad AVPlayer timeControlStatus .

La solución anterior a iOS 10.0 (9.0, 8.0 etc.) es rodar su propia solución. Una tasa de 0.0 significa que el video está en pausa. Cuando rate != 0.0 significa que el video está reproduciéndose o está atascado.

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 la hora actual del jugador en CMTime , por lo que una comparación de lastTime (la hora que se recibió por última vez del bloque) y currentTime (el tiempo que el bloque acaba de informar) indicará si el reproductor está reproduciendo o está estancado. Por ejemplo, si lastTime == currentTime y rate != 0.0 , entonces el jugador se ha estancado.

Como señalaron otros, averiguar AVPlayerItemDidPlayToEndTimeNotification indica si la reproducción finalizó.

Extensión Swift basada en la respuesta de maz

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

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) { 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!

Intereting Posts