簡體   English   中英

檢測 AVPlayer 何時播放

[英]Detect when AVPlayer is playing

您如何檢測AVPlayer何時正在播放? 在調用play()函數和實際播放視頻之間似乎有一點延遲。

對於視頻

AVPlayer有一個名為rate (Float)的參數,如果rate大於 0.0,則有當前正在播放的視頻。

您可以檢查rate是否為!=0 :(如果玩家倒退,比率可能為負)

if vidPlayer != nil && vidPlayer.rate != 0 {
  println("playing")
}

AVPlayer 類參考

據我所知,我同意你的看法,在調用play()函數和視頻實際播放之間有一個輕微的延遲(換句話說,視頻的第一幀被渲染的時間)。 延遲取決於一些標准,例如視頻類型(VOD 或實時流媒體)、網絡狀況……但是,幸運的是,我們能夠知道何時渲染視頻的第一幀,我的意思是視頻實際播放的確切時間.

通過觀察當前AVPlayerItemstatus ,只要它是AVPlayerItemStatusReadyToPlay ,就應該是第一幀已經渲染。

[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];

-(void)observeValueForKeyPath:(NSString*)path ofObject:(id)object change:(NSDictionary*)change context:(void*) context {

    if([self.playerItem status] == AVPlayerStatusReadyToPlay){
        NSLog(@"The video actually plays")
    }
}

順便說一下,還有另一種解決方案,我們觀察AVPlayerLayer readyForDisplay狀態,它也指示何時呈現視頻。 但是,此解決方案有一個缺點,如 Apple 文檔中所述

/*!
     @property      readyForDisplay
     @abstract      Boolean indicating that the first video frame has been made ready for display for the current item of the associated AVPlayer.
     @discusssion   Use this property as an indicator of when best to show or animate-in an AVPlayerLayer into view. 
                    An AVPlayerLayer may be displayed, or made visible, while this propoerty is NO, however the layer will not have any 
                    user-visible content until the value becomes YES. 
                    This property remains NO for an AVPlayer currentItem whose AVAsset contains no enabled video tracks.
 */
@property(nonatomic, readonly, getter=isReadyForDisplay) BOOL readyForDisplay;

這是示例代碼

self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; 
[self.playerLayer addObserver:self forKeyPath:@"readyForDisplay" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];

-(void)observeValueForKeyPath:(NSString*)path ofObject:(id)object change:(NSDictionary*)change context:(void*) context {
    if([self.playerLayer isReadyForDisplay]){
        NSLog(@"Ready to display");
    }
}

因此, [self.playerLayer isReadyForDisplay]應該返回YES,但是,作為文檔,它是沒有保證的。

我希望這會有所幫助。

斯威夫特 4

方法一

var rateObserver: NSKeyValueObservation?

self.rateObserver = myPlayer.observe(\.rate, options:  [.new, .old], changeHandler: { (player, change) in
     if player.rate == 1  {
          print("Playing")
      }else{
           print("Stop")
      }
 })

 // Later You Can Remove Observer      
 self.rateObserver?.invalidate()

方法二

myPlayer.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions(rawValue: NSKeyValueObservingOptions.new.rawValue | NSKeyValueObservingOptions.old.rawValue), context: nil)

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
      if keyPath == "rate" { 
          if player.rate == 1  {
              print("Playing")
          }else{
               print("Stop")
          }
      }
 }

您可能想要檢測兩件事:

  • AVPlayerLayerisReadyForDisplay是接收第一幀的時間。
  • AVPlayerItemreadyToPlay是視頻實際開始播放的時間。

為了檢查這兩種狀態,您可以使用可觀察性。

您將在類中定義三個對象:

var player: AVPlayer?
var playerItem: AVPlayerItem?
var playerLayer: AVPlayerLayer?

播放器實現將如下所示:

guard let videoURL = URL(string: "<videoPath>") else {
   return
}
asset = AVAsset(url: videoURL)
guard let asset = asset else {
   return
}
playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: requiredAssetKeys)
guard let playerItem = playerItem else {
   return
}
playerItem.addObserver(self,
                       forKeyPath: #keyPath(AVPlayerItem.status),
                       options: [.old, .new],
                       context: &playerItemContext)
player = AVPlayer(playerItem: playerItem)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.addObserver(self, 
                         forKeyPath: #keyPath(AVPlayerLayer.isReadyForDisplay), 
                         options: [.old, .new], 
                         context: &playerLayerContext)
avpController.player = player 
avpController.view.frame.size.height = playerView.frame.size.height
avpController.view.frame.size.width = playerView.frame.size.width
playerView.addSubview(avpController.view)
avpController.player?.play()

在這里,上下文要么是簡單的整數,要么是枚舉。

您可以通過覆蓋observeValue方法來處理事件。

override func observeValue(forKeyPath keyPath: String?,
                           of object: Any?,
                           change: [NSKeyValueChangeKey : Any]?,
                           context: UnsafeMutableRawPointer?) {
    guard (context == &playerItemContext) ||
        (context == &playerLayerContext) else {
        super.observeValue(forKeyPath: keyPath,
                           of: object,
                           change: change,
                           context: context)
        return
    }
    if keyPath == #keyPath(AVPlayerItem.status) {
        let status: AVPlayerItem.Status
        if let statusNumber = change?[.newKey] as? NSNumber {
            status = AVPlayerItem.Status(rawValue: statusNumber.intValue)!
        } else {
            status = .unknown
        }
        switch status {
        case .readyToPlay:
            print("Ready to play")
        case .failed:
            navigationController?.popViewController(animated: true)
        case .unknown:
            print("Unknown")
        @unknown default:
            print("Unknown default")
        }
    } else if keyPath == #keyPath(AVPlayerLayer.isReadyForDisplay) {
        if playerLayer?.isReadyForDisplay ?? false {
            print("Ready to display")
        }
    }
}

不要忘記移除觀察者。

deinit {
    playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.status))
    playerLayer?.removeObserver(self, forKeyPath: #keyPath(AVPlayerLayer.isReadyForDisplay))
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM