ios avplayer触发器流不受缓冲区限制

时间:2011-07-30 02:59:13

标签: ios audio-streaming avplayer

当流缓冲区为空时,我想重新连接到服务器。

如果AVPlayerAVPlayerItem缓冲区为空,我该如何触发方法?

我知道有playbackLikelyToKeepUpplaybackBufferEmptyplaybackBufferFull方法来检查缓冲区状态,但这些方法不是回调。

是否有任何回调函数或我应添加的任何观察者?

4 个答案:

答案 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")
}