Ios 检查AVPlayer的播放状态

Ios 检查AVPlayer的播放状态,ios,objective-c,iphone,avfoundation,avplayer,Ios,Objective C,Iphone,Avfoundation,Avplayer,有没有办法知道AVPlayer播放是否已暂停或已结束?获取项目结束通知(通过): [[NSNotificationCenter defaultCenter] addObserver: 选择器:@selector() 名称:AVPlayerItemDidPlayToEndTimeNotification 对象:; 要跟踪播放,您可以: 通过使用addPeriodicTimeObserverForInterval:queue:usingBlock:或addBoundaryTimeObserverF

有没有办法知道
AVPlayer
播放是否已暂停或已结束?

获取项目结束通知(通过):

[[NSNotificationCenter defaultCenter]
addObserver:
选择器:@selector()
名称:AVPlayerItemDidPlayToEndTimeNotification
对象:;
要跟踪播放,您可以:

通过使用addPeriodicTimeObserverForInterval:queue:usingBlock:addBoundaryTimeObserverForTimes:queue:usingBlock:来“跟踪播放头在AVPlayer对象中的位置变化”

苹果的例子如下:

// Assume a property: @property (retain) id playerObserver;

Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 1);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);
NSArray *times = [NSArray arrayWithObjects:[NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird], nil];

self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{
    // Passing NULL for the queue specifies the main queue.

    NSString *timeDescription = (NSString *)CMTimeCopyDescription(NULL, [self.player currentTime]);
    NSLog(@"Passed a boundary at %@", timeDescription);
    [timeDescription release];
}];
//假设一个属性:@property(retain)id playerObserver;
Float64 durationSeconds=CMTimeGetSeconds([duration]);
CMTime firstThird=CMTimeMakeWithSeconds(持续时间秒/3.0,1);
CMTime secondThird=CMTimeMakeWithSeconds(持续时间秒*2.0/3.0,1);
NSArray*times=[NSArray ARRAY WITH OBJECTS:[NSValue VALUE WITHCMTIME:FIRSTNERD]、[NSValue VALUE WITHCMTIME:SECONTHERD]、nil];
self.playerObserver=[addBoundaryTimeObserverForTimes:times队列:NULL usingBlock:^{
//为队列传递NULL指定主队列。
NSString*timeDescription=(NSString*)CMTimeCopyDescription(NULL,[self.player currentTime]);
NSLog(@“通过%@”的边界,时间描述);
[时间描述发布];
}];

您可以通过以下方式判断它正在播放:

AVPlayer *player = ...
if ((player.rate != 0) && (player.error == nil)) {
    // player is playing
}
Swift 3扩展:

extension AVPlayer {
    var isPlaying: Bool {
        return rate != 0 && error == nil
    }
}

对于Swift

AVPlayer:

更新
player.rate>0
条件更改为
player.rate!=0
因为如果视频反向播放,它可能是负片,这要感谢朱利安的指出
注意:这可能与上面(Maz)的答案相同,但使用Swift'!player.error'给了我一个编译器错误,因此您必须在Swift中使用'player.error==nil'检查错误。(因为error属性不是'Bool'类型)

AVAudioPlayer:

AVQueuePlayer:


NSNotification
更可靠的替代方法是将自己添加为玩家的
rate
属性的观察者

[self.player addObserver:self
              forKeyPath:@"rate"
                 options:NSKeyValueObservingOptionNew
                 context:NULL];
然后检查“观察速率”的新值是否为零,这意味着播放已因某种原因停止,例如到达终点或因缓冲区空而暂停

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSString *,id> *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"rate"]) {
        float rate = [change[NSKeyValueChangeNewKey] floatValue];
        if (rate == 0.0) {
            // Playback stopped
        } else if (rate == 1.0) {
            // Normal playback
        } else if (rate == -1.0) {
            // Reverse playback
        }
    }
}
当玩家到达终点时,别忘了让他停下来:


self.player.actionAtItemEnd=AVPlayerActionAtItemEndPause

maxkonovalov的Swift版本回答如下:

player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.New, context: nil)

重写函数observeValueForKeyPath(键路径:String?,对象的类型:AnyObject?,更改:[String:AnyObject]?,上下文:UnsafeMutablePointer){
如果keyPath==“速率”{
如果let rate=将[NSKeyValueChangeNewKey]更改为浮点数{
如果速率==0.0{
打印(“播放停止”)
}
如果速率==1.0{
打印(“正常播放”)
}
如果速率==-1.0{
打印(“反向播放”)
}
}
}
}

谢谢maxkonovalov

根据maz的回答进行Swift扩展

extension AVPlayer {

    var isPlaying: Bool {
        return ((rate != 0) && (error == nil))
    }
}

在iOS10中,现在有一个内置属性:timeControlStatus

例如,此功能根据avPlayer的状态播放或暂停avPlayer,并适当更新播放/暂停按钮

@IBAction func btnPlayPauseTap(_ sender: Any) {
    if aPlayer.timeControlStatus == .playing {
        aPlayer.pause()
        btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
    } else if aPlayer.timeControlStatus == .paused {
        aPlayer.play()
        btnPlay.setImage(UIImage(named: "control-pause"), for: .normal)
    }
}
至于你的第二个问题,要知道avPlayer是否到达终点,最简单的方法是设置一个通知

NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
例如,当它结束时,您可以让它倒回视频的开头,并重置暂停按钮以播放

@objc func didPlayToEnd() {
    aPlayer.seek(to: CMTimeMakeWithSeconds(0, 1))
    btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
}

如果您正在创建自己的控件,这些示例非常有用,但是如果您使用的是AVPlayerViewController,那么这些控件是内置的。

rate
不是检查视频是否正在播放(可能会暂停)的方法。从
费率的文档中

指示所需的播放速率;0.0表示“暂停”,1.0表示希望以当前项目的自然速度玩

关键词“渴望播放”-速率
1.0
并不意味着视频正在播放。

自iOS 10.0以来的解决方案是使用
AVPlayerTimeControlStatus
,这可以在
AVPlayer
timeControlStatus
属性上观察到

[self.player addObserver:self
              forKeyPath:@"rate"
                 options:NSKeyValueObservingOptionNew
                 context:NULL];
iOS 10.0(9.0、8.0等)之前的解决方案是推出自己的解决方案速率
0.0
表示视频暂停。当<代码>速率!=0.0
表示视频正在播放或暂停。

您可以通过以下方式观察玩家时间来找出差异:
func addPeriodicTimeObserver(forInterval interval:CMTime,queue:DispatchQueue?,使用block:@escaping(CMTime)->Void)->Any

块返回
CMTime
中的当前播放机时间,因此将
lastTime
(上次从块接收的时间)和
currentTime
(块刚刚报告的时间)进行比较,可以判断播放机是正在播放还是暂停。例如,如果
lastTime==currentTime
rate!=0.0,则播放机已暂停


正如其他人所指出的,确定播放是否已完成由
AVPlayerItemDidPlayToEndTimeNotification

指示。当前使用swift 5检查播放器是否正在播放或暂停最简单的方法是检查.timeControlStatus变量

player.timeControlStatus == .paused
player.timeControlStatus == .playing
答案是客观的C


不一定,它不会处理播放器由于文件中的错误而暂停的情况。我发现了一些困难的事情…正如德莫特所说,如果你在飞行模式下尝试播放某些东西,AVPlayer速率仍然设置为1.0,因为这意味着要播放。AVPlayer有一个错误属性,只需检查它是否为零,以及检查速率是否为0:)注意,答案已更新,以防止@Dermot场景。
extension AVPlayer {

    var isPlaying: Bool {
        return ((rate != 0) && (error == nil))
    }
}
@IBAction func btnPlayPauseTap(_ sender: Any) {
    if aPlayer.timeControlStatus == .playing {
        aPlayer.pause()
        btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
    } else if aPlayer.timeControlStatus == .paused {
        aPlayer.play()
        btnPlay.setImage(UIImage(named: "control-pause"), for: .normal)
    }
}
NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)
@objc func didPlayToEnd() {
    aPlayer.seek(to: CMTimeMakeWithSeconds(0, 1))
    btnPlay.setImage(UIImage(named: "control-play"), for: .normal)
}
player.timeControlStatus == .paused
player.timeControlStatus == .playing
if (player.timeControlStatus == AVPlayerTimeControlStatusPlaying) {
    //player is playing
}
else if (player.timeControlStatus == AVPlayerTimeControlStatusPaused) {
    //player is pause
}
else if (player.timeControlStatus == AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate) {
    //player is waiting to play
}
player.timeControlStatus == AVPlayer.TimeControlStatus.playing