Sem AVPlayer Delegado? Como controlar quando a música termina de tocar? Desenvolvimento Objective C iPhone

84

Olhei em volta, mas não consigo encontrar um protocolo de delegado para o AVPlayer class. O que da?

Estou usando sua subclasse,, AVQueuePlayerpara reproduzir um array de AVPlayerItems, cada um carregado de uma URL. Existe alguma maneira de chamar um método quando uma música termina de tocar? Principalmente no final da fila?

E se isso não for possível, há alguma maneira de chamar um método quando a música COMEÇA a tocar, após o armazenamento em buffer? Estou tentando colocar um ícone de carregamento lá, mas ele desliga o ícone antes de a música realmente começar, mesmo que seja depois da [audioPlayer play]ação.

Tyler
fonte

Respostas:

152

Sim, a classe AVPlayer não tem um protocolo delegado como o AVAudioPlayer. Você precisa se inscrever para receber notificações em um AVPlayerItem. Você pode criar um AVPlayerItem usando a mesma URL que você passaria para o -initWithURL:AVPlayer.

-(void)startPlaybackForItemWithURL:(NSURL*)url {

    // First create an AVPlayerItem
    AVPlayerItem* playerItem = [AVPlayerItem playerItemWithURL:url];

    // Subscribe to the AVPlayerItem's DidPlayToEndTime notification.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];

    // Pass the AVPlayerItem to a new player
    AVPlayer* player = [[[AVPlayer alloc] initWithPlayerItem:playerItem] autorelease];

    // Begin playback
    [player play]

} 

-(void)itemDidFinishPlaying:(NSNotification *) notification {
    // Will be called when AVPlayer finishes playing playerItem
}
arexx
fonte
1
Certifique-se de usar itemDidFinishPlaying:, por exemplo, com dois pontos. Da NSNotificationCenterdocumentação: o método especificado por notificationSelector deve ter um e apenas um argumento (uma instância de NSNotification).
Rudolf Adamkovič
@RudolfAdamkovic Obrigado pela nota - editei minha resposta de acordo.
arexx
8

Sim. Adicione um observador KVO ao status ou taxa do jogador:

- (IBAction)go {
   self.player = .....
   self.player.actionAtItemEnd = AVPlayerActionStop;
   [self.player addObserver:self forKeyPath:@"rate" options:0 context:0]; 
}

- (void)stopped {
    ...
    [self.player removeObserver:self]; //assumes we are the only observer
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context == 0) {
        if(player.rate==0.0) //stopped
            [self stopped];
    }
    else
        [super observeVal...];
}

Então, basicamente, é isso.

Isenção de responsabilidade: eu escrevi isso aqui, então não verifiquei se o código estava bom. TAMBÉM eu nunca usei o AVPlayer antes, mas deve estar certo.

Daij-Djan
fonte
Como remover o observador @ "taxa"?
Saumil Shah
Foi-me dito em uma edição que preciso observar a taxa com certeza. adaptado
Daij-Djan
4
Um problema com essa abordagem é que ela pensará que uma pausa é o fim da música. A taxa na pausa é zero, pois seria no final.
C6Silver
5

Swift 3 - adiciono um observador a AVPlayerItemcada vez que adiciono um vídeo ao player:

func playVideo(url: URL) {
  let playerItem = AVPlayerItem(asset: AVURLAsset(url: someVideoUrl))
  NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidPlayToEndTime), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)
  self.player.replaceCurrentItem(with: playerItem)
  self.player.play()
}

func playerItemDidPlayToEndTime() {
  // load next video or something
}
budiDino
fonte
1

Há muitas informações nos documentos da Apple AVFoundation Programming Guide (procure a seção de monitoramento de reprodução). Parece ser principalmente por meio do KVO, então você pode querer relembrar se não estiver muito familiarizado (há um guia para isso no Guia de Programação de Observação de Valores-Chave .

Paul.s
fonte
4
Obrigado, primeiro comecei a trabalhar monitorando o tempo por meio do KVO, mas depois implementei o AVPlayerItemDidPlayToEndTimeNotification, pois é mais limpo e menos intensivo no processador.
Tyler
1
Uma mistura meio maluca de NSNotificationCenter, KVO e com base em blocos. Decida-se pela Apple!
CupawnTae
1

Estou usando este e funciona:

_player = [[AVPlayer alloc]initWithURL:[NSURL URLWithString:_playingAudio.url]];
CMTime endTime = CMTimeMakeWithSeconds(_playingAudio.duration, 1);
timeObserver = [_player addBoundaryTimeObserverForTimes:[NSArray arrayWithObject:[NSValue valueWithCMTime:endTime]] queue:NULL usingBlock:^(void) {
    [_player removeTimeObserver:timeObserver];
    timeObserver = nil;
    //TODO play next sound
}];
[self play];

onde _playingAudioestá minha classe personalizada com algumas propriedades e timeObserveré idivar.

Timur Mustafaev
fonte