ios 如何检查我的 AVPlayer 是否正在缓冲?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38867190/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I check if my AVPlayer is buffering?
提问by vrwim
I want to detect if my AVPlayer is buffering for the current location, so that I can show a loader or something. But I can't seem to find anything in the documentation for AVPlayer.
我想检测我的 AVPlayer 是否正在缓冲当前位置,以便我可以显示加载程序或其他内容。但我似乎无法在 AVPlayer 的文档中找到任何内容。
回答by Marco Santarossa
You can observe the values of your player.currentItem
:
你可以观察你的值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)
then
然后
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
}
}
}
回答by aytek
The accepted answer didn't work for me, I used the code below to show the loader efficiently.
接受的答案对我不起作用,我使用下面的代码来有效地显示加载器。
Swift 3
斯威夫特 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.
}
}
}
回答by Mohit Kumar
For me above accepted answer didn't worked but this method does.You can use timeControlStatus but it is available only above iOS 10.
对我来说,上面接受的答案不起作用,但这种方法有效。您可以使用 timeControlStatus,但它仅适用于 iOS 10 以上。
According to apple's official documentation
根据苹果官方文档
A status that indicates whether playback is currently in progress, paused indefinitely, or suspended while waiting for appropriate network conditions
一种状态,指示播放当前是在进行中、无限期暂停还是在等待适当的网络条件时暂停
Add this observer to the player.
将此观察者添加到播放器。
player.addObserver(self, forKeyPath: “timeControlStatus”, options: [.old, .new], context: nil)
Then,Observe the changes in
然后,观察变化
func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
method.Use below code inside above method
方法。在上面的方法中使用下面的代码
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
}
}
}
}
}
This is tested on iOS 11 above with swift 4 and It is working.
这已在上面的 iOS 11 上使用 swift 4 进行了测试,并且正在运行。
回答by Amrit Tiwari
#Updated in Swift 4 and worked fine
#在 Swift 4 中更新并运行良好
As through i have gone with accepted answer but didn't work in swift 4for me so after certain research i have found this thinks from apple doc. There are two way to determine AVPlayer statesthat are,
正如我已经接受的答案一样,但没有在swift 4 中为我工作,所以经过某些研究后,我发现这是来自苹果文档的想法。有两种方法可以确定AVPlayer 状态,
- addPeriodicTimeObserverForInterval:queue:usingBlock: and
- addBoundaryTimeObserverForTimes:queue:usingBlock:
- addPeriodicTimeObserverForInterval:queue:usingBlock: 和
- addBoundaryTimeObserverForTimes:queue:usingBlock:
and using ways is like this
使用方法是这样的
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
}
}
}
And Don't forget to kill time observer to save from memory leak. method for killing instance, add this method according to your need but i have used it in viewWillDisappear method.
并且不要忘记杀死时间观察者以防止内存泄漏。杀死实例的方法,根据您的需要添加此方法,但我已在 viewWillDisappear 方法中使用它。
if let observer = self.observer{
self.avPlayer?.removeTimeObserver(observer)
observer = nil
}
回答by Roman Barzyczak
Swift 4 observations:
斯威夫特 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
}
}
Observers need to be removed after we are done observing.
完成观察后需要移除观察者。
To remove these three observers just set playbackBufferEmptyObserver
, playbackLikelyToKeepUpKeyPathObserver
and playbackBufferFullObserver
to nil
.
删除这三个观察者刚刚设置playbackBufferEmptyObserver
,playbackLikelyToKeepUpKeyPathObserver
并playbackBufferFullObserver
为nil
。
No need to remove them manually (this is specific for observe<Value>(_ keyPath:, options:, changeHandler:)
method.
无需手动删除它们(这是特定于observe<Value>(_ keyPath:, options:, changeHandler:)
方法的。
回答by xuzepei
Updated for Swift 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)
}
}
})
回答by SamB
Hmm, the accepted solution didn't work for me and the periodic observer solutions seem heavy handed.
嗯,公认的解决方案对我不起作用,定期观察者解决方案似乎很严厉。
Here's my suggestion, observer timeControlerStatus
on AVPlayer
.
这里是我的建议,观测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)
}
}
回答by Asis
Here is a simple method, that works with Swift 5.
这是一个简单的方法,适用于Swift 5。
This will add the loadingIndicator when your player is stalled
这将在您的播放器停止时添加 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
}
This will show loader Indicator when buffer is empty:
当缓冲区为空时,这将显示加载器指示器:
if let isPlayBackBufferEmpty = self.player?.currentItem?.isPlaybackBufferEmpty{
if isPlayBackBufferEmpty{
self.loadingIndicator.isHidden = false
self.playPauseButton.isHidden = true
}
}
This will hide the loader when player is ready to play:
当玩家准备好播放时,这将隐藏加载器:
if self.playerItem?.status == AVPlayerItem.Status.readyToPlay{
if let isPlaybackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp {
if isPlaybackLikelyToKeepUp{
self.loadingIndicator.isHidden = true
self.playPauseButton.isHidden = false
}
}
}
回答by mr5
Solution for Xamarin inspired by Marco's answer
受Marco 回答启发的 Xamarin 解决方案
// KVO registrations
private void Initialize()
{
playbackBufferEmptyObserver?.Dispose();
playbackBufferEmptyObserver = (NSObject)playerItem.AddObserver("playbackBufferEmpty",
NSKeyValueObservingOptions.New,
AVPlayerItem_BufferUpdated);
playbackLikelyToKeepUpObserver?.Dispose();
playbackLikelyToKeepUpObserver = (NSObject)playerItem.AddObserver("playbackLikelyToKeepUp",
NSKeyValueObservingOptions.New,
AVPlayerItem_BufferUpdated);
playbackBufferFullObserver?.Dispose();
playbackBufferFullObserver = (NSObject)playerItem.AddObserver("playbackBufferFull",
NSKeyValueObservingOptions.New,
AVPlayerItem_BufferUpdated);
}
private void AVPlayerItem_BufferUpdated(NSObservedChange e)
{
ReportVideoBuffering();
}
private void ReportVideoBuffering()
{
// currentPlayerItem is the current AVPlayerItem of AVPlayer
var isBuffering = !currentPlayerItem.PlaybackLikelyToKeepUp;
// NOTE don't make "buffering" as one of your PlayerState.
// Treat it as a separate property instead. Learned this the hard way.
Buffering?.Invoke(this, new BufferingEventArgs(isBuffering));
}
回答by Badr
Please note that
请注意
Use a weak reference to self in the callback block to prevent creating a retain cycle.
在回调块中使用对 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()
}
}
})
}
}
}