Respuestas:
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];
}];
-replaceCurrentItemWithPlayerItem:
, y no lo maneja correctamente. Una forma más confiable para mí es rastrear AVPlayer
el estado usando KVO. Vea mi respuesta a continuación para obtener más detalles: stackoverflow.com/a/34321993/3160561
AVPlayerItemFailedToPlayToEndTimeNotification
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
}
}
- [AVPlayer setRate:-1.0]
), porque -1.0
es menor que 0.
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.
rate
NO 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.0
no significa que el video se esté reproduciendo.
La solución desde iOS 10.0 es usar lo AVPlayerTimeControlStatus
que se puede observar en la AVPlayer
timeControlStatus
propiedad.
La solución anterior a iOS 10.0 (9.0, 8.0, etc.) es desarrollar su propia solución. Una tasa de 0.0
significa que el video está en pausa. Cuando rate != 0.0
significa 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 == currentTime
y 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
.
Una alternativa más confiable NSNotification
es agregarse como observador a la rate
propiedad 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.0
ejemplo, 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;
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.
AVPlayerItemDidPlayToEndTimeNotification
?
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 > 0
condición cambió a player.rate != 0
porque 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
}
}
player.rate = -1.0
), porque -1.0 es menor que 0.
Extensión rápida 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<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!
nil
! Pero necesito saber cuándo comienza la vista (no termina). ¿Alguna forma de hacer esto?
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
}
player.timeControlStatus == AVPlayer.TimeControlStatus.playing