ios avplayer触发器stream出缓冲区

我想在stream缓冲区为空时重新连接到服务器。

如何在AVPlayerAVPlayerItem缓冲区为空时触发方法?

我知道里面有playbackLikelyToKeepUpplaybackBufferEmptyplaybackBufferFull方法来检查缓冲区状态,但那些不是callback。

有没有callback函数,或者我应该添加什么观察者?

你可以为这些键添加观察者:

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

第一个会在你的缓冲区为空时发出警告,第二个在你的缓冲区很好的时候会再次发出警告。

然后要处理关键更改,您可以使用此代码:

 - (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 } } } 

你将需要下拉到核心audio和CFReadStream来做到这一点。 使用CFReadStream,您可以提供一个callback,在特定的stream事件上调用,如遇到结束,读取错误等。从那里您可以触发重新连接到服务器。 如果您正在使用HTTPstream,则可以将范围标题添加到HTTP请求中,以便您可以告诉服务器从指定的点(这将是您之前收到的最后一个字节+1)发送stream。

试试这个代码,它应该解决所有的噩梦:

 #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: "VideoBuffered", 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") }