ios avplayer trigger streaming está fuera de la memory intermedia

Quiero volver a conectar con el server cuando el búfer de transmisión está vacío.

¿Cómo puedo activar un método cuando el AVPlayer o AVPlayerItem está vacío?

Sé que hay methods playbackLikelyToKeepUp , playbackBufferEmpty y playbackBufferFull para verificar el estado del buffer, pero esos no son devoluciones de llamada.

¿Hay alguna function de callback o cualquier observador que debería agregar?

puede agregar un observador para esas keys:

 [playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil]; [playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; 

El primero te avisará cuando tu búfer esté vacío y el segundo cuando tu búfer esté listo para volver.

Entonces para manejar el cambio de key puede usar este código:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (!player) { return; } else if (object == playerItem && [keyPath isEqualToString:@"playbackBufferEmpty"]) { if (playerItem.playbackBufferEmpty) { //Your code here } } else if (object == playerItem && [keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { if (playerItem.playbackLikelyToKeepUp) { //Your code here } } } 

Tendrá que includese en Core Audio y CFReadStream para hacer esto. Con CFReadStream, puede proporcionar una callback que recibe la llamada en ciertos events de flujo como el final encontrado, error de lectura, etc. Desde allí puede desencadenar una reconnection al server. Si está consumiendo una secuencia HTTP, puede agregar el encabezado de range a la request HTTP para que pueda decirle al server que envíe la secuencia desde un punto que especifique (que sería el último byte que recibió antes de + 1).

Prueba este código y debe resolver todas tus pesadillas:

 #import <AVFoundation/AVFoundation.h> @interface CustomAVPlayerItem : AVPlayerItem { BOOL bufferEmptyVideoWasStopped; } -(void)manuallyRegisterEvents; @end =========== in the .m file #import "CustomAVPlayerItem.h" #import "AppDelegate.h" @implementation CustomAVPlayerItem -(void)manuallyRegisterEvents { //NSLog(@"manuallyRegisterEvents %lu", (unsigned long)self.hash); [self addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:NULL]; [self addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:NULL]; bufferEmptyVideoWasStopped=NO; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == self && [keyPath isEqualToString:@"playbackBufferEmpty"]) { if (self.playbackBufferEmpty) { //NSLog(@"AVPLAYER playbackBufferEmpty"); bufferEmptyVideoWasStopped=YES; [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_VIDEO_PLAYBACKBUFFER_EMPTY object:self]; } } else if (object == self && [keyPath isEqualToString:@"playbackLikelyToKeepUp"]) { if (self.playbackLikelyToKeepUp) { //NSLog(@"AVPLAYER playbackLikelyToKeepUp"); if(bufferEmptyVideoWasStopped) { bufferEmptyVideoWasStopped=NO; [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_VIDEO_PLAYBACKBUFFER_FULL object:self]; } else [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_VIDEO_PLAYBACKBUFFER_KEEP_UP object:self]; } } } -(void)dealloc { //NSLog(@"dealloc CustomAVPlayerItem %lu", (unsigned long)self.hash); [self removeObserver:self forKeyPath:@"playbackBufferEmpty"]; [self removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; } @end ===== and now where you want to use it -(void)startVideoWithSound { if([CustomLog jsonFieldAvailable:[videoObject objectForKey:@"video_url"]]) { CustomAVPlayerItem *playerItem=[[CustomAVPlayerItem alloc] initWithURL:[[OfflineManager new] getCachedURLForVideoURL:[videoObject objectForKey:@"video_url"]]]; AVPlayer *player=[AVPlayer playerWithPlayerItem:playerItem]; [playerItem manuallyRegisterEvents]; [player play]; player.muted=NO; [viewPlayer setPlayer:player]; viewPlayer.hidden=NO; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifBufferEmpty:) name:NOTIFICATION_VIDEO_PLAYBACKBUFFER_EMPTY object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifBufferFull:) name:NOTIFICATION_VIDEO_PLAYBACKBUFFER_FULL object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notifBufferKeepUp:) name:NOTIFICATION_VIDEO_PLAYBACKBUFFER_KEEP_UP object:nil]; } } - (void)notifBufferEmpty:(NSNotification *)notification { //NSLog(@"notifBufferEmpty"); [[viewPlayer player] play]; // resume it viewLoading.hidden=NO; } - (void)notifBufferFull:(NSNotification *)notification { //NSLog(@"notifBufferFull"); viewLoading.hidden=YES; } - (void)notifBufferKeepUp:(NSNotification *)notification { //NSLog(@"notifBufferKeepUp"); viewLoading.hidden=YES; } 
 /* Swift 3.0, Add Observers */ func setupPlayerObservers(){ player.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil) player.addObserver(self, forKeyPath: "rate", options: [.new], context: nil) player.currentItem?.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil) player.currentItem?.addObserver(self, forKeyPath: "playbackBufferEmpty", options: [.old, .new], context: nil) player.currentItem?.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: [.old, .new], context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { //this is when the player is ready and rendering frames if keyPath == "currentItem.loadedTimeRanges" { if !hasLoaded { activityIndicatorView.stopAnimating() Mixpanel.mainInstance().track(event: "Play", properties: ["success": true, "type": "clip"]) } hasLoaded = true } if keyPath == "rate" { if player.rate == 0.0 { playPauseButton.setImage(UIImage(named: "PlayButton"), for: UIControlState()) } else { playPauseButton.setImage(UIImage(named: "PauseButton"), for: UIControlState()) } } if keyPath == "status" { // Do something here if you get a failed || error status } if keyPath == "playbackBufferEmpty" { let time = Int(CMTimeGetSeconds(player.currentTime())) bufferingCount += 1 if bufferingCount % 4 == 0 { Mixpanel.mainInstance().track(event: "VideoBuffenetworking", properties: ["numTimes": bufferingCount, "type": "clip", "currentTime": time]) } activityIndicatorView.isHidden = false activityIndicatorView.startAnimating() } if keyPath == "playbackLikelyToKeepUp" { activityIndicatorView.isHidden = true activityIndicatorView.stopAnimating() } } /* Remove observers in deinit */ deinit { player.removeTimeObserver(timeObserver) player.removeObserver(self, forKeyPath: "currentItem.loadedTimeRanges") player.removeObserver(self, forKeyPath: "rate") player.currentItem?.removeObserver(self, forKeyPath: "playbackBufferEmpty") player.currentItem?.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp") player.currentItem?.removeObserver(self, forKeyPath: "status") }