简体   繁体   English

检查AVPlayer的播放状态

[英]Check play state of AVPlayer

有没有办法知道AVPlayer播放是停止还是结束?

You can tell it's playing using: 您可以使用以下命令告诉它正在播放:

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

Swift 3 extension: Swift 3扩展名:

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

To get notification for reaching the end of an item (via Apple ): 要获取到达商品末尾的通知(通过Apple ):

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

And to track playing you can: 要跟踪播放,您可以:

"track changes in the position of the playhead in an AVPlayer object" by using addPeriodicTimeObserverForInterval:queue:usingBlock: or addBoundaryTimeObserverForTimes:queue:usingBlock: . 通过使用addPeriodicTimeObserverForInterval:queue:usingBlock:addBoundaryTimeObserverForTimes:queue:usingBlock:跟踪 AVPlayer对象中播放头位置的变化。

Example is from Apple: 示例来自苹果:

// 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];
}];

In iOS10, there's a built in property for this now: timeControlStatus 在iOS10中,现在有一个内置属性: timeControlStatus

For example, this function plays or pauses the avPlayer based on it's status and updates the play/pause button appropriately. 例如,此功能根据其状态播放或暂停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)
    }
}

As for your second question, to know if the avPlayer reached the end, the easiest thing to do would be to set up a notification. 至于第二个问题,要知道avPlayer是否已结束,最简单的方法是设置一个通知。

NotificationCenter.default.addObserver(self, selector: #selector(self.didPlayToEnd), name: .AVPlayerItemDidPlayToEndTime, object: nil)

When it gets to the end, for example, you can have it rewind to the beginning of the video and reset the Pause button to Play. 例如,当播放到结尾时,您可以将其倒带到视频的开头,并将“暂停”按钮重置为“播放”。

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

These examples are useful if you're creating your own controls, but if you use a AVPlayerViewController, then the controls come built in. 如果您要创建自己的控件,那么这些示例很有用,但是如果您使用AVPlayerViewController,则控件是内置的。

rate is NOT the way to check whether a video is playing (it could stalled). rate 不是检查视频是否正在播放的方法 (它可能会停顿)。 From documentation of rate : rate文档中:

Indicates the desired rate of playback; 指示所需的播放速率; 0.0 means "paused", 1.0 indicates a desire to play at the natural rate of the current item. 0.0表示“已暂停”,1.0表示希望以当前项目的自然速率播放。

Key words "desire to play" - a rate of 1.0 does not mean the video is playing. 关键字“渴望播放”- 比率1.0并不表示视频正在播放。

The solution since iOS 10.0 is to use AVPlayerTimeControlStatus which can be observed on AVPlayer timeControlStatus property. 自iOS 10.0起的解决方案是使用AVPlayerTimeControlStatus ,可以在AVPlayer timeControlStatus属性上观察到。

The solution prior to iOS 10.0 (9.0, 8.0 etc.) is to roll your own solution. iOS 10.0(9.0、8.0等)之前的解决方案是推出您自己的解决方案。 A rate of 0.0 means that the video is paused. 速率为0.0表示视频已暂停。 When rate != 0.0 it means that the video is either playing or is stalled. rate != 0.0 ,表示视频正在播放停止播放。

You can find out the difference by observing player time via: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any 您可以通过以下方式观察玩家时间来找出差异: func addPeriodicTimeObserver(forInterval interval: CMTime, queue: DispatchQueue?, using block: @escaping (CMTime) -> Void) -> Any

The block returns the current player time in CMTime , so a comparison of lastTime (the time that was last received from the block) and currentTime (the time that the block just reported) will tell whether the player is playing or is stalled. 该块以CMTime返回当前玩家时间,因此, lastTime (从该块最后一次接收到的时间)和currentTime (该块刚刚报告的时间)之间的比较将表明玩家是在玩还是在停顿。 For example, if lastTime == currentTime and rate != 0.0 , then the player has stalled. 例如,如果lastTime == currentTimerate != 0.0 ,则玩家已停顿。

As noted by others, figuring out whether playback has finished is indicated by AVPlayerItemDidPlayToEndTimeNotification . 正如其他人所指出的那样,由AVPlayerItemDidPlayToEndTimeNotification指示找出播放是否已完成。

A more reliable alternative to NSNotification is to add yourself as observer to player's rate property. NSNotification一个更可靠的替代方法是将您自己添加为玩家的rate属性的观察者。

[self.player addObserver:self
              forKeyPath:@"rate"
                 options:NSKeyValueObservingOptionNew
                 context:NULL];

Then check if the new value for observed rate is zero, which means that playback has stopped for some reason, like reaching the end or stalling because of empty buffer. 然后检查观察到的速率的新值是否为零,这意味着播放由于某种原因已停止,例如到达结尾或由于空缓冲区而停止。

- (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
        }
    }
}

For rate == 0.0 case, to know what exactly caused the playback to stop, you can do the following checks: 对于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
}

And don't forget to make your player stop when it reaches the end: 并且不要忘记让播放器到达终点时停止播放:

self.player.actionAtItemEnd = AVPlayerActionAtItemEndPause;

For Swift : 对于Swift

AVPlayer : AVPlayer

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

Update : 更新
player.rate > 0 condition changed to player.rate != 0 because if video is playing in reverse it can be negative thanks to Julian for pointing out. player.rate > 0条件已更改为player.rate != 0因为如果视频反向播放,由于朱利安指出,视频可能为负。
Note : This might look same as above(Maz's) answer but in Swift '!player.error' was giving me a compiler error so you have to check for error using 'player.error == nil' in Swift.(because error property is not of 'Bool' type) 注意 :这可能与上面的答案(Maz's)相同,但是在Swift'!player.error'中给了我一个编译器错误,因此您必须在Swift中使用'player.error == nil'来检查错误。(因为error属性不是“布尔”类型)

AVAudioPlayer: AVAudioPlayer:

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

AVQueuePlayer: AVQueuePlayer:

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

Swift extension based on the answer by maz 基于maz答案的Swift扩展

extension AVPlayer {

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

The Swift version of maxkonovalov's answer is this: maxkonovalov的Swift版本的答案是这样的:

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

and

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

Thank you maxkonovalov! 谢谢maxkonovalov!

Currently with swift 5 the easiest way to check if the player is playing or paused is to check the .timeControlStatus variable. 当前,使用swift 5来检查播放器是播放还是暂停的最简单方法是检查.timeControlStatus变量。

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

Answer is Objective C 答案是目标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
}
player.timeControlStatus == AVPlayer.TimeControlStatus.playing

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM