2011-07-30 5 views
15

스트리밍 버퍼가 비어있을 때 서버에 다시 연결하려고합니다.ios avplayer 트리거 스트리밍 버퍼가 부족합니다.

AVPlayer 또는 AVPlayerItem 버퍼가 비어있을 때 어떻게 메소드를 트리거 할 수 있습니까?

버퍼 상태를 확인하는 방법은 playbackLikelyToKeepUp, playbackBufferEmptyplaybackBufferFull입니다.하지만 콜백이 아닙니다.

콜백 함수가 있습니까? 아니면 추가해야하는 옵저버가 있습니까?

답변

53

당신이 그 키에 대한 관찰자를 추가 할 수 있습니다

[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 
     } 
    } 
} 
+1

감사합니다. AVPlayer 또는 AVPlayerItem을 다시 연결시키는 좋은 방법을 알고 있습니까? 아니면 새 플레이어 나 항목을 만들지 않을까요? –

+0

제 경우에는 AVPlayer 객체에 재생 메시지를 보내면됩니다. – sciasxp

+0

Prefect Ans. 그것은 정말로 나를 위해 일한다. 감사. –

2

이렇게하려면 Core Audio 및 CFReadStream으로 드롭 다운해야합니다. CFReadStream을 사용하면 끝나거나 읽는 오류 등의 특정 스트림 이벤트에서 호출되는 콜백을 제공 할 수 있습니다. 거기에서 서버에 다시 연결할 수 있습니다. HTTP 스트림을 소비하는 경우 범위 헤더를 HTTP 요청에 추가하여 서버가 지정한 지점 (이전에받은 +1 바이트)에서 스트림을 보내도록 서버에 지시 할 수 있습니다.

+13

당신은 싶어 이렇게하지

그런 다음이 코드를 사용할 수있는 키 변경을 처리한다. 날 믿어. – Andy

1

:

#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; 
} 
0
/* 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") 
} 
관련 문제