当流缓冲区为空时,我想重新连接到服务器。
如果AVPlayer
或AVPlayerItem
缓冲区为空,我该如何触发方法?
我知道有playbackLikelyToKeepUp
,playbackBufferEmpty
和playbackBufferFull
方法来检查缓冲区状态,但这些方法不是回调。
是否有任何回调函数或我应添加的任何观察者?
答案 0 :(得分:55)
您可以为这些键添加观察者:
[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 :(得分:2)
您需要进入Core Audio和CFReadStream才能执行此操作。使用CFReadStream,您可以提供在某些流事件(如遇到的结束,读取错误等)上调用的回调。从那里,您可以触发重新连接到服务器。如果您正在使用HTTP流,则可以将范围标头添加到HTTP请求中,这样您就可以告诉服务器从您指定的点发送流(这将是您在+ 1之前收到的最后一个字节)。
答案 2 :(得分: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;
}
答案 3 :(得分: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")
}