Respostas:
Para obter uma notificação por chegar ao final de um item (via Apple ):
[[NSNotificationCenter defaultCenter]
addObserver:<self>
selector:@selector(<#The selector name#>)
name:AVPlayerItemDidPlayToEndTimeNotification
object:<#A player item#>];
E para acompanhar a reprodução, você pode:
"rastrear mudanças na posição do indicador de reprodução em um objeto AVPlayer" usando addPeriodicTimeObserverForInterval: queue: usingBlock: ou addBoundaryTimeObserverForTimes: queue: usingBlock: .
O exemplo é da 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:
, e não manuseá-lo corretamente. Uma maneira mais confiável para mim é rastrear AVPlayer
o status de usando o KVO. Veja minha resposta abaixo para mais detalhes: stackoverflow.com/a/34321993/3160561
AVPlayerItemFailedToPlayToEndTimeNotification
Você pode saber que está jogando usando:
AVPlayer *player = ...
if ((player.rate != 0) && (player.error == nil)) {
// player is playing
}
Extensão Swift 3:
extension AVPlayer {
var isPlaying: Bool {
return rate != 0 && error == nil
}
}
- [AVPlayer setRate:-1.0]
), porque -1.0
é inferior a 0.
No iOS10, há uma propriedade integrada para isso agora: timeControlStatus
Por exemplo, esta função reproduz ou pausa o avPlayer com base em seu status e atualiza o botão play / pause apropriadamente.
@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)
}
}
Quanto à sua segunda pergunta, para saber se o avPlayer chegou ao fim, a coisa mais fácil de fazer seria configurar uma notificação.
NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
Quando chegar ao final, por exemplo, você pode retroceder até o início do vídeo e redefinir o botão Pausa para Reproduzir.
@objc func didPlayToEnd() {
aPlayer.seek(to: CMTimeMakeWithSeconds(0, 1))
btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
}
Esses exemplos são úteis se você estiver criando seus próprios controles, mas se usar um AVPlayerViewController, os controles vêm integrados.
rate
NÃO é a maneira de verificar se um vídeo está sendo reproduzido (ele pode travar). Da documentação de rate
:
Indica a taxa desejada de reprodução; 0,0 significa "pausado", 1,0 indica um desejo de jogar na taxa natural do item atual.
Palavras-chave "desejo de reproduzir" - uma taxa de 1.0
não significa que o vídeo está sendo reproduzido.
A solução desde o iOS 10.0 é usar o AVPlayerTimeControlStatus
que pode ser observado na AVPlayer
timeControlStatus
propriedade.
A solução anterior ao iOS 10.0 (9.0, 8.0 etc.) é lançar sua própria solução. Uma taxa de 0.0
significa que o vídeo está pausado. Quando rate != 0.0
isso significa que o vídeo está sendo reproduzido ou está parado.
Você pode descobrir a diferença observando o tempo do jogador via: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any
O bloco retorna a hora atual do jogador CMTime
, então uma comparação de lastTime
(a hora que foi recebida pela última vez do bloco) e currentTime
(a hora que o bloco acabou de reportar) dirá se o jogador está jogando ou está parado. Por exemplo, se lastTime == currentTime
erate != 0.0
, então o jogador estagnou.
Conforme observado por outros, descobrir se a reprodução terminou é indicado por AVPlayerItemDidPlayToEndTimeNotification
.
Uma alternativa mais confiável NSNotification
é adicionar-se como observador à rate
propriedade do jogador .
[self.player addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionNew
context:NULL];
Em seguida, verifique se o novo valor para a taxa observada é zero, o que significa que a reprodução foi interrompida por algum motivo, como chegar ao fim ou travar devido ao buffer vazio.
- (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
exemplo, para saber o que exatamente causou a interrupção da reprodução, você pode fazer as seguintes verificações:
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
}
E não se esqueça de fazer o seu jogador parar quando chegar ao fim:
self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;
AVPlayerItemFailedToPlayToEndTimeNotification
. Se esse erro acontecer, nunca chegará ao horário de término. Ou lendo a resposta de maz, adicione ao seu código uma verificação para player.error != nil
, caso em que a reprodução foi "encerrada" devido a um erro.
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")
}
Atualização :
player.rate > 0
condição alterada para player.rate != 0
porque se o vídeo estiver sendo reproduzido ao contrário, pode ser negativo graças a Julian por apontar.
Nota : Isso pode ser parecido com a resposta acima (Maz), mas no Swift '! Player.error' estava me dando um erro do compilador, então você deve verificar se há erros usando 'player.error == nil' no Swift. (Devido à propriedade de erro não é do 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 é menor que 0.
Extensão Swift baseada na resposta de maz
extension AVPlayer {
var isPlaying: Bool {
return ((rate != 0) && (error == nil))
}
}
A versão Swift da resposta de maxkonovalov é esta:
player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.New, context: nil)
e
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")
}
}
}
}
Obrigado maxkonovalov!
nil
! Mas preciso saber quando a visualização começa (não termina). Alguma maneira de fazer isso?
A resposta é o 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