Risposte:
Per ricevere una notifica per aver raggiunto la fine di un elemento (tramite Apple ):
[[NSNotificationCenter defaultCenter]
addObserver:<self>
selector:@selector(<#The selector name#>)
name:AVPlayerItemDidPlayToEndTimeNotification
object:<#A player item#>];
E per tenere traccia della riproduzione puoi:
"tenere traccia delle modifiche nella posizione dell'indicatore di riproduzione in un oggetto AVPlayer" utilizzando addPeriodicTimeObserverForInterval: coda: usingBlock: o addBoundaryTimeObserverForTimes: coda: usingBlock: .
L'esempio è di 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 non lo gestisci correttamente. Un modo più affidabile per me è monitorare lo AVPlayer
stato di utilizzando KVO. Vedere la mia risposta qui sotto per maggiori dettagli: stackoverflow.com/a/34321993/3160561
AVPlayerItemFailedToPlayToEndTimeNotification
Puoi dire che sta giocando usando:
AVPlayer *player = ...
if ((player.rate != 0) && (player.error == nil)) {
// player is playing
}
Estensione Swift 3:
extension AVPlayer {
var isPlaying: Bool {
return rate != 0 && error == nil
}
}
- [AVPlayer setRate:-1.0]
), perché -1.0
è inferiore a 0.
In iOS10, c'è una proprietà incorporata per questo ora: timeControlStatus
Ad esempio, questa funzione riproduce o mette in pausa avPlayer in base al suo stato e aggiorna il pulsante di riproduzione / pausa in modo appropriato.
@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)
}
}
Per quanto riguarda la tua seconda domanda, per sapere se avPlayer è arrivato alla fine, la cosa più semplice da fare sarebbe impostare una notifica.
NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
Quando arriva alla fine, ad esempio, puoi farlo riavvolgere all'inizio del video e reimpostare il pulsante Pausa su Riproduci.
@objc func didPlayToEnd() {
aPlayer.seek(to: CMTimeMakeWithSeconds(0, 1))
btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
}
Questi esempi sono utili se stai creando i tuoi controlli, ma se usi un AVPlayerViewController, i controlli sono integrati.
rate
NON è il modo per verificare se un video è in riproduzione (potrebbe bloccarsi). Dalla documentazione di rate
:
Indica la velocità di riproduzione desiderata; 0.0 significa "in pausa", 1.0 indica il desiderio di giocare alla velocità naturale dell'elemento corrente.
Parole chiave "desiderio di giocare": un tasso di 1.0
non significa che il video è in riproduzione.
La soluzione da iOS 10.0 consiste nell'utilizzare ciò AVPlayerTimeControlStatus
che può essere osservato sulla AVPlayer
timeControlStatus
proprietà.
La soluzione precedente a iOS 10.0 (9.0, 8.0 ecc.) Consiste nel lanciare la propria soluzione. Una frequenza 0.0
indica che il video è in pausa. Quando rate != 0.0
significa che il video è in riproduzione o è bloccato.
Puoi scoprire la differenza osservando il tempo del giocatore tramite: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any
Il blocco restituisce il tempo del giocatore corrente in CMTime
, quindi un confronto tra lastTime
(il tempo che è stato ricevuto l'ultima volta dal blocco) e currentTime
(il tempo che il blocco appena segnalato) dirà se il giocatore sta giocando o è in stallo. Ad esempio, se lastTime == currentTime
e rate != 0.0
, il giocatore si è bloccato.
Come notato da altri, capire se la riproduzione è terminata è indicato da AVPlayerItemDidPlayToEndTimeNotification
.
Un'alternativa più affidabile NSNotification
è aggiungerti come osservatore alla rate
proprietà del giocatore .
[self.player addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionNew
context:NULL];
Quindi controlla se il nuovo valore per la velocità osservata è zero, il che significa che la riproduzione si è interrotta per qualche motivo, come raggiungere la fine o lo stallo a causa del buffer vuoto.
- (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
}
}
}
Ad esempio rate == 0.0
, per sapere cosa ha causato esattamente l'interruzione della riproduzione, è possibile eseguire i seguenti controlli:
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 non dimenticare di far fermare il tuo lettore quando raggiunge la fine:
self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;
AVPlayerItemFailedToPlayToEndTimeNotification
. Se si verifica questo errore, non raggiungerà mai l'ora di fine. Oppure, leggendo la risposta di maz, aggiungi al tuo codice un segno di spunta player.error != nil
, nel qual caso la riproduzione è "terminata" a causa di un errore.
AVPlayerItemDidPlayToEndTimeNotification
?
Per Swift :
AVPlayer :
let player = AVPlayer(URL: NSURL(string: "http://www.sample.com/movie.mov"))
if (player.rate != 0 && player.error == nil) {
println("playing")
}
Aggiornamento :
player.rate > 0
condizione cambiata in player.rate != 0
perché se il video viene riprodotto al contrario può essere negativo grazie a Julian per averlo segnalato.
Nota : potrebbe sembrare uguale alla risposta precedente (Maz) ma in Swift "! Player.error" mi dava un errore del compilatore, quindi devi verificare la presenza di errori utilizzando "player.error == nil" in Swift. (Perché la proprietà error non è di 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
), perché -1.0 è inferiore a 0.
Estensione rapida basata sulla risposta di maz
extension AVPlayer {
var isPlaying: Bool {
return ((rate != 0) && (error == nil))
}
}
La versione Swift della risposta di maxkonovalov è questa:
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")
}
}
}
}
Grazie maxkonovalov!
nil
! Ma ho bisogno di sapere quando inizia la visualizzazione (non finisce). Come posso farlo?
La risposta è l'obiettivo 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