检查AVPlayer的播放状态


Answers:


57

要获取到达商品末尾的通知(通过Apple):

[[NSNotificationCenter defaultCenter] 
      addObserver:<self>
      selector:@selector(<#The selector name#>)
      name:AVPlayerItemDidPlayToEndTimeNotification 
      object:<#A player item#>];

要跟踪播放,您可以:

通过使用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];
}];

您可以放入布尔值标志以检查播放状态。
moonman239

如果您更改播放器的项目(例如使用)-replaceCurrentItemWithPlayerItem:,并且处理不当,则通知可能会引起问题。对我来说,更可靠的方法是AVPlayer使用KVO 跟踪状态。请参阅以下我的答案以获取更多详细信息:stackoverflow.com/a/34321993/3160561
maxkonovalov

2
还需要通知错误吗? AVPlayerItemFailedToPlayToEndTimeNotification
ToolmakerSteve

332

您可以使用以下命令判断其正在播放:

AVPlayer *player = ...
if ((player.rate != 0) && (player.error == nil)) {
    // player is playing
}

Swift 3扩展名:

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

29
不一定,它不能处理由于文件错误而导致播放器停顿的情况。我发现了一些困难的方法……
德莫(Dermot)

7
正如Dermot所说,如果您尝试在飞行模式下播放某些内容,则AVPlayer速率仍设置为1.0,因为它暗示了播放的意图。
phi

4
AVPlayer具有错误属性,只需检查其是否为nil以及检查速率是否为0 :)
James Campbell

23
请注意,答案已更新,以防止出现@Dermot情况。因此,这实际上是可行的。
jomafer 2015年

2
反向播放视频素材(- [AVPlayer setRate:-1.0])时将不起作用,因为-1.0它小于
0。–朱利安·F·韦纳特

49

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

例如,此功能根据其状态播放或暂停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,则控件是内置的。


易于使用的iOS 10以上版本
Technerd's

2
@objc func didPlayToEnd()for Swift 4.2
Sean Stayns

21

rate不是要检查视频是否是这样(这可能停滞不前)。来自以下文档rate

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

关键字“渴望播放”- 播放1.0并不表示视频正在播放。

自iOS 10.0起使用的解决方案AVPlayerTimeControlStatus可以在AVPlayer timeControlStatus属性上观察到。

iOS 10.0(9.0、8.0等)之前的解决方案是推出您自己的解决方案。速率0.0表示视频已暂停。当rate != 0.0它意味着视频正在播放停转。

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

该区块返回当前的玩家时间in CMTime,因此lastTime(最后一次从该区块收到currentTime的时间)与(刚刚报告该区块的时间)的比较将表明玩家正在玩还是停滞了。例如,如果lastTime == currentTimerate != 0.0,则播放器已停止。

正如其他人所指出的那样,弄清楚播放是否已经完成AVPlayerItemDidPlayToEndTimeNotification


18

一个更可靠的替代方法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
        }
    }
}

例如rate == 0.0,要知道到底是什么原因导致播放停止,可以执行以下检查:

if (self.player.error != nil) {
    // Playback failed
}
if (CMTimeGetSeconds(self.player.currentTime) >=
    CMTimeGetSeconds(self.player.currentItem.duration)) {
    // Playback reached end
} else if (!self.player.currentItem.playbackLikelyToKeepUp) {
    // Not ready to play, wait until enough data is loaded
}

并且不要忘记让播放器到达终点时停止播放:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;


我认为还需要通知无法到达末尾AVPlayerItemFailedToPlayToEndTimeNotification。如果发生此错误,将永远不会达到结束时间。或阅读maz的答案,在代码中添加的检查player.error != nil,在这种情况下,由于错误,播放已“结束”。
ToolmakerSteve

顺便说一句,您发现使用NSNotification的“不可靠”之处是AVPlayerItemDidPlayToEndTimeNotification什么?
ToolmakerSteve

ToolmakerSteve,感谢您的来信,为我的回答添加了错误检查功能。对于不可靠的通知-当我在集合视图中实现重复和可重用的玩家视图时,我自己遇到了这个问题,KVO使事情变得更加清晰,因为它允许将其保持在更多本地,而不会引起不同玩家的通知之间的相互干扰。
maxkonovalov

17

对于Swift

AVPlayer

let player = AVPlayer(URL: NSURL(string: "http://www.sample.com/movie.mov"))
if (player.rate != 0 && player.error == nil) {
   println("playing")
}

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

AVAudioPlayer:

if let theAudioPlayer =  appDelegate.audioPlayer {
   if (theAudioPlayer.playing) {
       // playing
   }
}

AVQueuePlayer:

if let theAudioQueuePlayer =  appDelegate.audioPlayerQueue {
   if (theAudioQueuePlayer.rate != 0 && theAudioQueuePlayer.error == nil) {
       // playing
   }
}

2
AVPlayer!= AVAudioPlayer
2015年

2
警告:以上答案中提到的所有内容均在提出该问题的两年之前提出。
Dan Rosenstark 2015年

1
@Yar我知道答案上方也有投票,但这是针对swift的,在Swift'!player.error'不适用于我,它仅适用于bool类型,因此我添加了答案oops upvovoted your comment by to use给出答复此堆栈溢出应用程序:)
Aks

我担心的是,我不知道player.error不是nil的实际效果如何。
Dan Rosenstark 2015年

1
反向播放视频素材(player.rate = -1.0)时将不起作用,因为-1.0小于
0。–朱利安·F·韦纳特

9

基于maz答案的Swift扩展

extension AVPlayer {

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

6

maxkonovalov的Swift版本的答案是这样的:

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

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if keyPath == "rate" {
        if let rate = change?[NSKeyValueChangeNewKey] as? Float {
            if rate == 0.0 {
                print("playback stopped")
            }
            if rate == 1.0 {
                print("normal playback")
            }
            if rate == -1.0 {
                print("reverse playback")
            }
        }
    }
}

谢谢maxkonovalov!


您好Stanev先生,谢谢您的回答。这对我不起作用(又不在控制台中打印任何内容?)
Cesare

没关系,它确实有效-我的播放器是nil!但是我需要知道视图何时开始(而不是结束)。有什么办法吗?
切萨雷

3

当前,使用swift 5来检查播放器是播放还是暂停的最简单方法是检查.timeControlStatus变量。

player.timeControlStatus == .paused
player.timeControlStatus == .playing

1

答案是目标C

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
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.