我想检测我的AVPlayer是否正在缓冲当前位置,以便我可以显示加载器或其他东西。但我似乎无法在AVPlayer的文档中找到任何内容。
答案 0 :(得分:31)
您可以观察player.currentItem
:
playerItem.addObserver(self, forKeyPath: "playbackBufferEmpty", options: .New, context: nil)
playerItem.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options: .New, context: nil)
playerItem.addObserver(self, forKeyPath: "playbackBufferFull", options: .New, context: nil)
然后
override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if object is AVPlayerItem {
switch keyPath {
case "playbackBufferEmpty":
// Show loader
case "playbackLikelyToKeepUp":
// Hide loader
case "playbackBufferFull":
// Hide loader
}
}
}
答案 1 :(得分:16)
接受的答案对我不起作用,我使用下面的代码有效地显示加载程序。
Swift 3
//properties
var observer:Any!
var player:AVPlayer!
self.observer = self.player.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 600), queue: DispatchQueue.main) {
[weak self] time in
if self?.player.currentItem?.status == AVPlayerItemStatus.readyToPlay {
if let isPlaybackLikelyToKeepUp = self?.player.currentItem?.isPlaybackLikelyToKeepUp {
//do what ever you want with isPlaybackLikelyToKeepUp value, for example, show or hide a activity indicator.
}
}
}
答案 2 :(得分:5)
#在Swift 4中已更新,并且工作正常
通过我,我接受了可接受的答案,但是对我来说在 swift 4 中不起作用,因此在进行某些研究后我发现了this thinks from apple doc。确定 AVPlayer状态的方法有两种,
使用方式就像这样
var observer:Any?
var avplayer : AVPlayer?
func preriodicTimeObsever(){
if let observer = self.observer{
//removing time obse
avplayer?.removeTimeObserver(observer)
observer = nil
}
let intervel : CMTime = CMTimeMake(1, 10)
observer = avplayer?.addPeriodicTimeObserver(forInterval: intervel, queue: DispatchQueue.main) { [weak self] time in
guard let `self` = self else { return }
let sliderValue : Float64 = CMTimeGetSeconds(time)
//this is the slider value update if you are using UISlider.
let playbackLikelyToKeepUp = self.avPlayer?.currentItem?.isPlaybackLikelyToKeepUp
if playbackLikelyToKeepUp == false{
//Here start the activity indicator inorder to show buffering
}else{
//stop the activity indicator
}
}
}
别忘了杀死时间观察者以保存内存泄漏。杀死实例的方法,根据您的需要添加此方法,但是我在viewWillDisappear方法中使用了它。
if let observer = self.observer{
self.avPlayer?.removeTimeObserver(observer)
observer = nil
}
答案 3 :(得分:4)
斯威夫特4次观察:
var playerItem: AVPlayerItem?
var playbackLikelyToKeepUpKeyPathObserver: NSKeyValueObservation?
var playbackBufferEmptyObserver: NSKeyValueObservation?
var playbackBufferFullObserver: NSKeyValueObservation?
private func observeBuffering() {
let playbackBufferEmptyKeyPath = \AVPlayerItem.playbackBufferEmpty
playbackBufferEmptyObserver = playerItem?.observe(playbackBufferEmptyKeyPath, options: [.new]) { [weak self] (_, _) in
// show buffering
}
let playbackLikelyToKeepUpKeyPath = \AVPlayerItem.playbackLikelyToKeepUp
playbackLikelyToKeepUpKeyPathObserver = playerItem?.observe(playbackLikelyToKeepUpKeyPath, options: [.new]) { [weak self] (_, _) in
// hide buffering
}
let playbackBufferFullKeyPath = \AVPlayerItem.playbackBufferFull
playbackBufferFullObserver = playerItem?.observe(playbackBufferFullKeyPath, options: [.new]) { [weak self] (_, _) in
// hide buffering
}
}
观察完毕后需要删除观察员。
要删除这三个观察者,只需将playbackBufferEmptyObserver
,playbackLikelyToKeepUpKeyPathObserver
和playbackBufferFullObserver
设置为nil
。
无需手动删除它们(这是observe<Value>(_ keyPath:, options:, changeHandler:)
方法特有的。
答案 4 :(得分:2)
已为Swift 4.2更新
var player : AVPlayer? = nil
let videoUrl = URL(string: "https://wolverine.raywenderlich.com/content/ios/tutorials/video_streaming/foxVillage.mp4")
self.player = AVPlayer(url: videoUrl!)
self.player?.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1, timescale: 600), queue: DispatchQueue.main, using: { time in
if self.player?.currentItem?.status == AVPlayerItem.Status.readyToPlay {
if let isPlaybackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp {
//do what ever you want with isPlaybackLikelyToKeepUp value, for example, show or hide a activity indicator.
//MBProgressHUD.hide(for: self.view, animated: true)
}
}
})
答案 5 :(得分:1)
对我来说,上面接受的答案无效,但是此方法有效。您可以使用timeControlStatus,但仅在iOS 10以上版本可用。
根据Apple的官方文档
一种状态,指示当前是否正在播放, 无限期暂停,或在等待适当时暂停 网络条件
将此观察者添加到播放器中。
player.addObserver(self, forKeyPath: “timeControlStatus”, options: [.old, .new], context: nil)
然后,观察其中的变化
func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
方法。在上述方法中使用以下代码
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "timeControlStatus", let change = change, let newValue = change[NSKeyValueChangeKey.newKey] as? Int, let oldValue = change[NSKeyValueChangeKey.oldKey] as? Int {
let oldStatus = AVPlayer.TimeControlStatus(rawValue: oldValue)
let newStatus = AVPlayer.TimeControlStatus(rawValue: newValue)
if newStatus != oldStatus {
DispatchQueue.main.async {[weak self] in
if newStatus == .playing || newStatus == .paused {
self?.loaderView.isHidden = true
} else {
self?.loaderView.isHidden = false
}
}
}
}
}
这已在上面的iOS 11上使用swift 4进行了测试,并且可以正常工作。
答案 6 :(得分:1)
请注意
在回调块中使用对self的弱引用以防止创建保留周期。
func playRemote(url: URL) {
showSpinner()
let playerItem = AVPlayerItem(url: url)
avPlayer = AVPlayer(playerItem: playerItem)
avPlayer.rate = 1.0
avPlayer.play()
self.avPlayer.addPeriodicTimeObserver(forInterval: CMTimeMake(value: 1,
timescale: 600), queue: DispatchQueue.main, using: { [weak self] time in
if self?.avPlayer.currentItem?.status == AVPlayerItem.Status.readyToPlay {
if let isPlaybackLikelyToKeepUp = self?.avPlayer.currentItem?.isPlaybackLikelyToKeepUp {
self?.removeSpinner()
}
}
})
}
}
答案 7 :(得分:1)
在 Swift 5.3 中
变量:
private var playerItemBufferEmptyObserver: NSKeyValueObservation?
private var playerItemBufferKeepUpObserver: NSKeyValueObservation?
private var playerItemBufferFullObserver: NSKeyValueObservation?
添加观察者
playerItemBufferEmptyObserver = player.currentItem?.observe(\AVPlayerItem.isPlaybackBufferEmpty, options: [.new]) { [weak self] (_, _) in
guard let self = self else { return }
self.showLoadingIndicator(over: self)
}
playerItemBufferKeepUpObserver = player.currentItem?.observe(\AVPlayerItem.isPlaybackLikelyToKeepUp, options: [.new]) { [weak self] (_, _) in
guard let self = self else { return }
self.dismissLoadingIndicator()
}
playerItemBufferFullObserver = player.currentItem?.observe(\AVPlayerItem.isPlaybackBufferFull, options: [.new]) { [weak self] (_, _) in
guard let self = self else { return }
self.dismissLoadingIndicator()
}
移除观察者
playerItemBufferEmptyObserver?.invalidate()
playerItemBufferEmptyObserver = nil
playerItemBufferKeepUpObserver?.invalidate()
playerItemBufferKeepUpObserver = nil
playerItemBufferFullObserver?.invalidate()
playerItemBufferFullObserver = nil
答案 8 :(得分:0)
嗯,被接受的解决方案对我不起作用,并且周期性的观察者解决方案似乎举手之劳。
这是我的建议,timeControlerStatus
的旁观者AVPlayer
。
// Add observer
player.addObserver(self,
forKeyPath: #keyPath(AVPlayer.timeControlStatus),
options: [.new],
context: &playerItemContext)
// At some point you'll need to remove yourself as an observer otherwise
// your app will crash
self.player?.removeObserver(self, forKeyPath: #keyPath(AVPlayer.timeControlStatus))
// handle keypath callback
if keyPath == #keyPath(AVPlayer.timeControlStatus) {
guard let player = self.player else { return }
if let isPlaybackLikelyToKeepUp = player.currentItem?.isPlaybackLikelyToKeepUp,
player.timeControlStatus != .playing && !isPlaybackLikelyToKeepUp {
self.playerControls?.loadingStatusChanged(true)
} else {
self.playerControls?.loadingStatusChanged(false)
}
}
答案 9 :(得分:0)
这是一种简单的方法,可与 Swift 5 一起使用。
这将在播放器停顿时添加loadingIndicator
NotificationCenter.default.addObserver(self, selector:
#selector(playerStalled(_:)), name: NSNotification.Name.AVPlayerItemPlaybackStalled, object: self.player?.currentItem)
@objc func playerStalled(_ notification: Notification){
self.loadingIndicator.isHidden = false
self.playPauseButton.isHidden = true
}
当缓冲区为空时,这将显示加载程序指示器:
if let isPlayBackBufferEmpty = self.player?.currentItem?.isPlaybackBufferEmpty{
if isPlayBackBufferEmpty{
self.loadingIndicator.isHidden = false
self.playPauseButton.isHidden = true
}
}
这将在播放器准备播放时隐藏加载程序:
if self.playerItem?.status == AVPlayerItem.Status.readyToPlay{
if let isPlaybackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp {
if isPlaybackLikelyToKeepUp{
self.loadingIndicator.isHidden = true
self.playPauseButton.isHidden = false
}
}
}
答案 10 :(得分:0)
受Marco's answer启发的Xamarin解决方案
"""data= data clustered retrieved by function as you want"""
"""model = result from the data with got by KMeans"""
"""cluster = clusters formed by the model"""
from sklearn.cluster import KMeans
data = clusteredData()
model = KMeans(n_clusters=5, init='random', max_iter=100, n_init=1, verbose=1)
cluster = model.fit_predict(scale(data))
dictionary = {}
for index in range(len(data)):
if cluster[index] in dictionary:
value = []
value = dictionary[cluster[index]]
value.append(data[index])
dictionary[cluster[index]] = value
else:
dictionary[cluster[index]]=data[index]
答案 11 :(得分:0)
使用Combine,您可以轻松地订阅发布者,了解AVPlayerItem 何时缓冲或不缓冲:
// Subscribe to this and update your `View` appropriately
@Published var isBuffering = false
private var observation: AnyCancellable?
observation = avPlayer?.currentItem?.publisher(for: \.isPlaybackBufferEmpty).sink(receiveValue: { [weak self] isBuffering in
self?.isBuffering = isBuffering
})