[英]Detect when AVPlayer is playing
您如何檢測AVPlayer
何時正在播放? 在調用play()
函數和實際播放視頻之間似乎有一點延遲。
對於視頻:
AVPlayer
有一個名為rate (Float)
的參數,如果rate
大於 0.0,則有當前正在播放的視頻。
您可以檢查rate
是否為!=0
:(如果玩家倒退,比率可能為負)
if vidPlayer != nil && vidPlayer.rate != 0 {
println("playing")
}
據我所知,我同意你的看法,在調用play()
函數和視頻實際播放之間有一個輕微的延遲(換句話說,視頻的第一幀被渲染的時間)。 延遲取決於一些標准,例如視頻類型(VOD 或實時流媒體)、網絡狀況……但是,幸運的是,我們能夠知道何時渲染視頻的第一幀,我的意思是視頻實際播放的確切時間.
通過觀察當前AVPlayerItem
的status
,只要它是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")
}
}
}
您可能想要檢測兩件事:
AVPlayerLayer
的isReadyForDisplay
是接收第一幀的時間。AVPlayerItem
的readyToPlay
是視頻實際開始播放的時間。為了檢查這兩種狀態,您可以使用可觀察性。
您將在類中定義三個對象:
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.